Bug 1444301 - Move disable pop-up autohide feature to toolbox menu; r?jryans
MozReview-Commit-ID: 3cJKqIxaFkV
--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
@@ -87,19 +87,31 @@ add_task(async function testWebExtension
let testScript = function() {
/* eslint-disable no-undef */
let jsterm;
let popupFramePromise;
toolbox.selectTool("webconsole")
.then(async (console) => {
- dump(`Clicking the noautohide button\n`);
- toolbox.doc.getElementById("command-button-noautohide").click();
- dump(`Clicked the noautohide button\n`);
+ const clickNoAutoHideMenu = () => {
+ return new Promise(resolve => {
+ toolbox.doc.getElementById("toolbox-meatball-menu-button").click();
+ toolbox.doc.addEventListener("popupshown", () => {
+ const menuItem =
+ toolbox.doc.getElementById("toolbox-meatball-menu-noautohide");
+ menuItem.click();
+ resolve();
+ }, { once: true });
+ });
+ };
+
+ dump(`Clicking the menu button\n`);
+ await clickNoAutoHideMenu();
+ dump(`Clicked the menu button\n`);
popupFramePromise = new Promise(resolve => {
let listener = data => {
if (data.frames.some(({url}) => url && url.endsWith("popup.html"))) {
toolbox.target.off("frame-update", listener);
resolve();
}
};
--- a/devtools/client/framework/components/toolbox-controller.js
+++ b/devtools/client/framework/components/toolbox-controller.js
@@ -24,32 +24,34 @@ class ToolboxController extends Componen
toolboxButtons: [],
currentToolId: null,
highlightedTools: new Set(),
panelDefinitions: [],
hostTypes: [],
areDockOptionsEnabled: true,
canCloseToolbox: true,
isSplitConsoleActive: false,
+ disableAutohide: undefined,
canRender: false,
buttonIds: [],
checkedButtonsUpdated: () => {
this.forceUpdate();
}
};
this.setFocusedButton = this.setFocusedButton.bind(this);
this.setToolboxButtons = this.setToolboxButtons.bind(this);
this.setCurrentToolId = this.setCurrentToolId.bind(this);
this.highlightTool = this.highlightTool.bind(this);
this.unhighlightTool = this.unhighlightTool.bind(this);
this.setHostTypes = this.setHostTypes.bind(this);
this.setDockOptionsEnabled = this.setDockOptionsEnabled.bind(this);
this.setCanCloseToolbox = this.setCanCloseToolbox.bind(this);
this.setIsSplitConsoleActive = this.setIsSplitConsoleActive.bind(this);
+ this.setDisableAutohide = this.setDisableAutohide.bind(this);
this.setCanRender = this.setCanRender.bind(this);
this.setPanelDefinitions = this.setPanelDefinitions.bind(this);
this.updateButtonIds = this.updateButtonIds.bind(this);
this.updateFocusedButton = this.updateFocusedButton.bind(this);
}
shouldComponentUpdate() {
return this.state.canRender;
@@ -138,16 +140,20 @@ class ToolboxController extends Componen
setCanCloseToolbox(canCloseToolbox) {
this.setState({ canCloseToolbox }, this.updateButtonIds);
}
setIsSplitConsoleActive(isSplitConsoleActive) {
this.setState({ isSplitConsoleActive });
}
+ setDisableAutohide(disableAutohide) {
+ this.setState({ disableAutohide });
+ }
+
setPanelDefinitions(panelDefinitions) {
this.setState({ panelDefinitions }, this.updateButtonIds);
}
get panelDefinitions() {
return this.state.panelDefinitions;
}
--- a/devtools/client/framework/components/toolbox-toolbar.js
+++ b/devtools/client/framework/components/toolbox-toolbar.js
@@ -42,20 +42,29 @@ class ToolboxToolbar extends Component {
// Should the docking options be enabled? They are disabled in some
// contexts such as WebIDE.
areDockButtonsEnabled: PropTypes.bool,
// Do we need to add UI for closing the toolbox? We don't when the
// toolbox is undocked, for example.
canCloseToolbox: PropTypes.bool,
// Is the split console currently visible?
isSplitConsoleActive: PropTypes.bool,
+ // Are we disabling the behavior where pop-ups are automatically closed
+ // when clicking outside them?
+ //
+ // This is a tri-state value that may be true/false or undefined where
+ // undefined means that the option is not relevant in this context
+ // (i.e. we're not in a browser toolbox).
+ disableAutohide: PropTypes.bool,
// Function to select a tool based on its id.
selectTool: PropTypes.func,
// Function to turn the split console on / off.
toggleSplitConsole: PropTypes.func,
+ // Function to turn the disable pop-up autohide behavior on / off.
+ toggleNoAutohide: PropTypes.func,
// Function to completely close the toolbox.
closeToolbox: PropTypes.func,
// Keep a record of what button is focused.
focusButton: PropTypes.func,
// Hold off displaying the toolbar until enough information is ready for
// it to render nicely.
canRender: PropTypes.bool,
// Localization interface.
@@ -181,20 +190,26 @@ function renderSeparator() {
* They are not enabled in certain situations like when they are in the
* WebIDE.
* @param {boolean} canCloseToolbox
* Do we need to add UI for closing the toolbox? We don't when the
* toolbox is undocked, for example.
* @param {boolean} isSplitConsoleActive
* Is the split console currently visible?
* toolbox is undocked, for example.
+ * @param {boolean|undefined} disableAutohide
+ * Are we disabling the behavior where pop-ups are automatically
+ * closed when clicking outside them?
+ * (Only defined for the browser toolbox.)
* @param {Function} selectTool
* Function to select a tool based on its id.
* @param {Function} toggleSplitConsole
* Function to turn the split console on / off.
+ * @param {Function} toggleNoAutohide
+ * Function to turn the disable pop-up autohide behavior on / off.
* @param {Function} closeToolbox
* Completely close the toolbox.
* @param {Function} focusButton
* Keep a record of the currently focused button.
* @param {Object} L10N
* Localization interface.
*/
function renderToolboxControls(props) {
@@ -260,34 +275,42 @@ function renderToolboxControls(props) {
* @param {string} props.hostTypes[].position
* Position name.
* @param {Function} props.hostTypes[].switchHost
* Function to switch the host.
* This array will be empty if we shouldn't shouldn't show any dock
* options.
* @param {boolean} isSplitConsoleActive
* Is the split console currently visible?
+ * @param {boolean|undefined} disableAutohide
+ * Are we disabling the behavior where pop-ups are automatically
+ * closed when clicking outside them.
+ * (Only defined for the browser toolbox.)
* @param {Function} props.selectTool
* Function to select a tool based on its id.
* @param {Function} toggleSplitConsole
* Function to turn the split console on / off.
+ * @param {Function} toggleNoAutohide
+ * Function to turn the disable pop-up autohide behavior on / off.
* @param {Object} props.L10N
* Localization interface.
* @param {Object} props.toolbox
* The devtools toolbox. Used by the Menu component to determine which
* document to use.
*/
function showMeatballMenu(
menuButton,
{
currentToolId,
hostTypes,
isSplitConsoleActive,
+ disableAutohide,
selectTool,
toggleSplitConsole,
+ toggleNoAutohide,
L10N,
toolbox,
}
) {
const menu = new Menu({ id: "toolbox-meatball-menu" });
// Dock options
for (const hostType of hostTypes) {
@@ -309,16 +332,30 @@ function showMeatballMenu(
isSplitConsoleActive ? "hideconsole" : "splitconsole"
}.label`
),
accelerator: "Esc",
click: toggleSplitConsole,
}));
}
+ // Disable pop-up autohide
+ //
+ // If |disableAutohide| is undefined, it means this feature is not available
+ // in this context.
+ if (typeof disableAutohide !== "undefined") {
+ menu.append(new MenuItem({
+ id: "toolbox-meatball-menu-noautohide",
+ label: L10N.getStr("toolbox.meatballMenu.noautohide.label"),
+ type: "checkbox",
+ checked: disableAutohide,
+ click: toggleNoAutohide,
+ }));
+ }
+
if (menu.items.length) {
menu.append(new MenuItem({ type: "separator" }));
}
// Settings
menu.append(new MenuItem({
id: "toolbox-meatball-menu-settings",
label: L10N.getStr("toolbox.meatballMenu.settings.label"),
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -118,17 +118,17 @@ function Toolbox(target, selectedTool, h
// List of listeners for `devtools.network.onRequestFinished` WebExt API
this._requestFinishedListeners = new Set();
this._toolRegistered = this._toolRegistered.bind(this);
this._toolUnregistered = this._toolUnregistered.bind(this);
this._onWillNavigate = this._onWillNavigate.bind(this);
this._refreshHostTitle = this._refreshHostTitle.bind(this);
- this._toggleNoAutohide = this._toggleNoAutohide.bind(this);
+ this.toggleNoAutohide = this.toggleNoAutohide.bind(this);
this.showFramesMenu = this.showFramesMenu.bind(this);
this.handleKeyDownOnFramesButton = this.handleKeyDownOnFramesButton.bind(this);
this.showFramesMenuOnKeyDown = this.showFramesMenuOnKeyDown.bind(this);
this._updateFrames = this._updateFrames.bind(this);
this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
this.destroy = this.destroy.bind(this);
this.highlighterUtils = getHighlighterUtils(this);
this._highlighterReady = this._highlighterReady.bind(this);
@@ -503,55 +503,52 @@ Toolbox.prototype = {
this._componentMount.addEventListener("keypress", this._onToolbarArrowKeypress);
this._componentMount.setAttribute("aria-label", L10N.getStr("toolbox.label"));
this.webconsolePanel = this.doc.querySelector("#toolbox-panel-webconsole");
this.webconsolePanel.height = Services.prefs.getIntPref(SPLITCONSOLE_HEIGHT_PREF);
this.webconsolePanel.addEventListener("resize", this._saveSplitConsoleHeight);
- let buttonsPromise = this._buildButtons();
+ this._buildButtons();
this._pingTelemetry();
// The isTargetSupported check needs to happen after the target is
// remoted, otherwise we could have done it in the toolbox constructor
// (bug 1072764).
let toolDef = gDevTools.getToolDefinition(this._defaultToolId);
if (!toolDef || !toolDef.isTargetSupported(this._target)) {
this._defaultToolId = "webconsole";
}
// Start rendering the toolbox toolbar before selecting the tool, as the tools
// can take a few hundred milliseconds seconds to start up.
- // But wait for toolbar buttons to be set before updating this react component.
- buttonsPromise.then(() => {
- // Delay React rendering as Toolbox.open and buttonsPromise are synchronous.
- // Even if this involve promises, this is synchronous. Toolbox.open already loads
- // react modules and freeze the event loop for a significant time.
- // requestIdleCallback allows releasing it to allow user events to be processed.
- // Use 16ms maximum delay to allow one frame to be rendered at 60FPS
- // (1000ms/60FPS=16ms)
- this.win.requestIdleCallback(() => {
- this.component.setCanRender();
- }, {timeout: 16});
- });
+ //
+ // Delay React rendering as Toolbox.open is synchronous.
+ // Even if this involve promises, it is synchronous. Toolbox.open already loads
+ // react modules and freeze the event loop for a significant time.
+ // requestIdleCallback allows releasing it to allow user events to be processed.
+ // Use 16ms maximum delay to allow one frame to be rendered at 60FPS
+ // (1000ms/60FPS=16ms)
+ this.win.requestIdleCallback(() => {
+ this.component.setCanRender();
+ }, {timeout: 16});
await this.selectTool(this._defaultToolId);
// Wait until the original tool is selected so that the split
// console input will receive focus.
let splitConsolePromise = promise.resolve();
if (Services.prefs.getBoolPref(SPLITCONSOLE_ENABLED_PREF)) {
splitConsolePromise = this.openSplitConsole();
}
await promise.all([
splitConsolePromise,
- buttonsPromise,
framesPromise
]);
// Lazily connect to the profiler here and don't wait for it to complete,
// used to intercept console.profile calls before the performance tools are open.
let performanceFrontConnection = this.initPerformance();
// If in testing environment, wait for performance connection to finish,
@@ -1090,34 +1087,41 @@ Toolbox.prototype = {
msg.frameId = this.frameId;
this.win.parent.postMessage(msg, "*");
}
},
/**
* Initiate ToolboxTabs React component and all it's properties. Do the initial render.
*/
- _buildTabs: function() {
+ _buildTabs: async function() {
// Get the initial list of tab definitions. This list can be amended at a later time
// by tools registering themselves.
const definitions = gDevTools.getToolDefinitionArray();
definitions.forEach(definition => this._buildPanelForTool(definition));
// Get the definitions that will only affect the main tab area.
this.panelDefinitions = definitions.filter(definition =>
definition.isTargetSupported(this._target) && definition.id !== "options");
+
+ // Do async lookup of disable pop-up auto-hide state.
+ if (this.disableAutohideAvailable) {
+ let disable = await this._isDisableAutohideEnabled();
+ this.component.setDisableAutohide(disable);
+ }
},
_mountReactComponent: function() {
// Ensure the toolbar doesn't try to render until the tool is ready.
const element = this.React.createElement(this.ToolboxController, {
L10N,
currentToolId: this.currentToolId,
selectTool: this.selectTool,
toggleSplitConsole: this.toggleSplitConsole,
+ toggleNoAutohide: this.toggleNoAutohide,
closeToolbox: this.destroy,
focusButton: this._onToolbarFocus,
toolbox: this
});
this.component = this.ReactDOM.render(element, this._componentMount);
},
@@ -1179,22 +1183,21 @@ Toolbox.prototype = {
event.preventDefault();
event.stopPropagation();
},
/**
* Add buttons to the UI as specified in devtools/client/definitions.js
*/
- async _buildButtons() {
+ _buildButtons() {
// Beyond the normal preference filtering
this.toolbarButtons = [
this._buildPickerButton(),
this._buildFrameButton(),
- await this._buildNoAutoHideButton()
];
ToolboxButtons.forEach(definition => {
let button = this._createButtonState(definition);
this.toolbarButtons.push(button);
});
this.component.setToolboxButtons(this.toolbarButtons);
@@ -1213,35 +1216,16 @@ Toolbox.prototype = {
},
onKeyDown: this.handleKeyDownOnFramesButton
});
return this.frameButton;
},
/**
- * Button that disables/enables auto-hiding XUL pop-ups. When enabled, XUL
- * pop-ups will not automatically close when they lose focus.
- */
- async _buildNoAutoHideButton() {
- this.autohideButton = this._createButtonState({
- id: "command-button-noautohide",
- description: L10N.getStr("toolbox.noautohide.tooltip"),
- onClick: this._toggleNoAutohide,
- isTargetSupported: target => target.chrome
- });
-
- this._isDisableAutohideEnabled().then(enabled => {
- this.autohideButton.isChecked = enabled;
- });
-
- 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.
*/
@@ -2069,30 +2053,38 @@ Toolbox.prototype = {
return this.target.root.then(rootForm => {
let front = getPreferenceFront(this.target.client, rootForm);
this._preferenceFront = front;
return front;
});
});
},
- async _toggleNoAutohide() {
+ // Is the disable auto-hide of pop-ups feature available in this context?
+ get disableAutohideAvailable() {
+ return this._target.chrome;
+ },
+
+ async toggleNoAutohide() {
let front = await this.preferenceFront;
let toggledValue = !(await this._isDisableAutohideEnabled());
front.setBoolPref(DISABLE_AUTOHIDE_PREF, toggledValue);
- this.autohideButton.isChecked = toggledValue;
+ if (this.disableAutohideAvailable) {
+ this.component.setDisableAutohide(toggledValue);
+ }
this._autohideHasBeenToggled = true;
},
async _isDisableAutohideEnabled() {
- // Ensure that the tools are open, and the button is visible.
+ // Ensure that the tools are open and the feature is available in this
+ // context.
await this.isOpen;
- if (!this.autohideButton.isVisible) {
+ if (!this.disableAutohideAvailable) {
return false;
}
let prefFront = await this.preferenceFront;
return prefFront.getBoolPref(DISABLE_AUTOHIDE_PREF);
},
_listFrames: function(event) {
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -147,17 +147,16 @@ devtools.jar:
skin/images/command-screenshot.svg (themes/images/command-screenshot.svg)
skin/images/command-responsivemode.svg (themes/images/command-responsivemode.svg)
skin/images/command-pick.svg (themes/images/command-pick.svg)
skin/images/command-pick-accessibility.svg (themes/images/command-pick-accessibility.svg)
skin/images/command-frames.svg (themes/images/command-frames.svg)
skin/images/command-eyedropper.svg (themes/images/command-eyedropper.svg)
skin/images/command-rulers.svg (themes/images/command-rulers.svg)
skin/images/command-measure.svg (themes/images/command-measure.svg)
- skin/images/command-noautohide.svg (themes/images/command-noautohide.svg)
skin/markup.css (themes/markup.css)
skin/images/editor-error.png (themes/images/editor-error.png)
skin/images/breakpoint.svg (themes/images/breakpoint.svg)
skin/webconsole.css (themes/webconsole.css)
skin/images/webconsole.svg (themes/images/webconsole.svg)
skin/images/breadcrumbs-scrollbutton.svg (themes/images/breadcrumbs-scrollbutton.svg)
skin/animation.css (themes/animation.css)
skin/animationinspector.css (themes/animationinspector.css)
--- a/devtools/client/locales/en-US/toolbox.properties
+++ b/devtools/client/locales/en-US/toolbox.properties
@@ -141,22 +141,16 @@ toolbox.toggleHost.key=CmdOrCtrl+Shift+D
# the iframes menu list that appears only when the document has some.
# It allows you to switch the context of the whole toolbox.
toolbox.frames.tooltip=Select an iframe as the currently targeted document
# LOCALIZATION NOTE (toolbox.showFrames.key)
# Key shortcut used to show frames menu when 'frames' button is focused
toolbox.showFrames.key=Alt+Down
-# LOCALIZATION NOTE (toolbox.noautohide.tooltip): This is the label for
-# the button to force the popups/panels to stay visible on blur.
-# This is only visible in the browser toolbox as it is meant for
-# addon developers and Firefox contributors.
-toolbox.noautohide.tooltip=Disable popup auto hide
-
# LOCALIZATION NOTE (toolbox.meatballMenu.button.tooltip): This is the tooltip
# for the "..." button on the developer tools toolbox.
toolbox.meatballMenu.button.tooltip=Customize Developer Tools and get help
# LOCALIZATION NOTE (toolbox.meatballMenu.dock.*.label): These labels are shown
# in the "..." menu in the toolbox and represent the different arrangements for
# docking (or undocking) the developer tools toolbox.
toolbox.meatballMenu.dock.bottom.label=Dock to bottom
@@ -165,16 +159,23 @@ toolbox.meatballMenu.dock.window.label=U
# LOCALIZATION NOTE (toolbox.meatballMenu.{splitconsole,hideconsole}.label):
# These are the labels in the "..." menu in the toolbox for toggling the split
# console window.
# The keyboard shortcut will be shown to the side of the label.
toolbox.meatballMenu.splitconsole.label=Show split console
toolbox.meatballMenu.hideconsole.label=Hide split console
+# LOCALIZATION NOTE (toolbox.meatballMenu.noautohide.label): This is the label
+# in the "..." menu in the toolbox to force the popups/panels to stay visible on
+# blur.
+# This is only visible in the browser toolbox as it is meant for
+# addon developers and Firefox contributors.
+toolbox.meatballMenu.noautohide.label=Disable popup auto-hide
+
# LOCALIZATION NOTE (toolbox.meatballMenu.settings.label): This is the label for
# the item in the "..." menu in the toolbox that brings up the Settings
# (Options) panel.
# The keyboard shortcut will be shown to the side of the label.
toolbox.meatballMenu.settings.label=Settings
# LOCALIZATION NOTE (toolbox.closebutton.tooltip): This is the tooltip for
# the close button the developer tools toolbox.
deleted file mode 100755
--- a/devtools/client/themes/images/command-noautohide.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-<!-- 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/. -->
-<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
- <path d="M2 1.99v4.02C2 6 2 6 1.99 6h4.02C6 6 6 6 6 6.01V1.99C6 2 6 2 6.01 2H1.99C2 2 2 2 2 1.99zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H1.99A.996.996 0 0 1 1 6.01V1.99zM10 1.99v4.02C10 6 10 6 9.99 6h4.02C14 6 14 6 14 6.01V1.99c0 .01 0 .01.01.01H9.99C10 2 10 2 10 1.99zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H9.99A.996.996 0 0 1 9 6.01V1.99zM10 9.99v4.02c0-.01 0-.01-.01-.01h4.02c-.01 0-.01 0-.01.01V9.99c0 .01 0 .01.01.01H9.99c.01 0 .01 0 .01-.01zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H9.99a.996.996 0 0 1-.99-.99V9.99zM2 9.99v4.02C2 14 2 14 1.99 14h4.02C6 14 6 14 6 14.01V9.99c0 .01 0 .01.01.01H1.99C2 10 2 10 2 9.99zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H1.99a.996.996 0 0 1-.99-.99V9.99z"/>
-</svg>
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -9,17 +9,16 @@
--command-paintflashing-image: url(images/command-paintflashing.svg);
--command-screenshot-image: url(images/command-screenshot.svg);
--command-responsive-image: url(images/command-responsivemode.svg);
--command-scratchpad-image: url(images/tool-scratchpad.svg);
--command-pick-image: url(images/command-pick.svg);
--command-pick-accessibility-image: url(images/command-pick-accessibility.svg);
--command-frames-image: url(images/command-frames.svg);
- --command-noautohide-image: url(images/command-noautohide.svg);
--command-rulers-image: url(images/command-rulers.svg);
--command-measure-image: url(images/command-measure.svg);
}
/* Toolbox tabbar */
.devtools-tabbar {
-moz-appearance: none;
@@ -207,20 +206,16 @@
#command-button-pick::before {
background-image: var(--command-pick-image);
}
#command-button-pick.accessibility::before {
background-image: var(--command-pick-accessibility-image);
}
-#command-button-noautohide::before {
- background-image: var(--command-noautohide-image);
-}
-
#command-button-eyedropper::before {
background-image: var(--command-eyedropper-image);
}
#command-button-rulers::before {
background-image: var(--command-rulers-image);
}