Bug 1207394 - Make sure web_accessible_resources work with CSP/mixed content blocking, r=kmag
authorBob Silverberg <bsilverberg@mozilla.com>
Mon, 11 Apr 2016 18:08:54 -0400
changeset 331961 85616706a29b8944f3a09f5cc32e9e219f6ceb40
parent 331960 dea40ab40c2ad83c6cc68a9f9dcf10e0f31dac74
child 331962 1a63948d29b2920e89d0f301dea13338b293e6ef
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1207394
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1207394 - Make sure web_accessible_resources work with CSP/mixed content blocking, r=kmag MozReview-Commit-ID: 7UJ1IHwMosh
toolkit/components/extensions/test/mochitest/file_csp.html
toolkit/components/extensions/test/mochitest/file_csp.html^headers^
toolkit/components/extensions/test/mochitest/file_mixed.html
toolkit/components/extensions/test/mochitest/mochitest.ini
toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/file_csp.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+
+<html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+
+<div id="test">Sample text</div>
+<img id="bad-image" src="http://example.org/tests/toolkit/components/extensions/test/mochitest/file_image_bad.png" />
+<script id="bad-script" type="text/javascript" src="http://example.org/tests/toolkit/components/extensions/test/mochitest/file_script_bad.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/file_csp.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'self'
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/file_mixed.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+
+<html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+
+<div id="test">Sample text</div>
+<img id="bad-image" src="http://example.com/tests/toolkit/components/extensions/test/mochitest/file_image_bad.png" />
+
+</body>
+</html>
--- a/toolkit/components/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest.ini
@@ -1,12 +1,15 @@
 [DEFAULT]
 skip-if = buildapp == 'mulet' || asan
 support-files =
   head.js
+  file_mixed.html
+  file_csp.html
+  file_csp.html^headers^
   file_WebRequest_page1.html
   file_WebRequest_page2.html
   file_WebRequest_page3.html
   file_webNavigation_clientRedirect.html
   file_webNavigation_clientRedirect_httpHeaders.html
   file_webNavigation_clientRedirect_httpHeaders.html^headers^
   file_webNavigation_frameClientRedirect.html
   file_webNavigation_frameRedirect.html
--- a/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html
@@ -10,16 +10,56 @@
 </head>
 <body>
 
 <script type="text/javascript">
 "use strict";
 
 /* eslint-disable mozilla/balanced-listeners */
 
+SimpleTest.registerCleanupFunction(() => {
+  SpecialPowers.clearUserPref("security.mixed_content.block_display_content");
+});
+
+let image = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+                 "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
+const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer;
+
+function testImageLoading(src, expectedAction) {
+  let imageLoadingPromise = new Promise((resolve, reject) => {
+    let cleanupListeners;
+    let testImage = document.createElement("img");
+    testImage.setAttribute("src", src);
+
+    let loadListener = () => {
+      cleanupListeners();
+      resolve(expectedAction === "loaded");
+    };
+
+    let errorListener = () => {
+      cleanupListeners();
+      resolve(expectedAction === "blocked");
+    };
+
+    cleanupListeners = () => {
+      testImage.removeEventListener("load", loadListener);
+      testImage.removeEventListener("error", errorListener);
+    };
+
+    testImage.addEventListener("load", loadListener);
+    testImage.addEventListener("error", errorListener);
+
+    document.body.appendChild(testImage);
+  });
+
+  imageLoadingPromise.then(success => {
+    browser.runtime.sendMessage({name: "image-loading", expectedAction, success});
+  });
+}
+
 add_task(function* test_web_accessible_resources() {
   function background() {
     let gotURL;
     let tabId;
 
     function loadFrame(url) {
       return new Promise(resolve => {
         browser.tabs.sendMessage(tabId, ["load-iframe", url], reply => {
@@ -135,12 +175,178 @@ add_task(function* test_web_accessible_r
   let win = window.open("http://example.com/");
 
   yield extension.awaitFinish("web-accessible-resources");
 
   win.close();
 
   yield extension.unload();
 });
+
+add_task(function* test_web_accessible_resources_csp() {
+  function background() {
+    browser.runtime.onMessage.addListener((msg, sender) => {
+      if (msg.name === "image-loading") {
+        browser.test.assertTrue(msg.success, `Image was ${msg.expectedAction}`);
+        browser.test.sendMessage(`image-${msg.expectedAction}`);
+      } else {
+        browser.test.sendMessage(msg);
+      }
+    });
+
+    browser.test.sendMessage("background-ready");
+  }
+
+  function content() {
+    window.addEventListener("message", function rcv(event) {
+      browser.runtime.sendMessage("script-ran");
+      window.removeEventListener("message", rcv, false);
+    }, false);
+
+    testImageLoading(browser.extension.getURL("image.png"), "loaded");
+
+    let testScript = document.createElement("script");
+    testScript.setAttribute("src", browser.extension.getURL("test_script.js"));
+    document.head.appendChild(testScript);
+    browser.runtime.sendMessage("script-loaded");
+  }
+
+  function testScript() {
+    window.postMessage("test-script-loaded", "*");
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "content_scripts": [{
+        "matches": ["http://example.com/*/file_csp.html"],
+        "run_at": "document_start",
+        "js": ["content_script_helper.js", "content_script.js"],
+      }],
+      "web_accessible_resources": [
+        "image.png",
+        "test_script.js",
+      ],
+    },
+    background: `(${background})()`,
+    files: {
+      "content_script_helper.js": `${testImageLoading}`,
+      "content_script.js": `(${content})()`,
+      "test_script.js": `(${testScript})()`,
+      "image.png": IMAGE_ARRAYBUFFER,
+    },
+  });
+
+  // This is used to watch the blocked data bounce off CSP.
+  function examiner() {
+    SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+  }
+
+  let cspEventCount = 0;
+
+  examiner.prototype = {
+    observe: function(subject, topic, data) {
+      cspEventCount++;
+      let spec = SpecialPowers.wrap(subject).QueryInterface(SpecialPowers.Ci.nsIURI).spec;
+      ok(spec.includes("file_image_bad.png") || spec.includes("file_script_bad.js"),
+         `Expected file: ${spec} rejected by CSP`);
+    },
+
+    // We must eventually call this to remove the listener,
+    // or mochitests might get borked.
+    remove: function() {
+      SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+    },
+  };
+
+  let observer = new examiner();
+
+  yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
+
+  let win = window.open("http://example.com/tests/toolkit/components/extensions/test/mochitest/file_csp.html");
+
+  yield Promise.all([
+    extension.awaitMessage("image-loaded"),
+    extension.awaitMessage("script-loaded"),
+    extension.awaitMessage("script-ran"),
+  ]);
+  is(cspEventCount, 2, "Two items were rejected by CSP");
+  win.close();
+
+  observer.remove();
+  yield extension.unload();
+});
+
+add_task(function* test_web_accessible_resources_mixed_content() {
+  function background() {
+    browser.runtime.onMessage.addListener(msg => {
+      if (msg.name === "image-loading") {
+        browser.test.assertTrue(msg.success, `Image was ${msg.expectedAction}`);
+        browser.test.sendMessage(`image-${msg.expectedAction}`);
+      } else {
+        browser.test.sendMessage(msg);
+        if (msg === "accessible-script-loaded") {
+          browser.test.notifyPass("mixed-test");
+        }
+      }
+    });
+
+    browser.test.sendMessage("background-ready");
+  }
+
+  function content() {
+    testImageLoading("http://example.com/tests/toolkit/components/extensions/test/mochitest/file_image_bad.png", "blocked");
+    testImageLoading(browser.extension.getURL("image.png"), "loaded");
+
+    let testScript = document.createElement("script");
+    testScript.setAttribute("src", browser.extension.getURL("test_script.js"));
+    document.head.appendChild(testScript);
+
+    window.addEventListener("message", event => {
+      browser.runtime.sendMessage(event.data);
+    });
+  }
+
+  function testScript() {
+    window.postMessage("accessible-script-loaded", "*");
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "content_scripts": [{
+        "matches": ["https://example.com/*/file_mixed.html"],
+        "run_at": "document_start",
+        "js": ["content_script_helper.js", "content_script.js"],
+      }],
+      "web_accessible_resources": [
+        "image.png",
+        "test_script.js",
+      ],
+    },
+    background: `(${background})()`,
+    files: {
+      "content_script_helper.js": `${testImageLoading}`,
+      "content_script.js": `(${content})()`,
+      "test_script.js": `(${testScript})()`,
+      "image.png": IMAGE_ARRAYBUFFER,
+    },
+  });
+
+  SpecialPowers.setBoolPref("security.mixed_content.block_display_content", true);
+
+  yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
+
+  let win = window.open("https://example.com/tests/toolkit/components/extensions/test/mochitest/file_mixed.html");
+
+  yield Promise.all([
+    extension.awaitMessage("image-blocked"),
+    extension.awaitMessage("image-loaded"),
+    extension.awaitMessage("accessible-script-loaded"),
+  ]);
+  yield extension.awaitFinish("mixed-test");
+  win.close();
+
+  yield extension.unload();
+});
+
 </script>
 
 </body>
 </html>