Bug 1415388 - P1: Move the test_devtools_serviceworker_interception to a browser test. r=bkelly
authorTom Tung <shes050117@gmail.com>
Thu, 09 Nov 2017 09:24:20 +0800
changeset 437533 051a59c4a53a6434c25c2460155c549f69fedd24
parent 437532 092d8e20762a7ae160b62da4ae935d8df5fb0223
child 437534 39d8372dd2e7dd853c09d922a206f0fd3d09be49
push id117
push userfmarier@mozilla.com
push dateTue, 28 Nov 2017 20:17:16 +0000
reviewersbkelly
bugs1415388
milestone59.0a1
Bug 1415388 - P1: Move the test_devtools_serviceworker_interception to a browser test. r=bkelly
dom/workers/test/serviceworkers/browser.ini
dom/workers/test/serviceworkers/browser_devtools_serviceworker_interception.js
dom/workers/test/serviceworkers/chrome.ini
dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html
--- a/dom/workers/test/serviceworkers/browser.ini
+++ b/dom/workers/test/serviceworkers/browser.ini
@@ -1,18 +1,20 @@
 [DEFAULT]
 support-files =
   browser_base_force_refresh.html
   browser_cached_force_refresh.html
   download/window.html
   download/worker.js
+  fetch.js
   file_multie10s_update.html
   file_userContextId_openWindow.js
   force_refresh_browser_worker.js
   empty.html
   server_multie10s_update.sjs
 
+[browser_devtools_serviceworker_interception.js]
 [browser_force_refresh.js]
 [browser_download.js]
 [browser_multie10s_update.js]
 skip-if = !e10s || os != "win" # Bug 1404914
 [browser_userContextId_openWindow.js]
 skip-if = !e10s
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/browser_devtools_serviceworker_interception.js
@@ -0,0 +1,175 @@
+"use strict";
+
+const { classes: Cc, interfaces: Ci, results: Cr } = Components;
+
+const BASE_URI =
+  "http://mochi.test:8888/browser/dom/workers/test/serviceworkers/";
+const emptyDoc = BASE_URI + "empty.html";
+const fakeDoc = BASE_URI + "fake.html";
+const helloDoc = BASE_URI + "hello.html";
+const sw = BASE_URI + "fetch.js";
+
+// XXXtt: We should be able to move this check to chrome process after we move
+// the interception logic to chrome process.
+async function checkObserverInContent(aInput) {
+  let promiseResolve;
+
+  function observer(aSubject) {
+    var channel = aSubject.QueryInterface(Ci.nsIChannel);
+    // Since we cannot make sure that the network event triggered by the fetch()
+    // in this testcase is the very next event processed by ObserverService, we
+    // have to wait until we catch the one we want.
+    if (!channel.URI.spec.endsWith(aInput.url)) {
+      return;
+    }
+    var tc = aSubject.QueryInterface(Ci.nsITimedChannel);
+
+    // Check service worker related timings.
+    var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime,
+                                 end:   tc.launchServiceWorkerEndTime},
+                                {start: tc.dispatchFetchEventStartTime,
+                                 end:   tc.dispatchFetchEventEndTime},
+                                {start: tc.handleFetchEventStartTime,
+                                 end:   tc.handleFetchEventEndTime}];
+    if (aInput.intercepted) {
+      serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => {
+        ok(aPreviousTimings.start <= aCurrentTimings.start,
+           "Start time order check.");
+        ok(aPreviousTimings.end <= aCurrentTimings.end,
+           "End time order check.");
+        ok(aCurrentTimings.start <= aCurrentTimings.end,
+           "Start time should be smaller than end time.");
+        return aCurrentTimings;
+      });
+    } else {
+      serviceWorkerTimings.forEach(aTimings => {
+        is(aTimings.start, 0, "SW timings should be 0.");
+        is(aTimings.end, 0, "SW timings should be 0.");
+      });
+    }
+
+    // Check network related timings.
+    var networkTimings = [tc.domainLookupStartTime,
+                          tc.domainLookupEndTime,
+                          tc.connectStartTime,
+                          tc.connectEndTime,
+                          tc.requestStartTime,
+                          tc.responseStartTime,
+                          tc.responseEndTime];
+    if (aInput.fetch) {
+      networkTimings.reduce((aPreviousTiming, aCurrentTiming) => {
+        ok(aPreviousTiming <= aCurrentTiming, "Checking network timings");
+        return aCurrentTiming;
+      });
+    } else {
+      networkTimings.forEach(aTiming => is(aTiming, 0,
+                                           "Network timings should be 0."));
+    }
+
+    Services.obs.removeObserver(observer, topic);
+    promiseResolve();
+  }
+
+  const topic =  "http-on-stop-request";
+  Services.obs.addObserver(observer, topic);
+
+  await new Promise(resolve => { promiseResolve = resolve; });
+}
+
+async function contentFetch(aURL) {
+  await content.window.fetch(aURL);
+}
+
+async function registerSWAndWaitForActive(aServiceWorker) {
+  let swr = await content.navigator.serviceWorker.register(aServiceWorker);
+  await new Promise(resolve => {
+    let worker = swr.installing || swr.waiting || swr.active;
+    if (worker.state === 'activated') {
+      return resolve();
+    }
+
+    worker.addEventListener('statechange', () => {
+      if (worker.state === 'activated') {
+        return resolve();
+      }
+    });
+  });
+
+  await swr.active.postMessage('claim');
+}
+
+async function unregisterSW() {
+  let swr = await content.navigator.serviceWorker.getRegistration();
+  swr.unregister();
+}
+
+add_task(async function test_serivce_worker_interception() {
+  info("Setting the prefs to having e10s enabled");
+  await SpecialPowers.pushPrefEnv({"set": [
+    // Make sure observer and testing function run in the same process
+    ["dom.ipc.processCount", 1],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+  ]});
+
+  waitForExplicitFinish();
+
+  info("Open the tab");
+  let tab = BrowserTestUtils.addTab(gBrowser, emptyDoc);
+  let tabBrowser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(tabBrowser);
+
+  info("Open the tab for observing");
+  let tab_observer = BrowserTestUtils.addTab(gBrowser, emptyDoc);
+  let tabBrowser_observer = gBrowser.getBrowserForTab(tab_observer);
+  await BrowserTestUtils.browserLoaded(tabBrowser_observer);
+
+  let testcases = [
+    {
+      url: helloDoc,
+      intercepted: false,
+      fetch: true
+    },
+    {
+      url: fakeDoc,
+      intercepted: true,
+      fetch: false // should use HTTP cache
+    },
+    {
+      url: helloDoc + "?ForBypassingHttpCache=" + Date.now(),
+      intercepted: true,
+      fetch: true
+    }
+  ];
+
+  info("Test 1: Verify simple fetch");
+  let promise = ContentTask.spawn(tabBrowser_observer,
+                                  testcases[0],
+                                  checkObserverInContent);
+  await ContentTask.spawn(tabBrowser, testcases[0].url, contentFetch);
+  await promise;
+
+  info("Register a service worker");
+  await ContentTask.spawn(tabBrowser, sw, registerSWAndWaitForActive);
+
+  info("Test 2: Verify simple hijack");
+  promise = ContentTask.spawn(tabBrowser_observer,
+                              testcases[1],
+                              checkObserverInContent);
+  await ContentTask.spawn(tabBrowser, testcases[1].url, contentFetch);
+  await promise;
+
+  info("Test 3: Verify fetch without using http cache");
+  promise = ContentTask.spawn(tabBrowser_observer,
+                              testcases[2],
+                              checkObserverInContent);
+  await ContentTask.spawn(tabBrowser, testcases[2].url, contentFetch);
+  await promise;
+
+  info("Clean up");
+  await ContentTask.spawn(tabBrowser, undefined, unregisterSW);
+
+  gBrowser.removeTab(tab);
+  gBrowser.removeTab(tab_observer);
+});
+
--- a/dom/workers/test/serviceworkers/chrome.ini
+++ b/dom/workers/test/serviceworkers/chrome.ini
@@ -8,14 +8,13 @@ support-files =
   serviceworker.html
   serviceworkerinfo_iframe.html
   serviceworkermanager_iframe.html
   serviceworkerregistrationinfo_iframe.html
   utils.js
   worker.js
   worker2.js
 
-[test_devtools_serviceworker_interception.html]
 [test_devtools_track_serviceworker_time.html]
 [test_privateBrowsing.html]
 [test_serviceworkerinfo.xul]
 [test_serviceworkermanager.xul]
 [test_serviceworkerregistrationinfo.xul]
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html
+++ /dev/null
@@ -1,189 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1168875 - test devtools serviceworker interception.</title>
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet"
-        type="text/css"
-        href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script src="utils.js"></script>
-<script class="testbody" type="text/javascript">
-
-// Constants
-const Ci = Components.interfaces;
-const workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/";
-const workerURL = workerScope + "fetch.js";
-const contentPage = workerScope + "hello.html";
-
-function createTestWindow(aURL) {
-  var mainwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShellTreeItem)
-                         .rootTreeItem
-                         .QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindow);
-  var win = mainwindow.OpenBrowserWindow(contentPage);
-
-  return new Promise(aResolve => {
-    win.addEventListener("DOMContentLoaded", function callback() {
-      if (win.content.location.href != aURL) {
-        win.gBrowser.loadURI(aURL);
-        return;
-      }
-
-      win.removeEventListener("DOMContentLoaded", callback);
-      aResolve(win.content);
-    });
-  });
-}
-
-function executeTest(aWindow) {
-  var registration;
-
-  return Promise.resolve()
-    // Should not be intercepted.
-    .then(_ => fetchAndCheckTimedChannel(aWindow, false, true, "hello.html"))
-
-    // Regist a service worker.
-    .then(_ => register(aWindow, workerURL, workerScope))
-    .then(r => registration = r)
-
-    // If this test is re-run then we may end up resurrecting the previous
-    // registration and worker.  In those cases we will have an active instead
-    // of installing.  This happens because because the test window itself
-    // is controlled.  If we were using iframes we could ensure the registration
-    // was removed before ending the test.
-    .then(_ => waitForState(registration.installing || registration.active, 'activated'))
-
-    // When run consecutively we sometime end up resurrecting a previous
-    // service worker.  In that case our active event does not run and claim
-    // the window.  So do the claim for a message event instead.
-    .then(_ => registration.active.postMessage('claim'))
-    .then(_ => waitForControlled(aWindow))
-
-    // Should be intercepted and synthesized.
-    .then(_ => fetchAndCheckTimedChannel(aWindow, true, false, "fake.html"))
-
-    // Should be intercepted but still fetch from network.
-    .then(_ => fetchAndCheckTimedChannel(aWindow, true, true,
-                                         "hello.html?ForBypassingHttpCache=" + Date.now()))
-
-    // Tear down
-    .then(_ => registration.unregister())
-    .then(_ => aWindow.close());
-}
-
-function register(aWindow, aURL, aScope) {
-  return aWindow.navigator.serviceWorker.register(aURL, {scope: aScope})
-    .then(r => {
-      var worker = r.installing;
-      return new Promise(function(aResolve) {
-        worker.onstatechange = function() {
-          if (worker.state == "activated") {
-            aResolve(r);
-          }
-        }
-      });
-    });
-}
-
-function fetchAndCheckTimedChannel(aWindow, aIntercepted, aFetch, aURL) {
-  var resolveFunction;
-  var promise = new Promise(aResolve => resolveFunction = aResolve);
-
-  var topic = "http-on-stop-request";
-
-  function observer(aSubject) {
-    var channel = aSubject.QueryInterface(Ci.nsIChannel);
-
-    // Since we cannot make sure that the network event triggered by the fetch()
-    // in this testcase is the very next event processed by ObserverService, we
-    // have to wait until we catch the one we want.
-    if (!channel.URI.spec.endsWith(aURL)) {
-      return;
-    }
-
-    var tc = aSubject.QueryInterface(Ci.nsITimedChannel);
-
-    // Check service worker related timings.
-    var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime,
-                                 end:   tc.launchServiceWorkerEndTime},
-                                {start: tc.dispatchFetchEventStartTime,
-                                 end:   tc.dispatchFetchEventEndTime},
-                                {start: tc.handleFetchEventStartTime,
-                                 end:   tc.handleFetchEventEndTime}];
-    if (aIntercepted) {
-      serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => {
-        ok(aPreviousTimings.start <= aCurrentTimings.start,
-           "Start time order check.");
-        ok(aPreviousTimings.end <= aCurrentTimings.end,
-           "End time order check.");
-        ok(aCurrentTimings.start <= aCurrentTimings.end,
-           "Start time should be smaller than end time.");
-        return aCurrentTimings;
-      });
-    } else {
-      serviceWorkerTimings.forEach(aTimings => {
-        is(aTimings.start, 0, "SW timings should be 0.");
-        is(aTimings.end, 0, "SW timings should be 0.");
-      });
-    }
-
-    // Check network related timings.
-    var networkTimings = [tc.domainLookupStartTime,
-                          tc.domainLookupEndTime,
-                          tc.connectStartTime,
-                          tc.connectEndTime,
-                          tc.requestStartTime,
-                          tc.responseStartTime,
-                          tc.responseEndTime];
-    if (aFetch) {
-      networkTimings.reduce((aPreviousTiming, aCurrentTiming) => {
-        ok(aPreviousTiming <= aCurrentTiming, "Checking network timings");
-        return aCurrentTiming;
-      });
-    } else {
-      networkTimings.forEach(aTiming => is(aTiming, 0,
-                                           "Network timings should be 0."));
-    }
-
-    SpecialPowers.removeObserver(observer, topic);
-    resolveFunction();
-  }
-
-  SpecialPowers.addObserver(observer, topic);
-
-  // return promise;
-  return Promise.all([aWindow.fetch(aURL), promise]);
-}
-
-function runTest() {
-  return Promise.resolve()
-    .then(_ => createTestWindow(contentPage))
-    .then(w => executeTest(w))
-    .catch(e => ok(false, "Some test failed with error " + e))
-    .then(_ => SimpleTest.finish());
-}
-
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [
-  ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-  ["dom.serviceWorkers.enabled", true],
-  ["dom.serviceWorkers.testing.enabled", true],
-]}, runTest);
-
-</script>
-</pre>
-</body>
-</html>
-