Bug 1222617 - Filter out service worker messages that happened before a page load. r=bkelly, a=ritu
☠☠ backed out by 1624a69d46d7 ☠ ☠
authorBrian Grinstead <bgrinstead@mozilla.com>
Fri, 20 Nov 2015 06:52:32 -0800
changeset 305655 b02dc2a2305ef53488f5d6204da185863b1b0d36
parent 305654 3493d5fba50e5cc68c34e868200f5e1d312457f4
child 305656 b17fbacc53772ced13ed0c22d06c92565a40aa3a
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly, ritu
bugs1222617
milestone44.0a2
Bug 1222617 - Filter out service worker messages that happened before a page load. r=bkelly, a=ritu
devtools/server/actors/webconsole.js
devtools/shared/webconsole/test/chrome.ini
devtools/shared/webconsole/test/common.js
devtools/shared/webconsole/test/test_console_serviceworker.html
devtools/shared/webconsole/test/test_console_serviceworker_cached.html
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -727,19 +727,30 @@ WebConsoleActor.prototype =
 
     while (types.length > 0) {
       let type = types.shift();
       switch (type) {
         case "ConsoleAPI": {
           if (!this.consoleAPIListener) {
             break;
           }
+
+          let requestStartTime = this.window ?
+            this.window.performance.timing.requestStart : 0;
+
           let cache = this.consoleAPIListener
                       .getCachedMessages(!this.parentActor.isRootActor);
           cache.forEach((aMessage) => {
+            // Filter out messages that came from a ServiceWorker but happened
+            // before the page was requested.
+            if (aMessage.innerID === "ServiceWorker" &&
+                requestStartTime > aMessage.timeStamp) {
+              return;
+            }
+
             let message = this.prepareConsoleMessageForRemote(aMessage);
             message._type = type;
             messages.push(message);
           });
           break;
         }
         case "PageError": {
           if (!this.consoleServiceListener) {
--- a/devtools/shared/webconsole/test/chrome.ini
+++ b/devtools/shared/webconsole/test/chrome.ini
@@ -12,16 +12,17 @@ support-files =
 [test_basics.html]
 [test_bug819670_getter_throws.html]
 [test_cached_messages.html]
 [test_commands_other.html]
 [test_commands_registration.html]
 [test_consoleapi.html]
 [test_consoleapi_innerID.html]
 [test_console_serviceworker.html]
+[test_console_serviceworker_cached.html]
 [test_console_styling.html]
 [test_file_uri.html]
 [test_reflow.html]
 [test_jsterm.html]
 [test_jsterm_cd_iframe.html]
 [test_jsterm_last_result.html]
 [test_jsterm_queryselector.html]
 [test_network_get.html]
--- a/devtools/shared/webconsole/test/common.js
+++ b/devtools/shared/webconsole/test/common.js
@@ -103,16 +103,26 @@ function attachConsole(aListeners, aCall
 
 function closeDebugger(aState, aCallback)
 {
   aState.dbgClient.close(aCallback);
   aState.dbgClient = null;
   aState.client = null;
 }
 
+function checkConsoleAPICalls(consoleCalls, expectedConsoleCalls)
+{
+  is(consoleCalls.length, expectedConsoleCalls.length,
+    'received correct number of console calls');
+  expectedConsoleCalls.forEach(function(aMessage, aIndex) {
+    info("checking received console call #" + aIndex);
+    checkConsoleAPICall(consoleCalls[aIndex], expectedConsoleCalls[aIndex]);
+  });
+}
+
 function checkConsoleAPICall(aCall, aExpected)
 {
   if (aExpected.level != "trace" && aExpected.arguments) {
     is(aCall.arguments.length, aExpected.arguments.length,
        "number of arguments");
   }
 
   checkObject(aCall, aExpected);
@@ -213,8 +223,81 @@ function runTests(aTests, aEndCallback)
   gTestState.driver = driver();
   return gTestState.driver.next();
 }
 
 function nextTest(aMessage)
 {
   return gTestState.driver.next(aMessage);
 }
+
+function withFrame(url) {
+  return new Promise(resolve => {
+    let iframe = document.createElement("iframe");
+    iframe.onload = function() {
+      resolve(iframe);
+    };
+    iframe.src = url;
+    document.body.appendChild(iframe);
+  });
+}
+
+function navigateFrame(iframe, url) {
+  return new Promise(resolve => {
+    iframe.onload = function() {
+      resolve(iframe);
+    };
+    iframe.src = url;
+  });
+}
+
+function forceReloadFrame(iframe) {
+  return new Promise(resolve => {
+    iframe.onload = function() {
+      resolve(iframe);
+    };
+    iframe.contentWindow.location.reload(true);
+  });
+}
+
+function withActiveServiceWorker(win, url, scope) {
+  let opts = {};
+  if (scope) {
+    opts.scope = scope;
+  }
+  return win.navigator.serviceWorker.register(url, opts).then(swr => {
+    if (swr.active) {
+      return swr;
+    }
+
+    // Unfortunately we can't just use navigator.serviceWorker.ready promise
+    // here.  If the service worker is for a scope that does not cover the window
+    // then the ready promise will never resolve.  Instead monitor the service
+    // workers state change events to determine when its activated.
+    return new Promise(resolve => {
+      let sw = swr.waiting || swr.installing;
+      sw.addEventListener('statechange', function stateHandler(evt) {
+        if (sw.state === 'activated') {
+          sw.removeEventListener('statechange', stateHandler);
+          resolve(swr);
+        }
+      });
+    });
+  });
+}
+
+function messageServiceWorker(win, scope, message) {
+  return win.navigator.serviceWorker.getRegistration(scope).then(swr => {
+    return new Promise(resolve => {
+      win.navigator.serviceWorker.onmessage = evt => {
+        resolve();
+      };
+      let sw = swr.active || swr.waiting || swr.installing;
+      sw.postMessage({ type: 'PING', message: message });
+    });
+  })
+}
+
+function unregisterServiceWorker(win) {
+  return win.navigator.serviceWorker.ready.then(swr => {
+    return swr.unregister();
+  });
+}
--- a/devtools/shared/webconsole/test/test_console_serviceworker.html
+++ b/devtools/shared/webconsole/test/test_console_serviceworker.html
@@ -59,85 +59,16 @@ let startTest = Task.async(function*() {
       ["devtools.webconsole.filter.serviceworkers", true]
     ]}, resolve);
   });
 
   attachConsole(["ConsoleAPI"], onAttach, true);
 });
 addEventListener("load", startTest);
 
-function withFrame(url) {
-  return new Promise(resolve => {
-    let iframe = document.createElement("iframe");
-    iframe.onload = function() {
-      resolve(iframe);
-    };
-    iframe.src = url;
-    document.body.appendChild(iframe);
-  });
-}
-
-function navigateFrame(iframe, url) {
-  return new Promise(resolve => {
-    iframe.onload = function() {
-      resolve(iframe);
-    };
-    iframe.src = url;
-  });
-}
-
-function forceReloadFrame(iframe) {
-  return new Promise(resolve => {
-    iframe.onload = function() {
-      resolve(iframe);
-    };
-    iframe.contentWindow.location.reload(true);
-  });
-}
-
-function withActiveServiceWorker(win, url, scope) {
-  return win.navigator.serviceWorker.register(url, { scope: scope }).then(swr => {
-    if (swr.active) {
-      return swr;
-    }
-
-    // Unfortunately we can't just use navigator.serviceWorker.ready promise
-    // here.  If the service worker is for a scope that does not cover the window
-    // then the ready promise will never resolve.  Instead monitor the service
-    // workers state change events to determine when its activated.
-    return new Promise(resolve => {
-      let sw = swr.waiting || swr.installing;
-      sw.addEventListener('statechange', function stateHandler(evt) {
-        if (sw.state === 'activated') {
-          sw.removeEventListener('statechange', stateHandler);
-          resolve(swr);
-        }
-      });
-    });
-  });
-}
-
-function messageServiceWorker(win, scope, message) {
-  return win.navigator.serviceWorker.getRegistration(scope).then(swr => {
-    return new Promise(resolve => {
-      win.navigator.serviceWorker.onmessage = evt => {
-        resolve();
-      };
-      let sw = swr.active || swr.waiting || swr.installing;
-      sw.postMessage({ type: 'PING', message: message });
-    });
-  })
-}
-
-function unregisterServiceWorker(win) {
-  return win.navigator.serviceWorker.ready.then(swr => {
-    return swr.unregister();
-  });
-}
-
 let onAttach = Task.async(function*(state, response) {
   onConsoleAPICall = onConsoleAPICall.bind(null, state);
   state.dbgClient.addListener("consoleAPICall", onConsoleAPICall);
 
   let currentFrame;
   try {
     // First, we need a frame from which to register our script.  This
     // will not trigger any console calls.
@@ -189,22 +120,17 @@ let onAttach = Task.async(function*(stat
     info("Completed force refresh.  Messaging service worker.");
     yield messageServiceWorker(currentFrame.contentWindow, SCOPE, MESSAGE);
 
     info("Done messaging service worker.  Unregistering service worker.");
     yield unregisterServiceWorker(currentFrame.contentWindow);
 
     info('Service worker unregistered.  Checking console calls.');
     state.dbgClient.removeListener("consoleAPICall", onConsoleAPICall);
-    is(consoleCalls.length, expectedConsoleCalls.length,
-       'received correct number of console calls');
-    expectedConsoleCalls.forEach(function(aMessage, aIndex) {
-      info("checking received console call #" + aIndex);
-      checkConsoleAPICall(consoleCalls[aIndex], expectedConsoleCalls[aIndex]);
-    });
+    checkConsoleAPICalls(consoleCalls, expectedConsoleCalls);
   } catch(error) {
     ok(false, 'unexpected error: ' + error);
   } finally {
     if (currentFrame) {
       currentFrame.remove();
       currentFrame = null;
     }
     consoleCalls = [];
new file mode 100644
--- /dev/null
+++ b/devtools/shared/webconsole/test/test_console_serviceworker_cached.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+  <meta charset="utf8">
+  <title>Test for getCachedMessages and Service Workers</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript;version=1.8" src="common.js"></script>
+  <!-- Any copyright is dedicated to the Public Domain.
+     - http://creativecommons.org/publicdomain/zero/1.0/ -->
+</head>
+<body>
+<p>Test for getCachedMessages and Service Workers</p>
+
+<script class="testbody" type="text/javascript;version=1.8">
+SimpleTest.waitForExplicitFinish();
+
+let BASE_URL = "https://example.com/chrome/devtools/shared/webconsole/test/";
+let SERVICE_WORKER_URL = BASE_URL + "helper_serviceworker.js";
+let FRAME_URL = BASE_URL + "sandboxed_iframe.html";
+
+let firstTabExpectedCalls = [
+  {
+    level: "log",
+    filename: /helper_serviceworker/,
+    arguments: ['script evaluation'],
+  },
+  {
+    level: "log",
+    filename: /helper_serviceworker/,
+    arguments: ['install event'],
+  },
+  {
+    level: "log",
+    filename: /helper_serviceworker/,
+    arguments: ['activate event'],
+  },
+];
+
+let secondTabExpectedCalls = [
+  {
+    level: "log",
+    filename: /helper_serviceworker/,
+    arguments: ['fetch event: ' + FRAME_URL],
+  }
+];
+
+let startTest = Task.async(function*() {
+  removeEventListener("load", startTest);
+
+  yield new Promise(resolve => {
+    SpecialPowers.pushPrefEnv({"set": [
+      ["devtools.webconsole.filter.serviceworkers", true]
+    ]}, resolve);
+  });
+
+  info("Adding a tab and attaching a service worker");
+  let tab1 = yield addTab(FRAME_URL);
+  let swr = yield withActiveServiceWorker(tab1.linkedBrowser.contentWindow,
+                                SERVICE_WORKER_URL);
+
+  yield new Promise(resolve => {
+    info("Attaching console to tab 1");
+    attachConsoleToTab(["ConsoleAPI"], function(state) {
+      state.client.getCachedMessages(["ConsoleAPI"], function(calls) {
+        checkConsoleAPICalls(calls.messages, firstTabExpectedCalls);
+        closeDebugger(state, resolve);
+      });
+    });
+  });
+
+  // Because this tab is being added after the original messages happened,
+  // they shouldn't show up in a call to getCachedMessages.
+  // However, there is a fetch event which is logged due to loading the tab.
+  info("Adding a new tab at the same URL");
+  let tab2 = yield addTab(FRAME_URL);
+  yield new Promise(resolve => {
+    info("Attaching console to tab 2");
+    attachConsoleToTab(["ConsoleAPI"], function(state) {
+      state.client.getCachedMessages(["ConsoleAPI"], function(calls) {
+        checkConsoleAPICalls(calls.messages, secondTabExpectedCalls);
+        closeDebugger(state, resolve);
+      });
+    });
+  });
+
+  yield swr.unregister();
+
+  SimpleTest.finish();
+});
+addEventListener("load", startTest);
+
+// This test needs to add tabs that are controlled by a service worker
+// so use some special powers to dig around and find gBrowser
+let {gBrowser} = SpecialPowers._getTopChromeWindow(SpecialPowers.window.get());
+
+SimpleTest.registerCleanupFunction(() => {
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeCurrentTab();
+  }
+});
+
+function addTab(url) {
+  info("Adding a new tab with URL: '" + url + "'");
+  return new Promise(resolve => {
+    let tab = gBrowser.selectedTab = gBrowser.addTab(url);
+    gBrowser.selectedBrowser.addEventListener("load", function onload() {
+      gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+      info("URL '" + url + "' loading complete");
+      resolve(tab);
+    }, true);
+  });
+}
+
+</script>
+</body>
+</html>