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 499312 f6d88f69e8499bc13b794ac2fd0eeabd7e44c182
parent 499311 f30aa69dd3b1290de0b8808f44f7c69bdcb6861b
child 499313 75ed7377db616cdeb922a9ebf28e17aa7f97b492
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1495285
milestone64.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 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();
+  }
+}