Bug 1444301 - Move dock functions to a new meatball menu; r?jryans draft
authorBrian Birtles <birtles@gmail.com>
Thu, 05 Apr 2018 10:13:21 +0900
changeset 777588 4a106fe35d5947f7136bca48e7e2b64c92cdf997
parent 777587 d129f641bfe95cbc8ac783c0503d118d8e9d206c
child 777589 26426124eaccfefe3a1ed8727d6c4bed6189401f
push id105256
push userbmo:bbirtles@mozilla.com
push dateThu, 05 Apr 2018 01:56:46 +0000
reviewersjryans
bugs1444301
milestone61.0a1
Bug 1444301 - Move dock functions to a new meatball menu; r?jryans The Firebug SVG uses coloring based on tools-options.svg. MozReview-Commit-ID: IfFsiZnmw74
devtools/client/framework/components/toolbox-controller.js
devtools/client/framework/components/toolbox-toolbar.js
devtools/client/framework/toolbox.js
devtools/client/jar.mn
devtools/client/locales/en-US/toolbox.properties
devtools/client/themes/images/dock-bottom.svg
devtools/client/themes/images/dock-side.svg
devtools/client/themes/images/dock-undock.svg
devtools/client/themes/images/firebug/dock-bottom.svg
devtools/client/themes/images/firebug/dock-side.svg
devtools/client/themes/images/firebug/dock-undock.svg
devtools/client/themes/images/firebug/more.svg
devtools/client/themes/images/more.svg
devtools/client/themes/toolbox.css
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_nodes_highlight.js
devtools/client/webconsole/test/head.js
--- a/devtools/client/framework/components/toolbox-controller.js
+++ b/devtools/client/framework/components/toolbox-controller.js
@@ -21,33 +21,33 @@ class ToolboxController extends Componen
     // state, and for the definitions of the props that are expected to be passed in.
     this.state = {
       focusedButton: ELEMENT_PICKER_ID,
       toolboxButtons: [],
       currentToolId: null,
       highlightedTools: new Set(),
       panelDefinitions: [],
       hostTypes: [],
-      areDockButtonsEnabled: true,
+      areDockOptionsEnabled: true,
       canCloseToolbox: true,
       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.setOptionsPanel = this.setOptionsPanel.bind(this);
     this.setHostTypes = this.setHostTypes.bind(this);
-    this.setDockButtonsEnabled = this.setDockButtonsEnabled.bind(this);
+    this.setDockOptionsEnabled = this.setDockOptionsEnabled.bind(this);
     this.setCanCloseToolbox = this.setCanCloseToolbox.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() {
@@ -64,28 +64,26 @@ class ToolboxController extends Componen
    * The button and tab ids must be known in order to be able to focus left and right
    * using the arrow keys.
    */
   updateButtonIds() {
     const {
       toolboxButtons,
       panelDefinitions,
       optionsPanel,
-      hostTypes,
       canCloseToolbox,
     } = this.state;
 
     // This is a little gnarly, but go through all of the state and extract the IDs.
     this.setState({
       buttonIds: [
         ...toolboxButtons.filter(btn => btn.isInStartContainer).map(({id}) => id),
         ...panelDefinitions.map(({id}) => id),
         ...toolboxButtons.filter(btn => !btn.isInStartContainer).map(({id}) => id),
         optionsPanel ? optionsPanel.id : null,
-        ...hostTypes.map(({position}) => "toolbox-dock-" + position),
         canCloseToolbox ? "toolbox-close" : null
       ].filter(id => id)
     });
 
     this.updateFocusedButton();
   }
 
   updateFocusedButton() {
@@ -129,22 +127,22 @@ class ToolboxController extends Componen
   unhighlightTool(id) {
     let { highlightedTools } = this.state;
     if (highlightedTools.has(id)) {
       highlightedTools.delete(id);
       this.setState({ highlightedTools });
     }
   }
 
-  setDockButtonsEnabled(areDockButtonsEnabled) {
-    this.setState({ areDockButtonsEnabled }, this.updateButtonIds);
+  setDockOptionsEnabled(areDockOptionsEnabled) {
+    this.setState({ areDockOptionsEnabled });
   }
 
   setHostTypes(hostTypes) {
-    this.setState({ hostTypes }, this.updateButtonIds);
+    this.setState({ hostTypes });
   }
 
   setCanCloseToolbox(canCloseToolbox) {
     this.setState({ canCloseToolbox }, this.updateButtonIds);
   }
 
   setPanelDefinitions(panelDefinitions) {
     this.setState({ panelDefinitions }, this.updateButtonIds);
--- a/devtools/client/framework/components/toolbox-toolbar.js
+++ b/devtools/client/framework/components/toolbox-toolbar.js
@@ -3,16 +3,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const {div, button} = dom;
 
+const Menu = require("devtools/client/framework/menu");
+const MenuItem = require("devtools/client/framework/menu-item");
 const ToolboxTab = createFactory(require("devtools/client/framework/components/toolbox-tab"));
 const ToolboxTabs = createFactory(require("devtools/client/framework/components/toolbox-tabs"));
 
 /**
  * This is the overall component for the toolbox toolbar. It is designed to not know how
  * the state is being managed, and attempts to be as pure as possible. The
  * ToolboxController component controls the changing state, and passes in everything as
  * props.
@@ -70,17 +72,17 @@ class ToolboxToolbar extends Component {
       ? (
         div(
           containerProps,
           renderToolboxButtonsStart(this.props),
           ToolboxTabs(this.props),
           renderToolboxButtonsEnd(this.props),
           renderOptions(this.props),
           renderSeparator(),
-          renderDockButtons(this.props)
+          renderToolboxControls(this.props)
         )
       )
       : div(containerProps);
   }
 }
 
 module.exports = ToolboxToolbar;
 
@@ -186,77 +188,113 @@ function renderOptions({optionsPanel, cu
 /**
  * Render a separator.
  */
 function renderSeparator() {
   return div({className: "devtools-separator"});
 }
 
 /**
- * Render the dock buttons, and handle all the cases for what type of host the toolbox
- * is attached to. The following props are expected.
+ * Render the toolbox controls buttons. The following props are expected:
  *
  * @property {String} focusedButton - The id of the focused button.
  * @property {Array} hostTypes - Array of host type objects, containing:
- *                   @property {String} position - Position name
+ *                   @property {String} position - Position name.
  *                   @property {Function} switchHost - Function to switch the
  *                                                     host.
- * @property {Boolean} areDockButtonsEnabled - They are not enabled in certain
+ * @property {Boolean} areDockOptionsEnabled - They are not enabled in certain
  *                                             situations like when they are in
  *                                             the WebIDE.
  * @property {Boolean} canCloseToolbox - Do we need to add UI for closing the
  *                                       toolbox? We don't when the toolbox is
  *                                       undocked, for example.
  * @property {Function} closeToolbox - Completely close the toolbox.
  * @property {Function} focusButton - Keep a record of the currently focused
  *                                    button.
  * @property {Object} L10N - Localization interface.
  */
-function renderDockButtons(props) {
+function renderToolboxControls(props) {
   const {
     focusedButton,
     closeToolbox,
     hostTypes,
     focusButton,
     L10N,
-    areDockButtonsEnabled,
+    areDockOptionsEnabled,
     canCloseToolbox,
   } = props;
 
-  let buttons = [];
+  const toolboxMenuButtonId = "toolbox-menu-button";
 
-  if (areDockButtonsEnabled) {
-    hostTypes.forEach(hostType => {
-      const id = "toolbox-dock-" + hostType.position;
-      buttons.push(button({
-        id,
-        onFocus: () => focusButton(id),
-        className: "toolbox-dock-button devtools-button",
-        title: L10N.getStr(`toolboxDockButtons.${hostType.position}.tooltip`),
-        onClick: e => {
-          hostType.switchHost();
-          focusButton(id);
-        },
-        tabIndex: focusedButton === id ? "0" : "-1",
-      }));
-    });
-  }
+  const toolboxMenuButton = button({
+    id: toolboxMenuButtonId,
+    onFocus: () => focusButton(toolboxMenuButtonId),
+    className: "devtools-button",
+    title: L10N.getStr("toolbox.menubutton.tooltip"),
+    onClick: evt => {
+      showToolboxMenu(evt.target, {
+        ...props,
+        hostTypes: areDockOptionsEnabled ? hostTypes : [],
+      });
+    },
+    tabIndex: focusedButton === toolboxMenuButtonId ? "0" : "-1",
+  });
 
   const closeButtonId = "toolbox-close";
 
   const closeButton = canCloseToolbox
     ? button({
       id: closeButtonId,
       onFocus: () => focusButton(closeButtonId),
       className: "devtools-button",
       title: L10N.getStr("toolbox.closebutton.tooltip"),
       onClick: () => {
         closeToolbox();
       },
-      tabIndex: focusedButton === "toolbox-close" ? "0" : "-1",
+      tabIndex: focusedButton === closeButtonId ? "0" : "-1",
     })
     : null;
 
   return div({id: "toolbox-controls"},
-    div({id: "toolbox-dock-buttons"}, ...buttons),
+    toolboxMenuButton,
     closeButton
   );
 }
+
+/**
+ * Display the "..." menu (affectionately known as the meatball menu).
+ *
+ * @param {Object} menuButton - The <button> element from which the menu should
+ *                              pop out. The geometry of this element is used to
+ *                              position the menu.
+ * @param {Object} props - The following properties are expected:
+ *   @property {Array} hostTypes - Array of host type objects, each containing:
+ *      @property {String} position - Position name.
+ *      @property {Function} switchHost - Function to switch the host.
+ *      This array will be empty if we shouldn't shouldn't show any dock
+ *      options.
+ *   @property {Object} L10N - Localization interface.
+ *   @property {Object} toolbox - The devtools toolbox. Used by the Menu
+ *                                component to determine which document to use.
+ */
+function showToolboxMenu(menuButton, {hostTypes, L10N, toolbox}) {
+  const menu = new Menu({ id: "toolbox-menu" });
+
+  for (const hostType of hostTypes) {
+    menu.append(new MenuItem({
+      id: `toolbox-menu-dock-${hostType.position}`,
+      label: L10N.getStr(`toolbox.menu.dock.${hostType.position}.label`),
+      click: () => {
+        hostType.switchHost();
+      },
+    }));
+  }
+
+  // (Yes, it's true we might end up with an empty menu here. Don't worry,
+  // by the end of this patch series that won't be the case.)
+
+  const rect = menuButton.getBoundingClientRect();
+  const screenX = menuButton.ownerDocument.defaultView.mozInnerScreenX;
+  const screenY = menuButton.ownerDocument.defaultView.mozInnerScreenY;
+
+  // Display the popup below the button.
+  menu.popup(rect.left + screenX, rect.bottom + screenY, toolbox);
+}
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -482,17 +482,17 @@ Toolbox.prototype = {
 
       this.shortcuts = new KeyShortcuts({
         window: this.doc.defaultView
       });
       // Get the DOM element to mount the ToolboxController to.
       this._componentMount = this.doc.getElementById("toolbox-toolbar-mount");
 
       this._mountReactComponent();
-      this._buildDockButtons();
+      this._buildDockOptions();
       this._buildOptions();
       this._buildTabs();
       this._applyCacheSettings();
       this._applyServiceWorkersTestingSettings();
       this._addKeysToWindow();
       this._addReloadKeys();
       this._addHostListeners();
       this._registerOverlays();
@@ -1043,26 +1043,26 @@ Toolbox.prototype = {
       this._notificationBox = Object.assign(
         this.ReactDOM.render(NotificationBox({}), box),
         PriorityLevels);
     }
     return this._notificationBox;
   },
 
   /**
-   * Build the buttons for changing hosts. Called every time
+   * Build the options for changing hosts. Called every time
    * the host changes.
    */
-  _buildDockButtons: function() {
+  _buildDockOptions: function() {
     if (!this._target.isLocalTab) {
-      this.component.setDockButtonsEnabled(false);
+      this.component.setDockOptionsEnabled(false);
       return;
     }
 
-    this.component.setDockButtonsEnabled(true);
+    this.component.setDockOptionsEnabled(true);
     this.component.setCanCloseToolbox(this.hostType !== Toolbox.HostType.WINDOW);
 
     let sideEnabled = Services.prefs.getBoolPref(this._prefs.SIDE_ENABLED);
 
     let hostTypes = [];
     for (let type in Toolbox.HostType) {
       let position = Toolbox.HostType[type];
       if (position == this.hostType ||
@@ -2349,17 +2349,17 @@ Toolbox.prototype = {
     });
 
     return this.once("host-changed");
   },
 
   _onSwitchedHost: function({ hostType }) {
     this._hostType = hostType;
 
-    this._buildDockButtons();
+    this._buildDockOptions();
     this._addKeysToWindow();
 
     // We blurred the tools at start of switchHost, but also when clicking on
     // host switching button. We now have to restore the focus.
     this.focusTool(this.currentToolId, true);
 
     this.emit("host-changed");
     this._telemetry.log(HOST_HISTOGRAM, this._getTelemetryHostId());
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -182,31 +182,29 @@ devtools.jar:
     skin/images/item-toggle.svg (themes/images/item-toggle.svg)
     skin/images/item-arrow-dark-rtl.svg (themes/images/item-arrow-dark-rtl.svg)
     skin/images/item-arrow-dark-ltr.svg (themes/images/item-arrow-dark-ltr.svg)
     skin/images/item-arrow-rtl.svg (themes/images/item-arrow-rtl.svg)
     skin/images/item-arrow-ltr.svg (themes/images/item-arrow-ltr.svg)
     skin/images/dropmarker.svg (themes/images/dropmarker.svg)
     skin/boxmodel.css (themes/boxmodel.css)
     skin/images/geometry-editor.svg (themes/images/geometry-editor.svg)
+    skin/images/more.svg (themes/images/more.svg)
     skin/images/pause.svg (themes/images/pause.svg)
     skin/images/play.svg (themes/images/play.svg)
     skin/images/rewind.svg (themes/images/rewind.svg)
     skin/images/debugger-step-in.svg (themes/images/debugger-step-in.svg)
     skin/images/debugger-step-out.svg (themes/images/debugger-step-out.svg)
     skin/images/debugger-step-over.svg (themes/images/debugger-step-over.svg)
     skin/images/debugger-toggleBreakpoints.svg (themes/images/debugger-toggleBreakpoints.svg)
     skin/images/jump-definition.svg (themes/images/jump-definition.svg)
     skin/images/tracer-icon.png (themes/images/tracer-icon.png)
     skin/images/tracer-icon@2x.png (themes/images/tracer-icon@2x.png)
     skin/images/toggle-tools.png (themes/images/toggle-tools.png)
     skin/images/toggle-tools@2x.png (themes/images/toggle-tools@2x.png)
-    skin/images/dock-bottom.svg (themes/images/dock-bottom.svg)
-    skin/images/dock-side.svg (themes/images/dock-side.svg)
-    skin/images/dock-undock.svg (themes/images/dock-undock.svg)
     skin/floating-scrollbars-dark-theme.css (themes/floating-scrollbars-dark-theme.css)
     skin/floating-scrollbars-responsive-design.css (themes/floating-scrollbars-responsive-design.css)
     skin/inspector.css (themes/inspector.css)
     skin/images/profiler-stopwatch.svg (themes/images/profiler-stopwatch.svg)
     skin/images/debugging-addons.svg (themes/images/debugging-addons.svg)
     skin/images/debugging-tabs.svg (themes/images/debugging-tabs.svg)
     skin/images/debugging-workers.svg (themes/images/debugging-workers.svg)
     skin/images/gcli_sec_bad.svg (themes/images/gcli_sec_bad.svg)
@@ -272,30 +270,28 @@ devtools.jar:
 
     # Firebug Theme
     skin/images/firebug/read-only.svg (themes/images/firebug/read-only.svg)
     skin/images/firebug/twisty-closed-firebug.svg (themes/images/firebug/twisty-closed-firebug.svg)
     skin/images/firebug/twisty-open-firebug.svg (themes/images/firebug/twisty-open-firebug.svg)
     skin/images/firebug/arrow-down.svg (themes/images/firebug/arrow-down.svg)
     skin/images/firebug/arrow-up.svg (themes/images/firebug/arrow-up.svg)
     skin/images/firebug/close.svg (themes/images/firebug/close.svg)
+    skin/images/firebug/more.svg (themes/images/firebug/more.svg)
     skin/images/firebug/pause.svg (themes/images/firebug/pause.svg)
     skin/images/firebug/play.svg (themes/images/firebug/play.svg)
     skin/images/firebug/rewind.svg (themes/images/firebug/rewind.svg)
     skin/images/firebug/disable.svg (themes/images/firebug/disable.svg)
     skin/images/firebug/breakpoint.svg (themes/images/firebug/breakpoint.svg)
     skin/images/firebug/tool-options.svg (themes/images/firebug/tool-options.svg)
     skin/images/firebug/debugger-step-in.svg (themes/images/firebug/debugger-step-in.svg)
     skin/images/firebug/debugger-step-out.svg (themes/images/firebug/debugger-step-out.svg)
     skin/images/firebug/debugger-step-over.svg (themes/images/firebug/debugger-step-over.svg)
     skin/images/firebug/pane-collapse.svg (themes/images/firebug/pane-collapse.svg)
     skin/images/firebug/pane-expand.svg (themes/images/firebug/pane-expand.svg)
-    skin/images/firebug/dock-undock.svg (themes/images/firebug/dock-undock.svg)
-    skin/images/firebug/dock-side.svg (themes/images/firebug/dock-side.svg)
-    skin/images/firebug/dock-bottom.svg (themes/images/firebug/dock-bottom.svg)
     skin/images/firebug/commandline-icon.svg (themes/images/firebug/commandline-icon.svg)
     skin/images/firebug/debugger-blackbox.svg (themes/images/firebug/debugger-blackbox.svg)
     skin/images/firebug/debugger-prettyprint.svg (themes/images/firebug/debugger-prettyprint.svg)
     skin/images/firebug/debugger-toggleBreakpoints.svg (themes/images/firebug/debugger-toggleBreakpoints.svg)
     skin/images/firebug/tool-debugger-paused.svg (themes/images/firebug/tool-debugger-paused.svg)
     skin/images/firebug/command-pick.svg (themes/images/firebug/command-pick.svg)
     skin/images/firebug/command-console.svg (themes/images/firebug/command-console.svg)
     skin/images/firebug/command-eyedropper.svg (themes/images/firebug/command-eyedropper.svg)
@@ -343,9 +339,9 @@ devtools.jar:
     content/netmonitor/index.html (netmonitor/index.html)
     content/netmonitor/initializer.js (netmonitor/initializer.js)
 
     # Devtools-components
     skin/images/devtools-components/arrow.svg (themes/images/devtools-components/arrow.svg)
 
     # Devtools-reps
     skin/images/devtools-reps/jump-definition.svg (themes/images/devtools-reps/jump-definition.svg)
-    skin/images/devtools-reps/open-inspector.svg (themes/images/devtools-reps/open-inspector.svg)
\ No newline at end of file
+    skin/images/devtools-reps/open-inspector.svg (themes/images/devtools-reps/open-inspector.svg)
--- a/devtools/client/locales/en-US/toolbox.properties
+++ b/devtools/client/locales/en-US/toolbox.properties
@@ -1,16 +1,12 @@
 # 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/.
 
-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
-
 # LOCALIZATION NOTE (toolboxToggleButton.errors): Semi-colon list of plural
 # forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 number of errors in the current web page
 toolboxToggleButton.errors=#1 error;#1 errors
 
 # LOCALIZATION NOTE (toolboxToggleButton.warnings): Semi-colon list of plural
 # forms.
@@ -151,16 +147,27 @@ toolbox.frames.tooltip=Select an iframe 
 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.toolboxbutton.tooltip): This is the tooltip for
+# the "..." button on the developer tools toolbox.
+toolbox.menubutton.tooltip=Customize Developer Tools and get help
+
+# LOCALIZATION NOTE (toolbox.menu.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.menu.dock.bottom.label=Dock to bottom
+toolbox.menu.dock.side.label=Dock to side
+toolbox.menu.dock.window.label=Undock
+
 # LOCALIZATION NOTE (toolbox.closebutton.tooltip): This is the tooltip for
 # the close button the developer tools toolbox.
 toolbox.closebutton.tooltip=Close Developer Tools
 
 # LOCALIZATION NOTE (toolbox.allToolsButton.tooltip): This is the tooltip for the
 # "all tools" button displayed when some tools are hidden by overflow of the toolbar.
 toolbox.allToolsButton.tooltip=Select another tool
 
deleted file mode 100644
--- a/devtools/client/themes/images/dock-bottom.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="M10.004 3H.996C.999 3 1 3 1 3.002v9.996c0-.001.003.002-.004.002h9.008c-.003 0-.004 0-.004-.002V3.002c0 .001-.003-.002.004-.002zm0-1c.55 0 .996.456.996 1.002v9.996A.998.998 0 0 1 10.004 14H.996C.446 14 0 13.544 0 12.998V3.002A.998.998 0 0 1 .996 2h9.008zm-.41 8H.996v1h9.01v-1h-.41z"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/images/dock-side.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
-  <path d="M1 2.996v9.008c0-.003 0-.004.002-.004h9.996c-.001 0 .002-.003.002.004V2.996c0 .003 0 .004-.002.004H1.002C1.003 3 1 3.003 1 2.996zm-1 0C0 2.446.456 2 1.002 2h9.996A.998.998 0 0 1 12 2.996v9.008c0 .55-.456.996-1.002.996H1.002A.998.998 0 0 1 0 12.004V2.996zm8 .413V12h1V3H8v.41z"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/images/dock-undock.svg
+++ /dev/null
@@ -1,8 +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="M13.003 1.941H6.997c.008 0 .003.004.003.008v6.102c0 .004.004.008-.003.008h6.006c-.008 0-.003-.004-.003-.008V1.949c0-.004-.004-.008.003-.008zm0-.941c.55 0 .997.43.997.95v6.1c0 .525-.453.95-.997.95H6.997C6.447 9 6 8.57 6 8.05v-6.1c0-.525.453-.95.997-.95h6.006z"/>
-  <path d="M9 9.91v-.278h1v1.183c0 .516-.453.935-.997.935H2.997c-.55 0-.997-.43-.997-.95V4.7c0-.525.444-.95 1.006-.95h2.288v.941H3.006C3 4.691 3 4.691 3 4.7v6.102c0 .004.004.008-.003.008h6.006c-.004 0-.003-.001-.003.006v-.248-.657-.278h1v1.183c0 .516-.453.935-.997.935H2.997c-.55 0-.997-.43-.997-.95V4.7c0-.525.444-.95 1.006-.95h2.288v.941H3.006C3 4.691 3 4.691 3 4.7v6.102c0 .004.004.008-.003.008h6.006c-.004 0-.003-.001-.003.006v-.248-.657z"/>
-  <path d="M12.52 5H6.976v1h6.046V5zM6.5 7H2.975v1H7V7z"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/images/firebug/dock-bottom.svg
+++ /dev/null
@@ -1,25 +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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
-  <defs>
-    <linearGradient id="b">
-      <stop offset="0" stop-color="#f2451d"/>
-      <stop offset=".101" stop-color="#f01428" stop-opacity=".8"/>
-      <stop offset=".897" stop-color="#de8493"/>
-      <stop offset="1" stop-color="#efc3cc"/>
-    </linearGradient>
-    <linearGradient id="a">
-      <stop offset="0" stop-color="#520e0d"/>
-      <stop offset="1" stop-color="#c4181d"/>
-    </linearGradient>
-    <linearGradient x1="7.231" y1="1051.323" x2="7.231" y2="1037.401" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
-    <linearGradient x1="8.769" y1="1049.931" x2="8.769" y2="1038.668" id="c" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
-    <filter height="1.48" y="-.24" width="1.48" x="-.24" id="e" color-interpolation-filters="sRGB">
-      <feGaussianBlur stdDeviation=".8"/>
-    </filter>
-  </defs>
-  <rect y="1.5" x="1.5" ry="2" rx="2" height="13" width="13" fill="url(#c)" stroke="url(#d)" stroke-linejoin="round"/>
-  <path style="marker:none" d="M4.5 5v8h8V5zm1 1h6v4h-6z" color="#000" overflow="visible" opacity=".4" filter="url(#e)"/>
-  <path style="marker:none" d="M4 4v8h8V4zm1 1h6v4H5z" color="#000" overflow="visible" fill="#fff"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/images/firebug/dock-side.svg
+++ /dev/null
@@ -1,25 +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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
-  <defs>
-    <linearGradient id="b">
-      <stop offset="0" stop-color="#f2451d"/>
-      <stop offset=".101" stop-color="#f01428" stop-opacity=".8"/>
-      <stop offset=".897" stop-color="#de8493"/>
-      <stop offset="1" stop-color="#efc3cc"/>
-    </linearGradient>
-    <linearGradient id="a">
-      <stop offset="0" stop-color="#520e0d"/>
-      <stop offset="1" stop-color="#c4181d"/>
-    </linearGradient>
-    <linearGradient x1="7.231" y1="1051.323" x2="7.231" y2="1037.401" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
-    <linearGradient x1="8.769" y1="1049.931" x2="8.769" y2="1038.668" id="c" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
-    <filter height="1.48" y="-.24" width="1.48" x="-.24" id="e" color-interpolation-filters="sRGB">
-      <feGaussianBlur stdDeviation=".8"/>
-    </filter>
-  </defs>
-  <rect y="1.5" x="1.5" ry="2" rx="2" height="13" width="13" fill="url(#c)" stroke="url(#d)" stroke-linejoin="round"/>
-  <path style="marker:none" d="M4.5 5v8h8V5zm1 1h6v4h-6z" transform="rotate(-90 8.5 9)" color="#000" overflow="visible" opacity=".4" filter="url(#e)"/>
-  <path style="marker:none" d="M4 12h8V4H4zm1-1V5h4v6z" color="#000" overflow="visible" fill="#fff"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/images/firebug/dock-undock.svg
+++ /dev/null
@@ -1,27 +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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
-  <defs>
-    <linearGradient id="b">
-      <stop offset="0" stop-color="#f2451d"/>
-      <stop offset=".101" stop-color="#f01428" stop-opacity=".8"/>
-      <stop offset=".897" stop-color="#de8493"/>
-      <stop offset="1" stop-color="#efc3cc"/>
-    </linearGradient>
-    <linearGradient id="a">
-      <stop offset="0" stop-color="#520e0d"/>
-      <stop offset="1" stop-color="#c4181d"/>
-    </linearGradient>
-    <linearGradient x1="7.231" y1="1051.323" x2="7.231" y2="1037.401" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse"/>
-    <linearGradient x1="8.769" y1="1049.931" x2="8.769" y2="1038.668" id="c" xlink:href="#b" gradientUnits="userSpaceOnUse"/>
-    <filter x="-.24" y="-.24" width="1.48" height="1.48" color-interpolation-filters="sRGB" id="e">
-      <feGaussianBlur stdDeviation=".8"/>
-    </filter>
-  </defs>
-  <g transform="translate(0 -1036.362)">
-    <rect width="13" height="13" rx="2" ry="2" x="1.5" y="1037.862" fill="url(#c)" stroke="url(#d)" stroke-linejoin="round"/>
-    <path d="M6.5 1041.362v2h-2v6h6v-2h2v-6h-6zm1 1h4v4h-1v-3h-3v-1zm-2 2h4v4h-4v-4z" opacity=".4" filter="url(#e)"/>
-    <path d="M6 1040.362v2H4v6h6v-2h2v-6H6zm1 1h4v4h-1v-3H7v-1zm-2 2h4v4H5v-4z" fill="#fff"/>
-  </g>
-</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/firebug/more.svg
@@ -0,0 +1,21 @@
+<!-- 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 xmlns="http://www.w3.org/2000/svg" width="16" height="16"
+  viewBox="0 0 16 16">
+  <defs>
+    <linearGradient id="fill-grad" x1="0" x2="1" y1="0" y2="1">
+      <stop offset="0" stop-color="#abb3bd"/>
+      <stop offset="1" stop-color="#6a7786"/>
+    </linearGradient>
+    <linearGradient id="stroke-grad" x1="0" x2="1" y1="0" y2="1">
+      <stop offset="0" stop-color="#7e8b9a"/>
+      <stop offset="1" stop-color="#464f5a"/>
+    </linearGradient>
+  </defs>
+  <g fill="url(#fill-grad)" stroke="url(#stroke-grad)" stroke-width=".6">
+    <circle cx="2.3" cy="8" r="2"/>
+    <circle cx="8" cy="8" r="2"/>
+    <circle cx="13.7" cy="8" r="2"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/more.svg
@@ -0,0 +1,6 @@
+<!-- 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 xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <path fill="context-fill" d="M2 6a2 2 0 1 0 2 2 2 2 0 0 0-2-2zm6 0a2 2 0 1 0 2 2 2 2 0 0 0-2-2zm6 0a2 2 0 1 0 2 2 2 2 0 0 0-2-2z"></path>
+</svg>
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -1,37 +1,33 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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/. */
 
 :root {
   --close-button-image: url(chrome://devtools/skin/images/close.svg);
-  --dock-bottom-image: url(chrome://devtools/skin/images/dock-bottom.svg);
-  --dock-side-image: url(chrome://devtools/skin/images/dock-side.svg);
-  --dock-undock-image: url(chrome://devtools/skin/images/dock-undock.svg);
+  --more-button-image: url(chrome://devtools/skin/images/more.svg);
 
   --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-splitconsole-image: url(images/command-console.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);
 }
 
 .theme-firebug {
   --close-button-image: url(chrome://devtools/skin/images/firebug/close.svg);
-  --dock-bottom-image: url(chrome://devtools/skin/images/firebug/dock-bottom.svg);
-  --dock-side-image: url(chrome://devtools/skin/images/firebug/dock-side.svg);
-  --dock-undock-image: url(chrome://devtools/skin/images/firebug/dock-undock.svg);
+  --more-button-image: url(chrome://devtools/skin/images/firebug/more.svg);
 
   --command-paintflashing-image: url(images/firebug/command-paintflashing.svg);
   --command-screenshot-image: url(images/firebug/command-screenshot.svg);
   --command-responsive-image: url(images/firebug/command-responsivemode.svg);
   --command-scratchpad-image: url(images/firebug/command-scratchpad.svg);
   --command-pick-image: url(images/firebug/command-pick.svg);
   --command-frames-image: url(images/firebug/command-frames.svg);
   --command-splitconsole-image: url(images/firebug/command-console.svg);
@@ -75,18 +71,17 @@
   overflow: hidden;
 }
 
 /* Set flex attribute to Toolbox buttons and Picker container so,
    they don't overlap with the tab bar */
 #toolbox-buttons-start,
 #toolbox-buttons-end,
 #toolbox-option-container,
-#toolbox-controls,
-#toolbox-dock-buttons {
+#toolbox-controls {
   display: flex;
   align-items: stretch;
 }
 
 /* Toolbox tabs */
 
 .devtools-tab {
   position: relative;
@@ -215,33 +210,24 @@
   min-width: 12px;
   margin: 0 1px;
 }
 
 #toolbox-close::before {
   background-image: var(--close-button-image);
 }
 
-#toolbox-dock-bottom::before {
-  background-image: var(--dock-bottom-image);
-}
-
-#toolbox-dock-side::before {
-  background-image: var(--dock-side-image);
-}
-
-#toolbox-dock-window::before {
-  background-image: var(--dock-undock-image);
+#toolbox-menu-button::before {
+  background-image: var(--more-button-image);
 }
 
 /* Command buttons */
 
 .command-button,
-#toolbox-controls > button,
-#toolbox-dock-buttons > button {
+#toolbox-controls > button {
   /* !important is needed to override .devtools-button rules in common.css */
   padding: 0 !important;
   margin: 0 !important;
   border: none !important;
   border-radius: 0 !important;
   position: relative;
   min-width: 24px;
 }
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_nodes_highlight.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_nodes_highlight.js
@@ -38,14 +38,14 @@ add_task(async function() {
   let onNodeHighlight = toolbox.once("node-highlight");
   EventUtils.synthesizeMouseAtCenter(node, {type: "mousemove"}, view);
 
   let nodeFront = await onNodeHighlight;
   is(nodeFront.displayName, "h1", "The correct node was highlighted");
 
   info("Unhighlight the node by moving away from the node");
   let onNodeUnhighlight = toolbox.once("node-unhighlight");
-  let btn = toolbox.doc.querySelector(".toolbox-dock-button");
+  let btn = toolbox.doc.getElementById("toolbox-menu-button");
   EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"}, view);
 
   await onNodeUnhighlight;
   ok(true, "node-unhighlight event was fired when moving away from the node");
 });
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -1682,17 +1682,17 @@ function checkDomElementHighlightingForI
            "Expected attribute's name is present");
         is(attrs[i].value, testData.attrs[i].value,
            "Expected attribute's value is present");
       }
     }
 
     info("Unhighlight the node by moving away from the markup view");
     let onNodeUnhighlight = toolbox.once("node-unhighlight");
-    let btn = inspector.toolbox.doc.querySelector(".toolbox-dock-button");
+    let btn = inspector.toolbox.doc.getElementById("toolbox-menu-button");
     EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"},
       inspector.toolbox.win);
     yield onNodeUnhighlight;
 
     info("Switching back to the console");
     yield toolbox.selectTool("webconsole");
   }