Bug 1497917 - Add ClientWrapper to interact with DevTools client in aboutdebugging;r=ladybenko,daisuke
authorJulian Descottes <jdescottes@mozilla.com>
Fri, 09 Nov 2018 09:39:47 +0000
changeset 445397 0620a6320abfcb9177f601c40af8d89a0dc89b0d
parent 445396 88ef91cd3bd6f0ad01653654b53b4a94c66949b5
child 445398 dfd527956c4b0df389286e62b51f28e87b64676d
push id35015
push userdluca@mozilla.com
push dateFri, 09 Nov 2018 17:45:20 +0000
treeherdermozilla-central@2f1158e5e0ce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersladybenko, daisuke
bugs1497917
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 1497917 - Add ClientWrapper to interact with DevTools client in aboutdebugging;r=ladybenko,daisuke Depends on D9801 This a preparatory work that ensures that all the calls to DevTools client are made through a wrapper called ClientWrapper. I initially tried having a static "client" helper but the test code was really hard to follow. Keeping a "client-like" object makes this easier. Differential Revision: https://phabricator.services.mozilla.com/D10095
devtools/client/aboutdebugging-new/src/actions/debug-targets.js
devtools/client/aboutdebugging-new/src/actions/runtimes.js
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/types.js
--- a/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
+++ b/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
@@ -161,21 +161,17 @@ function requestExtensions() {
 
 function requestWorkers() {
   return async (dispatch, getState) => {
     dispatch({ type: REQUEST_WORKERS_START });
 
     const client = getCurrentClient(getState().runtimes);
 
     try {
-      const {
-        other: otherWorkers,
-        service: serviceWorkers,
-        shared: sharedWorkers,
-      } = await client.mainRoot.listAllWorkers();
+      const { otherWorkers, serviceWorkers, sharedWorkers } = await client.listWorkers();
 
       dispatch({
         type: REQUEST_WORKERS_SUCCESS,
         otherWorkers,
         serviceWorkers,
         sharedWorkers,
       });
     } catch (e) {
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -72,18 +72,17 @@ async function createClientForRuntime(ru
     return createUSBClient(socketPath);
   }
 
   return null;
 }
 
 async function getRuntimeInfo(runtime, client) {
   const { extra, type } = runtime;
-  const deviceFront = await client.mainRoot.getFront("device");
-  const { brandName: name, channel, version } = await deviceFront.getDescription();
+  const { name, channel, version } = await client.getDeviceDescription();
   const icon =
     (channel === "release" || channel === "beta" || channel === "aurora")
       ? `chrome://devtools/skin/images/aboutdebugging-firefox-${ channel }.svg`
       : "chrome://devtools/skin/images/aboutdebugging-firefox-nightly.svg";
 
   return {
     icon,
     deviceName: extra ? extra.deviceName : undefined,
@@ -102,19 +101,19 @@ function onUSBDebuggerClientClosed() {
 
 function connectRuntime(id) {
   return async (dispatch, getState) => {
     dispatch({ type: CONNECT_RUNTIME_START });
     try {
       const runtime = findRuntimeById(id, getState().runtimes);
       const { client, transportDetails } = await createClientForRuntime(runtime);
       const info = await getRuntimeInfo(runtime, client);
-      const preferenceFront = await client.mainRoot.getFront("preference");
-      const connectionPromptEnabled =
-        await preferenceFront.getBoolPref(RUNTIME_PREFERENCE.CONNECTION_PROMPT);
+
+      const promptPrefName = RUNTIME_PREFERENCE.CONNECTION_PROMPT;
+      const connectionPromptEnabled = await client.getPreference(promptPrefName);
       const runtimeDetails = { connectionPromptEnabled, client, info, transportDetails };
 
       if (runtime.type === RUNTIMES.USB) {
         // `closed` event will be emitted when disabling remote debugging
         // on the connected USB runtime.
         client.addOneTimeListener("closed", onUSBDebuggerClientClosed);
       }
 
@@ -163,22 +162,20 @@ function disconnectRuntime(id) {
 }
 
 function updateConnectionPromptSetting(connectionPromptEnabled) {
   return async (dispatch, getState) => {
     dispatch({ type: UPDATE_CONNECTION_PROMPT_SETTING_START });
     try {
       const runtime = getCurrentRuntime(getState().runtimes);
       const client = runtime.runtimeDetails.client;
-      const preferenceFront = await client.mainRoot.getFront("preference");
-      await preferenceFront.setBoolPref(RUNTIME_PREFERENCE.CONNECTION_PROMPT,
-                                        connectionPromptEnabled);
+      const promptPrefName = RUNTIME_PREFERENCE.CONNECTION_PROMPT;
+      await client.setPreference(promptPrefName, connectionPromptEnabled);
       // Re-get actual value from the runtime.
-      connectionPromptEnabled =
-        await preferenceFront.getBoolPref(RUNTIME_PREFERENCE.CONNECTION_PROMPT);
+      connectionPromptEnabled = await client.getPreference(promptPrefName);
 
       dispatch({ type: UPDATE_CONNECTION_PROMPT_SETTING_SUCCESS,
                  runtime, connectionPromptEnabled });
     } catch (e) {
       dispatch({ type: UPDATE_CONNECTION_PROMPT_SETTING_FAILURE, error: e });
     }
   };
 }
--- a/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
+++ b/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
@@ -55,48 +55,48 @@ function debugTargetListenerMiddleware(s
 
   return next => action => {
     switch (action.type) {
       case WATCH_RUNTIME_SUCCESS: {
         const { runtime } = action;
         const { client } = runtime.runtimeDetails;
 
         if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.TAB)) {
-          client.mainRoot.on("tabListChanged", onTabsUpdated);
+          client.addListener("tabListChanged", onTabsUpdated);
         }
 
         if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.EXTENSION)) {
           AddonManager.addAddonListener(extensionsListener);
         }
 
         if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.WORKER)) {
-          client.mainRoot.on("workerListChanged", onWorkersUpdated);
-          client.mainRoot.on("serviceWorkerRegistrationListChanged", onWorkersUpdated);
-          client.mainRoot.on("processListChanged", onWorkersUpdated);
+          client.addListener("workerListChanged", onWorkersUpdated);
+          client.addListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
+          client.addListener("processListChanged", onWorkersUpdated);
           client.addListener("registration-changed", onWorkersUpdated);
           client.addListener("push-subscription-modified", onWorkersUpdated);
         }
         break;
       }
       case UNWATCH_RUNTIME_START: {
         const { runtime } = action;
         const { client } = runtime.runtimeDetails;
 
         if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.TAB)) {
-          client.mainRoot.off("tabListChanged", onTabsUpdated);
+          client.removeListener("tabListChanged", onTabsUpdated);
         }
 
         if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.EXTENSION)) {
           AddonManager.removeAddonListener(extensionsListener);
         }
 
         if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.WORKER)) {
-          client.mainRoot.off("workerListChanged", onWorkersUpdated);
-          client.mainRoot.off("serviceWorkerRegistrationListChanged", onWorkersUpdated);
-          client.mainRoot.off("processListChanged", onWorkersUpdated);
+          client.removeListener("workerListChanged", onWorkersUpdated);
+          client.removeListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
+          client.removeListener("processListChanged", onWorkersUpdated);
           client.removeListener("registration-changed", onWorkersUpdated);
           client.removeListener("push-subscription-modified", onWorkersUpdated);
         }
         break;
       }
     }
 
     return next(action);
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
@@ -0,0 +1,114 @@
+/* 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 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 = [
+  "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;
+  }
+
+  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)) {
+      this.client.mainRoot.on(evt, listener);
+    } else {
+      this.client.addListener(evt, listener);
+    }
+  }
+
+  removeListener(evt, listener) {
+    if (MAIN_ROOT_EVENTS.includes(evt)) {
+      this.client.mainRoot.off(evt, listener);
+    } else {
+      this.client.removeListener(evt, listener);
+    }
+  }
+
+  async getDeviceDescription() {
+    const deviceFront = await this.client.mainRoot.getFront("device");
+    const { brandName, channel, version } = await deviceFront.getDescription();
+    return { name: brandName, channel, version };
+  }
+
+  async setPreference(prefName, value) {
+    const prefType = PREF_TO_TYPE[prefName];
+    const preferenceFront = await this.client.mainRoot.getFront("preference");
+    switch (prefType) {
+      case PREF_TYPES.BOOL:
+        return preferenceFront.setBoolPref(prefName, value);
+      default:
+        throw new Error("Unsupported preference" + prefName);
+    }
+  }
+
+  async getPreference(prefName) {
+    const prefType = PREF_TO_TYPE[prefName];
+    const preferenceFront = await this.client.mainRoot.getFront("preference");
+    switch (prefType) {
+      case PREF_TYPES.BOOL:
+        return preferenceFront.getBoolPref(prefName);
+      default:
+        throw new Error("Unsupported preference:" + prefName);
+    }
+  }
+
+  async listTabs(options) {
+    return this.client.listTabs(options);
+  }
+
+  async listAddons() {
+    return this.client.listAddons();
+  }
+
+  async listWorkers() {
+    const { other, service, shared } = await this.client.mainRoot.listAllWorkers();
+
+    return {
+      otherWorkers: other,
+      serviceWorkers: service,
+      sharedWorkers: shared,
+    };
+  }
+
+  async request(options) {
+    return this.client.request(options);
+  }
+
+  async close() {
+    return this.client.close();
+  }
+}
+
+exports.ClientWrapper = ClientWrapper;
--- a/devtools/client/aboutdebugging-new/src/modules/moz.build
+++ b/devtools/client/aboutdebugging-new/src/modules/moz.build
@@ -1,13 +1,14 @@
 # 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/.
 
 DevToolsModules(
+    'client-wrapper.js',
     'debug-target-collapsibilities.js',
     'debug-target-support.js',
     'extensions-helper.js',
     'l10n.js',
     'network-locations.js',
     'runtimes-state-helper.js',
     'test-helper.js',
     'usb-runtimes.js',
--- a/devtools/client/aboutdebugging-new/src/types.js
+++ b/devtools/client/aboutdebugging-new/src/types.js
@@ -1,15 +1,16 @@
 /* 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 PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { ClientWrapper } = require("./modules/client-wrapper");
 
 const runtimeInfo = {
   // device name which is running the runtime,
   // unavailable on this-firefox runtime
   deviceName: PropTypes.string,
 
   // icon which represents the kind of runtime
   icon: PropTypes.string.isRequired,
@@ -25,18 +26,18 @@ const runtimeTransportDetails = {
   // host name of tcp connection to debugger server
   host: PropTypes.string.isRequired,
 
   // port number of tcp connection to debugger server
   port: PropTypes.number.isRequired,
 };
 
 const runtimeDetails = {
-  // debugger client instance
-  client: PropTypes.object.isRequired,
+  // ClientWrapper built using a DebuggerClient for the runtime
+  client: PropTypes.instanceOf(ClientWrapper).isRequired,
 
   // reflect devtools.debugger.prompt-connection preference of this runtime
   connectionPromptEnabled: PropTypes.bool.isRequired,
 
   // runtime information
   info: PropTypes.shape(runtimeInfo).isRequired,
 
   // tcp connection information,