Bug 1469036 - Stop using the ServiceWorkerManager in the child. r=asuth
authorBlake Kaplan <mrbkap@gmail.com>
Mon, 26 Nov 2018 19:39:13 +0000
changeset 507307 5b79f38fa7eec7e493654f113c362ade06c4fae1
parent 507306 6417da60696e53fea6174ffff00f84cf7c614b0e
child 507308 b0fdcebfd20dd1319ea589ec79afc7dcad8a6c85
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1469036
milestone65.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 1469036 - Stop using the ServiceWorkerManager in the child. r=asuth With parent intercept enabled, the source of truth for ServiceWorker registrations lives in the parent process. In order to avoid sync messages or having to wait after every test, we count the number of active registrations in the parent and inform the children when there is or isn't a registration. In non-parent-intercept mode, the parent finds out about new service worker registrations too late, so we have to keep the old code hanging around for the time being. Differential Revision: https://phabricator.services.mozilla.com/D12151
testing/mochitest/tests/Harness_sanity/mochitest.ini
testing/specialpowers/content/SpecialPowersObserver.jsm
testing/specialpowers/content/SpecialPowersObserverAPI.js
testing/specialpowers/content/specialpowers.js
--- a/testing/mochitest/tests/Harness_sanity/mochitest.ini
+++ b/testing/mochitest/tests/Harness_sanity/mochitest.ini
@@ -5,20 +5,19 @@ skip-if = true #depends on fix for bug 1
 [test_importInMainProcess.html]
 skip-if = verify
 support-files = importtesting_chromescript.js
 [test_sanity.html]
 [test_sanityException.html]
 [test_sanityException2.html]
 [test_sanityParams.html]
 [test_sanityRegisteredServiceWorker.html]
-skip-if = serviceworker_e10s
 support-files = empty.js
 [test_sanityRegisteredServiceWorker2.html]
-skip-if = verify || serviceworker_e10s
+skip-if = verify
 support-files = empty.js
 [test_sanityWindowSnapshot.html]
 [test_SpecialPowersExtension.html]
 [test_SpecialPowersExtension2.html]
 support-files = file_SpecialPowersFrame1.html
 [test_SpecialPowersPushPermissions.html]
 support-files =
     specialPowers_framescript.js
--- a/testing/specialpowers/content/SpecialPowersObserver.jsm
+++ b/testing/specialpowers/content/SpecialPowersObserver.jsm
@@ -22,16 +22,17 @@ const CHILD_LOGGER_SCRIPT = "resource://
 
 // Glue to add in the observer API to this object.  This allows us to share code with chrome tests
 Services.scriptloader.loadSubScript("resource://specialpowers/SpecialPowersObserverAPI.js");
 
 /* XPCOM gunk */
 function SpecialPowersObserver() {
   this._isFrameScriptLoaded = false;
   this._messageManager = Services.mm;
+  this._serviceWorkerListener = null;
 }
 
 
 SpecialPowersObserver.prototype = new SpecialPowersObserverAPI();
 
 SpecialPowersObserver.prototype.QueryInterface = ChromeUtils.generateQI([Ci.nsIObserver]);
 
 SpecialPowersObserver.prototype.observe = function(aSubject, aTopic, aData) {
@@ -73,16 +74,17 @@ SpecialPowersObserver.prototype._loadFra
     this._messageManager.addMessageListener("SPSetTestPluginEnabledState", this);
     this._messageManager.addMessageListener("SPLoadExtension", this);
     this._messageManager.addMessageListener("SPStartupExtension", this);
     this._messageManager.addMessageListener("SPUnloadExtension", this);
     this._messageManager.addMessageListener("SPExtensionMessage", this);
     this._messageManager.addMessageListener("SPCleanUpSTSData", this);
     this._messageManager.addMessageListener("SPRequestDumpCoverageCounters", this);
     this._messageManager.addMessageListener("SPRequestResetCoverageCounters", this);
+    this._messageManager.addMessageListener("SPCheckServiceWorkers", this);
 
     this._messageManager.loadFrameScript(CHILD_LOGGER_SCRIPT, true);
     this._messageManager.loadFrameScript(CHILD_SCRIPT_API, true);
     this._isFrameScriptLoaded = true;
     this._createdFiles = null;
   }
 };
 
@@ -104,28 +106,54 @@ SpecialPowersObserver.prototype.init = f
   var manifestFile = Services.io.newFileURI(testsURI).
                        QueryInterface(Ci.nsIFileURL).file;
 
   Components.manager.QueryInterface(Ci.nsIComponentRegistrar).
                  autoRegister(manifestFile);
 
   obs.addObserver(this, "http-on-modify-request");
 
+  // We would like to check that tests don't leave service workers around
+  // after they finish, but that information lives in the parent process.
+  // Ideally, we'd be able to tell the child processes whenever service
+  // workers are registered or unregistered so they would know at all times,
+  // but service worker lifetimes are complicated enough to make that
+  // difficult. For the time being, let the child process know when a test
+  // registers a service worker so it can ask, synchronously, at the end if
+  // the service worker had unregister called on it.
+  let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
+              .getService(Ci.nsIServiceWorkerManager);
+  let self = this;
+  this._serviceWorkerListener = {
+    onRegister() {
+      self.onRegister();
+    },
+
+    onUnregister() {
+      // no-op
+    },
+  };
+  swm.addListener(this._serviceWorkerListener);
+
   this._loadFrameScript();
 };
 
 SpecialPowersObserver.prototype.uninit = function() {
   var obs = Services.obs;
   obs.removeObserver(this, "chrome-document-global-created");
   obs.removeObserver(this, "http-on-modify-request");
   this._registerObservers._topics.forEach((element) => {
     obs.removeObserver(this._registerObservers, element);
   });
   this._removeProcessCrashObservers();
 
+  let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
+              .getService(Ci.nsIServiceWorkerManager);
+  swm.removeListener(this._serviceWorkerListener);
+
   if (this._isFrameScriptLoaded) {
     this._messageManager.removeMessageListener("SPPrefService", this);
     this._messageManager.removeMessageListener("SPProcessCrashManagerWait", this);
     this._messageManager.removeMessageListener("SPProcessCrashService", this);
     this._messageManager.removeMessageListener("SPPingService", this);
     this._messageManager.removeMessageListener("SpecialPowers.Quit", this);
     this._messageManager.removeMessageListener("SpecialPowers.Focus", this);
     this._messageManager.removeMessageListener("SpecialPowers.CreateFiles", this);
@@ -139,16 +167,17 @@ SpecialPowersObserver.prototype.uninit =
     this._messageManager.removeMessageListener("SPSetTestPluginEnabledState", this);
     this._messageManager.removeMessageListener("SPLoadExtension", this);
     this._messageManager.removeMessageListener("SPStartupExtension", this);
     this._messageManager.removeMessageListener("SPUnloadExtension", this);
     this._messageManager.removeMessageListener("SPExtensionMessage", this);
     this._messageManager.removeMessageListener("SPCleanUpSTSData", this);
     this._messageManager.removeMessageListener("SPRequestDumpCoverageCounters", this);
     this._messageManager.removeMessageListener("SPRequestResetCoverageCounters", this);
+    this._messageManager.removeMessageListener("SPCheckServiceWorkers", this);
 
     this._messageManager.removeDelayedFrameScript(CHILD_LOGGER_SCRIPT);
     this._messageManager.removeDelayedFrameScript(CHILD_SCRIPT_API);
     this._isFrameScriptLoaded = false;
   }
 };
 
 SpecialPowersObserver.prototype._addProcessCrashObservers = function() {
@@ -277,8 +306,13 @@ SpecialPowersObserver.prototype.receiveM
         this._createdFiles = null;
       }
       break;
     default:
       return this._receiveMessage(aMessage);
   }
   return undefined;
 };
+
+SpecialPowersObserver.prototype.onRegister = function() {
+  this._sendAsyncMessage("SPServiceWorkerRegistered",
+    { registered: true });
+};
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -553,16 +553,22 @@ SpecialPowersObserverAPI.prototype = {
 
       case "SPRequestResetCoverageCounters": {
         PerTestCoverageUtils.beforeTest().then(() =>
           this._sendReply(aMessage, "SPRequestResetCoverageCounters", {})
         );
         return undefined; // See comment at the beginning of this function.
       }
 
+      case "SPCheckServiceWorkers": {
+        let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
+                    .getService(Ci.nsIServiceWorkerManager);
+        return { hasWorkers: swm.getAllRegistrations().length != 0 };
+      }
+
       case "SPLoadExtension": {
         let id = aMessage.data.id;
         let ext = aMessage.data.ext;
         let extension = ExtensionTestCommon.generate(ext);
 
         let resultListener = (...args) => {
           this._sendReply(aMessage, "SPExtensionMessage", {id, type: "testResult", args});
         };
@@ -635,17 +641,17 @@ SpecialPowersObserverAPI.prototype = {
           await extension._uninstallPromise;
           this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionUnloaded", args: []});
         };
         extension.shutdown().then(done, done);
         return undefined;
       }
 
       default:
-        throw new SpecialPowersError("Unrecognized Special Powers API");
+        throw new SpecialPowersError(`Unrecognized Special Powers API: ${aMessage.name}`);
     }
 
     // We throw an exception before reaching this explicit return because
     // we should never be arriving here anyway.
     throw new SpecialPowersError("Unreached code"); // eslint-disable-line no-unreachable
     return undefined;
   },
 };
--- a/testing/specialpowers/content/specialpowers.js
+++ b/testing/specialpowers/content/specialpowers.js
@@ -20,16 +20,17 @@ Cu.forcePermissiveCOWs();
 function SpecialPowers(window, mm) {
   this.mm = mm;
 
   this.window = Cu.getWeakReference(window);
   this._windowID = window.windowUtils.currentInnerWindowID;
   this._encounteredCrashDumpFiles = [];
   this._unexpectedCrashDumpFiles = { };
   this._crashDumpDir = null;
+  this._serviceWorkerRegistered = false;
   this.DOMWindowUtils = bindDOMWindowUtils(window);
   Object.defineProperty(this, "Components", {
       configurable: true, enumerable: true, value: this.getFullComponents(),
   });
   this._pongHandlers = [];
   this._messageListener = this._messageReceived.bind(this);
   this._grandChildFrameMM = null;
   this._createFilesOnError = null;
@@ -37,31 +38,33 @@ function SpecialPowers(window, mm) {
   this.SP_SYNC_MESSAGES = ["SPChromeScriptMessage",
                            "SPLoadChromeScript",
                            "SPImportInMainProcess",
                            "SPObserverService",
                            "SPPermissionManager",
                            "SPPrefService",
                            "SPProcessCrashService",
                            "SPSetTestPluginEnabledState",
-                           "SPCleanUpSTSData"];
+                           "SPCleanUpSTSData",
+                           "SPCheckServiceWorkers"];
 
   this.SP_ASYNC_MESSAGES = ["SpecialPowers.Focus",
                             "SpecialPowers.Quit",
                             "SpecialPowers.CreateFiles",
                             "SpecialPowers.RemoveFiles",
                             "SPPingService",
                             "SPLoadExtension",
                             "SPProcessCrashManagerWait",
                             "SPStartupExtension",
                             "SPUnloadExtension",
                             "SPExtensionMessage",
                             "SPRequestDumpCoverageCounters",
                             "SPRequestResetCoverageCounters"];
   mm.addMessageListener("SPPingService", this._messageListener);
+  mm.addMessageListener("SPServiceWorkerRegistered", this._messageListener);
   mm.addMessageListener("SpecialPowers.FilesCreated", this._messageListener);
   mm.addMessageListener("SpecialPowers.FilesError", this._messageListener);
   let self = this;
   Services.obs.addObserver(function onInnerWindowDestroyed(subject, topic, data) {
     var id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
     if (self._windowID === id) {
       Services.obs.removeObserver(onInnerWindowDestroyed, "inner-window-destroyed");
       try {
@@ -139,16 +142,20 @@ SpecialPowers.prototype._messageReceived
           handler();
         }
         if (this._grandChildFrameMM) {
           this._grandChildFrameMM.sendAsyncMessage("SPPingService", { op: "pong" });
         }
       }
       break;
 
+    case "SPServiceWorkerRegistered":
+      this._serviceWorkerRegistered = aMessage.data.registered;
+      break;
+
     case "SpecialPowers.FilesCreated":
       var createdHandler = this._createFilesOnSuccess;
       this._createFilesOnSuccess = null;
       this._createFilesOnError = null;
       if (createdHandler) {
         createdHandler(Cu.cloneInto(aMessage.data, this.mm.content));
       }
       break;
@@ -229,19 +236,35 @@ SpecialPowers.prototype.nestedFrameSetup
 
       let frameScript = "SpecialPowers.prototype.IsInNestedFrame=true;";
       mm.loadFrameScript("data:," + frameScript, false);
     }
   }, "remote-browser-shown");
 };
 
 SpecialPowers.prototype.isServiceWorkerRegistered = function() {
-  var swm = Cc["@mozilla.org/serviceworkers/manager;1"]
-              .getService(Ci.nsIServiceWorkerManager);
-  return swm.getAllRegistrations().length != 0;
+  // For the time being, if parent_intercept is false, we can assume that
+  // ServiceWorkers registered by the current test are all known to the SWM in
+  // this process.
+  if (!Services.prefs.getBoolPref("dom.serviceWorkers.parent_intercept", false)) {
+    let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
+                .getService(Ci.nsIServiceWorkerManager);
+    return swm.getAllRegistrations().length != 0;
+  }
+
+  // Please see the comment in SpecialPowersObserver.jsm above
+  // this._serviceWorkerListener's assignment for what this returns.
+  if (this._serviceWorkerRegistered) {
+    // This test registered at least one service worker. Send a synchronous
+    // call to the parent to make sure that it called unregister on all of its
+    // service workers.
+    return this._sendSyncMessage("SPCheckServiceWorkers")[0].hasWorkers;
+  }
+
+  return false;
 };
 
 // Attach our API to the window.
 function attachSpecialPowersToWindow(aWindow, mm) {
   try {
     if ((aWindow !== null) &&
         (aWindow !== undefined) &&
         (aWindow.wrappedJSObject) &&