Bug 1495285 - browser test to check how ServiceWorkers intercept tracking resources, r=baku
authorAndrew Sutherland <asutherland@asutherland.org>
Wed, 03 Oct 2018 17:00:36 -0400
changeset 489155 f6d88f69e8499bc13b794ac2fd0eeabd7e44c182
parent 489154 f30aa69dd3b1290de0b8808f44f7c69bdcb6861b
child 489156 75ed7377db616cdeb922a9ebf28e17aa7f97b492
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewersbaku
bugs1495285
milestone64.0a1
Bug 1495285 - browser test to check how ServiceWorkers intercept tracking resources, r=baku
dom/serviceworkers/test/browser.ini
dom/serviceworkers/test/browser_antitracking.js
dom/serviceworkers/test/empty_with_utils.html
dom/serviceworkers/test/page_post_controlled.html
dom/serviceworkers/test/utils.js
--- a/dom/serviceworkers/test/browser.ini
+++ b/dom/serviceworkers/test/browser.ini
@@ -6,20 +6,23 @@ support-files =
   download/worker.js
   download_canceled/page_download_canceled.html
   download_canceled/server-stream-download.sjs
   download_canceled/sw_download_canceled.js
   fetch.js
   file_userContextId_openWindow.js
   force_refresh_browser_worker.js
   empty.html
+  empty_with_utils.html
   empty.js
+  page_post_controlled.html
   storage_recovery_worker.sjs
   utils.js
 
+[browser_antitracking.js]
 [browser_devtools_serviceworker_interception.js]
 skip-if = serviceworker_e10s
 [browser_force_refresh.js]
 [browser_download.js]
 [browser_download_canceled.js]
 skip-if = verify
 [browser_storage_permission.js]
 skip-if = (verify && debug && (os == 'win' || os == 'mac'))
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/test/browser_antitracking.js
@@ -0,0 +1,104 @@
+
+const BEHAVIOR_ACCEPT         = Ci.nsICookieService.BEHAVIOR_ACCEPT;
+const BEHAVIOR_REJECT_TRACKER = Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
+
+let {UrlClassifierTestUtils} =
+  ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
+
+const TOP_DOMAIN = "http://mochi.test:8888/";
+const SW_DOMAIN = "https://tracking.example.org/";
+
+const TOP_TEST_ROOT = getRootDirectory(gTestPath)
+  .replace("chrome://mochitests/content/", TOP_DOMAIN);
+const SW_TEST_ROOT = getRootDirectory(gTestPath)
+  .replace("chrome://mochitests/content/", SW_DOMAIN);
+
+const TOP_EMPTY_PAGE = `${TOP_TEST_ROOT}empty_with_utils.html`;
+const SW_REGISTER_PAGE = `${SW_TEST_ROOT}empty_with_utils.html`;
+const SW_IFRAME_PAGE = `${SW_TEST_ROOT}page_post_controlled.html`;
+// An empty script suffices for our SW needs; it's by definition no-fetch.
+const SW_REL_SW_SCRIPT = "empty.js";
+
+/**
+ * Set up a no-fetch-optimized ServiceWorker on a domain that will be covered by
+ * tracking protection (but is not yet).  Once the SW is installed, activate TP
+ * and create a tab that embeds that tracking-site in an iframe.
+ */
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({'set': [
+    ['dom.serviceWorkers.enabled', true],
+    ['dom.serviceWorkers.exemptFromPerDomainMax', true],
+    ['dom.serviceWorkers.testing.enabled', true],
+    ['network.cookie.cookieBehavior', BEHAVIOR_ACCEPT],
+  ]});
+
+  // ## Install SW
+  info("Installing SW");
+  await BrowserTestUtils.withNewTab(
+    {
+      gBrowser,
+      url: SW_REGISTER_PAGE
+    },
+    async function(linkedBrowser) {
+      await ContentTask.spawn(
+        linkedBrowser,
+        { sw: SW_REL_SW_SCRIPT },
+        async function({ sw }) {
+          // Waive the xray to use the content utils.js script functions.
+          await content.wrappedJSObject.registerAndWaitForActive(sw);
+        }
+      );
+    }
+  );
+
+  // Enable Anti-tracking.
+  await SpecialPowers.pushPrefEnv({'set': [
+    ['privacy.trackingprotection.enabled', false],
+    ["privacy.trackingprotection.pbmode.enabled", false],
+    ["privacy.trackingprotection.annotate_channels", true],
+    ['network.cookie.cookieBehavior', BEHAVIOR_REJECT_TRACKER],
+  ]});
+  await UrlClassifierTestUtils.addTestTrackers();
+
+  // Open the top-level page.
+  info("Open top-level page");
+  let topTab = await BrowserTestUtils.openNewForegroundTab({
+    gBrowser,
+    opening: TOP_EMPTY_PAGE
+  });
+
+  // Create Iframe in the top-level page and verify its state.
+  let { controlled } = await ContentTask.spawn(
+    topTab.linkedBrowser,
+    { url: SW_IFRAME_PAGE },
+    async function ({ url }) {
+      const payload =
+        await content.wrappedJSObject.createIframeAndWaitForMessage(url);
+      return payload;
+    }
+  );
+
+  // Currently TP will allow this, although it probably shouldn't?
+  ok(controlled, "Should be controlled (and not crash.)");
+
+  // ## Cleanup
+  // Close the testing tab.
+  BrowserTestUtils.removeTab(topTab);
+  // Unregister the SW we registered for the tracking protection origin.
+  await BrowserTestUtils.withNewTab(
+    {
+      gBrowser,
+      url: SW_REGISTER_PAGE
+    },
+    async function(linkedBrowser) {
+      await ContentTask.spawn(
+        linkedBrowser,
+        {},
+        async function() {
+          // Waive the xray to use the content utils.js script functions.
+          await content.wrappedJSObject.unregisterAll();
+        }
+      );
+    }
+  );
+});
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/test/empty_with_utils.html
@@ -0,0 +1,13 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <script src="utils.js" type="text/javascript"></script>
+</head>
+<body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/test/page_post_controlled.html
@@ -0,0 +1,17 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+</head>
+<body>
+<script type="text/javascript">
+  window.parent.postMessage({
+    controlled: !!navigator.serviceWorker.controller
+  }, "*");
+</script>
+</body>
+</html>
--- a/dom/serviceworkers/test/utils.js
+++ b/dom/serviceworkers/test/utils.js
@@ -18,8 +18,52 @@ function waitForControlled(win) {
     if (win.navigator.serviceWorker.controller) {
       return resolve();
     }
 
     win.navigator.serviceWorker.addEventListener('controllerchange', resolve,
                                                  { once: true });
   });
 }
+
+/**
+ * Helper for browser tests to issue register calls from the content global and
+ * wait for the SW to progress to the active state, as most tests desire.
+ * From the ContentTask.spawn, use via
+ * `content.wrappedJSObject.registerAndWaitForActive`.
+ */
+async function registerAndWaitForActive(...args) {
+  console.log('...calling register');
+  const reg = await navigator.serviceWorker.register(...args);
+  // Unless registration resurrection happens, the SW should be in the
+  // installing slot.
+  console.log('...waiting for activation');
+  await waitForState(reg.installing, 'activated', reg);
+  console.log("...activated!");
+  return reg;
+}
+
+/**
+ * Helper to create an iframe with the given URL and return the first
+ * postMessage payload received.  This is intended to be used when creating
+ * cross-origin iframes.
+ *
+ * A promise will be returned that resolves with the payload of the postMessage
+ * call.
+ */
+function createIframeAndWaitForMessage(url) {
+  const iframe = document.createElement('iframe');
+  document.body.appendChild(iframe);
+  return new Promise((resolve) => {
+    window.addEventListener(
+      'message',
+      (event) => { resolve(event.data); },
+      { once: true });
+    iframe.src = url;
+  });
+}
+
+async function unregisterAll() {
+  const registrations = await navigator.serviceWorker.getRegistrations();
+  for (const reg of registrations) {
+    await reg.unregister();
+  }
+}