Bug 1266419 - HTML replacement for Toolbars and Toolbar buttons; r=bgrins, ntim
authorJan Odvarko <odvarko@gmail.com>
Wed, 04 May 2016 20:58:49 +0200
changeset 340538 aaae402831aa3948b5c0e3be27a6f43ca0137218
parent 340537 62ce2708b5beb734fc235e31498deb92f0e5fb2e
child 340539 703087e3d82998d8c0fddefaa04d357ff8a62f49
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins, ntim
bugs1266419
milestone49.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 1266419 - HTML replacement for Toolbars and Toolbar buttons; r=bgrins, ntim
devtools/client/framework/test/browser_toolbox_keyboard_navigation.js
devtools/client/framework/test/browser_toolbox_options_disable_buttons.js
devtools/client/framework/toolbox.js
devtools/client/framework/toolbox.xul
devtools/client/inspector/inspector-panel.js
devtools/client/inspector/inspector.xul
devtools/client/shared/developer-toolbar.js
devtools/client/themes/firebug-theme.css
devtools/client/themes/inspector.css
devtools/client/themes/toolbars.css
--- a/devtools/client/framework/test/browser_toolbox_keyboard_navigation.js
+++ b/devtools/client/framework/test/browser_toolbox_keyboard_navigation.js
@@ -28,17 +28,17 @@ function testFocus(aDoc, aToolbar, aElm)
 
 add_task(function* () {
   info("Create a test tab and open the toolbox");
   let toolbox = yield openNewTabAndToolbox(TEST_URL, "webconsole");
   let doc = toolbox.doc;
 
   let toolbar = doc.querySelector(".devtools-tabbar");
   let toolbarControls = [...toolbar.querySelectorAll(
-    ".devtools-tab, toolbarbutton")].filter(elm =>
+    ".devtools-tab, button")].filter(elm =>
       !elm.hidden && doc.defaultView.getComputedStyle(elm).getPropertyValue(
         "display") !== "none");
 
   // Put the keyboard focus onto the first toolbar control.
   toolbarControls[0].focus();
   ok(containsFocus(doc, toolbar), "Focus is within the toolbar");
 
   // Move the focus away from toolbar to a next focusable element.
--- a/devtools/client/framework/test/browser_toolbox_options_disable_buttons.js
+++ b/devtools/client/framework/test/browser_toolbox_options_disable_buttons.js
@@ -110,17 +110,17 @@ function testToggleToolboxButtons() {
       "There should be a single toggle checkbox for: " + id);
     is(matchedButtons.length, 1,
       "There should be a DOM button for: " + id);
     is(matchedButtons[0], tool.button,
       "DOM buttons should match for: " + id);
 
     is(matchedCheckboxes[0].nextSibling.textContent, tool.label,
       "The label for checkbox matches the tool definition.");
-    is(matchedButtons[0].getAttribute("tooltiptext"), tool.label,
+    is(matchedButtons[0].getAttribute("title"), tool.label,
       "The tooltip for button matches the tool definition.");
   }
 
   // Store modified pref names so that they can be cleared on error.
   for (let tool of toggleableTools) {
     let pref = tool.visibilityswitch;
     modifiedPrefs.push(pref);
   }
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -10,16 +10,17 @@ const MAX_ORDINAL = 99;
 const ZOOM_PREF = "devtools.toolbox.zoomValue";
 const SPLITCONSOLE_ENABLED_PREF = "devtools.toolbox.splitconsoleEnabled";
 const SPLITCONSOLE_HEIGHT_PREF = "devtools.toolbox.splitconsoleHeight";
 const MIN_ZOOM = 0.5;
 const MAX_ZOOM = 2;
 const OS_HISTOGRAM = "DEVTOOLS_OS_ENUMERATED_PER_USER";
 const OS_IS_64_BITS = "DEVTOOLS_OS_IS_64_BITS_PER_USER";
 const SCREENSIZE_HISTOGRAM = "DEVTOOLS_SCREEN_RESOLUTION_ENUMERATED_PER_USER";
+const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 var {Cc, Ci, Cu} = require("chrome");
 var promise = require("promise");
 var Services = require("Services");
 var {Task} = require("devtools/shared/task");
 var {gDevTools} = require("devtools/client/framework/devtools");
 var EventEmitter = require("devtools/shared/event-emitter");
 var Telemetry = require("devtools/client/shared/telemetry");
@@ -406,25 +407,25 @@ Toolbox.prototype = {
       // Attach the thread
       this._threadClient = yield attachThread(this);
       yield domReady.promise;
 
       this.isReady = true;
       let framesPromise = this._listFrames();
 
       this.closeButton = this.doc.getElementById("toolbox-close");
-      this.closeButton.addEventListener("command", this.destroy, true);
+      this.closeButton.addEventListener("click", this.destroy, true);
 
       gDevTools.on("pref-changed", this._prefChanged);
 
       let framesMenu = this.doc.getElementById("command-button-frames");
       framesMenu.addEventListener("click", this.showFramesMenu, false);
 
       let noautohideMenu = this.doc.getElementById("command-button-noautohide");
-      noautohideMenu.addEventListener("command", this._toggleAutohide, true);
+      noautohideMenu.addEventListener("click", this._toggleAutohide, true);
 
       this.textboxContextMenuPopup =
         this.doc.getElementById("toolbox-textbox-context-popup");
       this.textboxContextMenuPopup.addEventListener("popupshowing",
         this._updateTextboxMenuItems, true);
 
       var shortcuts = new KeyShortcuts({
         window: this.doc.defaultView
@@ -840,20 +841,21 @@ Toolbox.prototype = {
     }
 
     if (!this._target.isLocalTab) {
       return;
     }
 
     // Bottom-type host can be minimized, add a button for this.
     if (this.hostType == Toolbox.HostType.BOTTOM) {
-      let minimizeBtn = this.doc.createElement("toolbarbutton");
+      let minimizeBtn = this.doc.createElementNS(HTML_NS, "button");
       minimizeBtn.id = "toolbox-dock-bottom-minimize";
+      minimizeBtn.className = "devtools-button";
 
-      minimizeBtn.addEventListener("command", this._toggleMinimizeMode);
+      minimizeBtn.addEventListener("click", this._toggleMinimizeMode);
       dockBox.appendChild(minimizeBtn);
       // Show the button in its maximized state.
       this._onBottomHostMaximized();
 
       // Update the label and icon when the state changes.
       this._host.on("minimized", this._onBottomHostMinimized);
       this._host.on("maximized", this._onBottomHostMaximized);
       // Maximize again when a tool gets selected.
@@ -873,22 +875,22 @@ Toolbox.prototype = {
     for (let type in Toolbox.HostType) {
       let position = Toolbox.HostType[type];
       if (position == this.hostType ||
           position == Toolbox.HostType.CUSTOM ||
           (!sideEnabled && position == Toolbox.HostType.SIDE)) {
         continue;
       }
 
-      let button = this.doc.createElement("toolbarbutton");
+      let button = this.doc.createElementNS(HTML_NS, "button");
       button.id = "toolbox-dock-" + position;
-      button.className = "toolbox-dock-button";
-      button.setAttribute("tooltiptext", toolboxStrings("toolboxDockButtons." +
-                                                        position + ".tooltip"));
-      button.addEventListener("command", () => {
+      button.className = "toolbox-dock-button devtools-button";
+      button.setAttribute("title", toolboxStrings("toolboxDockButtons." +
+                                                  position + ".tooltip"));
+      button.addEventListener("click", () => {
         this.switchHost(position);
       });
 
       dockBox.appendChild(button);
     }
   },
 
   _getMinimizeButtonShortcutTooltip: function () {
@@ -896,26 +898,26 @@ Toolbox.prototype = {
     let key = KeyShortcuts.parseElectronKey(this.win, str);
     return "(" + KeyShortcuts.stringify(key) + ")";
   },
 
   _onBottomHostMinimized: function () {
     let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize");
     btn.className = "minimized";
 
-    btn.setAttribute("tooltiptext",
+    btn.setAttribute("title",
       toolboxStrings("toolboxDockButtons.bottom.maximize") + " " +
       this._getMinimizeButtonShortcutTooltip());
   },
 
   _onBottomHostMaximized: function () {
     let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize");
     btn.className = "maximized";
 
-    btn.setAttribute("tooltiptext",
+    btn.setAttribute("title",
       toolboxStrings("toolboxDockButtons.bottom.minimize") + " " +
       this._getMinimizeButtonShortcutTooltip());
   },
 
   _onToolSelectWhileMinimized: function () {
     this._host.maximize();
   },
 
@@ -1054,27 +1056,27 @@ Toolbox.prototype = {
     });
   },
 
   /**
    * Adding the element picker button is done here unlike the other buttons
    * since we want it to work for remote targets too
    */
   _buildPickerButton: function () {
-    this._pickerButton = this.doc.createElement("toolbarbutton");
+    this._pickerButton = this.doc.createElementNS(HTML_NS, "button");
     this._pickerButton.id = "command-button-pick";
-    this._pickerButton.className = "command-button command-button-invertable";
-    this._pickerButton.setAttribute("tooltiptext", toolboxStrings("pickButton.tooltip"));
+    this._pickerButton.className = "command-button command-button-invertable devtools-button";
+    this._pickerButton.setAttribute("title", toolboxStrings("pickButton.tooltip"));
     this._pickerButton.setAttribute("hidden", "true");
 
     let container = this.doc.querySelector("#toolbox-picker-container");
     container.appendChild(this._pickerButton);
 
     this._togglePicker = this.highlighterUtils.togglePicker.bind(this.highlighterUtils);
-    this._pickerButton.addEventListener("command", this._togglePicker, false);
+    this._pickerButton.addEventListener("click", this._togglePicker, false);
   },
 
   /**
    * Apply the current cache setting from devtools.cache.disabled to this
    * toolbox's tab.
    */
   _applyCacheSettings: function () {
     let pref = "devtools.cache.disabled";
@@ -1123,17 +1125,17 @@ Toolbox.prototype = {
       // Some buttons may not exist inside of Browser Toolbox
       if (!button) {
         return false;
       }
 
       return {
         id: options.id,
         button: button,
-        label: button.getAttribute("tooltiptext"),
+        label: button.getAttribute("title"),
         visibilityswitch: "devtools." + options.id + ".enabled",
         isTargetSupported: options.isTargetSupported
                            ? options.isTargetSupported
                            : target => target.isLocalTab,
       };
     }).filter(button=>button);
   },
 
@@ -1772,47 +1774,53 @@ Toolbox.prototype = {
     // We may receive this event before the toolbox is ready.
     if (!this.isReady) {
       return;
     }
 
     // Store (synchronize) data about all existing frames on the backend
     if (data.destroyAll) {
       this.frameMap.clear();
+      this.selectedFrameId = null;
     } else if (data.selected) {
       this.selectedFrameId = data.selected;
     } else if (data.frames) {
       data.frames.forEach(frame => {
         if (frame.destroy) {
           this.frameMap.delete(frame.id);
+
+          // Reset the currently selected frame if it's destroyed.
+          if (this.selectedFrameId == frame.id) {
+            this.selectedFrameId = null;
+          }
         } else {
           this.frameMap.set(frame.id, frame);
         }
       });
     }
 
     // If there is no selected frame select the first top level
     // frame by default. Note that there might be more top level
     // frames in case of the BrowserToolbox.
     if (!this.selectedFrameId) {
-      this.selectedFrameId = [...this.frameMap.values()].filter(
-        frame => !frame.parentID)[0].id;
+      let frames = [...this.frameMap.values()];
+      let topFrames = frames.filter(frame => !frame.parentID);
+      this.selectedFrameId = topFrames.length ? topFrames[0].id : null;
     }
 
     // Check out whether top frame is currently selected.
     // Note that only child frame has parentID.
     let frame = this.frameMap.get(this.selectedFrameId);
-    let topFrameSelected = !frame.parentID;
+    let topFrameSelected = frame ? !frame.parentID : false;
+    let button = this.doc.getElementById("command-button-frames");
+    button.removeAttribute("checked");
 
     // If non-top level frame is selected the toolbar button is
     // marked as 'checked' indicating that a child frame is active.
-    let button = this.doc.getElementById("command-button-frames");
-    if (topFrameSelected) {
-      button.removeAttribute("checked");
-    } else {
+    if (!topFrameSelected && this.selectedFrameId) {
       button.setAttribute("checked", "true");
     }
   },
 
   /**
    * Create a host object based on the given host type.
    *
    * Warning: some hosts require that the toolbox target provides a reference to
@@ -2109,17 +2117,17 @@ Toolbox.prototype = {
 
     this._lastFocusedElement = null;
 
     if (this.webconsolePanel) {
       this._saveSplitConsoleHeight();
       this.webconsolePanel.removeEventListener("resize",
         this._saveSplitConsoleHeight);
     }
-    this.closeButton.removeEventListener("command", this.destroy, true);
+    this.closeButton.removeEventListener("click", this.destroy, true);
     this.textboxContextMenuPopup.removeEventListener("popupshowing",
       this._updateTextboxMenuItems, true);
 
     let outstanding = [];
     for (let [id, panel] of this._toolPanels) {
       try {
         gDevTools.emit(id + "-destroy", this, panel);
         this.emit(id + "-destroy", panel);
@@ -2142,17 +2150,17 @@ Toolbox.prototype = {
         "serviceWorkersTestingEnabled": false
       });
     }
 
     // Destroying the walker and inspector fronts
     outstanding.push(this.destroyInspector().then(() => {
       // Removing buttons
       if (this._pickerButton) {
-        this._pickerButton.removeEventListener("command", this._togglePicker, false);
+        this._pickerButton.removeEventListener("click", this._togglePicker, false);
         this._pickerButton = null;
       }
     }));
 
     // Destroy the profiler connection
     outstanding.push(this.destroyPerformance());
 
     // Detach the thread
--- a/devtools/client/framework/toolbox.xul
+++ b/devtools/client/framework/toolbox.xul
@@ -11,17 +11,18 @@
 <!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
 %toolboxDTD;
 <!ENTITY % editMenuStrings SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
 %editMenuStrings;
 <!ENTITY % globalKeysDTD SYSTEM "chrome://global/locale/globalKeys.dtd">
 %globalKeysDTD;
 ]>
 
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        xmlns:html="http://www.w3.org/1999/xhtml">
 
   <script type="application/javascript;version=1.8"
           src="chrome://devtools/content/shared/theme-switching.js"/>
   <script type="application/javascript"
           src="chrome://global/content/viewSourceUtils.js"/>
 
   <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
   <script type="application/javascript;version=1.8"
@@ -45,32 +46,32 @@
   </popupset>
 
   <vbox id="toolbox-container" flex="1">
     <div xmlns="http://www.w3.org/1999/xhtml" id="toolbox-notificationbox"/>
     <toolbar class="devtools-tabbar">
       <hbox id="toolbox-picker-container" />
       <hbox id="toolbox-tabs" flex="1" role="tablist" />
       <hbox id="toolbox-buttons" pack="end">
-        <toolbarbutton id="command-button-frames"
-                       class="command-button command-button-invertable devtools-toolbarbutton"
-                       tooltiptext="&toolboxFramesTooltip;"
-                       hidden="true" />
-        <toolbarbutton id="command-button-noautohide"
-                       class="command-button command-button-invertable"
-                       tooltiptext="&toolboxNoAutoHideTooltip;"
-                       hidden="true" />
+        <html:button id="command-button-frames"
+                     class="command-button command-button-invertable devtools-button"
+                     title="&toolboxFramesTooltip;"
+                     hidden="true" />
+        <html:button id="command-button-noautohide"
+                     class="command-button command-button-invertable devtools-button"
+                     title="&toolboxNoAutoHideTooltip;"
+                     hidden="true" />
       </hbox>
       <vbox id="toolbox-controls-separator" class="devtools-separator"/>
       <hbox id="toolbox-option-container"/>
       <hbox id="toolbox-controls">
         <hbox id="toolbox-dock-buttons"/>
-        <toolbarbutton id="toolbox-close"
-                       class="devtools-closebutton"
-                       tooltiptext="&toolboxCloseButton.tooltip;"/>
+        <html:button id="toolbox-close"
+                     class="devtools-button"
+                     title="&toolboxCloseButton.tooltip;"/>
       </hbox>
     </toolbar>
     <vbox flex="1" class="theme-body">
       <!-- Set large flex to allow the toolbox-panel-webconsole to have a
            height set to a small value without flexing to fill up extra
            space. There must be a flex on both to ensure that the console
            panel itself is sized properly -->
       <box id="toolbox-deck" flex="1000" minheight="75" />
--- a/devtools/client/inspector/inspector-panel.js
+++ b/devtools/client/inspector/inspector-panel.js
@@ -93,16 +93,23 @@ function InspectorPanel(iframeWindow, to
   this._resetNodeMenu = this._resetNodeMenu.bind(this);
   this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
   this.onNewSelection = this.onNewSelection.bind(this);
   this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
   this.onDetached = this.onDetached.bind(this);
   this.onPaneToggleButtonClicked = this.onPaneToggleButtonClicked.bind(this);
   this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
 
+  let doc = this.panelDoc;
+
+  // Handle 'Add Node' toolbar button.
+  this.addNode = this.addNode.bind(this);
+  this.addNodeButton = doc.getElementById("inspector-element-add-button");
+  this.addNodeButton.addEventListener("click", this.addNode);
+
   this._target.on("will-navigate", this._onBeforeNavigate);
 
   EventEmitter.decorate(this);
 }
 
 exports.InspectorPanel = InspectorPanel;
 
 InspectorPanel.prototype = {
@@ -654,16 +661,18 @@ InspectorPanel.prototype = {
         front.destroy();
       }
     });
 
     this.sidebar.off("select", this._setDefaultSidebar);
     let sidebarDestroyer = this.sidebar.destroy();
     this.sidebar = null;
 
+    this.addNodeButton.removeEventListener("click", this.addNode);
+
     this.nodemenu.removeEventListener("popupshowing", this._setupNodeMenu, true);
     this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
     this.breadcrumbs.destroy();
     this._paneToggleButton.removeEventListener("mousedown",
       this.onPaneToggleButtonClicked);
     this._paneToggleButton = null;
     this.selection.off("new-node-front", this.onNewSelection);
     this.selection.off("before-new-node", this.onBeforeNewSelection);
--- a/devtools/client/inspector/inspector.xul
+++ b/devtools/client/inspector/inspector.xul
@@ -144,34 +144,33 @@
         oncommand="inspector.onFollowLink()"/>
       <menuitem id="node-menu-link-copy"
         oncommand="inspector.onCopyLink()"/>
     </menupopup>
   </popupset>
 
   <box flex="1" class="devtools-responsive-container theme-body">
     <vbox flex="1" class="devtools-main-content">
-      <toolbar id="inspector-toolbar"
+      <html:div id="inspector-toolbar"
         class="devtools-toolbar"
         nowindowdrag="true">
-        <toolbarbutton id="inspector-element-add-button"
-          class="devtools-toolbarbutton"
-          tooltiptext="&inspectorAddNode.label;"
-          oncommand="inspector.addNode()" />
-        <spacer flex="1"/>
-        <box id="inspector-searchlabel" />
+        <html:button id="inspector-element-add-button"
+          title="&inspectorAddNode.label;"
+          class="devtools-button" />
+        <html:div class="devtools-toolbar-spacer" />
+        <html:span id="inspector-searchlabel" />
         <textbox id="inspector-searchbox"
           type="search"
           timeout="50"
           class="devtools-searchinput"
           placeholder="&inspectorSearchHTML.label3;"/>
-        <toolbarbutton id="inspector-pane-toggle"
-          class="devtools-toolbarbutton"
+        <html:button id="inspector-pane-toggle"
+          class="devtools-button"
           tabindex="0" />
-      </toolbar>
+      </html:div>
       <vbox flex="1" id="markup-box">
       </vbox>
       <toolbar id="inspector-breadcrumbs-toolbar"
         class="devtools-toolbar"
         nowindowdrag="true">
         <arrowscrollbox id="inspector-breadcrumbs"
           class="breadcrumbs-widget-container"
           flex="1" orient="horizontal"
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -71,40 +71,42 @@ var CommandUtils = {
    * Warning: this method uses the unload event of the window that owns the
    * buttons that are of type checkbox. this means that we don't properly
    * unregister event handlers until the window is destroyed.
    */
   createButtons: function (toolbarSpec, target, document, requisition) {
     return util.promiseEach(toolbarSpec, typed => {
       // Ask GCLI to parse the typed string (doesn't execute it)
       return requisition.update(typed).then(() => {
-        let button = document.createElement("toolbarbutton");
+        let button = document.createElementNS(NS_XHTML, "button");
 
         // Ignore invalid commands
         let command = requisition.commandAssignment.value;
         if (command == null) {
           throw new Error("No command '" + typed + "'");
         }
 
         if (command.buttonId != null) {
           button.id = command.buttonId;
           if (command.buttonClass != null) {
             button.className = command.buttonClass;
           }
         }
         else {
           button.setAttribute("text-as-image", "true");
           button.setAttribute("label", command.name);
-          button.className = "devtools-toolbarbutton";
         }
+
+        button.classList.add("devtools-button");
+
         if (command.tooltipText != null) {
-          button.setAttribute("tooltiptext", command.tooltipText);
+          button.setAttribute("title", command.tooltipText);
         }
         else if (command.description != null) {
-          button.setAttribute("tooltiptext", command.description);
+          button.setAttribute("title", command.description);
         }
 
         button.addEventListener("click", () => {
           requisition.updateExec(typed);
         }, false);
 
         // Allow the command button to be toggleable
         if (command.state) {
--- a/devtools/client/themes/firebug-theme.css
+++ b/devtools/client/themes/firebug-theme.css
@@ -9,20 +9,19 @@
 
 :root {
   font-size: 11px;
   font-family: var(--proportional-font-family);
 }
 
 /* Remove filters on firebug specific images */
 
-.theme-firebug #toolbox-dock-buttons > toolbarbutton > image,
-.theme-firebug .devtools-closebutton > image,
+.theme-firebug .devtools-tabbar .devtools-button::before,
 .theme-firebug .devtools-option-toolbarbutton > image,
-.theme-firebug .command-button-invertable > image,
+.theme-firebug .command-button-invertable::before,
 .theme-firebug #sources-toolbar image,
 .theme-firebug [id$="pane-toggle"] > image,
 .theme-firebug #global-toolbar .devtools-button::before,
 .theme-firebug #element-picker::before,
 .theme-firebug #debugger-controls .toolbarbutton-icon,
 .theme-firebug #filter-button .toolbarbutton-icon {
   filter: none !important;
 }
@@ -239,12 +238,11 @@
   border-width: 1px !important;
   min-width: 24px;
 }
 
 .theme-firebug .devtools-toolbarbutton {
   min-width: 24px;
 }
 
-/* Move the Inspector button a bit down (looks better) */
-.theme-firebug #command-button-pick > image {
-  margin-bottom: -4px;
+.theme-firebug #command-button-frames {
+  min-width: 32px;
 }
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -1,43 +1,57 @@
 /* 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/. */
 
+/* Use flex layout for the Inspector toolbar. For now, it's done
+   specifically for the Inspector toolbar since general rule applied
+   on .devtools-toolbar breaks breadcrubs and also toolbars in other
+   panels (e.g. webconsole, debugger), these are not ready for HTML
+   layout yet. */
+#inspector-toolbar.devtools-toolbar {
+  display: flex;
+}
+
+#inspector-toolbar.devtools-toolbar .devtools-toolbar-spacer {
+  flex-grow: 1;
+  display: inline-block;
+}
+
 #inspector-searchlabel {
   overflow: hidden;
 }
 
 #inspector-breadcrumbs-toolbar {
   padding: 0px;
   border-bottom-width: 0px;
   border-top-width: 1px;
 }
 
 /* Expand/collapse panel toolbar button */
 
-#inspector-pane-toggle {
-  list-style-image: var(--theme-pane-collapse-image);
+#inspector-pane-toggle::before {
+  background-image: var(--theme-pane-collapse-image);
 }
 
-#inspector-pane-toggle[pane-collapsed] {
-  list-style-image: var(--theme-pane-expand-image);
+#inspector-pane-toggle[pane-collapsed]::before {
+  background-image: var(--theme-pane-expand-image);
 }
 
 @media (max-width: 700px) {
   #inspector-pane-toggle > .toolbarbutton-icon {
     transform: rotate(90deg);
   }
 }
 
 /* Add element toolbar button */
 
-#inspector-element-add-button {
-  list-style-image: url("chrome://devtools/skin/images/add.svg");
+#inspector-element-add-button::before {
+  background-image: url("chrome://devtools/skin/images/add.svg");
 }
 
 /* Tooltip: Events */
 
 #devtools-tooltip-events-container {
   margin: -4px; /* Compensate for the .panel-arrowcontent padding. */
   max-width: 590px;
   overflow-y: auto;
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -208,24 +208,24 @@
 }
 
 /* Icon button styles */
 .devtools-toolbarbutton:not([label]),
 .devtools-toolbarbutton[text-as-image] {
   min-width: 32px;
 }
 
-#toolbox-buttons .devtools-toolbarbutton[text-as-image] {
-  padding-inline-start: 5px;
-  padding-inline-end: 5px;
-  min-width: inherit;
+/* Set flex attribute to Toolbox buttons and Picker container so,
+   they don't overlapp with the tab bar */
+#toolbox-buttons {
+  display: flex;
 }
 
-#toolbox-buttons .devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker {
-  padding: 0 2px;
+#toolbox-picker-container {
+  display: flex;
 }
 
 /* Invert toolbox button icons in Firebug theme. */
 .theme-firebug #toolbox-buttons toolbarbutton image {
   filter: invert(1);
 }
 
 .devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
@@ -301,60 +301,52 @@
 /* Icon-and-text buttons */
 .devtools-toolbarbutton.icon-and-text .toolbarbutton-text {
   margin-inline-start: .5em !important;
   font-weight: 600;
 }
 
 /* Text-only buttons */
 .theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
-.theme-light .devtools-toolbarbutton[data-text-only],
-.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
+.theme-light .devtools-toolbarbutton[data-text-only] {
   background-color: var(--toolbar-tab-hover);
 }
 .theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
-.theme-dark .devtools-toolbarbutton[data-text-only],
-.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
+.theme-dark .devtools-toolbarbutton[data-text-only] {
   background-color: rgba(0, 0, 0, .2); /* Splitter */
 }
 
 /* Text-only button states */
 .theme-dark .devtools-button:not(:empty):not([disabled]):hover,
-.theme-dark #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover,
 .theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
   background: rgba(0, 0, 0, .3); /* Splitters */
 }
 .theme-light .devtools-button:not(:empty):not([disabled]):hover,
-.theme-light #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover,
 .theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
   background: rgba(170, 170, 170, .3); /* Splitters */
 }
 
 .theme-dark .devtools-button:not(:empty):not([disabled]):hover:active,
-.theme-dark #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover:active,
 .theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
   background: rgba(0, 0, 0, .4); /* Splitters */
 }
 .theme-light .devtools-button:not(:empty):not([disabled]):hover:active,
-.theme-light #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover:active,
 .theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
   background: var(--toolbar-tab-hover-active);
 }
 
 .theme-dark .devtools-toolbarbutton:not([disabled])[label][checked=true],
 .theme-dark .devtools-toolbarbutton:not([disabled])[label][open],
-.theme-dark .devtools-button:not(:empty)[checked=true],
-.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked=true] {
+.theme-dark .devtools-button:not(:empty)[checked=true] {
   background: var(--theme-selection-background-semitransparent);
   color: var(--theme-selection-color);
 }
 .theme-light .devtools-toolbarbutton:not([disabled])[label][checked=true],
 .theme-light .devtools-toolbarbutton:not([disabled])[label][open],
-.theme-light .devtools-button:not(:empty)[checked=true],
-.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked=true] {
+.theme-light .devtools-button:not(:empty)[checked=true] {
   background: rgba(76, 158, 217, .3); /* Select highlight blue */
 }
 
 :root {
   --clear-icon-url: url("chrome://devtools/skin/images/clear.svg");
 }
 
 .devtools-button.devtools-clear-icon::before {
@@ -534,47 +526,18 @@
 @media (min-resolution: 1.1dppx) {
   .devtools-searchinput {
     background-image: var(--magnifying-glass-image-2x);
   }
 }
 
 /* Close button */
 
-.devtools-closebutton {
-  -moz-appearance: none;
-  border: none;
-  margin: 0 4px;
-  min-width: 16px;
-  width: 16px;
-  opacity: 0.8;
-}
-
-.devtools-closebutton > image {
-  width: 16px;
-  height: 16px;
-  -moz-appearance: none;
-  background-size: cover;
+#toolbox-close::before {
   background-image: var(--close-button-image);
-  background-position: center center;
-  background-repeat: no-repeat;
-}
-
-.devtools-closebutton > .toolbarbutton-icon {
-  /* XXX Buttons have padding in widget/ that we don't want here but can't override with good CSS, so we must
-     use evil CSS to give the impression of smaller content */
-  margin: -4px;
-}
-
-.devtools-closebutton > .toolbarbutton-text {
-  display: none;
-}
-
-.devtools-closebutton:hover {
-  opacity: 1;
 }
 
 /* In-tools sidebar */
 .devtools-sidebar-tabs {
   -moz-appearance: none;
   margin: 0;
 }
 
@@ -664,75 +627,56 @@
   color: var(--theme-selection-color);
   background: var(--theme-selection-background);
 }
 
 /* Toolbox - moved from toolbox.css.
  * Rules that apply to the global toolbox like command buttons,
  * devtools tabs, docking buttons, etc. */
 
-#toolbox-controls > toolbarbutton,
-#toolbox-dock-buttons > toolbarbutton {
+#toolbox-controls > button,
+#toolbox-dock-buttons > button {
   -moz-appearance: none;
   -moz-user-focus: normal;
   border: none;
   margin: 0 4px;
   min-width: 16px;
   width: 16px;
 }
 
-#toolbox-controls > toolbarbutton > .toolbarbutton-text,
-#toolbox-dock-buttons > toolbarbutton > .toolbarbutton-text,
-.command-button > .toolbarbutton-text {
-  display: none;
-}
-
 /* Save space in Firebug theme */
-.theme-firebug #toolbox-controls toolbarbutton {
+.theme-firebug #toolbox-controls button {
   margin-inline-start: 0 !important;
-  width: 12px;
   min-width: 12px;
+  margin: 0 1px;
 }
 
-.theme-firebug #toolbox-controls toolbarbutton:hover {
-  background: none;
-}
-
-#toolbox-dock-buttons > toolbarbutton > image {
-  -moz-appearance: none;
-  width: 16px;
-  height: 16px;
-  background-size: 16px 16px;
-  background-position: 0 center;
-  background-repeat: no-repeat;
-}
-
-#toolbox-dock-bottom > image {
+#toolbox-dock-bottom::before {
   background-image: var(--dock-bottom-image);
 }
 
-#toolbox-dock-side  > image {
+#toolbox-dock-side::before {
   background-image: var(--dock-side-image);
 }
 
-#toolbox-dock-window > image {
+#toolbox-dock-window::before {
   background-image: var(--dock-undock-image);
 }
 
 #toolbox-dock-bottom-minimize {
   /* Bug 1177463 - The minimize button is currently hidden until we agree on
      the UI for it, and until bug 1173849 is fixed too. */
   display: none;
 }
 
-#toolbox-dock-bottom-minimize > image {
+#toolbox-dock-bottom-minimize::before {
   background-image: url("chrome://devtools/skin/images/dock-bottom-minimize@2x.png");
 }
 
-#toolbox-dock-bottom-minimize.minimized > image {
+#toolbox-dock-bottom-minimize.minimized::before {
   background-image: url("chrome://devtools/skin/images/dock-bottom-maximize@2x.png");
 }
 
 #toolbox-dock-window,
 #toolbox-dock-bottom,
 #toolbox-dock-side {
   opacity: 0.8;
 }
@@ -759,97 +703,101 @@
 
 #toolbox-controls-separator {
   margin: 0;
 }
 
 /* Command buttons */
 
 .command-button {
-  -moz-appearance: none;
-  border: none;
-  padding: 0 8px;
+  padding: 0;
   margin: 0;
-  width: 32px;
   position: relative;
   -moz-user-focus: normal;
 }
 
+.command-button::before {
+  opacity: 0.7;
+}
+
 .command-button:hover {
   background-color: var(--toolbar-tab-hover);
 }
-.command-button:hover:active, .command-button[checked=true]:not(:hover) {
+
+.command-button:hover:active,
+.command-button[checked=true]:not(:hover) {
   background-color: var(--toolbar-tab-hover-active)
 }
 
-.command-button > image {
-  -moz-appearance: none;
-  width: 16px;
-  height: 16px;
-  background-size: cover;
-  background-position: 0 center;
-  background-repeat: no-repeat;
-  opacity: 0.7;
-}
-.command-button:hover > image {
+.command-button:hover::before {
   opacity: 0.85;
 }
-.command-button:hover:active > image,
-.command-button[checked=true] > image,
-.command-button[open=true] > image {
+
+.command-button:hover:active::before,
+.command-button[checked=true]::before,
+.command-button[open=true]::before {
   opacity: 1;
 }
-.command-button[checked=true] > image {
-  filter: url(images/filters.svg#checked-icon-state) !important;
-}
 
-/* Toolbox buttons images */
+/* Toolbox command buttons */
 
-#command-button-paintflashing > image {
+#command-button-paintflashing::before {
   background-image: var(--command-paintflashing-image);
 }
 
-#command-button-screenshot > image {
+#command-button-screenshot::before {
   background-image: var(--command-screenshot-image);
 }
 
-#command-button-responsive > image {
+#command-button-responsive::before {
   background-image: var(--command-responsive-image);
 }
 
-#command-button-scratchpad > image {
+#command-button-scratchpad::before {
   background-image: var(--command-scratchpad-image);
 }
 
-#command-button-pick > image {
+#command-button-pick::before {
   background-image: var(--command-pick-image);
 }
 
-#command-button-frames > 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);
+}
+
+#command-button-rulers::before {
+  background-image: var(--command-rulers-image);
+}
+
+#command-button-measure::before {
+  background-image: var(--command-measure-image);
+}
+
+#command-button-frames::before {
   background-image: var(--command-frames-image);
 }
 
-#command-button-splitconsole > image {
-  background-image: var(--command-splitconsole-image);
-}
+#command-button-frames {
+  background: url("chrome://devtools/skin/images/dropmarker.svg") no-repeat right;
 
-#command-button-noautohide > image {
-  background-image: var(--command-noautohide-image);
+  /* Override background-size from the command-button.
+    The drop down arrow is smaller */
+  background-size: 8px 4px !important;
 }
 
-#command-button-eyedropper > image {
-  background-image: var(--command-eyedropper-image);
-}
-
-#command-button-rulers > image {
-  background-image: var(--command-rulers-image);
-}
-
-#command-button-measure > image {
-  background-image: var(--command-measure-image);
+#command-button-frames:-moz-dir(rtl) {
+  background-position: left;
 }
 
 /* Tabs */
 
 .devtools-tabbar {
   -moz-appearance: none;
   min-height: 24px;
   border: 0px solid;
@@ -1012,32 +960,34 @@
 #toolbox-tab-options > image {
   margin: 0 8px;
 }
 
 /* Invert the colors of certain dark theme images for displaying
  * inside of the light theme.
  */
 .theme-light .devtools-tab[icon-invertable] > image,
-.theme-light #toolbox-dock-buttons > toolbarbutton > image,
-.theme-light .command-button-invertable > image,
-.theme-light .devtools-closebutton > image,
 .theme-light .devtools-toolbarbutton > image,
 .theme-light .devtools-button::before,
 .theme-light #breadcrumb-separator-normal,
 .theme-light .scrollbutton-up > .toolbarbutton-icon,
 .theme-light .scrollbutton-down > .toolbarbutton-icon,
 .theme-light #black-boxed-message-button .button-icon,
 .theme-light #requests-menu-perf-notice-button .button-icon,
 .theme-light #requests-menu-network-summary-button .button-icon,
 .theme-light #toggle-breakpoints[checked] > image,
 .theme-light .event-tooltip-debugger-icon {
   filter: var(--icon-filter);
 }
 
+/* Reset the filter defined above */
+.theme-light .command-button:not(.command-button-invertable) {
+  filter: none !important;
+}
+
 /* Since selected backgrounds are blue, we want to use the normal
  * (light) icons. */
 .theme-light .devtools-tab[icon-invertable][selected] > image,
 .theme-light .devtools-tab[icon-invertable][highlighted] > image {
   filter: none !important;
 }
 
 .theme-light .command-button:hover {