Bug 1415388 - P2: Add a test to verify pass timing data correctlly after an internal redirect. r=bkelly
authorTom Tung <shes050117@gmail.com>
Tue, 14 Nov 2017 15:12:33 +0800
changeset 701901 39d8372dd2e7dd853c09d922a206f0fd3d09be49
parent 701900 051a59c4a53a6434c25c2460155c549f69fedd24
child 701902 d28a3ef4867908a8024ba705c1773c82c92171b8
push id90308
push userbmo:lhansen@mozilla.com
push dateWed, 22 Nov 2017 12:45:04 +0000
reviewersbkelly
bugs1415388
milestone59.0a1
Bug 1415388 - P2: Add a test to verify pass timing data correctlly after an internal redirect. r=bkelly
dom/workers/test/serviceworkers/browser_devtools_serviceworker_interception.js
dom/workers/test/serviceworkers/fetch.js
--- a/dom/workers/test/serviceworkers/browser_devtools_serviceworker_interception.js
+++ b/dom/workers/test/serviceworkers/browser_devtools_serviceworker_interception.js
@@ -2,42 +2,64 @@
 
 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 CROSS_URI = "http://example.com/browser/dom/workers/test/serviceworkers/";
+const crossRedirect = CROSS_URI + "redirect";
+const crossHelloDoc = CROSS_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 interceptedChannel = null;
   let promiseResolve;
 
   function observer(aSubject) {
-    var channel = aSubject.QueryInterface(Ci.nsIChannel);
+    let 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)) {
+    if (!(aInput.redirect && channel.URI.spec.includes(aInput.redirect)) &&
+        !(!aInput.redirect && channel.URI.spec.includes(aInput.url))) {
       return;
     }
-    var tc = aSubject.QueryInterface(Ci.nsITimedChannel);
+
+    // Wait for the service worker to intercept the request if it's expected to
+    // be intercepted
+    if (aInput.intercepted && interceptedChannel === null) {
+      return;
+    } else if (interceptedChannel) {
+      ok(aInput.intercepted,
+         "Service worker intercepted the channel as expected");
+    } else {
+      ok(!aInput.intercepted, "The channel doesn't be intercepted");
+    }
+
+    var tc = interceptedChannel
+               ? interceptedChannel.QueryInterface(Ci.nsITimedChannel)
+               : 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) {
+    if (aInput.swPresent) {
       serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => {
+        ok(aPreviousTimings.start !== 0, "Start time check.");
         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;
       });
@@ -61,27 +83,49 @@ async function checkObserverInContent(aI
         ok(aPreviousTiming <= aCurrentTiming, "Checking network timings");
         return aCurrentTiming;
       });
     } else {
       networkTimings.forEach(aTiming => is(aTiming, 0,
                                            "Network timings should be 0."));
     }
 
+    interceptedChannel = null;
     Services.obs.removeObserver(observer, topic);
     promiseResolve();
   }
 
-  const topic =  "http-on-stop-request";
+  function addInterceptedChannel(aSubject) {
+    let channel = aSubject.QueryInterface(Ci.nsIChannel);
+    if (!channel.URI.spec.includes(aInput.url)) {
+      return;
+    }
+
+    // Hold the interceptedChannel until checking timing information.
+    // Note: It's a interceptedChannel in the type of httpChannel
+    interceptedChannel = channel;
+    Services.obs.removeObserver(addInterceptedChannel, topic_SW);
+  }
+
+  const topic = "http-on-stop-request";
+  const topic_SW = "service-worker-synthesized-response";
+
   Services.obs.addObserver(observer, topic);
+  if (aInput.intercepted) {
+    Services.obs.addObserver(addInterceptedChannel, topic_SW);
+  }
 
   await new Promise(resolve => { promiseResolve = resolve; });
 }
 
 async function contentFetch(aURL) {
+  if (aURL.includes("redirect")) {
+    await content.window.fetch(aURL, { mode: "no-cors" });
+    return;
+  }
   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') {
@@ -122,26 +166,36 @@ add_task(async function test_serivce_wor
   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,
+      swPresent: false,
       intercepted: false,
       fetch: true
     },
     {
       url: fakeDoc,
+      swPresent: true,
       intercepted: true,
       fetch: false // should use HTTP cache
     },
-    {
+    { // Bypass http cache
       url: helloDoc + "?ForBypassingHttpCache=" + Date.now(),
+      swPresent: true,
+      intercepted: false,
+      fetch: true
+    },
+    { // no-cors mode redirect to no-cors mode (trigger internal redirect)
+      url: crossRedirect + "?url=" + crossHelloDoc + "&mode=no-cors",
+      swPresent: true,
+      redirect: "hello.html",
       intercepted: true,
       fetch: true
     }
   ];
 
   info("Test 1: Verify simple fetch");
   let promise = ContentTask.spawn(tabBrowser_observer,
                                   testcases[0],
@@ -161,15 +215,22 @@ add_task(async function test_serivce_wor
 
   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("Test 4: make a internal redirect");
+  promise = ContentTask.spawn(tabBrowser_observer,
+                              testcases[3],
+                              checkObserverInContent);
+  await ContentTask.spawn(tabBrowser, testcases[3].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/fetch.js
+++ b/dom/workers/test/serviceworkers/fetch.js
@@ -1,13 +1,33 @@
+function get_query_params(url) {
+  var search = (new URL(url)).search;
+  if (!search) {
+    return {};
+  }
+  var ret = {};
+  var params = search.substring(1).split('&');
+  params.forEach(function(param) {
+      var element = param.split('=');
+      ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]);
+  });
+  return ret;
+}
+
 addEventListener('fetch', function(event) {
-  if (event.request.url.indexOf("fail.html") !== -1) {
-    event.respondWith(fetch("hello.html", {"integrity": "abc"}));
-  } else if (event.request.url.indexOf("fake.html") !== -1) {
-    event.respondWith(fetch("hello.html"));
+  if (event.request.url.includes('fail.html')) {
+    event.respondWith(fetch('hello.html', { integrity: 'abc' }));
+  } else if (event.request.url.includes('fake.html')) {
+    event.respondWith(fetch('hello.html'));
+  } else if (event.request.url.includes('redirect')) {
+    let param = get_query_params(event.request.url);
+    let url = param['url'];
+    let mode = param['mode'];
+
+    event.respondWith(fetch(url, { mode: mode }));
   }
 });
 
 addEventListener('message', function(event) {
   if (event.data === 'claim') {
     event.waitUntil(clients.claim());
   }
 });