Bug 1452143: Reparse doc sheets after enabling error reporting on a docshell. r?jryans draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 12 Apr 2018 23:58:12 +0200
changeset 781835 880527e13209a90432477390d2a8273b23f21c9a
parent 781834 2d330dc2a1cd1d80bd7a57f77cdd12bd3b423934
child 781836 021be15397e2ee88a9f10e1e5824b15d5bdd06a6
push id106424
push userbmo:emilio@crisal.io
push dateFri, 13 Apr 2018 18:19:01 +0000
reviewersjryans
bugs1452143
milestone61.0a1
Bug 1452143: Reparse doc sheets after enabling error reporting on a docshell. r?jryans While at it, remove useless charset rule lookups, since charset rules aren't part of the OM, and have no effect at all anymore. I suspect I need to go through InspectorUtils.getAllSheets for the content toolbox... MozReview-Commit-ID: EefGrOZvmm7
devtools/server/actors/stylesheets.js
devtools/server/actors/tab.js
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -120,16 +120,105 @@ var MediaRuleActor = protocol.ActorClass
     return form;
   },
 
   _matchesChange: function() {
     this.emit("matches-change", this.matches);
   }
 });
 
+function getSheetText(sheet) {
+  let cssText = modifiedStyleSheets.get(sheet);
+  if (cssText !== undefined) {
+    return Promise.resolve(cssText);
+  }
+
+  if (!sheet.href) {
+    // this is an inline <style> sheet
+    let content = sheet.ownerNode.textContent;
+    return Promise.resolve(content);
+  }
+
+  return fetchStylesheet(sheet).then(({ content }) => content);
+}
+
+/**
+ * Get the charset of the stylesheet.
+ */
+function getCSSCharset(sheet) {
+  if (sheet) {
+    // charset attribute of <link> or <style> element, if it exists
+    if (sheet.ownerNode && sheet.ownerNode.getAttribute) {
+      let linkCharset = sheet.ownerNode.getAttribute("charset");
+      if (linkCharset != null) {
+        return linkCharset;
+      }
+    }
+
+    // charset of referring document.
+    if (sheet.ownerNode && sheet.ownerNode.ownerDocument.characterSet) {
+      return sheet.ownerNode.ownerDocument.characterSet;
+    }
+  }
+
+  // step 5: default to utf-8.
+  return "UTF-8";
+}
+
+/**
+ * Fetch a stylesheet at the provided URL. Returns a promise that will resolve the
+ * result of the fetch command.
+ *
+ * @return {Promise} a promise that resolves with an object with the following members
+ *         on success:
+ *           - content: the document at that URL, as a string,
+ *           - contentType: the content type of the document
+ *         If an error occurs, the promise is rejected with that error.
+ */
+async function fetchStylesheet(sheet) {
+  let options = {
+    loadFromCache: true,
+    policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
+    charset: getCSSCharset(sheet)
+  };
+
+  let href = sheet.href;
+
+  // Bug 1282660 - We use the system principal to load the default internal
+  // stylesheets instead of the content principal since such stylesheets
+  // require system principal to load. At meanwhile, we strip the loadGroup
+  // for preventing the assertion of the userContextId mismatching.
+
+  // chrome|file|resource|moz-extension protocols rely on the system principal.
+  let excludedProtocolsRe = /^(chrome|file|resource|moz-extension):\/\//;
+  if (!excludedProtocolsRe.test(href)) {
+    // Stylesheets using other protocols should use the content principal.
+    if (sheet.ownerNode) {
+      // eslint-disable-next-line mozilla/use-ownerGlobal
+      options.window = sheet.ownerNode.ownerDocument.defaultView;
+      options.principal = sheet.ownerNode.ownerDocument.nodePrincipal;
+    }
+  }
+
+  let result;
+  try {
+    result = await fetch(href, options);
+  } catch (e) {
+    // The list of excluded protocols can be missing some protocols, try to use the
+    // system principal if the first fetch failed.
+    console.error(`stylesheets actor: fetch failed for ${href},` +
+      ` using system principal instead.`);
+    options.window = undefined;
+    options.principal = undefined;
+    result = await fetch(href, options);
+  }
+
+  return result;
+}
+
 /**
  * A StyleSheetActor represents a stylesheet on the server.
  */
 var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
   toString: function() {
     return "[StyleSheetActor " + this.actorID + "]";
   },
 
@@ -367,89 +456,23 @@ var StyleSheetActor = protocol.ActorClas
    * @return {Promise}
    *         Promise that resolves with a string text of the stylesheet.
    */
   _getText: function() {
     if (typeof this.text === "string") {
       return Promise.resolve(this.text);
     }
 
-    let cssText = modifiedStyleSheets.get(this.rawSheet);
-    if (cssText !== undefined) {
-      this.text = cssText;
-      return Promise.resolve(cssText);
-    }
-
-    if (!this.href) {
-      // this is an inline <style> sheet
-      let content = this.ownerNode.textContent;
-      this.text = content;
-      return Promise.resolve(content);
-    }
-
-    return this.fetchStylesheet(this.href).then(({ content }) => {
-      this.text = content;
-      return content;
+    return getSheetText(this.rawSheet).then(text => {
+      this.text = text;
+      return text;
     });
   },
 
   /**
-   * Fetch a stylesheet at the provided URL. Returns a promise that will resolve the
-   * result of the fetch command.
-   *
-   * @param  {String} href
-   *         The href of the stylesheet to retrieve.
-   * @return {Promise} a promise that resolves with an object with the following members
-   *         on success:
-   *           - content: the document at that URL, as a string,
-   *           - contentType: the content type of the document
-   *         If an error occurs, the promise is rejected with that error.
-   */
-  async fetchStylesheet(href) {
-    // Check if network monitor observed this load, and if so, use that.
-    let result = this.fetchStylesheetFromNetworkMonitor(href);
-    if (result) {
-      return result;
-    }
-
-    let options = {
-      loadFromCache: true,
-      policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
-      charset: this._getCSSCharset()
-    };
-
-    // Bug 1282660 - We use the system principal to load the default internal
-    // stylesheets instead of the content principal since such stylesheets
-    // require system principal to load. At meanwhile, we strip the loadGroup
-    // for preventing the assertion of the userContextId mismatching.
-
-    // chrome|file|resource|moz-extension protocols rely on the system principal.
-    let excludedProtocolsRe = /^(chrome|file|resource|moz-extension):\/\//;
-    if (!excludedProtocolsRe.test(this.href)) {
-      // Stylesheets using other protocols should use the content principal.
-      options.window = this.ownerWindow;
-      options.principal = this.ownerDocument.nodePrincipal;
-    }
-
-    try {
-      result = await fetch(this.href, options);
-    } catch (e) {
-      // The list of excluded protocols can be missing some protocols, try to use the
-      // system principal if the first fetch failed.
-      console.error(`stylesheets actor: fetch failed for ${this.href},` +
-        ` using system principal instead.`);
-      options.window = undefined;
-      options.principal = undefined;
-      result = await fetch(this.href, options);
-    }
-
-    return result;
-  },
-
-  /**
    * Try to locate the console actor if it exists via our parent actor (the tab).
    */
   get _consoleActor() {
     if (this.parentActor.exited) {
       return null;
     }
     let form = this.parentActor.form();
     return this.conn._getOrCreateActor(form.consoleActor);
@@ -519,59 +542,16 @@ var StyleSheetActor = protocol.ActorClas
 
         mediaRules.push(actor);
       }
       return mediaRules;
     });
   },
 
   /**
-   * Get the charset of the stylesheet according to the character set rules
-   * defined in <http://www.w3.org/TR/CSS2/syndata.html#charset>.
-   * Note that some of the algorithm is implemented in DevToolsUtils.fetch.
-   */
-  _getCSSCharset: function() {
-    let sheet = this.rawSheet;
-    if (sheet) {
-      // Do we have a @charset rule in the stylesheet?
-      // step 2 of syndata.html (without the BOM check).
-      if (sheet.cssRules) {
-        let rules = sheet.cssRules;
-        if (rules.length
-            && rules.item(0).type == CSSRule.CHARSET_RULE) {
-          return rules.item(0).encoding;
-        }
-      }
-
-      // step 3: charset attribute of <link> or <style> element, if it exists
-      if (sheet.ownerNode && sheet.ownerNode.getAttribute) {
-        let linkCharset = sheet.ownerNode.getAttribute("charset");
-        if (linkCharset != null) {
-          return linkCharset;
-        }
-      }
-
-      // step 4 (1 of 2): charset of referring stylesheet.
-      let parentSheet = sheet.parentStyleSheet;
-      if (parentSheet && parentSheet.cssRules &&
-          parentSheet.cssRules[0].type == CSSRule.CHARSET_RULE) {
-        return parentSheet.cssRules[0].encoding;
-      }
-
-      // step 4 (2 of 2): charset of referring document.
-      if (sheet.ownerNode && sheet.ownerNode.ownerDocument.characterSet) {
-        return sheet.ownerNode.ownerDocument.characterSet;
-      }
-    }
-
-    // step 5: default to utf-8.
-    return "UTF-8";
-  },
-
-  /**
    * Update the style sheet in place with new text.
    *
    * @param  {object} request
    *         'text' - new text
    *         'transition' - whether to do CSS transition for change.
    *         'kind' - either UPDATE_PRESERVING_RULES or UPDATE_GENERAL
    */
   update: function(text, transition, kind = UPDATE_GENERAL) {
@@ -626,16 +606,17 @@ var StyleSheetActor = protocol.ActorClas
   _onTransitionEnd: function(kind) {
     this._transitionTimeout = null;
     removePseudoClassLock(this.document.documentElement, TRANSITION_PSEUDO_CLASS);
     this.emit("style-applied", kind, this);
   }
 });
 
 exports.StyleSheetActor = StyleSheetActor;
+exports.getSheetText = getSheetText;
 
 /**
  * Creates a StyleSheetsActor. StyleSheetsActor provides remote access to the
  * stylesheets of a document.
  */
 var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
   /**
    * The window we work with, taken from the parent actor.
--- a/devtools/server/actors/tab.js
+++ b/devtools/server/actors/tab.js
@@ -19,25 +19,27 @@ var {
   ActorPool, createExtraActors, appendExtraActors
 } = require("devtools/server/actors/common");
 var { DebuggerServer } = require("devtools/server/main");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { assert } = DevToolsUtils;
 var { TabSources } = require("./utils/TabSources");
 var makeDebugger = require("./utils/make-debugger");
 const EventEmitter = require("devtools/shared/event-emitter");
+const InspectorUtils = require("InspectorUtils");
 
 const EXTENSION_CONTENT_JSM = "resource://gre/modules/ExtensionContent.jsm";
 
 loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/thread", true);
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
 loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker-list", true);
 loader.lazyImporter(this, "ExtensionContent", EXTENSION_CONTENT_JSM);
 
 loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
+loader.lazyRequireGetter(this, "getSheetText", "devtools/server/actors/stylesheets", true);
 
 function getWindowID(window) {
   return window.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIDOMWindowUtils)
                .currentInnerWindowID;
 }
 
 function getDocShellChromeEventHandler(docShell) {
@@ -1011,17 +1013,22 @@ TabActor.prototype = {
    * Ensure that CSS error reporting is enabled.
    */
   ensureCSSErrorReportingEnabled(request) {
     if (!this.docShell || this.docShell.cssErrorReportingEnabled) {
       return {};
     }
 
     this.docShell.cssErrorReportingEnabled = true;
-    // FIXME(emilio): Reparse sheets.
+    for (let sheet of this.docShell.document.styleSheets) {
+      getSheetText(sheet).then(text => {
+        InspectorUtils.parseStyleSheet(sheet, text);
+      });
+    }
+
     return {};
   },
 
   /**
    * Handle logic to enable/disable JS/cache/Service Worker testing.
    */
   _toggleDevToolsSettings(options) {
     // Wait a tick so that the response packet can be dispatched before the