--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -1,26 +1,26 @@
/* 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/. */
"use strict";
-const { Cc, Ci, Cu } = require("chrome");
+const { Ci } = require("chrome");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const { TargetFactory } = require("devtools/client/framework/target");
const Telemetry = require("devtools/client/shared/telemetry");
const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+const {Task} = require("devtools/shared/task");
const NS_XHTML = "http://www.w3.org/1999/xhtml";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const { PluralForm } = require("devtools/shared/plural-form");
loader.lazyGetter(this, "prefBranch", function () {
return Services.prefs.getBranch(null)
.QueryInterface(Ci.nsIPrefBranch2);
});
@@ -39,17 +39,17 @@ var CommandUtils = {
/**
* Utility to ensure that things are loaded in the correct order
*/
createRequisition: function (target, options) {
if (!gcliInit) {
return promise.reject("Unable to load gcli");
}
return gcliInit.getSystem(target).then(system => {
- var Requisition = require("gcli/cli").Requisition;
+ let Requisition = require("gcli/cli").Requisition;
return new Requisition(system, options);
});
},
/**
* Destroy the remote side of the requisition as well as the local side
*/
destroyRequisition: function (requisition, target) {
@@ -85,28 +85,26 @@ var CommandUtils = {
throw new Error("No command '" + typed + "'");
}
if (command.buttonId != null) {
button.id = command.buttonId;
if (command.buttonClass != null) {
button.className = command.buttonClass;
}
- }
- else {
+ } else {
button.setAttribute("text-as-image", "true");
button.setAttribute("label", command.name);
}
button.classList.add("devtools-button");
if (command.tooltipText != null) {
button.setAttribute("title", command.tooltipText);
- }
- else if (command.description != null) {
+ } else if (command.description != null) {
button.setAttribute("title", command.description);
}
button.addEventListener("click",
requisition.updateExec.bind(requisition, typed));
button.addEventListener("keypress", (event) => {
if (ViewHelpers.isSpaceOrReturn(event)) {
@@ -123,44 +121,41 @@ var CommandUtils = {
/**
* The onChange event should be called with an event object that
* contains a target property which specifies which target the event
* applies to. For legacy reasons the event object can also contain
* a tab property.
*/
onChange = (eventName, ev) => {
if (ev.target == target || ev.tab == target.tab) {
-
let updateChecked = (checked) => {
if (checked) {
button.setAttribute("checked", true);
- }
- else if (button.hasAttribute("checked")) {
+ } else if (button.hasAttribute("checked")) {
button.removeAttribute("checked");
}
};
// isChecked would normally be synchronous. An annoying quirk
// of the 'csscoverage toggle' command forces us to accept a
// promise here, but doing Promise.resolve(reply).then(...) here
// makes this async for everyone, which breaks some tests so we
// treat non-promise replies separately to keep then synchronous.
let reply = command.state.isChecked(target);
if (typeof reply.then == "function") {
reply.then(updateChecked, console.error);
- }
- else {
+ } else {
updateChecked(reply);
}
}
};
command.state.onChange(target, onChange);
onChange("", { target: target });
- };
+ }
document.defaultView.addEventListener("unload", function (event) {
if (onChange && command.state.offChange) {
command.state.offChange(target, onChange);
}
button.remove();
button = null;
}, { once: true });
@@ -197,22 +192,24 @@ var CommandUtils = {
return this.target.tab.ownerDocument.defaultView;
},
get chromeDocument() {
return this.target.tab.ownerDocument.defaultView.document;
},
get window() {
- // throw new Error("environment.window is not available in runAt:client commands");
+ // throw new
+ // Error("environment.window is not available in runAt:client commands");
return this.chromeWindow.gBrowser.contentWindowAsCPOW;
},
get document() {
- // throw new Error("environment.document is not available in runAt:client commands");
+ // throw new
+ // Error("environment.document is not available in runAt:client commands");
return this.chromeWindow.gBrowser.contentDocumentAsCPOW;
}
};
},
};
exports.CommandUtils = CommandUtils;
@@ -228,25 +225,25 @@ loader.lazyGetter(this, "isLinux", funct
});
loader.lazyGetter(this, "isMac", function () {
return Services.appinfo.OS == "Darwin";
});
/**
* A component to manage the global developer toolbar, which contains a GCLI
* and buttons for various developer tools.
- * @param aChromeWindow The browser window to which this toolbar is attached
+ * @param chromeWindow The browser window to which this toolbar is attached
*/
-function DeveloperToolbar(aChromeWindow)
-{
- this._chromeWindow = aChromeWindow;
+function DeveloperToolbar(chromeWindow) {
+ this._chromeWindow = chromeWindow;
- this.target = null; // Will be setup when show() is called
+ // Will be setup when show() is called
+ this.target = null;
- this._doc = aChromeWindow.document;
+ this._doc = chromeWindow.document;
this._telemetry = new Telemetry();
this._errorsCount = {};
this._warningsCount = {};
this._errorListeners = {};
this._onToolboxReady = this._onToolboxReady.bind(this);
this._onToolboxDestroyed = this._onToolboxDestroyed.bind(this);
@@ -352,62 +349,61 @@ DeveloperToolbar.prototype.createToolbar
toolbar.appendChild(toolboxBtn);
toolbar.appendChild(close);
}
this._element = toolbar;
let bottomBox = this._doc.getElementById("browser-bottombox");
if (bottomBox) {
bottomBox.appendChild(this._element);
- } else { // SeaMonkey does not have a "browser-bottombox".
+ } else {
+ // SeaMonkey does not have a "browser-bottombox".
let statusBar = this._doc.getElementById("status-bar");
- if (statusBar)
+ if (statusBar) {
statusBar.parentNode.insertBefore(this._element, statusBar);
+ }
}
};
/**
* Called from browser.xul in response to menu-click or keyboard shortcut to
* toggle the toolbar
*/
DeveloperToolbar.prototype.toggle = function () {
if (this.visible) {
return this.hide().catch(console.error);
- } else {
- return this.show(true).catch(console.error);
}
+ return this.show(true).catch(console.error);
};
/**
* Called from browser.xul in response to menu-click or keyboard shortcut to
* toggle the toolbar
*/
DeveloperToolbar.prototype.focus = function () {
if (this.visible) {
this._input.focus();
return promise.resolve();
- } else {
- return this.show(true);
}
+ return this.show(true);
};
/**
* Called from browser.xul in response to menu-click or keyboard shortcut to
* toggle the toolbar
*/
DeveloperToolbar.prototype.focusToggle = function () {
if (this.visible) {
// If we have focus then the active element is the HTML input contained
// inside the xul input element
let active = this._chromeWindow.document.activeElement;
let position = this._input.compareDocumentPosition(active);
if (position & nodeConstants.DOCUMENT_POSITION_CONTAINED_BY) {
this.hide();
- }
- else {
+ } else {
this._input.focus();
}
} else {
this.show(true);
}
};
/**
@@ -421,155 +417,157 @@ DeveloperToolbar.introShownThisSession =
/**
* Show the developer toolbar
*/
DeveloperToolbar.prototype.show = function (focus) {
if (this._showPromise != null) {
return this._showPromise;
}
- // hide() is async, so ensure we don't need to wait for hide() to finish
- var waitPromise = this._hidePromise || promise.resolve();
+ this._showPromise = Task.spawn((function* () {
+ // hide() is async, so ensure we don't need to wait for hide() to
+ // finish. We unconditionally yield here, even if _hidePromise is
+ // null, so that the spawn call returns a promise before starting
+ // to do any real work.
+ yield this._hidePromise;
- this._showPromise = waitPromise.then(() => {
this.createToolbar();
Services.prefs.setBoolPref("devtools.toolbar.visible", true);
this._telemetry.toolOpened("developertoolbar");
this._notify(NOTIFICATIONS.LOAD);
this._input = this._doc.querySelector(".gclitoolbar-input-node");
// Initializing GCLI can only be done when we've got content windows to
// write to, so this needs to be done asynchronously.
let panelPromises = [
TooltipPanel.create(this),
OutputPanel.create(this)
];
- return promise.all(panelPromises).then(panels => {
- [ this.tooltipPanel, this.outputPanel ] = panels;
+ let panels = yield promise.all(panelPromises);
- this._doc.getElementById("menu_devToolbar").setAttribute("checked", "true");
+ [ this.tooltipPanel, this.outputPanel ] = panels;
+
+ this._doc.getElementById("menu_devToolbar").setAttribute("checked", "true");
- this.target = TargetFactory.forTab(this._chromeWindow.gBrowser.selectedTab);
- const options = {
- environment: CommandUtils.createEnvironment(this, "target"),
- document: this.outputPanel.document,
- };
- return CommandUtils.createRequisition(this.target, options).then(requisition => {
- this.requisition = requisition;
+ this.target = TargetFactory.forTab(this._chromeWindow.gBrowser.selectedTab);
+ const options = {
+ environment: CommandUtils.createEnvironment(this, "target"),
+ document: this.outputPanel.document,
+ };
+ let requisition = yield CommandUtils.createRequisition(this.target, options);
+ this.requisition = requisition;
- // The <textbox> `value` may still be undefined on the XUL binding if
- // we fetch it early
- let value = this._input.value || "";
- return this.requisition.update(value).then(() => {
- const Inputter = require("gcli/mozui/inputter").Inputter;
- const Completer = require("gcli/mozui/completer").Completer;
- const Tooltip = require("gcli/mozui/tooltip").Tooltip;
- const FocusManager = require("gcli/ui/focus").FocusManager;
+ // The <textbox> `value` may still be undefined on the XUL binding if
+ // we fetch it early
+ let value = this._input.value || "";
+ yield this.requisition.update(value);
- this.onOutput = this.requisition.commandOutputManager.onOutput;
+ const Inputter = require("gcli/mozui/inputter").Inputter;
+ const Completer = require("gcli/mozui/completer").Completer;
+ const Tooltip = require("gcli/mozui/tooltip").Tooltip;
+ const FocusManager = require("gcli/ui/focus").FocusManager;
- this.focusManager = new FocusManager(this._doc, requisition.system.settings);
+ this.onOutput = this.requisition.commandOutputManager.onOutput;
+
+ this.focusManager = new FocusManager(this._doc, requisition.system.settings);
- this.inputter = new Inputter({
- requisition: this.requisition,
- focusManager: this.focusManager,
- element: this._input,
- });
+ this.inputter = new Inputter({
+ requisition: this.requisition,
+ focusManager: this.focusManager,
+ element: this._input,
+ });
- this.completer = new Completer({
- requisition: this.requisition,
- inputter: this.inputter,
- backgroundElement: this._doc.querySelector(".gclitoolbar-stack-node"),
- element: this._doc.querySelector(".gclitoolbar-complete-node"),
- });
+ this.completer = new Completer({
+ requisition: this.requisition,
+ inputter: this.inputter,
+ backgroundElement: this._doc.querySelector(".gclitoolbar-stack-node"),
+ element: this._doc.querySelector(".gclitoolbar-complete-node"),
+ });
- this.tooltip = new Tooltip({
- requisition: this.requisition,
- focusManager: this.focusManager,
- inputter: this.inputter,
- element: this.tooltipPanel.hintElement,
- });
+ this.tooltip = new Tooltip({
+ requisition: this.requisition,
+ focusManager: this.focusManager,
+ inputter: this.inputter,
+ element: this.tooltipPanel.hintElement,
+ });
- this.inputter.tooltip = this.tooltip;
-
- this.focusManager.addMonitoredElement(this.outputPanel._frame);
- this.focusManager.addMonitoredElement(this._element);
+ this.inputter.tooltip = this.tooltip;
- this.focusManager.onVisibilityChange.add(this.outputPanel._visibilityChanged,
- this.outputPanel);
- this.focusManager.onVisibilityChange.add(this.tooltipPanel._visibilityChanged,
- this.tooltipPanel);
- this.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
+ this.focusManager.addMonitoredElement(this.outputPanel._frame);
+ this.focusManager.addMonitoredElement(this._element);
+
+ this.focusManager.onVisibilityChange.add(this.outputPanel._visibilityChanged,
+ this.outputPanel);
+ this.focusManager.onVisibilityChange.add(this.tooltipPanel._visibilityChanged,
+ this.tooltipPanel);
+ this.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
- let tabbrowser = this._chromeWindow.gBrowser;
- tabbrowser.tabContainer.addEventListener("TabSelect", this, false);
- tabbrowser.tabContainer.addEventListener("TabClose", this, false);
- tabbrowser.addEventListener("load", this, true);
- tabbrowser.addEventListener("beforeunload", this, true);
+ let tabbrowser = this._chromeWindow.gBrowser;
+ tabbrowser.tabContainer.addEventListener("TabSelect", this, false);
+ tabbrowser.tabContainer.addEventListener("TabClose", this, false);
+ tabbrowser.addEventListener("load", this, true);
+ tabbrowser.addEventListener("beforeunload", this, true);
- gDevTools.on("toolbox-ready", this._onToolboxReady);
- gDevTools.on("toolbox-destroyed", this._onToolboxDestroyed);
+ gDevTools.on("toolbox-ready", this._onToolboxReady);
+ gDevTools.on("toolbox-destroyed", this._onToolboxDestroyed);
- this._initErrorsCount(tabbrowser.selectedTab);
+ this._initErrorsCount(tabbrowser.selectedTab);
- this._element.hidden = false;
+ this._element.hidden = false;
- if (focus) {
- // If the toolbar was just inserted, the <textbox> may still have
- // its binding in process of being applied and not be focusable yet
- let waitForBinding = () => {
- // Bail out if the toolbar has been destroyed in the meantime
- if (!this._input) {
- return;
- }
- // mInputField is a xbl field of <xul:textbox>
- if (typeof this._input.mInputField != "undefined") {
- this._input.focus();
- this._notify(NOTIFICATIONS.SHOW);
- } else {
- this._input.ownerDocument.defaultView.setTimeout(waitForBinding, 50);
- }
- };
- waitForBinding();
- } else {
- this._notify(NOTIFICATIONS.SHOW);
- }
+ if (focus) {
+ // If the toolbar was just inserted, the <textbox> may still have
+ // its binding in process of being applied and not be focusable yet
+ let waitForBinding = () => {
+ // Bail out if the toolbar has been destroyed in the meantime
+ if (!this._input) {
+ return;
+ }
+ // mInputField is a xbl field of <xul:textbox>
+ if (typeof this._input.mInputField != "undefined") {
+ this._input.focus();
+ this._notify(NOTIFICATIONS.SHOW);
+ } else {
+ this._input.ownerDocument.defaultView.setTimeout(waitForBinding, 50);
+ }
+ };
+ waitForBinding();
+ } else {
+ this._notify(NOTIFICATIONS.SHOW);
+ }
- if (!DeveloperToolbar.introShownThisSession) {
- let intro = require("gcli/ui/intro");
- intro.maybeShowIntro(this.requisition.commandOutputManager,
- this.requisition.conversionContext,
- this.outputPanel);
- DeveloperToolbar.introShownThisSession = true;
- }
+ if (!DeveloperToolbar.introShownThisSession) {
+ let intro = require("gcli/ui/intro");
+ intro.maybeShowIntro(this.requisition.commandOutputManager,
+ this.requisition.conversionContext,
+ this.outputPanel);
+ DeveloperToolbar.introShownThisSession = true;
+ }
- this._showPromise = null;
- });
- });
- });
- });
+ this._showPromise = null;
+ }).bind(this));
return this._showPromise;
};
/**
* Hide the developer toolbar.
*/
DeveloperToolbar.prototype.hide = function () {
// If we're already in the process of hiding, just use the other promise
if (this._hidePromise != null) {
return this._hidePromise;
}
// show() is async, so ensure we don't need to wait for show() to finish
- var waitPromise = this._showPromise || promise.resolve();
+ let waitPromise = this._showPromise || promise.resolve();
this._hidePromise = waitPromise.then(() => {
this._element.hidden = true;
Services.prefs.setBoolPref("devtools.toolbar.visible", false);
this._doc.getElementById("menu_devToolbar").setAttribute("checked", "false");
this.destroy();
@@ -637,17 +635,18 @@ DeveloperToolbar.prototype._stopErrorsCo
this._updateErrorsCount();
};
/**
* Hide the developer toolbar
*/
DeveloperToolbar.prototype.destroy = function () {
if (this._input == null) {
- return; // Already destroyed
+ // Already destroyed
+ return;
}
let tabbrowser = this._chromeWindow.gBrowser;
tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
tabbrowser.tabContainer.removeEventListener("TabClose", this, false);
tabbrowser.removeEventListener("load", this, true);
tabbrowser.removeEventListener("beforeunload", this, true);
@@ -716,21 +715,19 @@ DeveloperToolbar.prototype.handleEvent =
});
if (ev.type == "TabSelect") {
let toolboxOpen = gDevToolsBrowser.hasToolboxOpened(this._chromeWindow);
this._errorCounterButton.setAttribute("checked", toolboxOpen);
this._initErrorsCount(ev.target);
}
}
- }
- else if (ev.type == "TabClose") {
+ } else if (ev.type == "TabClose") {
this._stopErrorsCount(ev.target);
- }
- else if (ev.type == "beforeunload") {
+ } else if (ev.type == "beforeunload") {
this._onPageBeforeUnload(ev);
}
};
/**
* Update toolbox toggle button when toolbox goes on and off
*/
DeveloperToolbar.prototype._onToolboxReady = function () {
@@ -863,17 +860,17 @@ function OutputPanel() {
* called on Windows and OSX (bug 692348) ... this prevents the panel from
* appearing the first time it is shown. Setting the panel's height to 1px
* before calling openPopup works around this issue as we resize it ourselves
* anyway.
*
* @param devtoolbar The parent DeveloperToolbar object
*/
OutputPanel.create = function (devtoolbar) {
- var outputPanel = Object.create(OutputPanel.prototype);
+ let outputPanel = Object.create(OutputPanel.prototype);
return outputPanel._init(devtoolbar);
};
/**
* @private See OutputPanel.create
*/
OutputPanel.prototype._init = function (devtoolbar) {
this._devtoolbar = devtoolbar;
@@ -1061,18 +1058,17 @@ OutputPanel.prototype._outputChanged = f
}
this.remove();
this.displayedOutput = ev.output;
if (this.displayedOutput.completed) {
this._update();
- }
- else {
+ } else {
this.displayedOutput.promise.then(this._update, this._update)
.then(null, console.error);
}
};
/**
* Called when displayed Output says it's changed or from outputChanged, which
* happens when there is a new displayed Output.
@@ -1094,18 +1090,18 @@ OutputPanel.prototype._update = function
if (node == null) {
return;
}
while (this._div.hasChildNodes()) {
this._div.removeChild(this._div.firstChild);
}
- var links = node.querySelectorAll("*[href]");
- for (var i = 0; i < links.length; i++) {
+ let links = node.querySelectorAll("*[href]");
+ for (let i = 0; i < links.length; i++) {
links[i].setAttribute("target", "_blank");
}
this._div.appendChild(node);
this.show();
});
}
};
@@ -1183,27 +1179,26 @@ function TooltipPanel() {
* called on Windows and OSX (bug 692348) ... this prevents the panel from
* appearing the first time it is shown. Setting the panel's height to 1px
* before calling openPopup works around this issue as we resize it ourselves
* anyway.
*
* @param devtoolbar The parent DeveloperToolbar object
*/
TooltipPanel.create = function (devtoolbar) {
- var tooltipPanel = Object.create(TooltipPanel.prototype);
+ let tooltipPanel = Object.create(TooltipPanel.prototype);
return tooltipPanel._init(devtoolbar);
};
/**
* @private See TooltipPanel.create
*/
TooltipPanel.prototype._init = function (devtoolbar) {
let deferred = defer();
- let chromeDocument = devtoolbar._doc;
this._devtoolbar = devtoolbar;
this._input = devtoolbar._doc.querySelector(".gclitoolbar-input-node");
this._toolbar = devtoolbar._doc.querySelector("#developer-toolbar");
this._dimensions = { start: 0, end: 0 };
/*
<tooltip|panel id="gcli-tooltip"
type="arrow"