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.
MozReview-Commit-ID: EefGrOZvmm7
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -120,16 +120,147 @@ var MediaRuleActor = protocol.ActorClass
return form;
},
_matchesChange: function() {
this.emit("matches-change", this.matches);
}
});
+function getSheetText(sheet, consoleActor) {
+ 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, consoleActor).then(({ content }) => content);
+}
+
+exports.getSheetText = getSheetText;
+
+/**
+ * Try to fetch the stylesheet text from the network monitor. If it was enabled during
+ * the load, it should have a copy of the text saved.
+ *
+ * @param string href
+ * The URL of the sheet to fetch.
+ */
+function fetchStylesheetFromNetworkMonitor(href, consoleActor) {
+ if (!consoleActor) {
+ return null;
+ }
+ let request = consoleActor.getNetworkEventActorForURL(href);
+ if (!request) {
+ return null;
+ }
+ let content = request._response.content;
+ if (request._discardResponseBody || request._truncated || !content) {
+ return null;
+ }
+ if (content.text.type != "longString") {
+ // For short strings, the text is available directly.
+ return {
+ content: content.text,
+ contentType: content.mimeType,
+ };
+ }
+ // For long strings, look up the actor that holds the full text.
+ let longStringActor = consoleActor.conn._getOrCreateActor(content.text.actor);
+ if (!longStringActor) {
+ return null;
+ }
+ return {
+ content: longStringActor.rawValue(),
+ contentType: content.mimeType,
+ };
+}
+
+/**
+ * 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;
+ }
+ }
+
+ 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, consoleActor) {
+ let href = sheet.href;
+
+ let result = fetchStylesheetFromNetworkMonitor(href, consoleActor);
+ if (result) {
+ return result;
+ }
+
+ let options = {
+ loadFromCache: true,
+ policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
+ charset: getCSSCharset(sheet)
+ };
+
+ // 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;
+ }
+ }
+
+ 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,138 +498,36 @@ 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, this._consoleActor).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.
+ * Try to locate the console actor if it exists via our parent actor (the tab).
*
- * @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).
+ * Keep this in sync with the TabActor version.
*/
get _consoleActor() {
if (this.parentActor.exited) {
return null;
}
let form = this.parentActor.form();
return this.conn._getOrCreateActor(form.consoleActor);
},
/**
- * Try to fetch the stylesheet text from the network monitor. If it was enabled during
- * the load, it should have a copy of the text saved.
- *
- * @param string href
- * The URL of the sheet to fetch.
- */
- fetchStylesheetFromNetworkMonitor(href) {
- let consoleActor = this._consoleActor;
- if (!consoleActor) {
- return null;
- }
- let request = consoleActor.getNetworkEventActorForURL(href);
- if (!request) {
- return null;
- }
- let content = request._response.content;
- if (request._discardResponseBody || request._truncated || !content) {
- return null;
- }
- if (content.text.type != "longString") {
- // For short strings, the text is available directly.
- return {
- content: content.text,
- contentType: content.mimeType,
- };
- }
- // For long strings, look up the actor that holds the full text.
- let longStringActor = this.conn._getOrCreateActor(content.text.actor);
- if (!longStringActor) {
- return null;
- }
- return {
- content: longStringActor.rawValue(),
- contentType: content.mimeType,
- };
- },
-
- /**
* Protocol method to get the media rules for the stylesheet.
*/
getMediaRules: function() {
return this._getMediaRules();
},
/**
* Get all the @media rules in this stylesheet.
@@ -519,59 +548,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) {
--- 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) {
@@ -253,16 +255,27 @@ TabActor.prototype = {
get exited() {
return this._exited;
},
get attached() {
return !!this._attached;
},
+ /**
+ * Try to locate the console actor if it exists.
+ */
+ get _consoleActor() {
+ if (this.exited) {
+ return null;
+ }
+ let form = this.form();
+ return this.conn._getOrCreateActor(form.consoleActor);
+ },
+
_tabPool: null,
get tabActorPool() {
return this._tabPool;
},
_contextPool: null,
get contextActorPool() {
return this._contextPool;
@@ -1006,22 +1019,36 @@ TabActor.prototype = {
return {};
},
/**
* Ensure that CSS error reporting is enabled.
*/
ensureCSSErrorReportingEnabled(request) {
- if (!this.docShell || this.docShell.cssErrorReportingEnabled) {
- return {};
+ for (let docShell of this.docShells) {
+ if (docShell.cssErrorReportingEnabled) {
+ continue;
+ }
+ try {
+ docShell.cssErrorReportingEnabled = true;
+ } catch (e) {
+ continue;
+ }
+ // We don't really want to reparse UA sheets and such, but want to do
+ // Shadow DOM / XBL.
+ let sheets =
+ InspectorUtils.getAllStyleSheets(docShell.document, /* documentOnly = */ true);
+ for (let sheet of sheets) {
+ getSheetText(sheet, this._consoleActor).then(text => {
+ InspectorUtils.parseStyleSheet(sheet, text, /* aUpdate = */ false);
+ });
+ }
}
- this.docShell.cssErrorReportingEnabled = true;
- // FIXME(emilio): Reparse sheets.
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