Bug 1359090 - load devtools-browser.css dynamically when starting devtools;r=ochameau draft
authorJulian Descottes <jdescottes@mozilla.com>
Thu, 27 Apr 2017 16:05:31 +0200
changeset 570388 a4a1e993657ff3abcc975249812723ab46bc84a8
parent 569503 622115120ac1349b63175b6de088269e9f17e1ed
child 626474 ef815180dd17786010cd728f04d862824f9b3aa4
push id56470
push userjdescottes@mozilla.com
push dateFri, 28 Apr 2017 19:53:28 +0000
reviewersochameau
bugs1359090
milestone55.0a1
Bug 1359090 - load devtools-browser.css dynamically when starting devtools;r=ochameau This changeset modifies devtools-browser.css to import: - commandline-browser.css (needed for GCLI) - responsivedesign.css (needed for the old RDM) These files are no longer included in the main browser.css files. devtools-browser.css is also no longer loaded by browser.xul. Instead it is dynamically loaded when devtools need the browser stylesheet: - when creating a side or bottom host (need the splitter) - when opening gcli - when opening the old responsive design devtools-browser.js keeps track of the browser stylesheets loaded in the various tracked windows and will remove the stylesheet when the window is unloaded. MozReview-Commit-ID: AL3CxS7mvdO
browser/base/content/browser.xul
browser/themes/linux/browser.css
browser/themes/osx/browser.css
browser/themes/windows/browser.css
devtools/client/framework/devtools-browser.js
devtools/client/framework/toolbox-hosts.js
devtools/client/responsivedesign/responsivedesign.jsm
devtools/client/shared/developer-toolbar.js
devtools/client/themes/commandline-browser.css
devtools/client/themes/commandline.inc.css
devtools/client/themes/devtools-browser.css
devtools/client/themes/moz.build
devtools/client/themes/responsivedesign.css
devtools/client/themes/responsivedesign.inc.css
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -4,17 +4,16 @@
 #
 # 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/.
 
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
-<?xml-stylesheet href="chrome://devtools/skin/devtools-browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/controlcenter/panel.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/customizableui/panelUI.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
 
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1486,18 +1486,16 @@ toolbarbutton.chevron > .toolbarbutton-i
 .statuspanel-label:-moz-locale-dir(ltr)[mirror] {
   border-left-style: solid;
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
 %include ../shared/fullscreen/warning.inc.css
 %include ../shared/ctrlTab.inc.css
-%include ../../../devtools/client/themes/responsivedesign.inc.css
-%include ../../../devtools/client/themes/commandline.inc.css
 %include ../shared/plugin-doorhanger.inc.css
 
 notification.pluginVulnerable > .notification-inner > .messageCloseButton:not(:hover) {
   background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 80, 16, 64);
 }
 
 
 %include downloads/indicator.css
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3098,18 +3098,16 @@ html|*.addon-webext-perm-list {
 .statuspanel-label:-moz-locale-dir(ltr)[mirror] {
   border-left-style: solid;
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
 %include ../shared/fullscreen/warning.inc.css
 %include ../shared/ctrlTab.inc.css
-%include ../../../devtools/client/themes/responsivedesign.inc.css
-%include ../../../devtools/client/themes/commandline.inc.css
 %include ../shared/plugin-doorhanger.inc.css
 
 %include downloads/indicator.css
 
 /* On mac, the popup notification contents are indented by default and so
   the default closebutton margins from notification.css require adjustment */
 
 .click-to-play-plugins-notification-description-box > .popup-notification-closebutton {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2260,18 +2260,16 @@ notification[value="translation"] {
   /* disabled for triggering grayscale AA (bug 659213)
   border-top-left-radius: .3em;
   */
   margin-left: 1em;
 }
 
 %include ../shared/fullscreen/warning.inc.css
 %include ../shared/ctrlTab.inc.css
-%include ../../../devtools/client/themes/responsivedesign.inc.css
-%include ../../../devtools/client/themes/commandline.inc.css
 %include ../shared/plugin-doorhanger.inc.css
 
 notification.pluginVulnerable > .notification-inner > .messageCloseButton {
   list-style-image: url("chrome://global/skin/icons/close-inverted.png");
 }
 
 @media (min-resolution: 1.1dppx) {
   notification.pluginVulnerable > .notification-inner > .messageCloseButton {
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -21,43 +21,52 @@ const { gDevTools } = require("./devtool
 
 // Load target and toolbox lazily as they need gDevTools to be fully initialized
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "BrowserMenus", "devtools/client/framework/browser-menus");
 loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
+loader.lazyRequireGetter(this, "appendStyleSheet", "devtools/client/shared/stylesheet-utils", true);
 
 loader.lazyImporter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm");
 loader.lazyImporter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");
 loader.lazyImporter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 const TABS_OPEN_PEAK_HISTOGRAM = "DEVTOOLS_TABS_OPEN_PEAK_LINEAR";
 const TABS_OPEN_AVG_HISTOGRAM = "DEVTOOLS_TABS_OPEN_AVERAGE_LINEAR";
 const TABS_PINNED_PEAK_HISTOGRAM = "DEVTOOLS_TABS_PINNED_PEAK_LINEAR";
 const TABS_PINNED_AVG_HISTOGRAM = "DEVTOOLS_TABS_PINNED_AVERAGE_LINEAR";
 
 const COMPACT_LIGHT_ID = "firefox-compact-light@mozilla.org";
 const COMPACT_DARK_ID = "firefox-compact-dark@mozilla.org";
 
+const BROWSER_STYLESHEET_URL = "chrome://devtools/skin/devtools-browser.css";
+
 /**
  * gDevToolsBrowser exposes functions to connect the gDevTools instance with a
  * Firefox instance.
  */
 var gDevToolsBrowser = exports.gDevToolsBrowser = {
   /**
    * A record of the windows whose menus we altered, so we can undo the changes
    * as the window is closed
    */
   _trackedBrowserWindows: new Set(),
 
+  /**
+   * WeakMap keeping track of the devtools-browser stylesheets loaded in the various
+   * tracked windows.
+   */
+  _browserStyleSheets: new WeakMap(),
+
   _telemetry: new Telemetry(),
 
   _tabStats: {
     peakOpen: 0,
     peakPinned: 0,
     histOpen: [],
     histPinned: []
   },
@@ -490,16 +499,35 @@ var gDevToolsBrowser = exports.gDevTools
   },
 
   isWebIDEWidgetInstalled: function () {
     let widgetWrapper = CustomizableUI.getWidget("webide-button");
     return !!(widgetWrapper && widgetWrapper.provider == CustomizableUI.PROVIDER_API);
   },
 
   /**
+   * Add the devtools-browser stylesheet to browser window's document. Returns a promise.
+   *
+   * @param  {Window} win
+   *         The window on which the stylesheet should be added.
+   * @return {Promise} promise that resolves when the stylesheet is loaded (or rejects
+   *         if it fails to load).
+   */
+  loadBrowserStyleSheet: function (win) {
+    if (this._browserStyleSheets.has(win)) {
+      return Promise.resolve();
+    }
+
+    let doc = win.document;
+    let {styleSheet, loadPromise} = appendStyleSheet(doc, BROWSER_STYLESHEET_URL);
+    this._browserStyleSheets.set(win, styleSheet);
+    return loadPromise;
+  },
+
+  /**
    * The deferred promise will be resolved by WebIDE's UI.init()
    */
   isWebIDEInitialized: defer(),
 
   /**
    * Uninstall WebIDE widget
    */
   uninstallWebIDEWidget: function () {
@@ -751,16 +779,22 @@ var gDevToolsBrowser = exports.gDevTools
 
     // Destroy toolboxes for closed window
     for (let [target, toolbox] of gDevTools._toolboxes) {
       if (target.tab && target.tab.ownerDocument.defaultView == win) {
         toolbox.destroy();
       }
     }
 
+    let styleSheet = this._browserStyleSheets.get(win);
+    if (styleSheet) {
+      styleSheet.remove();
+      this._browserStyleSheets.delete(win);
+    }
+
     // Destroy the Developer toolbar if it has been accessed
     let desc = Object.getOwnPropertyDescriptor(win, "DeveloperToolbar");
     if (desc && !desc.get) {
       win.DeveloperToolbar.destroy();
     }
 
     let tabContainer = win.gBrowser.tabContainer;
     tabContainer.removeEventListener("TabSelect", this);
--- a/devtools/client/framework/toolbox-hosts.js
+++ b/devtools/client/framework/toolbox-hosts.js
@@ -8,16 +8,17 @@
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const Services = require("Services");
 const {DOMHelpers} = require("resource://devtools/client/shared/DOMHelpers.jsm");
 
 loader.lazyRequireGetter(this, "system", "devtools/shared/system");
+loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
 
 /* A host should always allow this much space for the page to be displayed.
  * There is also a min-height on the browser, but we still don't want to set
  * frame.height to be larger than that, since it can cause problems with
  * resizing the toolbox and panel layout. */
 const MIN_PAGE_SIZE = 25;
 
 /**
@@ -48,18 +49,18 @@ function BottomHost(hostTab) {
 BottomHost.prototype = {
   type: "bottom",
 
   heightPref: "devtools.toolbox.footer.height",
 
   /**
    * Create a box at the bottom of the host tab.
    */
-  create: function () {
-    let deferred = defer();
+  create: async function () {
+    await gDevToolsBrowser.loadBrowserStyleSheet(this.hostTab.ownerGlobal);
 
     let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
     let ownerDocument = gBrowser.ownerDocument;
     this._nbox = gBrowser.getNotificationBox(this.hostTab.linkedBrowser);
 
     this._splitter = ownerDocument.createElement("splitter");
     this._splitter.setAttribute("class", "devtools-horizontal-splitter");
     // Avoid resizing notification containers
@@ -70,32 +71,32 @@ BottomHost.prototype = {
     this.frame.height = Math.min(
       Services.prefs.getIntPref(this.heightPref),
       this._nbox.clientHeight - MIN_PAGE_SIZE
     );
 
     this._nbox.appendChild(this._splitter);
     this._nbox.appendChild(this.frame);
 
-    let frameLoad = () => {
-      this.emit("ready", this.frame);
-      deferred.resolve(this.frame);
-    };
-
     this.frame.tooltip = "aHTMLTooltip";
 
     // we have to load something so we can switch documents if we have to
     this.frame.setAttribute("src", "about:blank");
 
-    let domHelper = new DOMHelpers(this.frame.contentWindow);
-    domHelper.onceDOMReady(frameLoad);
+    let frame = await new Promise(resolve => {
+      let domHelper = new DOMHelpers(this.frame.contentWindow);
+      let frameLoad = () => {
+        this.emit("ready", this.frame);
+        resolve(this.frame);
+      };
+      domHelper.onceDOMReady(frameLoad);
+      focusTab(this.hostTab);
+    });
 
-    focusTab(this.hostTab);
-
-    return deferred.promise;
+    return frame;
   },
 
   /**
    * Raise the host.
    */
   raise: function () {
     focusTab(this.hostTab);
   },
@@ -194,18 +195,18 @@ function SidebarHost(hostTab) {
 SidebarHost.prototype = {
   type: "side",
 
   widthPref: "devtools.toolbox.sidebar.width",
 
   /**
    * Create a box in the sidebar of the host tab.
    */
-  create: function () {
-    let deferred = defer();
+  create: async function () {
+    await gDevToolsBrowser.loadBrowserStyleSheet(this.hostTab.ownerGlobal);
 
     let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
     let ownerDocument = gBrowser.ownerDocument;
     this._sidebar = gBrowser.getSidebarContainer(this.hostTab.linkedBrowser);
 
     this._splitter = ownerDocument.createElement("splitter");
     this._splitter.setAttribute("class", "devtools-side-splitter");
 
@@ -215,30 +216,30 @@ SidebarHost.prototype = {
     this.frame.width = Math.min(
       Services.prefs.getIntPref(this.widthPref),
       this._sidebar.clientWidth - MIN_PAGE_SIZE
     );
 
     this._sidebar.appendChild(this._splitter);
     this._sidebar.appendChild(this.frame);
 
-    let frameLoad = () => {
-      this.emit("ready", this.frame);
-      deferred.resolve(this.frame);
-    };
-
     this.frame.tooltip = "aHTMLTooltip";
     this.frame.setAttribute("src", "about:blank");
 
-    let domHelper = new DOMHelpers(this.frame.contentWindow);
-    domHelper.onceDOMReady(frameLoad);
+    let frame = await new Promise(resolve => {
+      let domHelper = new DOMHelpers(this.frame.contentWindow);
+      let frameLoad = () => {
+        this.emit("ready", this.frame);
+        resolve(this.frame);
+      };
+      domHelper.onceDOMReady(frameLoad);
+      focusTab(this.hostTab);
+    });
 
-    focusTab(this.hostTab);
-
-    return deferred.promise;
+    return frame;
   },
 
   /**
    * Raise the host.
    */
   raise: function () {
     focusTab(this.hostTab);
   },
--- a/devtools/client/responsivedesign/responsivedesign.jsm
+++ b/devtools/client/responsivedesign/responsivedesign.jsm
@@ -14,16 +14,18 @@ const EventEmitter = require("devtools/s
 
 loader.lazyImporter(this, "SystemAppProxy",
                     "resource://gre/modules/SystemAppProxy.jsm");
 loader.lazyImporter(this, "BrowserUtils",
                     "resource://gre/modules/BrowserUtils.jsm");
 loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
 loader.lazyRequireGetter(this, "showDoorhanger",
                          "devtools/client/shared/doorhanger", true);
+loader.lazyRequireGetter(this, "gDevToolsBrowser",
+                         "devtools/client/framework/devtools-browser", true);
 loader.lazyRequireGetter(this, "TouchEventSimulator",
                          "devtools/shared/touch/simulator", true);
 loader.lazyRequireGetter(this, "flags",
                          "devtools/shared/flags");
 loader.lazyRequireGetter(this, "EmulationFront",
                          "devtools/shared/fronts/emulation", true);
 loader.lazyRequireGetter(this, "DebuggerClient",
                          "devtools/shared/client/main", true);
@@ -222,16 +224,18 @@ ResponsiveUI.prototype = {
   },
 
   init: Task.async(function* () {
     debug("INIT BEGINS");
     let ready = this.waitForMessage("ResponsiveMode:ChildScriptReady");
     this.mm.loadFrameScript("resource://devtools/client/responsivedesign/responsivedesign-child.js", true);
     yield ready;
 
+    yield gDevToolsBrowser.loadBrowserStyleSheet(this.mainWindow);
+
     let requiresFloatingScrollbars =
       !this.mainWindow.matchMedia("(-moz-overlay-scrollbars)").matches;
     let started = this.waitForMessage("ResponsiveMode:Start:Done");
     debug("SEND START");
     this.mm.sendAsyncMessage("ResponsiveMode:Start", {
       requiresFloatingScrollbars,
       // Tests expect events on resize to yield on various size changes
       notifyOnResize: flags.testing,
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -373,16 +373,19 @@ DeveloperToolbar.prototype.show = functi
 
   this._showPromise = Task.spawn((function* () {
     // hide() is async, so ensure we don't need to wait for hide() to
     // finish.  We unconditionally yield here, even if _hidePromise is
     // null, so that the spawn call returns a promise before starting
     // to do any real work.
     yield this._hidePromise;
 
+    // Append the browser-level stylesheet to the browser document.
+    yield gDevToolsBrowser.loadBrowserStyleSheet(this._chromeWindow);
+
     this.createToolbar();
 
     Services.prefs.setBoolPref("devtools.toolbar.visible", true);
 
     this._telemetry.toolOpened("developertoolbar");
 
     this._notify(NOTIFICATIONS.LOAD);
 
rename from devtools/client/themes/commandline.inc.css
rename to devtools/client/themes/commandline-browser.css
--- a/devtools/client/themes/commandline.inc.css
+++ b/devtools/client/themes/commandline-browser.css
@@ -1,16 +1,16 @@
-%if 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/. */
-%endif
 
 /* Developer toolbar */
 
+@namespace html url("http://www.w3.org/1999/xhtml");
+
 /* NOTE: THESE NEED TO STAY IN SYNC WITH LIGHT-THEME.CSS AND DARK-THEME.CSS.
    We are copy/pasting variables from light-theme and dark-theme,
    since they aren't loaded in this context (within browser.css). */
 #browser-bottombox[devtoolstheme="light"] #developer-toolbar {
   --gcli-background-color: #fcfcfc; /* --theme-tab-toolbar-background */
   --gcli-input-background: #fcfcfc; /* --theme-toolbar-background */
   --gcli-input-focused-background: #ffffff; /* --theme-sidebar-background */
   --gcli-input-color: #393f4c; /* --theme-body-color */
--- a/devtools/client/themes/devtools-browser.css
+++ b/devtools/client/themes/devtools-browser.css
@@ -1,13 +1,15 @@
 /* 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/. */
 
 @import url("resource://devtools/client/themes/splitters.css");
+@import url("resource://devtools/client/themes/commandline-browser.css");
+@import url("resource://devtools/client/themes/responsivedesign.css");
 
 /* Bottom-docked toolbox minimize transition */
 .devtools-toolbox-bottom-iframe {
   transition: margin-bottom .1s;
 }
 
 .devtools-toolbox-side-iframe {
   min-width: 465px;
--- a/devtools/client/themes/moz.build
+++ b/devtools/client/themes/moz.build
@@ -4,13 +4,15 @@
 # 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/.
 
 DIRS += [
     'audio',
 ]
 
 DevToolsModules(
+    'commandline-browser.css',
     'common.css',
+    'responsivedesign.css',
     'splitters.css',
     'toolbars.css',
     'variables.css',
 )
rename from devtools/client/themes/responsivedesign.inc.css
rename to devtools/client/themes/responsivedesign.css
--- a/devtools/client/themes/responsivedesign.inc.css
+++ b/devtools/client/themes/responsivedesign.css
@@ -1,16 +1,16 @@
-%if 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/. */
-%endif
 
 /* Responsive Mode */
 
+@namespace html url("http://www.w3.org/1999/xhtml");
+
 .browserContainer[responsivemode] {
   background-color: #222;
   padding: 0 20px 20px 20px;
 }
 
 .browserStack[responsivemode] {
   box-shadow: 0 0 7px black;
 }