Bug 1444301 - Move split console function to meatball menu; r=jryans
authorBrian Birtles <birtles@gmail.com>
Thu, 05 Apr 2018 10:13:22 +0900
changeset 412182 f00ccb9d81b7ada9c0aa1c97e8d6c5d09225f28a
parent 412181 220140d1c28ea1f09f758cb9f5da23f2cb845dc7
child 412183 9140b553db46cc8d8fcd39ef30a74d68649fb780
push id62302
push userbbirtles@mozilla.com
push dateFri, 06 Apr 2018 21:15:54 +0000
treeherderautoland@c5e2ad71fce5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans
bugs1444301
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1444301 - Move split console function to meatball menu; r=jryans MozReview-Commit-ID: GkMRdZMIUVa
devtools/client/definitions.js
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/startup.properties
devtools/client/locales/en-US/toolbox.properties
devtools/client/themes/images/command-console.svg
devtools/client/themes/toolbox.css
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_split.js
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_split_persist.js
devtools/client/webconsole/test/browser_webconsole_split.js
devtools/client/webconsole/test/browser_webconsole_split_persist.js
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -503,32 +503,16 @@ exports.defaultThemes = [
   Tools.darkTheme,
   Tools.lightTheme,
 ];
 
 // White-list buttons that can be toggled to prevent adding prefs for
 // addons that have manually inserted toolbarbuttons into DOM.
 // (By default, supported target is only local tab)
 exports.ToolboxButtons = [
-  { id: "command-button-splitconsole",
-    description: l10n("toolbox.buttons.splitconsole", "Esc"),
-    isTargetSupported: target => !target.isAddon,
-    onClick(event, toolbox) {
-      toolbox.toggleSplitConsole();
-    },
-    isChecked(toolbox) {
-      return toolbox.splitConsole;
-    },
-    setup(toolbox, onChange) {
-      toolbox.on("split-console", onChange);
-    },
-    teardown(toolbox, onChange) {
-      toolbox.off("split-console", onChange);
-    }
-  },
   { id: "command-button-paintflashing",
     description: l10n("toolbox.buttons.paintflashing"),
     isTargetSupported: target => target.isLocalTab,
     onClick(event, toolbox) {
       CommandUtils.executeOnTarget(toolbox.target, "paintflashing toggle");
     },
     isChecked(toolbox) {
       return CommandState.isEnabledForTarget(toolbox.target, "paintflashing");
--- a/devtools/client/framework/components/toolbox-controller.js
+++ b/devtools/client/framework/components/toolbox-controller.js
@@ -23,31 +23,33 @@ class ToolboxController extends Componen
       focusedButton: ELEMENT_PICKER_ID,
       toolboxButtons: [],
       currentToolId: null,
       highlightedTools: new Set(),
       panelDefinitions: [],
       hostTypes: [],
       areDockOptionsEnabled: true,
       canCloseToolbox: true,
+      isSplitConsoleActive: false,
       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.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;
@@ -132,16 +134,20 @@ class ToolboxController extends Componen
   setHostTypes(hostTypes) {
     this.setState({ hostTypes });
   }
 
   setCanCloseToolbox(canCloseToolbox) {
     this.setState({ canCloseToolbox }, this.updateButtonIds);
   }
 
+  setIsSplitConsoleActive(isSplitConsoleActive) {
+    this.setState({ isSplitConsoleActive });
+  }
+
   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
@@ -40,18 +40,22 @@ class ToolboxToolbar extends Component {
         switchHost: PropTypes.func.isRequired,
       })),
       // 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,
       // 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 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.
@@ -174,18 +178,23 @@ function renderSeparator() {
  * @param {Function} hostTypes[].switchHost
  *        Function to switch the host.
  * @param {boolean} areDockOptionsEnabled
  *        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 {Function} selectTool
  *        Function to select a tool based on its id.
+ * @param {Function} toggleSplitConsole
+ *        Function to turn the split console 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) {
@@ -239,46 +248,77 @@ function renderToolboxControls(props) {
 /**
  * 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
  *        Properties as described below.
+ * @param {string} props.currentToolId
+ *        The id of the currently selected tool.
  * @param {Object[]} props.hostTypes
  *        Array of host type objects.
  * @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 {Function} props.selectTool
  *        Function to select a tool based on its id.
+ * @param {Function} toggleSplitConsole
+ *        Function to turn the split console 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, {hostTypes, selectTool, L10N, toolbox}) {
+function showMeatballMenu(
+  menuButton,
+  {
+    currentToolId,
+    hostTypes,
+    isSplitConsoleActive,
+    selectTool,
+    toggleSplitConsole,
+    L10N,
+    toolbox,
+  }
+) {
   const menu = new Menu({ id: "toolbox-meatball-menu" });
 
   // Dock options
   for (const hostType of hostTypes) {
     menu.append(new MenuItem({
       id: `toolbox-meatball-menu-dock-${hostType.position}`,
       label: L10N.getStr(
         `toolbox.meatballMenu.dock.${hostType.position}.label`
       ),
       click: () => hostType.switchHost(),
     }));
   }
 
+  // Split console
+  if (currentToolId !== "webconsole") {
+    menu.append(new MenuItem({
+      id: "toolbox-meatball-menu-splitconsole",
+      label: L10N.getStr(
+        `toolbox.meatballMenu.${
+          isSplitConsoleActive ? "hideconsole" : "splitconsole"
+        }.label`
+      ),
+      accelerator: "Esc",
+      click: toggleSplitConsole,
+    }));
+  }
+
   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
@@ -151,16 +151,17 @@ function Toolbox(target, selectedTool, h
   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.toggleSplitConsole = this.toggleSplitConsole.bind(this);
 
   this._target.on("close", this.destroy);
 
   if (!selectedTool) {
     selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL);
   }
   this._defaultToolId = selectedTool;
 
@@ -1107,16 +1108,17 @@ Toolbox.prototype = {
   },
 
   _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,
       closeToolbox: this.destroy,
       focusButton: this._onToolbarFocus,
       toolbox: this
     });
 
     this.component = this.ReactDOM.render(element, this._componentMount);
   },
 
@@ -1903,31 +1905,33 @@ Toolbox.prototype = {
 
     // Ensure split console is visible if console was already loaded in background
     let iframe = this.webconsolePanel.querySelector(".toolbox-panel-iframe");
     if (iframe) {
       this.setIframeVisible(iframe, true);
     }
 
     return this.loadTool("webconsole").then(() => {
+      this.component.setIsSplitConsoleActive(true);
       this.emit("split-console");
       this.focusConsoleInput();
     });
   },
 
   /**
    * Closes the split console.
    *
    * @returns {Promise} a promise that resolves once the tool has been
    *          closed.
    */
   closeSplitConsole: function() {
     this._splitConsole = false;
     Services.prefs.setBoolPref(SPLITCONSOLE_ENABLED_PREF, false);
     this._refreshConsoleDisplay();
+    this.component.setIsSplitConsoleActive(false);
     this.emit("split-console");
 
     if (this._lastFocusedElement) {
       this._lastFocusedElement.focus();
     }
     return promise.resolve();
   },
 
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -143,17 +143,16 @@ devtools.jar:
     skin/rules.css (themes/rules.css)
     skin/commandline.css (themes/commandline.css)
     skin/images/command-paintflashing.svg (themes/images/command-paintflashing.svg)
     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-console.svg (themes/images/command-console.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)
--- a/devtools/client/locales/en-US/startup.properties
+++ b/devtools/client/locales/en-US/startup.properties
@@ -257,22 +257,16 @@ accessibility.panelLabel=Accessibility P
 accessibility.accesskey=y
 
 # LOCALIZATION NOTE (accessibility.tooltip2):
 # This string is displayed in the tooltip of the tab when the Accessibility is
 # displayed inside the developer tools window.
 # Keyboard shortcut for Accessibility panel will be shown inside the brackets.
 accessibility.tooltip2=Accessibility
 
-# LOCALIZATION NOTE (toolbox.buttons.splitconsole):
-# This is the tooltip of the button in the toolbox toolbar used to toggle
-# the split console.
-# Keyboard shortcut will be shown inside brackets.
-toolbox.buttons.splitconsole = Toggle split console (%S)
-
 # LOCALIZATION NOTE (toolbox.buttons.responsive):
 # This is the tooltip of the button in the toolbox toolbar that toggles
 # the Responsive mode.
 # Keyboard shortcut will be shown inside brackets.
 toolbox.buttons.responsive = Responsive Design Mode (%S)
 
 # LOCALIZATION NOTE (toolbox.buttons.paintflashing):
 # This is the tooltip of the paintflashing button in the toolbox toolbar
--- a/devtools/client/locales/en-US/toolbox.properties
+++ b/devtools/client/locales/en-US/toolbox.properties
@@ -154,16 +154,23 @@ toolbox.meatballMenu.button.tooltip=Cust
 
 # 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
 toolbox.meatballMenu.dock.side.label=Dock to side
 toolbox.meatballMenu.dock.window.label=Undock
 
+# 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.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 100644
--- a/devtools/client/themes/images/command-console.svg
+++ /dev/null
@@ -1,7 +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" viewBox="0 0 16 16" fill="context-fill #0b0b0b">
-  <path d="M6.8 9.7c0-.2 0-.3-.2-.4L4.9 7.6c-.3-.3-.7-.3-.9 0s-.3.6 0 .9l1.3 1.4L4 11.3c-.3.3-.3.6 0 .9s.6.3.9 0l1.8-1.8c.1-.2.2-.5.1-.7z"/>
-  <path d="M14.2 2H1.8c-.4 0-.8.4-.8.9v11.2c0 .4.3.9.8.9h12.4c.4 0 .8-.4.8-.9V2.9c0-.7-.6-.9-.8-.9zM14 14H2V6h12v8zm0-9H2V3h12v2z"/>
-</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-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);
 }
 
 /* Toolbox tabbar */
 
 .devtools-tabbar {
@@ -208,20 +207,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-splitconsole::before {
-  background-image: var(--command-splitconsole-image);
-}
-
 #command-button-noautohide::before {
   background-image: var(--command-noautohide-image);
 }
 
 #command-button-eyedropper::before {
   background-image: var(--command-eyedropper-image);
 }
 
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_split.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_split.js
@@ -2,16 +2,19 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,Web Console test for splitting";
 const {Toolbox} = require("devtools/client/framework/toolbox");
+const {LocalizationHelper} = require("devtools/shared/l10n");
+const L10N =
+  new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 // Test is slow on Linux EC2 instances - Bug 962931
 requestLongerTimeout(2);
 
 add_task(async function() {
   let toolbox;
 
   await addTab(TEST_URI);
@@ -29,18 +32,18 @@ add_task(async function() {
   checkHostType(Toolbox.HostType.WINDOW);
   checkToolboxUI();
   await toolbox.switchHost(Toolbox.HostType.BOTTOM);
 
   async function testConsoleLoadOnDifferentPanel() {
     info("About to check console loads even when non-webconsole panel is open");
 
     await openPanel("inspector");
-    let webconsoleReady = toolbox.once("webconsole-ready");
-    toolbox.toggleSplitConsole();
+    const webconsoleReady = toolbox.once("webconsole-ready");
+    await toolbox.toggleSplitConsole();
     await webconsoleReady;
     ok(true, "Webconsole has been triggered as loaded while another tool is active");
   }
 
   async function testKeyboardShortcuts() {
     info("About to check that panel responds to ESCAPE keyboard shortcut");
 
     let splitConsoleReady = toolbox.once("split-console");
@@ -55,144 +58,176 @@ add_task(async function() {
     await openAndCheckPanel("inspector");
     await openAndCheckPanel("styleeditor");
     await openAndCheckPanel("performance");
     await openAndCheckPanel("netmonitor");
 
     await checkWebconsolePanelOpened();
   }
 
-  function getCurrentUIState() {
+  async function getCurrentUIState() {
     let deck = toolbox.doc.querySelector("#toolbox-deck");
     let webconsolePanel = toolbox.webconsolePanel;
     let splitter = toolbox.doc.querySelector("#toolbox-console-splitter");
 
     let containerHeight = deck.parentNode.getBoundingClientRect().height;
     let deckHeight = deck.getBoundingClientRect().height;
     let webconsoleHeight = webconsolePanel.getBoundingClientRect().height;
     let splitterVisibility = !splitter.getAttribute("hidden");
     let openedConsolePanel = toolbox.currentToolId === "webconsole";
-    let cmdButton = toolbox.doc.querySelector("#command-button-splitconsole");
+    let menuLabel = await getMenuLabel(toolbox);
 
     return {
       deckHeight: deckHeight,
       containerHeight: containerHeight,
       webconsoleHeight: webconsoleHeight,
       splitterVisibility: splitterVisibility,
       openedConsolePanel: openedConsolePanel,
-      buttonSelected: cmdButton.classList.contains("checked")
+      menuLabel,
     };
   }
 
+  function getMenuLabel() {
+    return new Promise(resolve => {
+      const button = toolbox.doc.getElementById("toolbox-meatball-menu-button");
+      EventUtils.sendMouseEvent({ type: "click" }, button);
+
+      toolbox.doc.addEventListener("popupshown", () => {
+        const menuItem =
+          toolbox.doc.getElementById("toolbox-meatball-menu-splitconsole");
+
+        // Return undefined if the menu item is not available
+        let label;
+        if (menuItem) {
+          label =
+            menuItem.label ===
+            L10N.getStr("toolbox.meatballMenu.hideconsole.label")
+              ? "hide"
+              : "split";
+        }
+
+        // Wait for menu to close
+        toolbox.doc.addEventListener("popuphidden", () => {
+          resolve(label);
+        }, { once: true });
+        EventUtils.synthesizeKey("KEY_Escape");
+      }, { once: true });
+    });
+  }
+
   async function checkWebconsolePanelOpened() {
     info("About to check special cases when webconsole panel is open.");
 
     // Start with console split, so we can test for transition to main panel.
     await toolbox.toggleSplitConsole();
 
-    let currentUIState = getCurrentUIState();
+    let currentUIState = await getCurrentUIState();
 
     ok(currentUIState.splitterVisibility,
        "Splitter is visible when console is split");
     ok(currentUIState.deckHeight > 0,
        "Deck has a height > 0 when console is split");
     ok(currentUIState.webconsoleHeight > 0,
        "Web console has a height > 0 when console is split");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(currentUIState.buttonSelected, "The command button is selected");
+    is(currentUIState.menuLabel, "hide",
+       "The menu item indicates the console is split");
 
     await openPanel("webconsole");
-    currentUIState = getCurrentUIState();
+    currentUIState = await getCurrentUIState();
 
     ok(!currentUIState.splitterVisibility,
        "Splitter is hidden when console is opened.");
     is(currentUIState.deckHeight, 0,
        "Deck has a height == 0 when console is opened.");
     is(currentUIState.webconsoleHeight, currentUIState.containerHeight,
        "Web console is full height.");
     ok(currentUIState.openedConsolePanel,
        "The console panel is the current tool");
-    ok(currentUIState.buttonSelected,
-       "The command button is still selected.");
+    is(currentUIState.menuLabel, undefined,
+       "The menu item is hidden when console is opened");
 
     // Make sure splitting console does nothing while webconsole is opened
     await toolbox.toggleSplitConsole();
 
-    currentUIState = getCurrentUIState();
+    currentUIState = await getCurrentUIState();
 
     ok(!currentUIState.splitterVisibility,
        "Splitter is hidden when console is opened.");
     is(currentUIState.deckHeight, 0,
        "Deck has a height == 0 when console is opened.");
     is(currentUIState.webconsoleHeight, currentUIState.containerHeight,
        "Web console is full height.");
     ok(currentUIState.openedConsolePanel,
        "The console panel is the current tool");
-    ok(currentUIState.buttonSelected,
-       "The command button is still selected.");
+    is(currentUIState.menuLabel, undefined,
+       "The menu item is hidden when console is opened");
 
     // Make sure that split state is saved after opening another panel
     await openPanel("inspector");
-    currentUIState = getCurrentUIState();
+    currentUIState = await getCurrentUIState();
     ok(currentUIState.splitterVisibility,
        "Splitter is visible when console is split");
     ok(currentUIState.deckHeight > 0,
        "Deck has a height > 0 when console is split");
     ok(currentUIState.webconsoleHeight > 0,
        "Web console has a height > 0 when console is split");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(currentUIState.buttonSelected,
-       "The command button is still selected.");
+    is(currentUIState.menuLabel, "hide",
+       "The menu item still indicates the console is split");
 
     await toolbox.toggleSplitConsole();
   }
 
   async function checkToolboxUI() {
-    let currentUIState = getCurrentUIState();
+    let currentUIState = await getCurrentUIState();
 
     ok(!currentUIState.splitterVisibility, "Splitter is hidden by default");
     is(currentUIState.deckHeight, currentUIState.containerHeight,
        "Deck has a height > 0 by default");
     is(currentUIState.webconsoleHeight, 0,
        "Web console is collapsed by default");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(!currentUIState.buttonSelected, "The command button is not selected.");
+    is(currentUIState.menuLabel, "split",
+       "The menu item indicates the console is not split");
 
     await toolbox.toggleSplitConsole();
 
-    currentUIState = getCurrentUIState();
+    currentUIState = await getCurrentUIState();
 
     ok(currentUIState.splitterVisibility,
        "Splitter is visible when console is split");
     ok(currentUIState.deckHeight > 0,
        "Deck has a height > 0 when console is split");
     ok(currentUIState.webconsoleHeight > 0,
        "Web console has a height > 0 when console is split");
     is(Math.round(currentUIState.deckHeight + currentUIState.webconsoleHeight),
        currentUIState.containerHeight,
        "Everything adds up to container height");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(currentUIState.buttonSelected, "The command button is selected.");
+    is(currentUIState.menuLabel, "hide",
+       "The menu item indicates the console is split");
 
     await toolbox.toggleSplitConsole();
 
-    currentUIState = getCurrentUIState();
+    currentUIState = await getCurrentUIState();
 
     ok(!currentUIState.splitterVisibility, "Splitter is hidden after toggling");
     is(currentUIState.deckHeight, currentUIState.containerHeight,
        "Deck has a height > 0 after toggling");
     is(currentUIState.webconsoleHeight, 0,
        "Web console is collapsed after toggling");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(!currentUIState.buttonSelected, "The command button is not selected.");
+    is(currentUIState.menuLabel, "split",
+       "The menu item indicates the console is not split");
   }
 
   async function openPanel(toolId) {
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     toolbox = await gDevTools.showToolbox(target, toolId);
   }
 
   async function openAndCheckPanel(toolId) {
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_split_persist.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_split_persist.js
@@ -2,57 +2,63 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that the split console state is persisted.
 
+let {LocalizationHelper} = require("devtools/shared/l10n");
+let L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+
 const TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for splitting</p>";
 
 add_task(async function() {
   info("Opening a tab while there is no user setting on split console pref");
   let toolbox = await openNewTabAndToolbox(TEST_URI, "inspector");
   ok(!toolbox.splitConsole, "Split console is hidden by default");
-  ok(!isCommandButtonChecked(toolbox), "Split console button is unchecked by default.");
+  ok(!(await doesMenuSayHide(toolbox)),
+     "Split console menu item says split by default");
 
   await toggleSplitConsoleWithEscape(toolbox);
   ok(toolbox.splitConsole, "Split console is now visible.");
-  ok(isCommandButtonChecked(toolbox), "Split console button is now checked.");
+  ok(await doesMenuSayHide(toolbox), "Split console menu item now says hide");
   ok(getVisiblePrefValue(), "Visibility pref is true");
 
   is(getHeightPrefValue(), toolbox.webconsolePanel.height,
      "Panel height matches the pref");
   toolbox.webconsolePanel.height = 200;
 
   await toolbox.destroy();
 
   info("Opening a tab while there is a true user setting on split console pref");
   toolbox = await openNewTabAndToolbox(TEST_URI, "inspector");
   ok(toolbox.splitConsole, "Split console is visible by default.");
 
-  ok(isCommandButtonChecked(toolbox), "Split console button is checked by default.");
+  ok(await doesMenuSayHide(toolbox),
+     "Split console menu item initially says hide");
   is(getHeightPrefValue(), 200, "Height is set based on panel height after closing");
 
   let activeElement = getActiveElement(toolbox.doc);
   let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode;
   is(activeElement, inputNode, "Split console input is focused by default");
 
   toolbox.webconsolePanel.height = 1;
   ok(toolbox.webconsolePanel.clientHeight > 1,
      "The actual height of the console is bound with a min height");
 
   toolbox.webconsolePanel.height = 10000;
   ok(toolbox.webconsolePanel.clientHeight < 10000,
      "The actual height of the console is bound with a max height");
 
   await toggleSplitConsoleWithEscape(toolbox);
   ok(!toolbox.splitConsole, "Split console is now hidden.");
-  ok(!isCommandButtonChecked(toolbox), "Split console button is now unchecked.");
+  ok(!(await doesMenuSayHide(toolbox)),
+     "Split console menu item now says split");
   ok(!getVisiblePrefValue(), "Visibility pref is false");
 
   await toolbox.destroy();
 
   is(getHeightPrefValue(), 10000, "Height is set based on panel height after closing");
 
   info("Opening a tab while there is a false user setting on split " +
        "console pref");
@@ -75,19 +81,38 @@ function getActiveElement(doc) {
 function getVisiblePrefValue() {
   return Services.prefs.getBoolPref("devtools.toolbox.splitconsoleEnabled");
 }
 
 function getHeightPrefValue() {
   return Services.prefs.getIntPref("devtools.toolbox.splitconsoleHeight");
 }
 
-function isCommandButtonChecked(toolbox) {
-  return toolbox.doc.querySelector("#command-button-splitconsole")
-    .classList.contains("checked");
+function doesMenuSayHide(toolbox) {
+  return new Promise(resolve => {
+    const button = toolbox.doc.getElementById("toolbox-meatball-menu-button");
+    EventUtils.sendMouseEvent({ type: "click" }, button);
+
+    toolbox.doc.addEventListener("popupshown", () => {
+      const menuItem =
+        toolbox.doc.getElementById("toolbox-meatball-menu-splitconsole");
+
+      const result =
+        menuItem &&
+        menuItem.label ===
+          L10N.getStr("toolbox.meatballMenu.hideconsole.label");
+
+      toolbox.doc.addEventListener("popuphidden", () => {
+        resolve(result);
+      },
+      { once: true });
+      EventUtils.synthesizeKey("KEY_Escape");
+    },
+    { once: true });
+  });
 }
 
 function toggleSplitConsoleWithEscape(toolbox) {
   let onceSplitConsole = toolbox.once("split-console");
   let toolboxWindow = toolbox.win;
   toolboxWindow.focus();
   EventUtils.sendKey("ESCAPE", toolboxWindow);
   return onceSplitConsole;
--- a/devtools/client/webconsole/test/browser_webconsole_split.js
+++ b/devtools/client/webconsole/test/browser_webconsole_split.js
@@ -8,32 +8,39 @@
 const TEST_URI = "data:text/html;charset=utf-8,Web Console test for splitting";
 
 function test() {
   waitForExplicitFinish();
   // Test is slow on Linux EC2 instances - Bug 962931
   requestLongerTimeout(2);
 
   let {Toolbox} = require("devtools/client/framework/toolbox");
+  let {LocalizationHelper} = require("devtools/shared/l10n");
+  let L10N =
+    new LocalizationHelper("devtools/client/locales/toolbox.properties");
   let toolbox;
 
   loadTab(TEST_URI).then(testConsoleLoadOnDifferentPanel);
 
   function testConsoleLoadOnDifferentPanel() {
     info("About to check console loads even when non-webconsole panel is open");
 
     openPanel("inspector").then(() => {
+      let initialOpenComplete;
+
       toolbox.on("webconsole-ready", () => {
         ok(true, "Webconsole has been triggered as loaded while another tool " +
                  "is active");
-        testKeyboardShortcuts();
+        initialOpenComplete.then(() => {
+          testKeyboardShortcuts();
+        });
       });
 
       // Opens split console.
-      toolbox.toggleSplitConsole();
+      initialOpenComplete = toolbox.toggleSplitConsole();
     });
   }
 
   function testKeyboardShortcuts() {
     info("About to check that panel responds to ESCAPE keyboard shortcut");
 
     toolbox.once("split-console", () => {
       ok(true, "Split console has been triggered via ESCAPE keypress");
@@ -54,148 +61,179 @@ function test() {
       yield openAndCheckPanel("performance");
       yield openAndCheckPanel("netmonitor");
 
       yield checkWebconsolePanelOpened();
       testBottomHost();
     });
   }
 
-  function getCurrentUIState() {
+  async function getCurrentUIState() {
     let win = toolbox.win;
     let deck = toolbox.doc.querySelector("#toolbox-deck");
     let webconsolePanel = toolbox.webconsolePanel;
     let splitter = toolbox.doc.querySelector("#toolbox-console-splitter");
 
     let containerHeight = parseFloat(win.getComputedStyle(deck.parentNode)
       .getPropertyValue("height"));
     let deckHeight = parseFloat(win.getComputedStyle(deck)
       .getPropertyValue("height"));
     let webconsoleHeight = parseFloat(win.getComputedStyle(webconsolePanel)
       .getPropertyValue("height"));
     let splitterVisibility = !splitter.getAttribute("hidden");
     let openedConsolePanel = toolbox.currentToolId === "webconsole";
-    let cmdButton = toolbox.doc.querySelector("#command-button-splitconsole");
 
     return {
       deckHeight: deckHeight,
       containerHeight: containerHeight,
       webconsoleHeight: webconsoleHeight,
       splitterVisibility: splitterVisibility,
       openedConsolePanel: openedConsolePanel,
-      buttonSelected: cmdButton.classList.contains("checked")
+      menuLabel: await getMenuLabel(toolbox),
     };
   }
 
+  function getMenuLabel(toolbox) {
+    return new Promise(resolve => {
+      const button = toolbox.doc.getElementById("toolbox-meatball-menu-button");
+      EventUtils.sendMouseEvent({ type: "click" }, button);
+
+      toolbox.doc.addEventListener("popupshown", () => {
+        const menuItem =
+          toolbox.doc.getElementById("toolbox-meatball-menu-splitconsole");
+
+        // Return undefined if the menu item is not available
+        let label;
+        if (menuItem) {
+          label =
+            menuItem.label ===
+            L10N.getStr("toolbox.meatballMenu.hideconsole.label")
+              ? "hide"
+              : "split";
+        }
+
+        // Wait for menu to close
+        toolbox.doc.addEventListener("popuphidden", () => {
+          resolve(label);
+        }, { once: true });
+        EventUtils.synthesizeKey("KEY_Escape");
+      }, { once: true });
+    });
+  }
+
   const checkWebconsolePanelOpened = Task.async(function* () {
     info("About to check special cases when webconsole panel is open.");
 
     // Start with console split, so we can test for transition to main panel.
     yield toolbox.toggleSplitConsole();
 
-    let currentUIState = getCurrentUIState();
+    let currentUIState = yield getCurrentUIState();
 
     ok(currentUIState.splitterVisibility,
        "Splitter is visible when console is split");
     ok(currentUIState.deckHeight > 0,
        "Deck has a height > 0 when console is split");
     ok(currentUIState.webconsoleHeight > 0,
        "Web console has a height > 0 when console is split");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(currentUIState.buttonSelected, "The command button is selected");
+    is(currentUIState.menuLabel, "hide",
+       "The menu item indicates the console is split");
 
     yield openPanel("webconsole");
-    currentUIState = getCurrentUIState();
+    currentUIState = yield getCurrentUIState();
 
     ok(!currentUIState.splitterVisibility,
        "Splitter is hidden when console is opened.");
     is(currentUIState.deckHeight, 0,
        "Deck has a height == 0 when console is opened.");
     is(currentUIState.webconsoleHeight, currentUIState.containerHeight,
        "Web console is full height.");
     ok(currentUIState.openedConsolePanel,
        "The console panel is the current tool");
-    ok(currentUIState.buttonSelected,
-       "The command button is still selected.");
+    is(currentUIState.menuLabel, undefined,
+       "The menu item is hidden when console is opened");
 
     // Make sure splitting console does nothing while webconsole is opened
     yield toolbox.toggleSplitConsole();
 
-    currentUIState = getCurrentUIState();
+    currentUIState = yield getCurrentUIState();
 
     ok(!currentUIState.splitterVisibility,
        "Splitter is hidden when console is opened.");
     is(currentUIState.deckHeight, 0,
        "Deck has a height == 0 when console is opened.");
     is(currentUIState.webconsoleHeight, currentUIState.containerHeight,
        "Web console is full height.");
     ok(currentUIState.openedConsolePanel,
        "The console panel is the current tool");
-    ok(currentUIState.buttonSelected,
-       "The command button is still selected.");
+    is(currentUIState.menuLabel, undefined,
+       "The menu item is hidden when console is opened");
 
     // Make sure that split state is saved after opening another panel
     yield openPanel("inspector");
-    currentUIState = getCurrentUIState();
+    currentUIState = yield getCurrentUIState();
     ok(currentUIState.splitterVisibility,
        "Splitter is visible when console is split");
     ok(currentUIState.deckHeight > 0,
        "Deck has a height > 0 when console is split");
     ok(currentUIState.webconsoleHeight > 0,
        "Web console has a height > 0 when console is split");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(currentUIState.buttonSelected,
-       "The command button is still selected.");
+    is(currentUIState.menuLabel, "hide",
+       "The menu item still indicates the console is split");
 
     yield toolbox.toggleSplitConsole();
   });
 
   const checkToolboxUI = Task.async(function* () {
-    let currentUIState = getCurrentUIState();
+    let currentUIState = yield getCurrentUIState();
 
     ok(!currentUIState.splitterVisibility, "Splitter is hidden by default");
     is(currentUIState.deckHeight, currentUIState.containerHeight,
        "Deck has a height > 0 by default");
     is(currentUIState.webconsoleHeight, 0,
        "Web console is collapsed by default");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(!currentUIState.buttonSelected, "The command button is not selected.");
+    is(currentUIState.menuLabel, "split",
+       "The menu item indicates the console is not split");
 
     yield toolbox.toggleSplitConsole();
 
-    currentUIState = getCurrentUIState();
+    currentUIState = yield getCurrentUIState();
 
     ok(currentUIState.splitterVisibility,
        "Splitter is visible when console is split");
     ok(currentUIState.deckHeight > 0,
        "Deck has a height > 0 when console is split");
     ok(currentUIState.webconsoleHeight > 0,
        "Web console has a height > 0 when console is split");
     is(Math.round(currentUIState.deckHeight + currentUIState.webconsoleHeight),
        currentUIState.containerHeight,
        "Everything adds up to container height");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(currentUIState.buttonSelected, "The command button is selected.");
+    is(currentUIState.menuLabel, "hide",
+       "The menu item indicates the console is split");
 
     yield toolbox.toggleSplitConsole();
 
-    currentUIState = getCurrentUIState();
+    currentUIState = yield getCurrentUIState();
 
     ok(!currentUIState.splitterVisibility, "Splitter is hidden after toggling");
     is(currentUIState.deckHeight, currentUIState.containerHeight,
        "Deck has a height > 0 after toggling");
     is(currentUIState.webconsoleHeight, 0,
        "Web console is collapsed after toggling");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
-    ok(!currentUIState.buttonSelected, "The command button is not selected.");
+    is(currentUIState.menuLabel, "split",
+       "The menu item indicates the console is not split");
   });
 
   function openPanel(toolId) {
     let deferred = defer();
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     gDevTools.showToolbox(target, toolId).then(function (box) {
       toolbox = box;
       deferred.resolve();
--- a/devtools/client/webconsole/test/browser_webconsole_split_persist.js
+++ b/devtools/client/webconsole/test/browser_webconsole_split_persist.js
@@ -3,50 +3,54 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
  "use strict";
 
  function test() {
   info("Test that the split console state is persisted");
 
+  let {LocalizationHelper} = require("devtools/shared/l10n");
+  let L10N =
+    new LocalizationHelper("devtools/client/locales/toolbox.properties");
+
   let toolbox;
   let TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " +
                  "splitting</p>";
 
   Task.spawn(runner).then(finish);
 
   function* runner() {
     info("Opening a tab while there is no user setting on split console pref");
     let {tab} = yield loadTab(TEST_URI);
     let target = TargetFactory.forTab(tab);
     toolbox = yield gDevTools.showToolbox(target, "inspector");
 
     ok(!toolbox.splitConsole, "Split console is hidden by default.");
-    ok(!isCommandButtonChecked(), "Split console button is unchecked by " +
-                                  "default.");
+    ok(!(yield doesMenuSayHide()),
+       "Split console menu item says split by default");
     yield toggleSplitConsoleWithEscape();
     ok(toolbox.splitConsole, "Split console is now visible.");
-    ok(isCommandButtonChecked(), "Split console button is now checked.");
+    ok(yield doesMenuSayHide(), "Split console menu item now says hide");
     ok(getVisiblePrefValue(), "Visibility pref is true");
 
     is(getHeightPrefValue(), toolbox.webconsolePanel.height,
        "Panel height matches the pref");
     toolbox.webconsolePanel.height = 200;
 
     yield toolbox.destroy();
 
     info("Opening a tab while there is a true user setting on split console " +
          "pref");
     ({tab} = yield loadTab(TEST_URI));
     target = TargetFactory.forTab(tab);
     toolbox = yield gDevTools.showToolbox(target, "inspector");
 
     ok(toolbox.splitConsole, "Split console is visible by default.");
-    ok(isCommandButtonChecked(), "Split console button is checked by default.");
+    ok(yield doesMenuSayHide(), "Split console menu item initially says hide");
     is(getHeightPrefValue(), 200, "Height is set based on panel height after " +
                                   "closing");
 
     // Use the binding element since jsterm.inputNode is a XUL textarea element.
     let activeElement = getActiveElement(toolbox.doc);
     activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
     let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode;
     is(activeElement, inputNode, "Split console input is focused by default");
@@ -56,17 +60,17 @@
        "The actual height of the console is bound with a min height");
 
     toolbox.webconsolePanel.height = 10000;
     ok(toolbox.webconsolePanel.clientHeight < 10000,
        "The actual height of the console is bound with a max height");
 
     yield toggleSplitConsoleWithEscape();
     ok(!toolbox.splitConsole, "Split console is now hidden.");
-    ok(!isCommandButtonChecked(), "Split console button is now unchecked.");
+    ok(!(yield doesMenuSayHide()), "Split console menu item now says split");
     ok(!getVisiblePrefValue(), "Visibility pref is false");
 
     yield toolbox.destroy();
 
     is(getHeightPrefValue(), 10000,
        "Height is set based on panel height after closing");
 
     info("Opening a tab while there is a false user setting on split " +
@@ -92,26 +96,45 @@
   function getVisiblePrefValue() {
     return Services.prefs.getBoolPref("devtools.toolbox.splitconsoleEnabled");
   }
 
   function getHeightPrefValue() {
     return Services.prefs.getIntPref("devtools.toolbox.splitconsoleHeight");
   }
 
-  function isCommandButtonChecked() {
-    return toolbox.doc.querySelector("#command-button-splitconsole")
-      .classList.contains("checked");
+  function doesMenuSayHide() {
+    return new Promise(resolve => {
+      const button = toolbox.doc.getElementById("toolbox-meatball-menu-button");
+      EventUtils.sendMouseEvent({ type: "click" }, button);
+
+      toolbox.doc.addEventListener("popupshown", () => {
+        const menuItem =
+          toolbox.doc.getElementById("toolbox-meatball-menu-splitconsole");
+
+        const result =
+          menuItem &&
+          menuItem.label ===
+            L10N.getStr("toolbox.meatballMenu.hideconsole.label");
+
+        toolbox.doc.addEventListener("popuphidden", () => {
+          resolve(result);
+        },
+        { once: true });
+        EventUtils.synthesizeKey("KEY_Escape");
+      },
+      { once: true });
+    });
   }
 
   function toggleSplitConsoleWithEscape() {
     let onceSplitConsole = toolbox.once("split-console");
     let contentWindow = toolbox.win;
     contentWindow.focus();
-    EventUtils.sendKey("ESCAPE", contentWindow);
+    EventUtils.synthesizeKey("KEY_Escape");
     return onceSplitConsole;
   }
 
   function finish() {
     toolbox = TEST_URI = null;
     finishTest();
   }
 }