Bug 1233127 - Use createProcessingIntruction instead of addon-sdk to load stylesheets in tools;r=pbrosset draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Wed, 16 Dec 2015 11:52:52 -0800
changeset 315861 165daf4a6ae81770fb6323ef1e032741a5c8d926
parent 315860 e564d6f8056de7f11e0d7ba1def66635c6cefb8f
child 512090 13ec213b6b620e159b86347878c1af183509149a
push id8469
push userbgrinstead@mozilla.com
push dateWed, 16 Dec 2015 19:53:11 +0000
reviewerspbrosset
bugs1233127
milestone46.0a1
Bug 1233127 - Use createProcessingIntruction instead of addon-sdk to load stylesheets in tools;r=pbrosset
devtools/client/shared/test/browser_theme_switching.js
devtools/client/shared/theme-switching.js
--- a/devtools/client/shared/test/browser_theme_switching.js
+++ b/devtools/client/shared/test/browser_theme_switching.js
@@ -2,25 +2,42 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var toolbox;
 
 add_task(function*() {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   let toolbox = yield gDevTools.showToolbox(target);
-  let root = toolbox.frame.contentDocument.documentElement;
+  let doc = toolbox.frame.contentDocument;
+  let root = doc.documentElement;
 
   let platform = root.getAttribute("platform");
   let expectedPlatform = getPlatform();
   is(platform, expectedPlatform, ":root[platform] is correct");
 
   let theme = Services.prefs.getCharPref("devtools.theme");
   let className = "theme-" + theme;
-  ok(root.classList.contains(className), ":root has " + className + " class (current theme)");
+  ok(root.classList.contains(className),":root has " + className + " class (current theme)");
+
+  // Convert the xpath result into an array of strings
+  // like `href="{URL}" type="text/css"`
+  let sheetsIterator = doc.evaluate("processing-instruction('xml-stylesheet')",
+                       doc, null, XPathResult.ANY_TYPE, null);
+  let sheetsInDOM = [];
+  let sheet;
+  while (sheet = sheetsIterator.iterateNext()) {
+    sheetsInDOM.push(sheet.data);
+  }
+
+  let sheetsFromTheme = gDevTools.getThemeDefinition(theme).stylesheets;
+  info ("Checking for existence of " + sheetsInDOM.length + " sheets");
+  for (let sheet of sheetsFromTheme) {
+    ok(sheetsInDOM.some(s=>s.includes(sheet)), "There is a stylesheet for " + sheet);
+  }
 
   yield toolbox.destroy();
 });
 
 function getPlatform() {
   let {OS} = Services.appinfo;
   if (OS == "WINNT") {
     return "win";
--- a/devtools/client/shared/theme-switching.js
+++ b/devtools/client/shared/theme-switching.js
@@ -1,78 +1,85 @@
 /* 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/. */
 
 (function() {
-  const DEVTOOLS_SKIN_URL = "chrome://devtools/skin/";
+  const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-light.css";
   let documentElement = document.documentElement;
+  let devtoolsStyleSheets = new WeakMap();
+  let floatingScrollbarSheet = null;
 
   function forceStyle() {
     let computedStyle = window.getComputedStyle(documentElement);
     if (!computedStyle) {
       // Null when documentElement is not ready. This method is anyways not
       // required then as scrollbars would be in their state without flushing.
       return;
     }
     let display = computedStyle.display; // Save display value
     documentElement.style.display = "none";
     window.getComputedStyle(documentElement).display; // Flush
     documentElement.style.display = display; // Restore
   }
 
+  function appendStyleSheet(url) {
+    let styleSheetAttr = `href="${url}" type="text/css"`;
+    let styleSheet = document.createProcessingInstruction(
+      "xml-stylesheet", styleSheetAttr);
+    document.insertBefore(styleSheet, documentElement);
+    return styleSheet;
+  }
+
   function switchTheme(newTheme, oldTheme) {
     if (newTheme === oldTheme) {
       return;
     }
 
     let oldThemeDef = gDevTools.getThemeDefinition(oldTheme);
 
     // Unload all theme stylesheets related to the old theme.
     if (oldThemeDef) {
-      for (let url of oldThemeDef.stylesheets) {
-        StylesheetUtils.removeSheet(window, url, "author");
+      for (let sheet of devtoolsStyleSheets.get(oldThemeDef) || []) {
+        sheet.remove();
       }
     }
 
     // Load all stylesheets associated with the new theme.
     let newThemeDef = gDevTools.getThemeDefinition(newTheme);
 
     // The theme might not be available anymore (e.g. uninstalled)
     // Use the default one.
     if (!newThemeDef) {
       newThemeDef = gDevTools.getThemeDefinition("light");
     }
 
+    // Store the sheets in a WeakMap for access later when the theme gets
+    // unapplied.  It's hard to query for processing instructions so this
+    // is an easy way to access them later without storing a property on
+    // the window
+    devtoolsStyleSheets.set(newThemeDef, []);
+
     for (let url of newThemeDef.stylesheets) {
-      StylesheetUtils.loadSheet(window, url, "author");
+      let styleSheet = appendStyleSheet(url);
+      devtoolsStyleSheets.get(newThemeDef).push(styleSheet);
     }
 
     // Floating scroll-bars like in OSX
     let hiddenDOMWindow = Cc["@mozilla.org/appshell/appShellService;1"]
                  .getService(Ci.nsIAppShellService)
                  .hiddenDOMWindow;
 
     // TODO: extensions might want to customize scrollbar styles too.
     if (!hiddenDOMWindow.matchMedia("(-moz-overlay-scrollbars)").matches) {
-      let scrollbarsUrl = Services.io.newURI(
-        DEVTOOLS_SKIN_URL + "floating-scrollbars-light.css", null, null);
-
       if (newTheme == "dark") {
-        StylesheetUtils.loadSheet(
-          window,
-          scrollbarsUrl,
-          "agent"
-        );
-      } else if (oldTheme == "dark") {
-        StylesheetUtils.removeSheet(
-          window,
-          scrollbarsUrl,
-          "agent"
-        );
+        floatingScrollbarSheet = appendStyleSheet(SCROLLBARS_URL);
+      } else if (oldTheme == "dark" && floatingScrollbarSheet) {
+        floatingScrollbarSheet.remove();
+        floatingScrollbarSheet = null;
       }
       forceStyle();
     }
 
     if (oldThemeDef) {
       for (let name of oldThemeDef.classList) {
         documentElement.classList.remove(name);
       }
@@ -96,21 +103,18 @@
 
   function handlePrefChange(event, data) {
     if (data.pref == "devtools.theme") {
       switchTheme(data.newValue, data.oldValue);
     }
   }
 
   const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
   Cu.import("resource://gre/modules/Services.jsm");
   Cu.import("resource://devtools/client/framework/gDevTools.jsm");
-  const {require} = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
-  const StylesheetUtils = require("sdk/stylesheet/utils");
 
   let os;
   let platform = navigator.platform;
   if (platform.startsWith("Win")) {
     os = "win";
   } else if (platform.startsWith("Mac")) {
     os = "mac";
   } else {