Bug 1518581 - Move worker listening logic to client wrapper;r=ladybenko,daisuke a=lizzard
authorJulian Descottes <jdescottes@mozilla.com>
Thu, 14 Feb 2019 08:19:17 +0000
changeset 516182 7fc7867411b58196980f5f724f086b6250643750
parent 516181 3b281fad428520421d5d7a8aaff2cbbad814d461
child 516183 cf46b5a6e3ed1f8db130127dca8d14b9fddb7cfa
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersladybenko, daisuke, lizzard
bugs1518581
milestone66.0
Bug 1518581 - Move worker listening logic to client wrapper;r=ladybenko,daisuke a=lizzard This will make it easier to mock the behavior Differential Revision: https://phabricator.services.mozilla.com/D18234
devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
devtools/client/aboutdebugging-new/src/modules/moz.build
devtools/client/aboutdebugging-new/src/modules/workers-listener.js
devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_select_page_with_serviceworker.js
devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_workers_remote_runtime.js
devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
--- a/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
+++ b/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
@@ -31,61 +31,32 @@ function debugTargetListenerMiddleware(s
 
         // Tabs
         clientWrapper.addListener("tabListChanged", onTabsUpdated);
 
         // Addons
         clientWrapper.addListener("addonListChanged", onExtensionsUpdated);
 
         // Workers
-        clientWrapper.addListener("workerListChanged", onWorkersUpdated);
-        clientWrapper.onFront("contentProcessTarget", front => {
-          clientWrapper.contentProcessFronts.push(front);
-          front.on("workerListChanged", onWorkersUpdated);
-        });
-
-        clientWrapper.onFront("serviceWorkerRegistration", front => {
-          clientWrapper.serviceWorkerRegistrationFronts.push(front);
-          front.on("push-subscription-modified", onWorkersUpdated);
-          front.on("registration-changed", onWorkersUpdated);
-        });
-
-        clientWrapper.addListener("serviceWorkerRegistrationListChanged",
-          onWorkersUpdated);
-        clientWrapper.addListener("processListChanged", onWorkersUpdated);
+        clientWrapper.addListener("workersUpdated", onWorkersUpdated);
 
         break;
       }
       case UNWATCH_RUNTIME_START: {
         const { runtime } = action;
         const { clientWrapper } = runtime.runtimeDetails;
 
         // Tabs
         clientWrapper.removeListener("tabListChanged", onTabsUpdated);
 
         // Addons
         clientWrapper.removeListener("addonListChanged", onExtensionsUpdated);
 
         // Workers
-        clientWrapper.removeListener("workerListChanged", onWorkersUpdated);
-        clientWrapper.removeListener("serviceWorkerRegistrationListChanged",
-          onWorkersUpdated);
-
-        for (const front of clientWrapper.contentProcessFronts) {
-          front.off("workerListChanged", onWorkersUpdated);
-        }
-        clientWrapper.contentProcessFronts = [];
-
-        for (const front of clientWrapper.serviceWorkerRegistrationFronts) {
-          front.off("push-subscription-modified", onWorkersUpdated);
-          front.off("registration-changed", onWorkersUpdated);
-        }
-        clientWrapper.serviceWorkerRegistrationFronts = [];
-
-        clientWrapper.removeListener("processListChanged", onWorkersUpdated);
+        clientWrapper.removeListener("workersUpdated", onWorkersUpdated);
 
         break;
       }
     }
 
     return next(action);
   };
 }
--- a/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
+++ b/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
@@ -1,64 +1,64 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { RUNTIME_PREFERENCE } = require("../constants");
+const { WorkersListener } = require("./workers-listener");
 
 const PREF_TYPES = {
   BOOL: "BOOL",
 };
 
 // Map of preference to preference type.
 const PREF_TO_TYPE = {
   [RUNTIME_PREFERENCE.CONNECTION_PROMPT]: PREF_TYPES.BOOL,
 };
 
 // Some events are fired by mainRoot rather than client.
 const MAIN_ROOT_EVENTS = [
   "addonListChanged",
-  "processListChanged",
-  "serviceWorkerRegistrationListChanged",
   "tabListChanged",
-  "workerListChanged",
 ];
 
 /**
  * The ClientWrapper class is used to isolate aboutdebugging from the DevTools client API
  * The modules of about:debugging should never call DevTools client APIs directly.
  */
 class ClientWrapper {
   constructor(client) {
     this.client = client;
-    // Array of contentProcessTarget fronts on which we will listen for worker events.
-    this.contentProcessFronts = [];
-    this.serviceWorkerRegistrationFronts = [];
+    this.workersListener = new WorkersListener(client.mainRoot);
   }
 
   addOneTimeListener(evt, listener) {
     if (MAIN_ROOT_EVENTS.includes(evt)) {
       this.client.mainRoot.once(evt, listener);
     } else {
       this.client.addOneTimeListener(evt, listener);
     }
   }
 
   addListener(evt, listener) {
-    if (MAIN_ROOT_EVENTS.includes(evt)) {
+    if (evt === "workersUpdated") {
+      this.workersListener.addListener(listener);
+    } else if (MAIN_ROOT_EVENTS.includes(evt)) {
       this.client.mainRoot.on(evt, listener);
     } else {
       this.client.addListener(evt, listener);
     }
   }
 
   removeListener(evt, listener) {
-    if (MAIN_ROOT_EVENTS.includes(evt)) {
+    if (evt === "workersUpdated") {
+      this.workersListener.removeListener(listener);
+    } else if (MAIN_ROOT_EVENTS.includes(evt)) {
       this.client.mainRoot.off(evt, listener);
     } else {
       this.client.removeListener(evt, listener);
     }
   }
 
   async getFront(typeName) {
     return this.client.mainRoot.getFront(typeName);
--- a/devtools/client/aboutdebugging-new/src/modules/moz.build
+++ b/devtools/client/aboutdebugging-new/src/modules/moz.build
@@ -7,9 +7,10 @@ DevToolsModules(
     'debug-target-collapsibilities.js',
     'debug-target-support.js',
     'extensions-helper.js',
     'l10n.js',
     'network-locations.js',
     'runtime-client-factory.js',
     'runtimes-state-helper.js',
     'usb-runtimes.js',
+    'workers-listener.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/modules/workers-listener.js
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Listening to worker updates requires watching various sources. This class provides
+ * a single addListener/removeListener API that will aggregate all possible worker update
+ * events.
+ *
+ * Only supports one listener at a time.
+ */
+class WorkersListener {
+  constructor(rootFront) {
+    this.rootFront = rootFront;
+
+    // Array of contentProcessTarget fronts on which we will listen for worker events.
+    this._contentProcessFronts = [];
+    this._serviceWorkerRegistrationFronts = [];
+    this._listener = null;
+  }
+
+  addListener(listener) {
+    if (this._listener) {
+      throw new Error("WorkersListener addListener called twice.");
+    }
+
+    this._listener = listener;
+    this.rootFront.on("workerListChanged", this._listener);
+    this.rootFront.onFront("contentProcessTarget", front => {
+      this._contentProcessFronts.push(front);
+      front.on("workerListChanged", this._listener);
+    });
+
+    this.rootFront.onFront("serviceWorkerRegistration", front => {
+      this._serviceWorkerRegistrationFronts.push(front);
+      front.on("push-subscription-modified", this._listener);
+      front.on("registration-changed", this._listener);
+    });
+
+    this.rootFront.on("serviceWorkerRegistrationListChanged", this._listener);
+    this.rootFront.on("processListChanged", this._listener);
+  }
+
+  removeListener(listener) {
+    if (!this._listener) {
+      return;
+    }
+
+    this.rootFront.off("workerListChanged", this._listener);
+
+    for (const front of this._contentProcessFronts) {
+      front.off("workerListChanged", this._listener);
+    }
+
+    for (const front of this._serviceWorkerRegistrationFronts) {
+      front.off("push-subscription-modified", this._listener);
+      front.off("registration-changed", this._listener);
+    }
+
+    this.rootFront.off("serviceWorkerRegistrationListChanged", this._listener);
+    this.rootFront.off("processListChanged", this._listener);
+
+    this._contentProcessFronts = [];
+    this._serviceWorkerRegistrationFronts = [];
+    this._listener = null;
+  }
+}
+exports.WorkersListener = WorkersListener;
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_select_page_with_serviceworker.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_select_page_with_serviceworker.js
@@ -31,17 +31,17 @@ add_task(async function() {
     otherWorkers: [],
     serviceWorkers: [{
       name: WORKER_NAME,
       workerTargetFront: { actorID: WORKER_NAME },
     }],
     sharedWorkers: [],
   };
   networkClient.listWorkers = () => workers;
-  networkClient._eventEmitter.emit("workerListChanged");
+  networkClient._eventEmitter.emit("workersUpdated");
 
   info("Wait until the service worker is displayed");
   await waitUntil(() => findDebugTargetByText(WORKER_NAME, document));
 
   info("Go to This Firefox again");
   const thisFirefoxSidebarItem = findSidebarItemByText("This Firefox", document);
   const thisFirefoxLink = thisFirefoxSidebarItem.querySelector(".js-sidebar-link");
   info("Click on the ThisFirefox item in the sidebar");
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_workers_remote_runtime.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_workers_remote_runtime.js
@@ -87,40 +87,40 @@ async function testWorkerOnMockedRemoteC
     [propertyName]: [{
       name: workerName,
       workerTargetFront: {
         actorID: workerName,
       },
     }],
   });
   remoteClient.listWorkers = () => workers;
-  remoteClient._eventEmitter.emit("workerListChanged");
+  remoteClient._eventEmitter.emit("workersUpdated");
 
   info("Wait until the worker appears");
   await waitUntil(() => !workersPane.querySelector(".js-debug-target-list-empty"));
 
   const workerTarget = findDebugTargetByText(workerName, document);
   ok(workerTarget, "Worker target appeared for the remote runtime");
 
   // Check that the list of REMOTE workers are NOT updated when the local this-firefox
-  // emits a workerListChanged event.
+  // emits a workersUpdated event.
   info("Remove the worker from the remote client WITHOUT sending an event");
   remoteClient.listWorkers = () => EMPTY_WORKERS_RESPONSE;
 
   info("Simulate a worker update on the ThisFirefox client");
-  firefoxClient._eventEmitter.emit("workerListChanged");
+  firefoxClient._eventEmitter.emit("workersUpdated");
 
   // To avoid wait for a set period of time we trigger another async update, adding a new
   // tab. We assume that if the worker update mechanism had started, it would also be done
   // when the new tab was processed.
   info("Wait until the tab target for 'http://some.random/url.com' appears");
   const testTab = { outerWindowID: 0, url: "http://some.random/url.com" };
   remoteClient.listTabs = () => [testTab];
   remoteClient._eventEmitter.emit("tabListChanged");
   await waitUntil(() => findDebugTargetByText("http://some.random/url.com", document));
 
   ok(findDebugTargetByText(workerName, document),
     "The test worker is still visible");
 
-  info("Emit `workerListChanged` on remoteClient and wait for the target list to update");
-  remoteClient._eventEmitter.emit("workerListChanged");
+  info("Emit `workersUpdated` on remoteClient and wait for the target list to update");
+  remoteClient._eventEmitter.emit("workersUpdated");
   await waitUntil(() => !findDebugTargetByText(workerName, document));
 }
--- a/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
+++ b/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
@@ -32,17 +32,16 @@ function createClientMock() {
       eventEmitter.once(evt, listener);
     },
     addListener: (evt, listener) => {
       eventEmitter.on(evt, listener);
     },
     removeListener: (evt, listener) => {
       eventEmitter.off(evt, listener);
     },
-
     client: {
       addOneTimeListener: (evt, listener) => {
         eventEmitter.once(evt, listener);
       },
       addListener: (evt, listener) => {
         eventEmitter.on(evt, listener);
       },
       removeListener: (evt, listener) => {