# HG changeset patch # User Yura Zenevich # Date 1518670518 18000 # Node ID e0ac728caeaa88cddaf54f0bd0936587158151f3 # Parent 3491d949cde1cdb1477dbf7f1c8211fa041ede88 Bug 1428443 - allow panels to override picker functionality when they are active. r=pbro MozReview-Commit-ID: 6VE4Y8FYfoN diff --git a/devtools/client/framework/components/toolbox-toolbar.js b/devtools/client/framework/components/toolbox-toolbar.js --- a/devtools/client/framework/components/toolbox-toolbar.js +++ b/devtools/client/framework/components/toolbox-toolbar.js @@ -109,24 +109,26 @@ function renderToolboxButtons({toolboxBu return null; } return div({id: `toolbox-buttons-${isStart ? "start" : "end"}`}, ...visibleButtons.map(command => { const { id, description, + disabled, onClick, isChecked, className: buttonClass, onKeyDown } = command; return button({ id, title: description, + disabled, className: ( "command-button devtools-button " + buttonClass + (isChecked ? " checked" : "") ), onClick: (event) => { onClick(event); focusButton(id); }, diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js --- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -149,16 +149,17 @@ function Toolbox(target, selectedTool, h this._onToolbarFocus = this._onToolbarFocus.bind(this); this._onToolbarArrowKeypress = this._onToolbarArrowKeypress.bind(this); this._onPickerClick = this._onPickerClick.bind(this); this._onPickerKeypress = this._onPickerKeypress.bind(this); this._onPickerStarted = this._onPickerStarted.bind(this); this._onPickerStopped = this._onPickerStopped.bind(this); this._onInspectObject = this._onInspectObject.bind(this); this._onNewSelectedNodeFront = this._onNewSelectedNodeFront.bind(this); + this._updatePickerButton = this._updatePickerButton.bind(this); this.selectTool = this.selectTool.bind(this); this._target.on("close", this.destroy); if (!selectedTool) { selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL); } this._defaultToolId = selectedTool; @@ -172,16 +173,17 @@ function Toolbox(target, selectedTool, h this._target.on("will-navigate", this._onWillNavigate); this._target.on("navigate", this._refreshHostTitle); this._target.on("frame-update", this._updateFrames); this._target.on("inspect-object", this._onInspectObject); this.on("host-changed", this._refreshHostTitle); this.on("select", this._refreshHostTitle); + this.on("select", this._updatePickerButton); this.on("ready", this._showDevEditionPromo); gDevTools.on("tool-registered", this._toolRegistered); gDevTools.on("tool-unregistered", this._toolUnregistered); this.on("picker-started", this._onPickerStarted); this.on("picker-stopped", this._onPickerStopped); @@ -718,16 +720,17 @@ Toolbox.prototype = { * components to listen and respond to updates. * * @param {Object} options: * * @property {String} id - The id of the button or command. * @property {String} className - An optional additional className for the button. * @property {String} description - The value that will display as a tooltip and in * the options panel for enabling/disabling. + * @property {Boolean} disabled - An optional disabled state for the button. * @property {Function} onClick - The function to run when the button is activated by * click or keyboard shortcut. First argument will be the 'click' * event, and second argument is the toolbox instance. * @property {Boolean} isInStartContainer - Buttons can either be placed at the start * of the toolbar, or at the end. * @property {Function} setup - Function run immediately to listen for events changing * whenever the button is checked or unchecked. The toolbox object * is passed as first argument and a callback is passed as second @@ -744,29 +747,31 @@ Toolbox.prototype = { * the button should be displayed as toggled on. */ _createButtonState: function (options) { let isCheckedValue = false; const { id, className, description, + disabled, onClick, isInStartContainer, setup, teardown, isTargetSupported, isChecked, onKeyDown } = options; const toolbox = this; const button = { id, className, description, + disabled, onClick(event) { if (typeof onClick == "function") { onClick(event, toolbox); } }, onKeyDown(event) { if (typeof onKeyDown == "function") { onKeyDown(event, toolbox); @@ -1304,30 +1309,43 @@ Toolbox.prototype = { return this.autohideButton; }), /** * Toggle the picker, but also decide whether or not the highlighter should * focus the window. This is only desirable when the toolbox is mounted to the * window. When devtools is free floating, then the target window should not * pop in front of the viewer when the picker is clicked. + * + * Note: Toggle picker can be overwritten by panel other than the inspector to + * allow for custom picker behaviour. */ _onPickerClick: function () { let focus = this.hostType === Toolbox.HostType.BOTTOM || this.hostType === Toolbox.HostType.SIDE; - this.highlighterUtils.togglePicker(focus); + let currentPanel = this.getCurrentPanel(); + if (currentPanel.togglePicker) { + currentPanel.togglePicker(focus); + } else { + this.highlighterUtils.togglePicker(focus); + } }, /** * If the picker is activated, then allow the Escape key to deactivate the * functionality instead of the default behavior of toggling the console. */ _onPickerKeypress: function (event) { if (event.keyCode === KeyCodes.DOM_VK_ESCAPE) { - this.highlighterUtils.cancelPicker(); + let currentPanel = this.getCurrentPanel(); + if (currentPanel.cancelPicker) { + currentPanel.cancelPicker(); + } else { + this.highlighterUtils.cancelPicker(); + } // Stop the console from toggling. event.stopImmediatePropagation(); } }, _onPickerStarted: function () { this.doc.addEventListener("keypress", this._onPickerKeypress, true); }, @@ -1393,16 +1411,39 @@ Toolbox.prototype = { updateToolboxButtonsVisibility() { this.toolbarButtons.forEach(button => { button.isVisible = this._commandIsVisible(button); }); this.component.setToolboxButtons(this.toolbarButtons); }, /** + * Visually update picker button. + * This function is called on every "select" event. Newly selected panel can + * update the visual state of the picker button such as disabled state, + * additional CSS classes (className), and tooltip (description). + */ + _updatePickerButton() { + const button = this.pickerButton; + let currentPanel = this.getCurrentPanel(); + + if (currentPanel && currentPanel.updatePickerButton) { + currentPanel.updatePickerButton(); + } else { + // If the current panel doesn't define a custom updatePickerButton, + // revert the button to its default state + button.description = L10N.getStr("pickButton.tooltip"); + button.className = null; + button.disabled = null; + } + + this.component.setToolboxButtons(this.toolbarButtons); + }, + + /** * Ensure the visibility of each toolbox button matches the preference value. */ _commandIsVisible: function (button) { const { isTargetSupported, visibilityswitch } = button; @@ -2565,17 +2606,23 @@ Toolbox.prototype = { if (!this._inspector) { return; } // Ensure that the inspector isn't still being initiated, otherwise race conditions // in the initialization process can throw errors. yield this._initInspector; - yield this.highlighterUtils.stopPicker(); + let currentPanel = this.getCurrentPanel(); + if (currentPanel.stopPicker) { + yield currentPanel.stopPicker(); + } else { + yield this.highlighterUtils.stopPicker(); + } + yield this._inspector.destroy(); if (this._highlighter) { // Note that if the toolbox is closed, this will work fine, but will fail // in case the browser is closed and will trigger a noSuchActor message. // We ignore the promise that |_hideBoxModel| returns, since we should still // proceed with the rest of destruction if it fails. // FF42+ now does the cleanup from the actor. if (!this.highlighter.traits.autoHideOnDestroy) { @@ -2624,16 +2671,17 @@ Toolbox.prototype = { this.emit("destroy"); this._target.off("inspect-object", this._onInspectObject); this._target.off("will-navigate", this._onWillNavigate); this._target.off("navigate", this._refreshHostTitle); this._target.off("frame-update", this._updateFrames); this.off("select", this._refreshHostTitle); + this.off("select", this._updatePickerButton); this.off("host-changed", this._refreshHostTitle); this.off("ready", this._showDevEditionPromo); gDevTools.off("tool-registered", this._toolRegistered); gDevTools.off("tool-unregistered", this._toolUnregistered); Services.prefs.removeObserver("devtools.cache.disabled", this._applyCacheSettings); Services.prefs.removeObserver("devtools.serviceWorkers.testing.enabled",