Bug 1408708 - Fetch stylesheet content via stylesheet window instead of top level content window. r=pbro draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Fri, 20 Oct 2017 01:53:13 -0700
changeset 686230 ed2521c0d07f2a3c917128fb6423d974f21df185
parent 684511 ce1a86d3b4db161c95d1147676bbed839d7a4732
child 737332 439e067d8833b5acdac99fe538a747db6132567b
push id86131
push userbmo:poirot.alex@gmail.com
push dateWed, 25 Oct 2017 16:05:43 +0000
reviewerspbro
bugs1408708
milestone58.0a1
Bug 1408708 - Fetch stylesheet content via stylesheet window instead of top level content window. r=pbro MozReview-Commit-ID: AKXQLNAwy8t
devtools/client/styleeditor/test/browser.ini
devtools/client/styleeditor/test/browser_styleeditor_bug_1405342_serviceworker_iframes.js
devtools/client/styleeditor/test/bug_1405342_serviceworker_iframes.html
devtools/client/styleeditor/test/iframe_service_worker.js
devtools/client/styleeditor/test/iframe_with_service_worker.html
devtools/server/actors/stylesheets.js
--- a/devtools/client/styleeditor/test/browser.ini
+++ b/devtools/client/styleeditor/test/browser.ini
@@ -1,16 +1,19 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   autocomplete.html
   browser_styleeditor_cmd_edit.html
+  bug_1405342_serviceworker_iframes.html
   four.html
   head.js
+  iframe_with_service_worker.html
+  iframe_service_worker.js
   import.css
   import.html
   import2.css
   inline-1.html
   inline-2.html
   longload.html
   media-small.css
   media.html
@@ -64,16 +67,17 @@ support-files =
 
 [browser_styleeditor_add_stylesheet.js]
 [browser_styleeditor_autocomplete.js]
 [browser_styleeditor_autocomplete-disabled.js]
 [browser_styleeditor_bom.js]
 [browser_styleeditor_bug_740541_iframes.js]
 [browser_styleeditor_bug_851132_middle_click.js]
 [browser_styleeditor_bug_870339.js]
+[browser_styleeditor_bug_1405342_serviceworker_iframes.js]
 [browser_styleeditor_cmd_edit.js]
 [browser_styleeditor_enabled.js]
 [browser_styleeditor_fetch-from-cache.js]
 [browser_styleeditor_filesave.js]
 [browser_styleeditor_highlight-selector.js]
 [browser_styleeditor_import.js]
 [browser_styleeditor_import_rule.js]
 [browser_styleeditor_init.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/styleeditor/test/browser_styleeditor_bug_1405342_serviceworker_iframes.js
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that sheets inside cross origin iframes, served from a service worker
+// are correctly fetched via the service worker in the stylesheet editor.
+
+add_task(async function () {
+  const TEST_URL = "https://test1.example.com/browser/devtools/client/styleeditor/test/bug_1405342_serviceworker_iframes.html";
+  let { ui } = await openStyleEditorForURL(TEST_URL);
+
+  is(ui.editors.length, 1, "Got the iframe stylesheet");
+
+  await ui.selectStyleSheet(ui.editors[0].styleSheet);
+  let editor = await ui.editors[0].getSourceEditor();
+  let text = editor.sourceEditor.getText();
+  is(text, "* {\n color: green;\n}\n", "stylesheet content is the one served by the service worker");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/styleeditor/test/bug_1405342_serviceworker_iframes.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Bug 1405342</title>
+</head>
+<body>
+ <iframe src="https://test2.example.com/browser/devtools/client/styleeditor/test/iframe_with_service_worker.html"><iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/styleeditor/test/iframe_service_worker.js
@@ -0,0 +1,8 @@
+onfetch = function(event) {
+  if (event.request.url.includes("sheet.css")) {
+    return event.respondWith(new Response("* { color: green; }"));
+  }
+}
+onactivate =  function(event) {
+  event.waitUntil(clients.claim());
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/styleeditor/test/iframe_with_service_worker.html
@@ -0,0 +1,28 @@
+Iframe loading a stylesheet via a service worker
+<script>
+function waitForActive(swr) {
+  let sw = swr.installing || swr.waiting || swr.active;
+  return new Promise(resolve => {
+    if (sw.state === 'activated') {
+      resolve(swr);
+      return;
+    }
+    sw.addEventListener('statechange', function onStateChange(evt) {
+      if (sw.state === 'activated') {
+        sw.removeEventListener('statechange', onStateChange);
+        resolve(swr);
+      }
+    });
+  });
+}
+
+navigator.serviceWorker.register("iframe_service_worker.js", {scope: "."})
+  .then(registration => waitForActive(registration))
+  .then(() => {
+    let link = document.createElement("link");
+    link.setAttribute("rel", "stylesheet");
+    link.setAttribute("type", "text/css");
+    link.setAttribute("href", "sheet.css");
+    document.documentElement.appendChild(link);
+  });
+</script>
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -135,26 +135,40 @@ var StyleSheetActor = protocol.ActorClas
   toString: function () {
     return "[StyleSheetActor " + this.actorID + "]";
   },
 
   /**
    * Window of target
    */
   get window() {
-    return this._window || this.parentActor.window;
+    return this.parentActor.window;
   },
 
   /**
    * Document of target.
    */
   get document() {
     return this.window.document;
   },
 
+  /**
+   * StyleSheet's window.
+   */
+  get ownerWindow() {
+    return this.ownerDocument.defaultView;
+  },
+
+  /**
+   * StyleSheet's document.
+   */
+  get ownerDocument() {
+    return this.ownerNode.ownerDocument;
+  },
+
   get ownerNode() {
     return this.rawSheet.ownerNode;
   },
 
   /**
    * URL of underlying stylesheet.
    */
   get href() {
@@ -197,25 +211,23 @@ var StyleSheetActor = protocol.ActorClas
   destroy: function () {
     if (this._transitionTimeout && this.window) {
       this.window.clearTimeout(this._transitionTimeout);
       removePseudoClassLock(
                    this.document.documentElement, TRANSITION_PSEUDO_CLASS);
     }
   },
 
-  initialize: function (styleSheet, parentActor, window) {
+  initialize: function (styleSheet, parentActor) {
     protocol.Actor.prototype.initialize.call(this, null);
 
     this.rawSheet = styleSheet;
     this.parentActor = parentActor;
     this.conn = this.parentActor.conn;
 
-    this._window = window;
-
     // text and index are unknown until source load
     this.text = null;
     this._styleSheetIndex = -1;
   },
 
   /**
    * Test whether all the rules in this sheet have associated source.
    * @return {Boolean} true if all the rules have source; false if
@@ -412,18 +424,18 @@ var StyleSheetActor = protocol.ActorClas
     // 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.window;
-      options.principal = this.document.nodePrincipal;
+      options.window = this.ownerWindow;
+      options.principal = this.ownerDocument.nodePrincipal;
     }
 
     let result;
     try {
       result = yield 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.