Merge m-c to fx-team

This commit is contained in:
Panos Astithas 2012-12-21 10:28:48 +02:00
commit 867d635d7f
52 changed files with 900 additions and 240 deletions

View File

@ -1035,7 +1035,6 @@ pref("devtools.debugger.remote-connection-retries", 3);
pref("devtools.debugger.remote-timeout", 20000);
// The default Debugger UI settings
pref("devtools.debugger.ui.height", 250);
pref("devtools.debugger.ui.win-x", 0);
pref("devtools.debugger.ui.win-y", 0);
pref("devtools.debugger.ui.win-width", 900);

View File

@ -147,8 +147,9 @@
</menupopup>
</splitmenu>
<menuseparator class="appmenu-menuseparator"/>
<menu id="appmenu_webDeveloper"
label="&appMenuWebDeveloper.label;">
<splitmenu id="appmenu_webDeveloper"
command="Tools:DevToolbox"
label="&appMenuWebDeveloper.label;">
<menupopup id="appmenu_webDeveloper_popup">
<menuitem id="appmenu_devToolbox"
observes="devtoolsMenuBroadcaster_DevToolbox"/>
@ -165,7 +166,6 @@
observes="devtoolsMenuBroadcaster_PageSource"/>
<menuitem id="appmenu_errorConsole"
observes="devtoolsMenuBroadcaster_ErrorConsole"/>
<menuseparator id="appmenu_devToolsConnectSeparator"/>
<menuitem id="appmenu_devtools_connect"
observes="devtoolsMenuBroadcaster_connect"/>
<menuseparator id="appmenu_devToolsEndSeparator"/>
@ -182,7 +182,7 @@
observes="workOfflineMenuitemState"
oncommand="BrowserOffline.toggleOfflineStatus();"/>
</menupopup>
</menu>
</splitmenu>
<menuseparator class="appmenu-menuseparator"/>
#define ID_PREFIX appmenu_
#define OMIT_ACCESSKEYS

View File

@ -584,7 +584,6 @@
<menuitem id="javascriptConsole"
observes="devtoolsMenuBroadcaster_ErrorConsole"
accesskey="&errorConsoleCmd.accesskey;"/>
<menuseparator id="menu_devToolsConnectSeparator"/>
<menuitem id="menu_devtools_connect"
observes="devtoolsMenuBroadcaster_connect"/>
<menuseparator id="devToolsEndSeparator"/>

View File

@ -97,7 +97,7 @@
<command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
<command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()" disabled="true" hidden="true"/>
<command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)"/>
<command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)" disabled="true" hidden="true"/>
<command id="Tools:Sanitize"
oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
<command id="Tools:PrivateBrowsing"

View File

@ -1444,6 +1444,14 @@ var gBrowserInit = {
cmd.removeAttribute("hidden");
}
// Enable DevTools connection screen, if the preference allows this.
let devtoolsRemoteEnabled = gPrefService.getBoolPref("devtools.debugger.remote-enabled");
if (devtoolsRemoteEnabled) {
let cmd = document.getElementById("Tools:DevToolsConnect");
cmd.removeAttribute("disabled");
cmd.removeAttribute("hidden");
}
#ifdef MENUBAR_CAN_AUTOHIDE
// If the user (or the locale) hasn't enabled the top-level "Character
// Encoding" menu via the "browser.menu.showCharacterEncoding" preference,

View File

@ -173,12 +173,6 @@ DebuggerUI.prototype = {
return '_chromeDebugger' in this ? this._chromeDebugger : null;
},
/**
* Get the preferences associated with the debugger frontend.
* @return object
*/
get preferences() Prefs,
/**
* Currently, there can only be one debugger per tab.
* Show an asynchronous notification which asks the user to switch the
@ -279,7 +273,6 @@ DebuggerPane.prototype = {
this._splitter.setAttribute("class", "devtools-horizontal-splitter");
this._frame = ownerDocument.createElement("iframe");
this._frame.height = Prefs.height;
this._nbox = gBrowser.getNotificationBox(this._tab.linkedBrowser);
this._nbox.appendChild(this._splitter);
@ -326,7 +319,6 @@ DebuggerPane.prototype = {
}, true)
}
Prefs.height = this._frame.height;
this._frame.removeEventListener("Debugger:Unloaded", this.close, true);
this._nbox.removeChild(this._splitter);
this._nbox.removeChild(this._frame);
@ -599,21 +591,7 @@ XPCOMUtils.defineLazyGetter(L10N, "stringBundle", function() {
/**
* Shortcuts for accessing various debugger preferences.
*/
let Prefs = {
/**
* Gets the preferred height of the debugger pane.
* @return number
*/
get height()
Services.prefs.getIntPref("devtools.debugger.ui.height"),
/**
* Sets the preferred height of the debugger pane.
* @param number aValue
*/
set height(aValue)
Services.prefs.setIntPref("devtools.debugger.ui.height", aValue)
};
let Prefs = {};
/**
* Gets the preferred default remote debugging host.

View File

@ -11,6 +11,7 @@ const Cu = Components.utils;
const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
const NEW_SCRIPT_DISPLAY_DELAY = 200; // ms
const FETCH_SOURCE_RESPONSE_DELAY = 50; // ms
const FRAME_STEP_CLEAR_DELAY = 100; // ms
const CALL_STACK_PAGE_SIZE = 25; // frames
@ -1166,6 +1167,7 @@ SourceScripts.prototype = {
DebuggerView.Sources.push(label, url, {
forced: aOptions.forced,
tooltip: url,
attachment: aSource
});
},
@ -1177,18 +1179,29 @@ SourceScripts.prototype = {
* The source object coming from the active thread.
* @param function aCallback
* Function called after the source text has been loaded.
* @param function aOnTimeout
* Function called when the source text takes too long to fetch.
*/
getText: function SS_getText(aSource, aCallback) {
getText: function SS_getText(aSource, aCallback, aOnTimeout) {
// If already loaded, return the source text immediately.
if (aSource.loaded) {
aCallback(aSource.url, aSource.text);
return;
}
// If the source text takes too long to fetch, invoke a timeout to
// avoid blocking any operations.
if (aOnTimeout) {
var fetchTimeout = window.setTimeout(aOnTimeout, FETCH_SOURCE_RESPONSE_DELAY);
}
// Get the source text from the active thread.
this.activeThread.source(aSource.source).source(function(aResponse) {
window.clearTimeout(fetchTimeout);
if (aResponse.error) {
Cu.reportError("Error loading " + aUrl);
Cu.reportError("Error loading " + aSource.url + "\n" + aResponse.error);
aCallback(aSource.url, "");
return;
}
aSource.loaded = true;
@ -1609,7 +1622,6 @@ let Prefs = {
}
};
Prefs.map("Int", "height", "devtools.debugger.ui.height");
Prefs.map("Int", "windowX", "devtools.debugger.ui.win-x");
Prefs.map("Int", "windowY", "devtools.debugger.ui.win-y");
Prefs.map("Int", "windowWidth", "devtools.debugger.ui.win-width");

View File

@ -1260,6 +1260,7 @@ function GlobalSearchView() {
MenuContainer.call(this);
this._startSearch = this._startSearch.bind(this);
this._onFetchSourceFinished = this._onFetchSourceFinished.bind(this);
this._onFetchSourceTimeout = this._onFetchSourceTimeout.bind(this);
this._onFetchSourcesFinished = this._onFetchSourcesFinished.bind(this);
this._createItemView = this._createItemView.bind(this);
this._onScroll = this._onScroll.bind(this);
@ -1402,25 +1403,29 @@ create({ constructor: GlobalSearchView, proto: MenuContainer.prototype }, {
this._sourcesCount = locations.length;
this._searchedToken = aQuery;
this._fetchSources(
this._onFetchSourceFinished,
this._onFetchSourcesFinished, locations);
this._fetchSources(locations, {
onFetch: this._onFetchSourceFinished,
onTimeout: this._onFetchSourceTimeout,
onFinished: this._onFetchSourcesFinished
});
},
/**
* Starts fetching all the sources, silently.
*
* @param function aFetchCallback
* Called after each source is fetched.
* @param function aFetchedCallback
* Called if all the sources were already fetched.
* @param array aLocations
* The locations for the sources to fetch.
* @param object aCallbacks
* An object containing the callback functions to invoke:
* - onFetch: called after each source is fetched
* - onTimeout: called when a source's text takes too long to fetch
* - onFinished: called if all the sources were already fetched
*/
_fetchSources: function DVGS__fetchSources(aFetchCallback, aFetchedCallback, aLocations) {
_fetchSources:
function DVGS__fetchSources(aLocations, { onFetch, onTimeout, onFinished }) {
// If all the sources were already fetched, then don't do anything.
if (this._cache.size == aLocations.length) {
aFetchedCallback();
onFinished();
return;
}
@ -1430,7 +1435,8 @@ create({ constructor: GlobalSearchView, proto: MenuContainer.prototype }, {
continue;
}
let sourceItem = DebuggerView.Sources.getItemByValue(location);
DebuggerController.SourceScripts.getText(sourceItem.attachment, aFetchCallback);
let sourceObject = sourceItem.attachment;
DebuggerController.SourceScripts.getText(sourceObject, onFetch, onTimeout);
}
},
@ -1452,10 +1458,24 @@ create({ constructor: GlobalSearchView, proto: MenuContainer.prototype }, {
}
},
/**
* Called when a source's text takes too long to fetch.
*/
_onFetchSourceTimeout: function DVGS__onFetchSourceTimeout() {
// Remove the source from the load queue.
this._sourcesCount--;
// Check if the remaining sources were fetched and stored in the cache.
if (this._cache.size == this._sourcesCount) {
this._onFetchSourcesFinished();
}
},
/**
* Called when all the sources have been fetched.
*/
_onFetchSourcesFinished: function DVGS__onFetchSourcesFinished() {
// At least one source needs to be present to perform a global search.
if (!this._sourcesCount) {
return;
}

View File

@ -11,7 +11,6 @@
*/
function ToolbarView() {
dumpn("ToolbarView was instantiated");
this._onCloseClick = this._onCloseClick.bind(this);
this._onTogglePanesPressed = this._onTogglePanesPressed.bind(this);
this._onResumePressed = this._onResumePressed.bind(this);
this._onStepOverPressed = this._onStepOverPressed.bind(this);
@ -108,13 +107,6 @@ ToolbarView.prototype = {
this._sources.setAttribute("hidden", !aVisibleFlag);
},
/**
* Listener handling the close button click event.
*/
_onCloseClick: function DVT__onCloseClick() {
DebuggerController._shutdownDebugger();
},
/**
* Listener handling the toggle button click event.
*/

View File

@ -618,6 +618,7 @@ MenuContainer.prototype = {
* - unsorted: true if the items should not always remain sorted
* - relaxed: true if this container should allow dupes & degenerates
* - description: an optional description of the item
* - tooltip: an optional tooltip for the item
* - attachment: some attached primitive/object
* @return MenuItem
* The item associated with the displayed element if a forced push,
@ -629,7 +630,7 @@ MenuContainer.prototype = {
// Batch the item to be added later.
if (!aOptions.forced) {
this._stagedItems.push(item);
this._stagedItems.push({ item: item, options: aOptions });
}
// Immediately insert the item at the specified index.
else if (aOptions.forced && aOptions.forced.atIndex !== undefined) {
@ -657,11 +658,12 @@ MenuContainer.prototype = {
// By default, sort the items before adding them to this container.
if (!aOptions.unsorted) {
stagedItems.sort(function(a, b) a.label.toLowerCase() > b.label.toLowerCase());
stagedItems.sort(function(a, b) a.item.label.toLowerCase() >
b.item.label.toLowerCase());
}
// Append the prepared items to this container.
for (let item of stagedItems) {
this._appendItem(item, aOptions);
for (let { item, options } of stagedItems) {
this._appendItem(item, options);
}
// Recreate the temporary items list for ulterior pushes.
this._stagedItems = [];
@ -750,7 +752,7 @@ MenuContainer.prototype = {
*/
containsLabel: function DVMC_containsLabel(aLabel) {
return this._itemsByLabel.has(aLabel) ||
this._stagedItems.some(function(o) o.label == aLabel);
this._stagedItems.some(function({item}) item.label == aLabel);
},
/**
@ -764,7 +766,7 @@ MenuContainer.prototype = {
*/
containsValue: function DVMC_containsValue(aValue) {
return this._itemsByValue.has(aValue) ||
this._stagedItems.some(function(o) o.value == aValue);
this._stagedItems.some(function({item}) item.value == aValue);
},
/**
@ -788,7 +790,7 @@ MenuContainer.prototype = {
return true;
}
}
return this._stagedItems.some(function(o) aTrim(o.value) == trimmedValue);
return this._stagedItems.some(function({item}) aTrim(item.value) == trimmedValue);
},
/**
@ -1046,8 +1048,15 @@ MenuContainer.prototype = {
return null;
}
return this._entangleItem(aItem, this._container.appendItem(
this._entangleItem(aItem, this._container.appendItem(
aItem.label, aItem.value, "", aOptions.attachment));
// Handle any additional options after entangling the item.
if (aOptions.tooltip) {
aItem._target.setAttribute("tooltiptext", aOptions.tooltip);
}
return aItem;
},
/**
@ -1068,8 +1077,15 @@ MenuContainer.prototype = {
return null;
}
return this._entangleItem(aItem, this._container.insertItemAt(
this._entangleItem(aItem, this._container.insertItemAt(
aIndex, aItem.label, aItem.value, "", aOptions.attachment));
// Handle any additional options after entangling the item.
if (aOptions.tooltip) {
aItem._target.setAttribute("tooltiptext", aOptions.tooltip);
}
return aItem;
},
/**

View File

@ -114,6 +114,16 @@ ToolSidebar.prototype = {
return currentID;
},
/**
* Returns the requested tab based on the id.
*
* @param String id
* unique id of the requested tab.
*/
getTab: function ToolSidebar_getTab(id) {
return this._tabbox.tabpanels.querySelector("#sidebar-panel-" + id);
},
/**
* Event handler.
*/
@ -176,7 +186,7 @@ ToolSidebar.prototype = {
}
this._destroyed = true;
this._tabbox.removeEventListener("select", this, true);
this._tabbox.tabpanels.removeEventListener("select", this, true);
while (this._tabbox.tabpanels.hasChildNodes()) {
this._tabbox.tabpanels.removeChild(this._tabbox.tabpanels.firstChild);

View File

@ -295,9 +295,10 @@ TabWebProgressListener.prototype = {
onSecurityChange: function() {},
onStatusChange: function() {},
onLocationChange: function TwPL_onLocationChange(webProgress) {
let window = webProgress.DOMWindow;
if (this.target) {
onLocationChange: function TwPL_onLocationChange(webProgress, request, URI, flags) {
if (this.target &&
!(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
let window = webProgress.DOMWindow;
this.target.emit("navigate", window);
}
},

View File

@ -69,6 +69,8 @@ let webConsoleDefinition = {
icon: "chrome://browser/skin/devtools/webconsole-tool-icon.png",
url: "chrome://browser/content/devtools/webconsole.xul",
label: l10n("ToolboxWebconsole.label", webConsoleStrings),
tooltip: l10n("ToolboxWebconsole.tooltip", webConsoleStrings),
isTargetSupported: function(target) {
return true;
},
@ -88,6 +90,7 @@ let debuggerDefinition = {
icon: "chrome://browser/skin/devtools/tools-icons-small.png",
url: "chrome://browser/content/debugger.xul",
label: l10n("ToolboxDebugger.label", debuggerStrings),
tooltip: l10n("ToolboxDebugger.tooltip", debuggerStrings),
isTargetSupported: function(target) {
return true;
@ -108,6 +111,7 @@ let inspectorDefinition = {
icon: "chrome://browser/skin/devtools/tools-icons-small.png",
url: "chrome://browser/content/devtools/inspector/inspector.xul",
label: l10n("inspector.label", inspectorStrings),
tooltip: l10n("inspector.tooltip", inspectorStrings),
isTargetSupported: function(target) {
return !target.isRemote;
@ -127,6 +131,7 @@ let styleEditorDefinition = {
modifiers: "shift",
label: l10n("ToolboxStyleEditor.label", styleEditorStrings),
url: "chrome://browser/content/styleeditor.xul",
tooltip: l10n("ToolboxStyleEditor.tooltip", styleEditorStrings),
isTargetSupported: function(target) {
return !target.isRemote && !target.isChrome;
@ -144,6 +149,7 @@ let profilerDefinition = {
icon: "chrome://browser/skin/devtools/tools-icons-small.png",
url: "chrome://browser/content/profiler.xul",
label: l10n("profiler.label", profilerStrings),
tooltip: l10n("profiler.tooltip", profilerStrings),
isTargetSupported: function (target) {
if (target.isRemote || target.isChrome) {

View File

@ -16,6 +16,17 @@ XPCOMUtils.defineLazyModuleGetter(this, "Hosts",
"resource:///modules/devtools/ToolboxHosts.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CommandUtils",
"resource:///modules/devtools/DeveloperToolbar.jsm");
XPCOMUtils.defineLazyGetter(this, "toolboxStrings", function() {
let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
let l10n = function(name) {
try {
return bundle.GetStringFromName(name);
} catch (ex) {
Services.console.logStringMessage("Error reading '" + name + "'");
}
};
return l10n;
});
// DO NOT put Require.jsm or gcli.jsm into lazy getters as this breaks the
// requisition import a few lines down.
@ -285,6 +296,8 @@ Toolbox.prototype = {
let button = this.doc.createElement("toolbarbutton");
button.id = "toolbox-dock-" + position;
button.className = "toolbox-dock-button";
button.setAttribute("tooltiptext", toolboxStrings("toolboxDockButtons." +
position + ".tooltip"));
button.addEventListener("command", function(position) {
this.switchHost(position);
}.bind(this, position));
@ -348,6 +361,7 @@ Toolbox.prototype = {
radio.className = "toolbox-tab devtools-tab";
radio.id = "toolbox-tab-" + id;
radio.setAttribute("toolid", id);
radio.setAttribute("tooltiptext", toolDefinition.tooltip);
let ordinal = (typeof toolDefinition.ordinal == "number") ?
toolDefinition.ordinal : MAX_ORDINAL;

View File

@ -1,14 +1,25 @@
html {
background: url("chrome://browser/skin/newtab/noise.png");
background-color: #111;
background-image: url("chrome://browser/skin/newtab/noise.png");
}
body {
font-family: Arial;
padding: 20px;
border-radius: 3px;
font-family: Arial, sans-serif;
color: white;
max-width: 600px;
min-height: 400px;
margin: 10px auto 0;
margin: 30px auto 0;
box-shadow: 0 2px 3px black;
background-color: #3C3E40;
}
h1 {
margin: 0;
padding: 20px;
background-color: rgba(0,0,0,0.12);
background-image: radial-gradient(ellipse farthest-corner at center top , rgb(159, 223, 255), rgba(101, 203, 255, 0.3)), radial-gradient(ellipse farthest-side at center top , rgba(101, 203, 255, 0.4), rgba(101, 203, 255, 0));
background-size: 100% 2px, 100% 5px;
background-repeat: no-repeat;
border-bottom: 1px solid rgba(0,0,0,0.1);
}
label {
@ -29,18 +40,21 @@ label > span {
margin-left: 160px;
}
#actors, #connection-form {
margin: 20px;
input:invalid {
box-shadow: 0 0 2px 2px #F06;
}
input {
border: 1px solid grey;
section {
min-height: 160px;
margin: 60px 20px;
display: none; /* By default, hidden */
}
#connection-form,
#connecting,
#actors-list {
.error-message {
color: red;
}
.error-message:not(.active) {
display: none;
}
@ -60,20 +74,9 @@ body.connecting > #connecting {
text-align: center;
}
#throbber {
height: 7px; width: 7px;
border-radius: 50%;
background: black;
display: inline-block;
animation-duration: 0.6s;
animation-name: anim;
animation-direction: alternate;
animation-iteration-count: infinite;
animation-timing-function: linear;
#connecting > p > img {
vertical-align: top;
}
@keyframes anim {to {
transform: scale(0.5) rotate(0.1deg);
}}
#actors {
padding-left: 0;
@ -84,4 +87,22 @@ body.connecting > #connecting {
display: block;
margin: 5px;
padding: 5px;
color: white;
}
.remote-process {
font-style: italic;
opacity: 0.8;
}
footer {
padding: 10px;
background-color: rgba(0,0,0,0.12);
border-top: 1px solid rgba(0,0,0,0.1);
font-size: small;
}
footer > a,
footer > a:visited {
color: white;
}

View File

@ -10,91 +10,158 @@ const Cu = Components.utils;
Cu.import("resource:///modules/devtools/Target.jsm");
Cu.import("resource:///modules/devtools/Toolbox.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm");
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
let gClient;
let gConnectionTimeout;
XPCOMUtils.defineLazyGetter(window, 'l10n', function () {
return Services.strings.createBundle('chrome://browser/locale/devtools/connection-screen.properties');
});
/**
* Once DOM is ready, we prefil the host/port inputs with
* pref-stored values.
*/
window.addEventListener("DOMContentLoaded", function onDOMReady() {
window.removeEventListener("DOMContentLoaded", onDOMReady, true);
let host = Services.prefs.getCharPref("devtools.debugger.remote-host");
let port = Services.prefs.getIntPref("devtools.debugger.remote-port");
if (host) {
document.getElementById("host").value = host;
}
if (port) {
document.getElementById("port").value = port;
}
}, true);
/**
* Called when the "connect" button is clicked.
*/
function submit() {
// Show the "connecting" screen
document.body.classList.add("connecting");
// Save the host/port values
let host = document.getElementById("host").value;
Services.prefs.setCharPref("devtools.debugger.remote-host", host);
let port = document.getElementById("port").value;
if (!host) {
host = Services.prefs.getCharPref("devtools.debugger.remote-host");
} else {
Services.prefs.setCharPref("devtools.debugger.remote-host", host);
}
if (!port) {
port = Services.prefs.getIntPref("devtools.debugger.remote-port");
} else {
Services.prefs.setIntPref("devtools.debugger.remote-port", port);
}
Services.prefs.setIntPref("devtools.debugger.remote-port", port);
// Initiate the connection
let transport = debuggerSocketConnect(host, port);
let client = gClient = new DebuggerClient(transport);
gClient = new DebuggerClient(transport);
let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout");
gConnectionTimeout = setTimeout(handleConnectionTimeout, delay);
gClient.connect(onConnectionReady);
}
client.connect(function(aType, aTraits) {
client.listTabs(function(aResponse) {
document.body.classList.remove("connecting");
document.body.classList.add("actors-mode");
/**
* Connection is ready. List actors and build buttons.
*/
function onConnectionReady(aType, aTraits) {
clearTimeout(gConnectionTimeout);
gClient.listTabs(function(aResponse) {
document.body.classList.remove("connecting");
document.body.classList.add("actors-mode");
let parent = document.getElementById("actors");
let focusSet = false;
let parent = document.getElementById("actors");
// Add Global Process debugging...
let globals = JSON.parse(JSON.stringify(aResponse));
delete globals.tabs;
delete globals.selected;
// ...only if there are appropriate actors (a 'from' property will always
// be there).
if (Object.keys(globals).length > 1) {
let a = document.createElement("a");
a.onclick = function() {
connect(globals, true);
}
// Add Global Process debugging...
let globals = JSON.parse(JSON.stringify(aResponse));
delete globals.tabs;
delete globals.selected;
// ...only if there are appropriate actors (a 'from' property will always
// be there).
a.title = a.textContent = "Remote process";
a.href = "#";
// Add one entry for each open tab.
for (let i = 0; i < aResponse.tabs.length; i++) {
buildLink(aResponse.tabs[i], parent, i == aResponse.selected);
}
// Build the Remote Process button
if (Object.keys(globals).length > 1) {
let a = document.createElement("a");
a.onclick = function() {
openToolbox(globals, true);
parent.appendChild(a);
}
a.title = a.textContent = window.l10n.GetStringFromName("remoteProcess");
a.className = "remote-process";
a.href = "#";
parent.appendChild(a);
}
// Move the selected tab on top
let selectedLink = parent.querySelector("a.selected");
if (selectedLink) {
parent.insertBefore(selectedLink, parent.firstChild);
}
// Add one entry for each open tab.
if (aResponse.tabs.length > 0) {
let header = document.createElement("div");
header.innerHTML = "Tabs:";
parent.appendChild(header);
}
for (let i = 0; i < aResponse.tabs.length; i++) {
let tab = aResponse.tabs[i];
// Ensure the first link is focused
let firstLink = parent.querySelector("a:first-of-type");
if (firstLink) {
firstLink.focus();
}
let a = document.createElement("a");
a.onclick = function() {
connect(tab);
}
a.title = a.textContent = tab.title;
a.href = "#";
if (i == aResponse.selected) {
a.title += " [*]";
a.textContent = a.title;
}
parent.appendChild(a);
if (!focusSet) {
a.focus();
focusSet = true;
}
}
});
});
}
function connect(form, chrome=false) {
/**
* Build one button for an actor.
*/
function buildLink(tab, parent, selected) {
let a = document.createElement("a");
a.onclick = function() {
openToolbox(tab);
}
a.textContent = tab.title;
a.title = tab.url;
if (!a.textContent) {
a.textContent = tab.url;
}
a.href = "#";
if (selected) {
a.classList.add("selected");
}
parent.appendChild(a);
}
/**
* An error occured. Let's show it and return to the first screen.
*/
function showError(type) {
document.body.className = "error";
let activeError = document.querySelector(".error-message.active");
if (activeError) {
activeError.classList.remove("active");
}
activeError = document.querySelector(".error-" + type);
if (activeError) {
activeError.classList.add("active");
}
}
/**
* Connection timeout.
*/
function handleConnectionTimeout() {
showError("timeout");
}
/**
* The user clicked on one of the buttons.
* Opens the toolbox.
*/
function openToolbox(form, chrome=false) {
let target = TargetFactory.forRemote(form, gClient, chrome);
gDevTools.showToolbox(target, "webconsole", Toolbox.HostType.WINDOW);
window.close();
}

View File

@ -12,34 +12,37 @@
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head>
<title>&title;</title>
<link rel="stylesheet" href="chrome://browser/skin/devtools/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/content/devtools/connect.css" type="text/css"/>
<script type="application/javascript;version=1.8" src="connect.js"></script>
</head>
<body>
<p>
</p>
<h1>&header;</h1>
<section id="connection-form">
<form onsubmit="window.submit()" action="#">
<form validate="validate" onsubmit="window.submit()" action="#">
<label>
<span>&host;</span>
<input id="host" type="text" placeholder="localhost"></input>
<input required="required" class="devtools-textinput" id="host" type="text"></input>
</label>
<label>
<span>&port;</span>
<input id="port" type="number" placeholder="6000"></input>
<input required="required" class="devtools-textinput" id="port" type="number" pattern="\d+"></input>
</label>
<label>
<input id="submit" type="submit" value="&connect;"></input>
<input class="devtools-toolbarbutton" id="submit" type="submit" value="&connect;"></input>
</label>
</form>
<p class="error-message error-timeout">&errorTimeout;</p>
<p class="error-message error-refused">&errorRefused;</p>
<p class="error-message error-unexpected">&errorUnexpected;</p>
</section>
<section id="actors-list">
<p>&availability;</p>
<ul id="actors"></ul>
</section>
<section id="connecting">
<p>&connecting;</p>
<div id="throbber"></div>
<p><img src="chrome://browser/skin/tabbrowser/loading.png"></img> &connecting;</p>
</section>
<footer>&help;</footer>
</body>
</html>

View File

@ -2,6 +2,10 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE window [
<!ENTITY % toolboxDTD SYSTEM "chrome://browser/locale/devtools/toolbox.dtd" >
%toolboxDTD;
]>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/devtools/shared/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/devtools/framework/toolbox.css" type="text/css"?>
@ -15,7 +19,9 @@
<toolbar class="devtools-tabbar">
#ifdef XP_MACOSX
<hbox id="toolbox-controls">
<toolbarbutton id="toolbox-close" class="devtools-closebutton"></toolbarbutton>
<toolbarbutton id="toolbox-close"
class="devtools-closebutton"
tooltiptext="&toolboxCloseButton.tooltip;"/>
<hbox id="toolbox-dock-buttons"/>
</hbox>
#endif
@ -25,7 +31,9 @@
#ifndef XP_MACOSX
<hbox id="toolbox-controls">
<hbox id="toolbox-dock-buttons"/>
<toolbarbutton id="toolbox-close" class="devtools-closebutton"></toolbarbutton>
<toolbarbutton id="toolbox-close"
class="devtools-closebutton"
tooltiptext="&toolboxCloseButton.tooltip;"/>
</hbox>
#endif
</toolbar>

View File

@ -83,7 +83,6 @@ HTMLBreadcrumbs.prototype = {
this.update = this.update.bind(this);
this.updateSelectors = this.updateSelectors.bind(this);
this.selection.on("new-node", this.update);
this.selection.on("detached", this.update);
this.selection.on("pseudoclass", this.updateSelectors);
this.selection.on("attribute-changed", this.updateSelectors);
this.update();
@ -296,7 +295,6 @@ HTMLBreadcrumbs.prototype = {
});
this.selection.off("new-node", this.update);
this.selection.off("detached", this.update);
this.selection.off("pseudoclass", this.updateSelectors);
this.selection.off("attribute-changed", this.updateSelectors);

View File

@ -133,7 +133,6 @@ Highlighter.prototype = {
this.selection.on("new-node", this.highlight);
this.selection.on("new-node", this.updateInfobar);
this.selection.on("detached", this.highlight);
this.selection.on("pseudoclass", this.updateInfobar);
this.selection.on("attribute-changed", this.updateInfobar);
@ -168,7 +167,6 @@ Highlighter.prototype = {
this.selection.off("new-node", this.highlight);
this.selection.off("new-node", this.updateInfobar);
this.selection.off("detached", this.highlight);
this.selection.off("pseudoclass", this.updateInfobar);
this.selection.off("attribute-changed", this.updateInfobar);

View File

@ -68,6 +68,8 @@ InspectorPanel.prototype = {
this._selection = new Selection();
this.onNewSelection = this.onNewSelection.bind(this);
this.selection.on("new-node", this.onNewSelection);
this.onDetached = this.onDetached.bind(this);
this.selection.on("detached", this.onDetached);
this.breadcrumbs = new HTMLBreadcrumbs(this);
@ -170,6 +172,7 @@ InspectorPanel.prototype = {
}.bind(this);
this.sidebar.on("select", this._setDefaultSidebar);
this.toggleHighlighter = this.toggleHighlighter.bind(this);
this.sidebar.addTab("ruleview",
"chrome://browser/content/devtools/cssruleview.xul",
@ -183,6 +186,10 @@ InspectorPanel.prototype = {
"chrome://browser/content/devtools/layoutview/view.xhtml",
"layoutview" == defaultTab);
let ruleViewTab = this.sidebar.getTab("ruleview");
ruleViewTab.addEventListener("mouseover", this.toggleHighlighter, false);
ruleViewTab.addEventListener("mouseout", this.toggleHighlighter, false);
this.sidebar.show();
},
@ -275,6 +282,15 @@ InspectorPanel.prototype = {
this.cancelLayoutChange();
},
/**
* When a node is deleted, select its parent node.
*/
onDetached: function InspectorPanel_onDetached(event, parentNode) {
this.cancelLayoutChange();
this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
this.selection.setNode(parentNode, "detached");
},
/**
* Destroy the inspector.
*/
@ -310,6 +326,7 @@ InspectorPanel.prototype = {
this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
this.breadcrumbs.destroy();
this.selection.off("new-node", this.onNewSelection);
this.selection.off("detached", this.onDetached);
this._destroyMarkup();
this._selection.destroy();
this._selection = null;
@ -447,6 +464,19 @@ InspectorPanel.prototype = {
this.selection.emit("pseudoclass");
},
/**
* Toggle the highlighter when ruleview is hovered.
*/
toggleHighlighter: function InspectorPanel_toggleHighlighter(event)
{
if (event.type == "mouseover") {
this.highlighter.hide();
}
else if (event.type == "mouseout") {
this.highlighter.show();
}
},
/**
* Copy the innerHTML of the selected Node to the clipboard.
*/

View File

@ -70,12 +70,14 @@ Selection.prototype = {
_onMutations: function(mutations) {
let attributeChange = false;
let detached = false;
let parentNode = null;
for (let m of mutations) {
if (!attributeChange && m.type == "attributes") {
attributeChange = true;
}
if (m.type == "childList") {
if (!detached && !this.isConnected()) {
parentNode = m.target;
detached = true;
}
}
@ -84,7 +86,7 @@ Selection.prototype = {
if (attributeChange)
this.emit("attribute-changed");
if (detached)
this.emit("detached");
this.emit("detached", parentNode);
},
_attachEvents: function SN__attachEvents() {

View File

@ -35,6 +35,7 @@ _BROWSER_FILES = \
browser_inspector_highlighter_autohide.js \
browser_inspector_changes.js \
browser_inspector_bug_674871.js \
browser_inspector_bug_817558_delete_node.js \
head.js \
helpers.js \
$(NULL)

View File

@ -104,7 +104,7 @@ function test() {
is(para.textContent, "test2", "paragraph content is correct");
let root = content.document.documentElement;
ok(inspector.selection.node, root, "Selection is the root of the new page.");
is(inspector.selection.node, root, "Selection is the root of the new page.");
ok(alertActive1_called, "first notification box has been showed");
ok(alertActive2_called, "second notification box has been showed");

View File

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test()
{
waitForExplicitFinish();
//ignoreAllUncaughtExceptions();
let node, iframe, inspector;
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
waitForFocus(setupTest, content);
}, true);
content.location = "http://mochi.test:8888/browser/browser/devtools/inspector/test/browser_inspector_destroyselection.html";
function setupTest()
{
iframe = content.document.querySelector("iframe");
node = iframe.contentDocument.querySelector("span");
openInspector(runTests);
}
function runTests(aInspector)
{
inspector = aInspector;
inspector.selection.setNode(node);
let parentNode = node.parentNode;
parentNode.removeChild(node);
let tmp = {};
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
executeSoon(function() {
is(inspector.selection.node, parentNode, "parent of selection got selected");
finishUp();
});
}
function finishUp() {
node = null;
gBrowser.removeCurrentTab();
finish();
}
}

View File

@ -35,7 +35,6 @@ LayoutView.prototype = {
this.update = this.update.bind(this);
this.onNewNode = this.onNewNode.bind(this);
this.onHighlighterLocked = this.onHighlighterLocked.bind(this);
this.inspector.selection.on("detached", this.onNewNode);
this.inspector.selection.on("new-node", this.onNewNode);
this.inspector.sidebar.on("layoutview-selected", this.onNewNode);
if (this.inspector.highlighter) {
@ -101,7 +100,6 @@ LayoutView.prototype = {
destroy: function LV_destroy() {
this.inspector.sidebar.off("layoutview-selected", this.onNewNode);
this.inspector.selection.off("new-node", this.onNewNode);
this.inspector.selection.off("detached", this.onNewNode);
if (this.browser) {
this.browser.removeEventListener("MozAfterPaint", this.update, true);
}

View File

@ -12,6 +12,7 @@ const Ci = Components.interfaces;
const PAGE_SIZE = 10;
const PREVIEW_AREA = 700;
const DEFAULT_MAX_CHILDREN = 100;
this.EXPORTED_SYMBOLS = ["MarkupView"];
@ -20,6 +21,7 @@ Cu.import("resource:///modules/devtools/CssRuleView.jsm");
Cu.import("resource:///modules/devtools/Templater.jsm");
Cu.import("resource:///modules/devtools/Undo.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/**
* Vocabulary for the purposes of this file:
@ -46,6 +48,12 @@ this.MarkupView = function MarkupView(aInspector, aFrame, aControllerWindow)
this.doc = this._frame.contentDocument;
this._elt = this.doc.querySelector("#root");
try {
this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
} catch(ex) {
this.maxChildren = DEFAULT_MAX_CHILDREN;
}
this.undo = new UndoStack();
this.undo.installController(aControllerWindow);
@ -69,7 +77,7 @@ this.MarkupView = function MarkupView(aInspector, aFrame, aControllerWindow)
MarkupView.prototype = {
_selectedContainer: null,
template: function MT_template(aName, aDest, aOptions)
template: function MT_template(aName, aDest, aOptions={stack: "markup-view.xhtml"})
{
let node = this.doc.getElementById("template-" + aName).cloneNode(true);
node.removeAttribute("id");
@ -288,7 +296,6 @@ MarkupView.prototype = {
let walker = documentWalker(aNode);
let parent = walker.parentNode();
if (parent) {
// Make sure parents of this node are imported too.
var container = new MarkupContainer(this, aNode);
} else {
var container = new RootContainer(this, aNode);
@ -298,12 +305,15 @@ MarkupView.prototype = {
// Fake a childList mutation here.
this._mutationObserver([{target: aEvent.target, type: "childList"}]);
}.bind(this), true);
}
this._containers.set(aNode, container);
// FIXME: set an expando to prevent the the wrapper from disappearing
// See bug 819131 for details.
aNode.__preserveHack = true;
container.expanded = aExpand;
container.childrenDirty = true;
this._updateChildren(container);
if (parent) {
@ -327,6 +337,7 @@ MarkupView.prototype = {
if (mutation.type === "attributes" || mutation.type === "characterData") {
container.update();
} else if (mutation.type === "childList") {
container.childrenDirty = true;
this._updateChildren(container);
}
}
@ -339,10 +350,12 @@ MarkupView.prototype = {
*/
showNode: function MT_showNode(aNode, centered)
{
this.importNode(aNode);
let container = this.importNode(aNode);
this._updateChildren(container);
let walker = documentWalker(aNode);
let parent;
while (parent = walker.parentNode()) {
this._updateChildren(this.getContainer(parent));
this.expandNode(parent);
}
LayoutHelpers.scrollIntoViewIfNeeded(this._containers.get(aNode).editor.elt, centered);
@ -421,9 +434,32 @@ MarkupView.prototype = {
this._selectedContainer.selected = true;
}
this._ensureSelectionVisible();
return true;
},
/**
* Make sure that every ancestor of the selection are updated
* and included in the list of visible children.
*/
_ensureSelectionVisible: function MT_ensureSelectionVisible()
{
let node = this._selectedContainer.node;
let walker = documentWalker(node);
while (node) {
let container = this._containers.get(node);
let parent = walker.parentNode();
if (!container.elt.parentNode) {
let parentContainer = this._containers.get(parent);
parentContainer.childrenDirty = true;
this._updateChildren(parentContainer, node);
}
node = parent;
}
},
/**
* Unmark selected node (no node selected).
*/
@ -448,29 +484,139 @@ MarkupView.prototype = {
/**
* Make sure all children of the given container's node are
* imported and attached to the container in the right order.
* @param aCentered If provided, this child will be included
* in the visible subset, and will be roughly centered
* in that list.
*/
_updateChildren: function MT__updateChildren(aContainer)
_updateChildren: function MT__updateChildren(aContainer, aCentered)
{
if (!aContainer.childrenDirty) {
return false;
}
// Get a tree walker pointing at the first child of the node.
let treeWalker = documentWalker(aContainer.node);
let child = treeWalker.firstChild();
aContainer.hasChildren = !!child;
if (aContainer.expanded) {
let lastContainer = null;
while (child) {
let container = this.importNode(child, false);
// Make sure children are in the right order.
let before = lastContainer ? lastContainer.nextSibling : aContainer.children.firstChild;
aContainer.children.insertBefore(container.elt, before);
lastContainer = container.elt;
child = treeWalker.nextSibling();
if (!aContainer.expanded) {
return;
}
aContainer.childrenDirty = false;
let children = this._getVisibleChildren(aContainer, aCentered);
let fragment = this.doc.createDocumentFragment();
for (child of children.children) {
let container = this.importNode(child, false);
fragment.appendChild(container.elt);
}
while (aContainer.children.firstChild) {
aContainer.children.removeChild(aContainer.children.firstChild);
}
if (!(children.hasFirst && children.hasLast)) {
let data = {
showing: this.strings.GetStringFromName("markupView.more.showing"),
showAll: this.strings.formatStringFromName(
"markupView.more.showAll",
[aContainer.node.children.length.toString()], 1),
allButtonClick: function() {
aContainer.maxChildren = -1;
aContainer.childrenDirty = true;
this._updateChildren(aContainer);
}.bind(this)
};
if (!children.hasFirst) {
let span = this.template("more-nodes", data);
fragment.insertBefore(span, fragment.firstChild);
}
while (aContainer.children.lastChild != lastContainer) {
aContainer.children.removeChild(aContainer.children.lastChild);
if (!children.hasLast) {
let span = this.template("more-nodes", data);
fragment.appendChild(span);
}
}
aContainer.children.appendChild(fragment);
return true;
},
/**
* Return a list of the children to display for this container.
*/
_getVisibleChildren: function MV__getVisibleChildren(aContainer, aCentered)
{
let maxChildren = aContainer.maxChildren || this.maxChildren;
if (maxChildren == -1) {
maxChildren = Number.MAX_VALUE;
}
let firstChild = documentWalker(aContainer.node).firstChild();
let lastChild = documentWalker(aContainer.node).lastChild();
if (!firstChild) {
// No children, we're done.
return { hasFirst: true, hasLast: true, children: [] };
}
// By default try to put the selected child in the middle of the list.
let start = aCentered || firstChild;
// Start by reading backward from the starting point....
let nodes = [];
let backwardWalker = documentWalker(start);
if (backwardWalker.previousSibling()) {
let backwardCount = Math.floor(maxChildren / 2);
let backwardNodes = this._readBackward(backwardWalker, backwardCount);
nodes = backwardNodes;
}
// Then read forward by any slack left in the max children...
let forwardWalker = documentWalker(start);
let forwardCount = maxChildren - nodes.length;
nodes = nodes.concat(this._readForward(forwardWalker, forwardCount));
// If there's any room left, it means we've run all the way to the end.
// In that case, there might still be more items at the front.
let remaining = maxChildren - nodes.length;
if (remaining > 0 && nodes[0] != firstChild) {
let firstNodes = this._readBackward(backwardWalker, remaining);
// Then put it all back together.
nodes = firstNodes.concat(nodes);
}
return {
hasFirst: nodes[0] == firstChild,
hasLast: nodes[nodes.length - 1] == lastChild,
children: nodes
};
},
_readForward: function MV__readForward(aWalker, aCount)
{
let ret = [];
let node = aWalker.currentNode;
do {
ret.push(node);
node = aWalker.nextSibling();
} while (node && --aCount);
return ret;
},
_readBackward: function MV__readBackward(aWalker, aCount)
{
let ret = [];
let node = aWalker.currentNode;
do {
ret.push(node);
node = aWalker.previousSibling();
} while(node && --aCount);
ret.reverse();
return ret;
},
/**
@ -618,9 +764,7 @@ function MarkupContainer(aMarkupView, aNode)
this.expander = null;
this.codeBox = null;
this.children = null;
let options = { stack: "markup-view.xhtml" };
this.markup.template("container", this, options);
this.markup.template("container", this);
this.elt.container = this;
this.expander.addEventListener("click", function() {
@ -734,7 +878,7 @@ MarkupContainer.prototype = {
if (focusable) {
focusable.focus();
}
}
},
}
/**
@ -841,13 +985,12 @@ function ElementEditor(aContainer, aNode)
this.attrList = null;
this.newAttr = null;
this.closeElt = null;
let options = { stack: "markup-view.xhtml" };
// Create the main editor
this.template("element", this, options);
this.template("element", this);
// Create the closing tag
this.template("elementClose", this, options);
this.template("elementClose", this);
// Make the tag name editable (unless this is a document element)
if (aNode != aNode.ownerDocument.documentElement) {
@ -927,8 +1070,7 @@ ElementEditor.prototype = {
let data = {
attrName: aAttr.name,
};
let options = { stack: "markup-view.xhtml" };
this.template("attribute", data, options);
this.template("attribute", data);
var {attr, inner, name, val} = data;
// Figure out where we should place the attribute.
@ -1260,3 +1402,8 @@ function whitespaceTextFilter(aNode)
return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
}
}
XPCOMUtils.defineLazyGetter(MarkupView.prototype, "strings", function () {
return Services.strings.createBundle(
"chrome://browser/locale/devtools/inspector.properties");
});

View File

@ -16,6 +16,8 @@
<div id="templates" style="display:none">
<ul>
<li id="template-container" save="${elt}" class="container"><span save="${expander}" class="expander"></span><span save="${codeBox}" class="codebox"><ul save="${children}" class="children"></ul></span></li>
<li id="template-more-nodes" class="more-nodes devtools-class-comment" save="${elt}"><span>${showing}</span> <button href="#" onclick="${allButtonClick}">${showAll}</button></li>
</ul>
<span id="template-element" save="${elt}" class="editor"><span>&lt;</span><span save="${tag}" class="tagname devtools-theme-tagname"></span><span save="${attrList}"></span><span save="${newAttr}" class="newattr" tabindex="0"></span>&gt;</span>

View File

@ -17,7 +17,9 @@ _BROWSER_FILES = \
browser_inspector_markup_mutation.html \
browser_inspector_markup_mutation.js \
browser_inspector_markup_edit.html \
browser_inspector_markup_edit.js \
browser_inspector_markup_edit.js \
browser_inspector_markup_subset.html \
browser_inspector_markup_subset.js \
head.js \
$(NULL)

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html class="html">
<body class="body">
<div id="a"></div>
<div id="b"></div>
<div id="c"></div>
<div id="d"></div>
<div id="e"></div>
<div id="f"></div>
<div id="g"></div>
<div id="h"></div>
<div id="i"></div>
<div id="j"></div>
<div id="k"></div>
<div id="l"></div>
<div id="m"></div>
<div id="n"></div>
<div id="o"></div>
<div id="p"></div>
<div id="q"></div>
<div id="r"></div>
<div id="s"></div>
<div id="t"></div>
<div id="u"></div>
<div id="v"></div>
<div id="w"></div>
<div id="x"></div>
<div id="y"></div>
<div id="z"></div>
</body>
</html>

View File

@ -0,0 +1,146 @@
/* Any copyright", " is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the markup view loads only as many nodes as specified
* by the devtools.markup.pagesize preference.
*/
registerCleanupFunction(function() {
Services.prefs.clearUserPref("devtools.markup.pagesize");
});
Services.prefs.setIntPref("devtools.markup.pagesize", 5);
function test() {
waitForExplicitFinish();
// Will hold the doc we're viewing
let doc;
let inspector;
// Holds the MarkupTool object we're testing.
let markup;
function assertChildren(expected)
{
let container = markup.getContainer(doc.querySelector("body"));
let found = [];
for (let child of container.children.children) {
if (child.classList.contains("more-nodes")) {
found += "*more*";
} else {
found += child.container.node.getAttribute("id");
}
}
is(expected, found, "Got the expected children.");
}
function forceReload()
{
let container = markup.getContainer(doc.querySelector("body"));
container.childrenDirty = true;
}
let selections = [
{
desc: "Select the first item",
selector: "#a",
before: function() {
},
after: function() {
assertChildren("abcde*more*");
}
},
{
desc: "Select the last item",
selector: "#z",
before: function() {},
after: function() {
assertChildren("*more*vwxyz");
}
},
{
desc: "Select an already-visible item",
selector: "#v",
before: function() {},
after: function() {
// Because "v" was already visible, we shouldn't have loaded
// a different page.
assertChildren("*more*vwxyz");
},
},
{
desc: "Verify childrenDirty reloads the page",
selector: "#w",
before: function() {
forceReload();
},
after: function() {
// But now that we don't already have a loaded page, selecting
// w should center around w.
assertChildren("*more*uvwxy*more*");
},
},
];
// Create the helper tab for parsing...
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
doc = content.document;
waitForFocus(setupTest, content);
}, true);
content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_subset.html";
function setupTest() {
var target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
toolbox.once("inspector-selected", function SE_selected(id, aInspector) {
inspector = aInspector;
markup = inspector.markup;
runNextSelection();
});
});
}
function runTests() {
inspector.selection.once("new-node", startTests);
executeSoon(function() {
inspector.selection.setNode(doc.body);
});
}
function runNextSelection() {
let selection = selections.shift();
if (!selection) {
clickMore();
return;
}
info(selection.desc);
selection.before();
inspector.selection.once("new-node", function() {
selection.after();
runNextSelection();
});
inspector.selection.setNode(doc.querySelector(selection.selector));
}
function clickMore() {
info("Check that clicking more loads the whole thing.");
// Make sure that clicking the "more" button loads all the nodes.
let container = markup.getContainer(doc.querySelector("body"));
let button = container.elt.querySelector("button");
button.click();
assertChildren("abcdefghijklmnopqrstuvwxyz");
finishUp();
}
function finishUp() {
doc = inspector = null;
gBrowser.removeCurrentTab();
finish();
}
}

View File

@ -158,9 +158,6 @@ function ResponsiveUI(aWindow, aTab)
this.buildUI();
this.checkMenus();
let target = TargetFactory.forTab(this.tab);
this.toolboxWasOpen = !!gDevTools.getToolbox(target);
try {
if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) {
this.rotate();
@ -239,17 +236,9 @@ ResponsiveUI.prototype = {
if (aEvent.keyCode == this.mainWindow.KeyEvent.DOM_VK_ESCAPE &&
this.mainWindow.gBrowser.selectedBrowser == this.browser) {
// If the toolbox wasn't open at first but is open now,
// we don't want to close the Responsive Mode on Escape.
// We let the toolbox close first.
let target = TargetFactory.forTab(this.tab);
let isToolboxOpen = !!gDevTools.getToolbox(target);
if (this.toolboxWasOpen || !isToolboxOpen) {
aEvent.preventDefault();
aEvent.stopPropagation();
this.close();
}
aEvent.preventDefault();
aEvent.stopPropagation();
this.close();
}
},

View File

@ -69,11 +69,29 @@ this.ScratchpadManager = {
saveOpenWindows: function SPM_saveOpenWindows() {
this._scratchpads = [];
function clone(src) {
let dest = {};
for (let key in src) {
if (src.hasOwnProperty(key)) {
dest[key] = src[key];
}
}
return dest;
}
// We need to clone objects we get from Scratchpad instances
// because such (cross-window) objects have a property 'parent'
// that holds on to a ChromeWindow instance. This means that
// such objects are not primitive-values-only anymore so they
// can leak.
let enumerator = Services.wm.getEnumerator("devtools:scratchpad");
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
if (!win.closed && win.Scratchpad.initialized) {
this._scratchpads.push(win.Scratchpad.getState());
this._scratchpads.push(clone(win.Scratchpad.getState()));
}
}
},
@ -106,6 +124,7 @@ this.ScratchpadManager = {
let win = Services.ww.openWindow(null, SCRATCHPAD_WINDOW_URL, "_blank",
SCRATCHPAD_WINDOW_FEATURES, params);
// Only add the shutdown observer if we've opened a scratchpad window.
ShutdownObserver.init();
@ -128,6 +147,7 @@ var ShutdownObserver = {
}
Services.obs.addObserver(this, "quit-application-granted", false);
this._initialized = true;
},
@ -143,4 +163,4 @@ var ShutdownObserver = {
{
Services.obs.removeObserver(this, "quit-application-granted");
}
};
};

View File

@ -133,6 +133,7 @@ this.CssHtmlTree = function CssHtmlTree(aStyleInspector)
this.getRTLAttr = chromeReg.isLocaleRTL("global") ? "rtl" : "ltr";
// Create bound methods.
this.siFocusWindow = this.focusWindow.bind(this);
this.siBoundMenuUpdate = this.computedViewMenuUpdate.bind(this);
this.siBoundCopy = this.computedViewCopy.bind(this);
this.siBoundCopyDeclaration = this.computedViewCopyDeclaration.bind(this);
@ -140,6 +141,7 @@ this.CssHtmlTree = function CssHtmlTree(aStyleInspector)
this.siBoundCopyPropertyValue = this.computedViewCopyPropertyValue.bind(this);
this.styleDocument.addEventListener("copy", this.siBoundCopy);
this.styleDocument.addEventListener("mousedown", this.siFocusWindow);
// Nodes used in templating
this.root = this.styleDocument.getElementById("root");
@ -557,6 +559,17 @@ CssHtmlTree.prototype = {
menuitem.disabled = disablePropertyItems;
},
/**
* Focus the window on mousedown.
*
* @param aEvent The event object
*/
focusWindow: function si_focusWindow(aEvent)
{
let win = this.styleDocument.defaultView;
win.focus();
},
/**
* Copy selected text.
*
@ -704,6 +717,7 @@ CssHtmlTree.prototype = {
// Remove bound listeners
this.styleDocument.removeEventListener("copy", this.siBoundCopy);
this.styleDocument.removeEventListener("mousedown", this.siFocusWindow);
// Nodes used in templating
delete this.root;

View File

@ -1402,6 +1402,8 @@ RuleEditor.prototype = {
}.bind(this), false);
this.element.addEventListener("mousedown", function() {
this.doc.defaultView.focus();
let editorNodes =
this.doc.querySelectorAll(".styleinspector-propertyeditor");

View File

@ -181,7 +181,6 @@ TiltVisualizer.prototype = {
if (panel) {
this.inspector = panel;
this.inspector.selection.on("new-node", this.onNewNodeFromInspector);
this.inspector.selection.on("detached", this.onNewNodeFromInspector);
this.onNewNodeFromInspector();
}
}
@ -196,7 +195,6 @@ TiltVisualizer.prototype = {
if (this.inspector) {
this.inspector.selection.off("new-node", this.onNewNodeFromInspector);
this.inspector.selection.off("detached", this.onNewNodeFromInspector);
this.inspector = null;
}
@ -217,7 +215,6 @@ TiltVisualizer.prototype = {
if (toolbox.target.tab === this._browserTab) {
this.inspector = panel;
this.inspector.selection.on("new-node", this.onNewNodeFromInspector);
this.inspector.selection.on("detached", this.onNewNodeFromInspector);
this.onNewNodeFromTilt();
}
},
@ -231,7 +228,6 @@ TiltVisualizer.prototype = {
this.inspector) {
if (this.inspector.selection) {
this.inspector.selection.off("new-node", this.onNewNodeFromInspector);
this.inspector.selection.off("detached", this.onNewNodeFromInspector);
}
this.inspector = null;
}

View File

@ -2,14 +2,21 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- LOCALIZATION NOTE : FILE This file contains the Remove Connection strings.
- The Remote Connection window can be start by running the command:
- `devtools connect`
<!-- LOCALIZATION NOTE : FILE This file contains the Remote Connection strings.
- The Remote Connection window can reached from the "connect…" menuitem
- in the Web Developer menu.
- -->
<!ENTITY title "Connect">
<!ENTITY header "Connect to remote device">
<!ENTITY host "Host:">
<!ENTITY port "Port:">
<!ENTITY connect "Connect">
<!ENTITY connecting "Connecting…">
<!ENTITY availability "Available remote objects:">
<!ENTITY remoteProcess "remote process">
<!ENTITY connectionError "Error:">
<!ENTITY errorTimeout "Error: connection timeout.">
<!ENTITY errorRefused "Error: connection refused.">
<!ENTITY errorUnexpected "Unexpected error.">
<!ENTITY help "Firefox Developer Tools can debug remote devices (Firefox for Android and Firefox OS for example). Make sure that you have turned on the 'Debugger Server' option on the remote device. See <a target='_' href='https://developer.mozilla.org/en-US/docs/Tools/Debugger#Remote_Debugging'>documentation</a>.">

View File

@ -0,0 +1,9 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# LOCALIZATION NOTE : FILE This file contains the Remote Connection strings.
# The Remote Connection window can reached from the "connect…" menuitem
# in the Web Developer menu.
remoteProcess=Remote Process

View File

@ -186,6 +186,11 @@ globalScopeLabel=Global
# displayed inside the developer tools window and in the Developer Tools Menu.
ToolboxDebugger.label=Debugger
# LOCALIZATION NOTE (ToolboxDebugger.tooltip):
# This string is displayed in the tooltip of the tab when the debugger is
# displayed inside the developer tools window..
ToolboxDebugger.tooltip=JavaScript Debugger
# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed
# in the variables list on an item with an editable name.
variablesEditableNameTooltip=Double click to edit

View File

@ -33,3 +33,10 @@ nodeMenu.tooltiptext=Node operations
inspector.label=Inspector
inspector.commandkey=I
inspector.accesskey=I
# LOCALIZATION NOTE (markupView.more.*)
# When there are too many nodes to load at once, we will offer to
# show all the nodes.
markupView.more.showing=Some nodes were hidden.
markupView.more.showAll=Show All %S Nodes
inspector.tooltip=DOM and Style Inspector

View File

@ -13,4 +13,9 @@
# LOCALIZATION NOTE (profiler.label):
# This string is displayed in the title of the tab when the profiler is
# displayed inside the developer tools window and in the Developer Tools Menu.
profiler.label=Profiler
profiler.label=Profiler
# LOCALIZATION NOTE (profiler.tooltip):
# This string is displayed in the tooltip of the tab when the profiler is
# displayed inside the developer tools window.
profiler.tooltip=Profiler

View File

@ -78,3 +78,8 @@ redo.commandkey=Z
# This string is displayed in the title of the tab when the debugger is
# displayed inside the developer tools window and in the Developer Tools Menu.
ToolboxStyleEditor.label=Style Editor
# LOCALIZATION NOTE (ToolboxStyleEditor.tooltip):
# This string is displayed in the tooltip of the tab when the debugger is
# displayed inside the developer tools window.
ToolboxStyleEditor.tooltip=CSS Stylesheets Editor

View File

@ -5,3 +5,5 @@
<!ENTITY window.title "Developer Tools">
<!ENTITY closeCmd.key "W">
<!ENTITY toolboxCloseButton.tooltip "Close Developer Tools">

View File

@ -0,0 +1,3 @@
toolboxDockButtons.bottom.tooltip=Dock to bottom of browser window
toolboxDockButtons.side.tooltip=Dock to side of browser window
toolboxDockButtons.window.tooltip=Show in separate window

View File

@ -174,6 +174,11 @@ listTabs.globalConsoleActor=*Global Console*
# as webConsoleWindowTitleAndURL before the '-'
ToolboxWebconsole.label=Web Console
# LOCALIZATION NOTE (ToolboxWebconsole.tooltip):
# This string is displayed in the tooltip of the tab when the web console is
# displayed inside the developer tools window.
ToolboxWebconsole.tooltip=Web Console
# LOCALIZATION NOTE (longStringEllipsis): The string displayed after a long
# string. This string is clickable such that the rest of the string is retrieved
# from the server.

View File

@ -41,6 +41,7 @@
locale/browser/devtools/toolbox.dtd (%chrome/browser/devtools/toolbox.dtd)
locale/browser/devtools/inspector.dtd (%chrome/browser/devtools/inspector.dtd)
locale/browser/devtools/connection-screen.dtd (%chrome/browser/devtools/connection-screen.dtd)
locale/browser/devtools/connection-screen.properties (%chrome/browser/devtools/connection-screen.properties)
locale/browser/newTab.dtd (%chrome/browser/newTab.dtd)
locale/browser/newTab.properties (%chrome/browser/newTab.properties)
locale/browser/openLocation.dtd (%chrome/browser/openLocation.dtd)

View File

@ -128,27 +128,33 @@
margin: 0 3px;
}
/* Search input */
/* Text input */
.devtools-textinput,
.devtools-searchinput {
-moz-appearance: none;
margin: 0 3px;
border: 1px solid hsla(210,8%,5%,.6);
border-radius: 2px;
background-color: transparent;
background-image: url(magnifying-glass.png), -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
background-repeat: no-repeat;
background-position: 4px center, top left, top left;
padding-top: 0;
padding-bottom: 0;
-moz-padding-start: 18px;
-moz-padding-end: 12px;
background-image: -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
padding: 3px;
box-shadow: 0 1px 1px hsla(210,8%,5%,.3) inset,
0 0 0 1px hsla(210,16%,76%,.1) inset,
0 1px 0 hsla(210,16%,76%,.15);
color: inherit;
}
.devtools-searchinput {
padding-top: 0;
padding-bottom: 0;
-moz-padding-start: 18px;
-moz-padding-end: 12px;
background-image: url(magnifying-glass.png), -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
background-position: 4px center, top left, top left;
background-repeat: no-repeat;
}
.devtools-searchinput:-moz-locale-dir(rtl) {
background-position: calc(100% - 4px) center, top left, top left;
}

View File

@ -49,6 +49,10 @@ li.container {
-moz-appearance: treetwistyopen;
}
.more-nodes {
padding-left: 16px;
}
.styleinspector-propertyeditor {
border: 1px solid #CCC;
}

View File

@ -134,14 +134,24 @@
border: 0;
}
/* Search input */
/* Text input */
.devtools-textinput,
.devtools-searchinput {
-moz-appearance: none;
margin: 0 3px;
background-color: transparent;
border: 1px solid hsla(210,8%,5%,.6);
border-radius: 20px;
background-image: -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
padding: 3px;
box-shadow: 0 1px 1px hsla(210,8%,5%,.3) inset,
0 0 0 1px hsla(210,16%,76%,.1) inset,
0 1px 0 hsla(210,16%,76%,.15);
color: inherit;
}
.devtools-searchinput {
background-image: url(magnifying-glass.png), -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
background-repeat: no-repeat;
background-position: 4px center, top left, top left;
@ -149,10 +159,6 @@
padding-bottom: 0;
-moz-padding-start: 18px;
-moz-padding-end: 12px;
box-shadow: 0 1px 1px hsla(210,8%,5%,.3) inset,
0 0 0 1px hsla(210,16%,76%,.1) inset,
0 1px 0 hsla(210,16%,76%,.15);
color: inherit;
}
.devtools-searchinput:-moz-locale-dir(rtl) {

View File

@ -52,6 +52,10 @@ li.container {
-moz-appearance: treetwistyopen;
}
.more-nodes {
padding-left: 16px;
}
.styleinspector-propertyeditor {
border: 1px solid #CCC;
}

View File

@ -132,8 +132,9 @@
-moz-box-align: center;
}
/* Search input */
/* Text input */
.devtools-textinput,
.devtools-searchinput {
-moz-appearance: none;
margin: 0 3px;
@ -141,6 +142,15 @@
box-shadow: inset 0 1px 0 hsla(211,68%,6%,.05), 0 0 0 1px hsla(210,40%,83%,.1);
border-radius: 2px;
background-color: transparent;
background-image: -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
padding: 3px;
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
color: inherit;
}
.devtools-searchinput {
background-image: url(magnifying-glass.png), -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
background-repeat: no-repeat;
background-position: 4px center, top left, top left;
@ -148,10 +158,6 @@
padding-bottom: 0;
-moz-padding-start: 18px;
-moz-padding-end: 12px;
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
color: inherit;
}
.devtools-searchinput[focused] {

View File

@ -54,6 +54,10 @@ li.container {
background-image: url("chrome://global/skin/tree/twisty-open.png");
}
.more-nodes {
padding-left: 16px;
}
.styleinspector-propertyeditor {
border: 1px solid #CCC;
}