Bug 1495382 - Check isMultiE10s from runtime info;r=daisuke,ladybenko
authorJulian Descottes <jdescottes@mozilla.com>
Wed, 09 Jan 2019 09:44:15 +0000
changeset 510121 f829749915aa4360071ffcef1ce5aa503c15788a
parent 510120 17b9f7326ef3a3176837edaa5b722fbf76a28336
child 510122 7c0760b4c3a47282e421925d182b4f49d6e9bd6d
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdaisuke, ladybenko
bugs1495382
milestone66.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 1495382 - Check isMultiE10s from runtime info;r=daisuke,ladybenko Depends on D15081 Differential Revision: https://phabricator.services.mozilla.com/D15399
devtools/client/aboutdebugging-new/aboutdebugging.js
devtools/client/aboutdebugging-new/src/actions/runtimes.js
devtools/client/aboutdebugging-new/src/actions/ui.js
devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerAction.js
devtools/client/aboutdebugging-new/src/constants.js
devtools/client/aboutdebugging-new/src/create-store.js
devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
devtools/client/aboutdebugging-new/src/modules/runtimes-state-helper.js
devtools/client/aboutdebugging-new/src/reducers/runtimes-state.js
devtools/client/aboutdebugging-new/src/reducers/ui-state.js
devtools/client/aboutdebugging-new/src/types/index.js
devtools/client/aboutdebugging-new/src/types/runtime.js
devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
devtools/client/aboutdebugging/components/workers/Panel.js
devtools/client/shared/moz.build
devtools/client/shared/multi-e10s-helper.js
devtools/server/actors/device.js
devtools/shared/moz.build
devtools/shared/multi-e10s-helper.js
devtools/shared/specs/device.js
devtools/shared/system.js
--- a/devtools/client/aboutdebugging-new/aboutdebugging.js
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.js
@@ -27,39 +27,32 @@ const {
   removeNetworkLocationsObserver,
 } = require("./src/modules/network-locations");
 const {
   addUSBRuntimesObserver,
   getUSBRuntimes,
   removeUSBRuntimesObserver,
 } = require("./src/modules/usb-runtimes");
 
-const {
-  addMultiE10sListener,
-  isMultiE10s,
-  removeMultiE10sListener,
-} = require("devtools/client/shared/multi-e10s-helper");
-
 loader.lazyRequireGetter(this, "adbAddon", "devtools/shared/adb/adb-addon", true);
 
 const Router = createFactory(require("devtools/client/shared/vendor/react-router-dom").HashRouter);
 const App = createFactory(require("./src/components/App"));
 
 const AboutDebugging = {
   async init() {
     if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
       // If DevTools are disabled, navigate to about:devtools.
       window.location = "about:devtools?reason=AboutDebugging";
       return;
     }
 
     this.onAdbAddonUpdated = this.onAdbAddonUpdated.bind(this);
     this.onNetworkLocationsUpdated = this.onNetworkLocationsUpdated.bind(this);
     this.onUSBRuntimesUpdated = this.onUSBRuntimesUpdated.bind(this);
-    this.onMultiE10sUpdated = this.onMultiE10sUpdated.bind(this);
 
     this.store = configureStore();
     this.actions = bindActionCreators(actions, this.store.dispatch);
 
     await l10n.init();
 
     render(
       Provider(
@@ -85,22 +78,16 @@ const AboutDebugging = {
     this.onUSBRuntimesUpdated();
     addUSBRuntimesObserver(this.onUSBRuntimesUpdated);
 
     adbAddon.on("update", this.onAdbAddonUpdated);
     this.onAdbAddonUpdated();
 
     // Remove deprecated remote debugging extensions.
     await adbAddon.uninstallUnsupportedExtensions();
-
-    addMultiE10sListener(this.onMultiE10sUpdated);
-  },
-
-  onMultiE10sUpdated() {
-    this.actions.updateMultiE10sStatus(isMultiE10s());
   },
 
   onAdbAddonUpdated() {
     this.actions.updateAdbAddonStatus(adbAddon.status);
   },
 
   onNetworkLocationsUpdated() {
     this.actions.updateNetworkLocations(getNetworkLocations());
@@ -118,17 +105,16 @@ const AboutDebugging = {
     const currentRuntimeId = state.runtimes.selectedRuntimeId;
     if (currentRuntimeId) {
       await this.actions.unwatchRuntime(currentRuntimeId);
     }
 
     // Remove all client listeners.
     this.actions.removeRuntimeListeners();
 
-    removeMultiE10sListener(this.onMultiE10sUpdated);
     removeNetworkLocationsObserver(this.onNetworkLocationsUpdated);
     removeUSBRuntimesObserver(this.onUSBRuntimesUpdated);
     adbAddon.off("update", this.onAdbAddonUpdated);
     setDebugTargetCollapsibilities(state.ui.debugTargetCollapsibilities);
     unmountComponentAtNode(this.mount);
   },
 
   get mount() {
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -30,62 +30,80 @@ const {
   RUNTIME_PREFERENCE,
   RUNTIMES,
   UNWATCH_RUNTIME_FAILURE,
   UNWATCH_RUNTIME_START,
   UNWATCH_RUNTIME_SUCCESS,
   UPDATE_CONNECTION_PROMPT_SETTING_FAILURE,
   UPDATE_CONNECTION_PROMPT_SETTING_START,
   UPDATE_CONNECTION_PROMPT_SETTING_SUCCESS,
+  UPDATE_RUNTIME_MULTIE10S_FAILURE,
+  UPDATE_RUNTIME_MULTIE10S_START,
+  UPDATE_RUNTIME_MULTIE10S_SUCCESS,
   WATCH_RUNTIME_FAILURE,
   WATCH_RUNTIME_START,
   WATCH_RUNTIME_SUCCESS,
 } = require("../constants");
 
 async function getRuntimeInfo(runtime, clientWrapper) {
   const { type } = runtime;
-  const { name, channel, deviceName, version } =
+  const { name, channel, deviceName, isMultiE10s, version } =
     await clientWrapper.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 {
+    deviceName,
     icon,
-    deviceName,
+    isMultiE10s,
     name,
     type,
     version,
   };
 }
 
 function onUSBDebuggerClientClosed() {
   // After scanUSBRuntimes action, updateUSBRuntimes action is called.
   // The closed runtime will be unwatched and disconnected explicitly in the action
   // if needed.
   window.AboutDebugging.store.dispatch(Actions.scanUSBRuntimes());
 }
 
+function onMultiE10sUpdated() {
+  window.AboutDebugging.store.dispatch(updateMultiE10s());
+}
+
 function connectRuntime(id) {
   return async (dispatch, getState) => {
     dispatch({ type: CONNECT_RUNTIME_START });
     try {
       const runtime = findRuntimeById(id, getState().runtimes);
       const clientWrapper = await createClientForRuntime(runtime);
       const info = await getRuntimeInfo(runtime, clientWrapper);
+      const { isMultiE10s } = info;
+      delete info.isMultiE10s;
 
       const promptPrefName = RUNTIME_PREFERENCE.CONNECTION_PROMPT;
       const connectionPromptEnabled = await clientWrapper.getPreference(promptPrefName);
       const runtimeDetails = {
         clientWrapper,
         connectionPromptEnabled,
         info,
+        isMultiE10s,
       };
 
+      clientWrapper.addListener("closed", onUSBDebuggerClientClosed);
+
+      const deviceFront = await clientWrapper.getFront("device");
+      if (deviceFront) {
+        deviceFront.on("multi-e10s-updated", onMultiE10sUpdated);
+      }
+
       if (runtime.type === RUNTIMES.USB) {
         // `closed` event will be emitted when disabling remote debugging
         // on the connected USB runtime.
         clientWrapper.addOneTimeListener("closed", onUSBDebuggerClientClosed);
       }
 
       dispatch({
         type: CONNECT_RUNTIME_SUCCESS,
@@ -103,16 +121,21 @@ function connectRuntime(id) {
 
 function disconnectRuntime(id) {
   return async (dispatch, getState) => {
     dispatch({ type: DISCONNECT_RUNTIME_START });
     try {
       const runtime = findRuntimeById(id, getState().runtimes);
       const { clientWrapper } = runtime.runtimeDetails;
 
+      const deviceFront = await clientWrapper.getFront("device");
+      if (deviceFront) {
+        deviceFront.off("multi-e10s-updated", onMultiE10sUpdated);
+      }
+
       if (runtime.type === RUNTIMES.USB) {
         clientWrapper.removeListener("closed", onUSBDebuggerClientClosed);
       }
 
       await clientWrapper.close();
 
       dispatch({
         type: DISCONNECT_RUNTIME_SUCCESS,
@@ -141,16 +164,32 @@ function updateConnectionPromptSetting(c
       dispatch({ type: UPDATE_CONNECTION_PROMPT_SETTING_SUCCESS,
                  runtime, connectionPromptEnabled });
     } catch (e) {
       dispatch({ type: UPDATE_CONNECTION_PROMPT_SETTING_FAILURE, error: e });
     }
   };
 }
 
+function updateMultiE10s() {
+  return async (dispatch, getState) => {
+    dispatch({ type: UPDATE_RUNTIME_MULTIE10S_START });
+    try {
+      const runtime = getCurrentRuntime(getState().runtimes);
+      const { clientWrapper } = runtime.runtimeDetails;
+      // Re-get actual value from the runtime.
+      const { isMultiE10s } = await clientWrapper.getDeviceDescription();
+
+      dispatch({ type: UPDATE_RUNTIME_MULTIE10S_SUCCESS, runtime, isMultiE10s });
+    } catch (e) {
+      dispatch({ type: UPDATE_RUNTIME_MULTIE10S_FAILURE, error: e });
+    }
+  };
+}
+
 function watchRuntime(id) {
   return async (dispatch, getState) => {
     dispatch({ type: WATCH_RUNTIME_START });
 
     try {
       if (id === RUNTIMES.THIS_FIREFOX) {
         // THIS_FIREFOX connects and disconnects on the fly when opening the page.
         await dispatch(connectRuntime(RUNTIMES.THIS_FIREFOX));
--- a/devtools/client/aboutdebugging-new/src/actions/ui.js
+++ b/devtools/client/aboutdebugging-new/src/actions/ui.js
@@ -8,17 +8,16 @@ const {
   ADB_ADDON_INSTALL_START,
   ADB_ADDON_INSTALL_SUCCESS,
   ADB_ADDON_INSTALL_FAILURE,
   ADB_ADDON_UNINSTALL_START,
   ADB_ADDON_UNINSTALL_SUCCESS,
   ADB_ADDON_UNINSTALL_FAILURE,
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
-  MULTI_E10S_UPDATED,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_SELECTED,
   PAGE_TYPES,
   USB_RUNTIMES_SCAN_START,
   USB_RUNTIMES_SCAN_SUCCESS,
 } = require("../constants");
 
 const NetworkLocationsModule = require("../modules/network-locations");
@@ -122,26 +121,19 @@ function scanUSBRuntimes() {
     }
 
     dispatch({ type: USB_RUNTIMES_SCAN_START });
     await refreshUSBRuntimes();
     dispatch({ type: USB_RUNTIMES_SCAN_SUCCESS });
   };
 }
 
-function updateMultiE10sStatus(isMultiE10s) {
-  return (dispatch, getState) => {
-    dispatch({ type: MULTI_E10S_UPDATED, isMultiE10s});
-  };
-}
-
 module.exports = {
   addNetworkLocation,
   installAdbAddon,
   removeNetworkLocation,
   scanUSBRuntimes,
   selectPage,
   uninstallAdbAddon,
   updateAdbAddonStatus,
   updateDebugTargetCollapsibility,
-  updateMultiE10sStatus,
   updateNetworkLocations,
 };
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerAction.js
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerAction.js
@@ -6,49 +6,52 @@
 
 const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 
+const { getCurrentRuntimeDetails } = require("../../modules/runtimes-state-helper");
+
 const InspectAction = createFactory(require("./InspectAction"));
 
 const Actions = require("../../actions/index");
 const Types = require("../../types/index");
 
 /**
  * This component displays buttons for service worker.
  */
 class ServiceWorkerAction extends PureComponent {
   static get propTypes() {
     return {
       dispatch: PropTypes.func.isRequired,
       // Provided by wrapping the component with FluentReact.withLocalization.
       getString: PropTypes.func.isRequired,
       // Provided by redux state
-      isMultiE10s: PropTypes.bool.isRequired,
+      runtimeDetails: Types.runtimeDetails.isRequired,
       target: Types.debugTarget.isRequired,
     };
   }
 
   push() {
     const { dispatch, target } = this.props;
     dispatch(Actions.pushServiceWorker(target.id));
   }
 
   start() {
     const { dispatch, target } = this.props;
     dispatch(Actions.startServiceWorker(target.details.registrationActor));
   }
 
   _renderAction() {
-    const { dispatch, isMultiE10s, target } = this.props;
+    const { dispatch, runtimeDetails, target } = this.props;
     const { isActive, isRunning } = target.details;
+    const { isMultiE10s } = runtimeDetails;
 
     if (!isRunning) {
       const startLabel = this.props.getString("about-debugging-worker-action-start");
       return this._renderButton({
         className: "default-button",
         disabled: isMultiE10s,
         label: startLabel,
         onClick: this.start.bind(this),
@@ -90,14 +93,14 @@ class ServiceWorkerAction extends PureCo
       },
       this._renderAction()
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
-    isMultiE10s: state.ui.isMultiE10s,
+    runtimeDetails: getCurrentRuntimeDetails(state.runtimes),
   };
 };
 
 module.exports = FluentReact.withLocalization(
   connect(mapStateToProps)(ServiceWorkerAction));
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -14,17 +14,16 @@ const actionTypes = {
   ADB_ADDON_STATUS_UPDATED: "ADB_ADDON_STATUS_UPDATED",
   CONNECT_RUNTIME_FAILURE: "CONNECT_RUNTIME_FAILURE",
   CONNECT_RUNTIME_START: "CONNECT_RUNTIME_START",
   CONNECT_RUNTIME_SUCCESS: "CONNECT_RUNTIME_SUCCESS",
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED: "DEBUG_TARGET_COLLAPSIBILITY_UPDATED",
   DISCONNECT_RUNTIME_FAILURE: "DISCONNECT_RUNTIME_FAILURE",
   DISCONNECT_RUNTIME_START: "DISCONNECT_RUNTIME_START",
   DISCONNECT_RUNTIME_SUCCESS: "DISCONNECT_RUNTIME_SUCCESS",
-  MULTI_E10S_UPDATED: "MULTI_E10S_UPDATED",
   NETWORK_LOCATIONS_UPDATED: "NETWORK_LOCATIONS_UPDATED",
   PAGE_SELECTED: "PAGE_SELECTED",
   REMOTE_RUNTIMES_UPDATED: "REMOTE_RUNTIMES_UPDATED",
   REQUEST_EXTENSIONS_FAILURE: "REQUEST_EXTENSIONS_FAILURE",
   REQUEST_EXTENSIONS_START: "REQUEST_EXTENSIONS_START",
   REQUEST_EXTENSIONS_SUCCESS: "REQUEST_EXTENSIONS_SUCCESS",
   REQUEST_TABS_FAILURE: "REQUEST_TABS_FAILURE",
   REQUEST_TABS_START: "REQUEST_TABS_START",
@@ -36,16 +35,19 @@ const actionTypes = {
   TEMPORARY_EXTENSION_INSTALL_START: "TEMPORARY_EXTENSION_INSTALL_START",
   TEMPORARY_EXTENSION_INSTALL_SUCCESS: "TEMPORARY_EXTENSION_INSTALL_SUCCESS",
   UNWATCH_RUNTIME_FAILURE: "UNWATCH_RUNTIME_FAILURE",
   UNWATCH_RUNTIME_START: "UNWATCH_RUNTIME_START",
   UNWATCH_RUNTIME_SUCCESS: "UNWATCH_RUNTIME_SUCCESS",
   UPDATE_CONNECTION_PROMPT_SETTING_FAILURE: "UPDATE_CONNECTION_PROMPT_SETTING_FAILURE",
   UPDATE_CONNECTION_PROMPT_SETTING_START: "UPDATE_CONNECTION_PROMPT_SETTING_START",
   UPDATE_CONNECTION_PROMPT_SETTING_SUCCESS: "UPDATE_CONNECTION_PROMPT_SETTING_SUCCESS",
+  UPDATE_RUNTIME_MULTIE10S_FAILURE: "UPDATE_RUNTIME_MULTIE10S_FAILURE",
+  UPDATE_RUNTIME_MULTIE10S_START: "UPDATE_RUNTIME_MULTIE10S_START",
+  UPDATE_RUNTIME_MULTIE10S_SUCCESS: "UPDATE_RUNTIME_MULTIE10S_SUCCESS",
   USB_RUNTIMES_SCAN_START: "USB_RUNTIMES_SCAN_START",
   USB_RUNTIMES_SCAN_SUCCESS: "USB_RUNTIMES_SCAN_SUCCESS",
   WATCH_RUNTIME_FAILURE: "WATCH_RUNTIME_FAILURE",
   WATCH_RUNTIME_START: "WATCH_RUNTIME_START",
   WATCH_RUNTIME_SUCCESS: "WATCH_RUNTIME_SUCCESS",
 };
 
 const DEBUG_TARGETS = {
--- a/devtools/client/aboutdebugging-new/src/create-store.js
+++ b/devtools/client/aboutdebugging-new/src/create-store.js
@@ -4,17 +4,16 @@
 
 "use strict";
 
 const Services = require("Services");
 
 const { applyMiddleware, createStore } = require("devtools/client/shared/vendor/redux");
 const { thunk } = require("devtools/client/shared/redux/middleware/thunk.js");
 const { waitUntilService } = require("devtools/client/shared/redux/middleware/wait-service.js");
-const { isMultiE10s } = require("devtools/client/shared/multi-e10s-helper");
 
 const rootReducer = require("./reducers/index");
 const { DebugTargetsState } = require("./reducers/debug-targets-state");
 const { RuntimesState } = require("./reducers/runtimes-state");
 const { UiState } = require("./reducers/ui-state");
 const debugTargetListenerMiddleware = require("./middleware/debug-target-listener");
 const errorLoggingMiddleware = require("./middleware/error-logging");
 const extensionComponentDataMiddleware = require("./middleware/extension-component-data");
@@ -46,12 +45,12 @@ function configureStore() {
 function getUiState() {
   const collapsibilities = getDebugTargetCollapsibilities();
   const locations = getNetworkLocations();
   const networkEnabled = Services.prefs.getBoolPref(PREFERENCES.NETWORK_ENABLED, false);
   const wifiEnabled = Services.prefs.getBoolPref(PREFERENCES.WIFI_ENABLED, false);
   const showSystemAddons = Services.prefs.getBoolPref(PREFERENCES.SHOW_SYSTEM_ADDONS,
     false);
   return new UiState(locations, collapsibilities, networkEnabled, wifiEnabled,
-    showSystemAddons, isMultiE10s());
+    showSystemAddons);
 }
 
 exports.configureStore = configureStore;
--- a/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
+++ b/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
@@ -54,28 +54,33 @@ class ClientWrapper {
   removeListener(evt, listener) {
     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);
+  }
+
   onFront(typeName, listener) {
     this.client.mainRoot.onFront(typeName, listener);
   }
 
   async getDeviceDescription() {
-    const deviceFront = await this.client.mainRoot.getFront("device");
-    const { brandName, channel, deviceName, version } =
+    const deviceFront = await this.getFront("device");
+    const { brandName, channel, deviceName, isMultiE10s, version } =
       await deviceFront.getDescription();
     // Only expose a specific set of properties.
     return {
       channel,
       deviceName,
+      isMultiE10s,
       name: brandName,
       version,
     };
   }
 
   async setPreference(prefName, value) {
     const prefType = PREF_TO_TYPE[prefName];
     const preferenceFront = await this.client.mainRoot.getFront("preference");
--- a/devtools/client/aboutdebugging-new/src/modules/runtimes-state-helper.js
+++ b/devtools/client/aboutdebugging-new/src/modules/runtimes-state-helper.js
@@ -41,8 +41,9 @@ function getAllRuntimes(runtimesState) {
   ];
 }
 exports.getAllRuntimes = getAllRuntimes;
 
 function getCurrentRuntimeDetails(runtimesState) {
   const runtime = getCurrentRuntime(runtimesState);
   return runtime ? runtime.runtimeDetails : null;
 }
+exports.getCurrentRuntimeDetails = getCurrentRuntimeDetails;
--- a/devtools/client/aboutdebugging-new/src/reducers/runtimes-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/runtimes-state.js
@@ -5,16 +5,17 @@
 "use strict";
 
 const {
   CONNECT_RUNTIME_SUCCESS,
   DISCONNECT_RUNTIME_SUCCESS,
   RUNTIMES,
   UNWATCH_RUNTIME_SUCCESS,
   UPDATE_CONNECTION_PROMPT_SETTING_SUCCESS,
+  UPDATE_RUNTIME_MULTIE10S_SUCCESS,
   REMOTE_RUNTIMES_UPDATED,
   WATCH_RUNTIME_SUCCESS,
 } = require("../constants");
 
 const {
   findRuntimeById,
 } = require("../modules/runtimes-state-helper");
 
@@ -92,16 +93,25 @@ function runtimesReducer(state = Runtime
       const { connectionPromptEnabled } = action;
       const { id: runtimeId } = action.runtime;
       const runtime = findRuntimeById(runtimeId, state);
       const runtimeDetails =
         Object.assign({}, runtime.runtimeDetails, { connectionPromptEnabled });
       return _updateRuntimeById(runtimeId, { runtimeDetails }, state);
     }
 
+    case UPDATE_RUNTIME_MULTIE10S_SUCCESS: {
+      const { isMultiE10s } = action;
+      const { id: runtimeId } = action.runtime;
+      const runtime = findRuntimeById(runtimeId, state);
+      const runtimeDetails =
+        Object.assign({}, runtime.runtimeDetails, { isMultiE10s });
+      return _updateRuntimeById(runtimeId, { runtimeDetails }, state);
+    }
+
     case REMOTE_RUNTIMES_UPDATED: {
       const { runtimes, runtimeType } = action;
       const key = TYPE_TO_RUNTIMES_KEY[runtimeType];
       return Object.assign({}, state, {
         [key]: runtimes,
       });
     }
 
--- a/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
@@ -2,32 +2,30 @@
  * 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 {
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
-  MULTI_E10S_UPDATED,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_SELECTED,
   TEMPORARY_EXTENSION_INSTALL_FAILURE,
   TEMPORARY_EXTENSION_INSTALL_SUCCESS,
   USB_RUNTIMES_SCAN_START,
   USB_RUNTIMES_SCAN_SUCCESS,
 } = require("../constants");
 
 function UiState(locations = [], debugTargetCollapsibilities = {},
                  networkEnabled = false, wifiEnabled = false,
-                 showSystemAddons = false, isMultiE10s = false) {
+                 showSystemAddons = false) {
   return {
     adbAddonStatus: null,
     debugTargetCollapsibilities,
-    isMultiE10s,
     isScanningUsb: false,
     networkEnabled,
     networkLocations: locations,
     selectedPage: null,
     selectedRuntime: null,
     showSystemAddons,
     temporaryInstallError: null,
     wifiEnabled,
@@ -43,21 +41,16 @@ function uiReducer(state = UiState(), ac
 
     case DEBUG_TARGET_COLLAPSIBILITY_UPDATED: {
       const { isCollapsed, key } = action;
       const debugTargetCollapsibilities = new Map(state.debugTargetCollapsibilities);
       debugTargetCollapsibilities.set(key, isCollapsed);
       return Object.assign({}, state, { debugTargetCollapsibilities });
     }
 
-    case MULTI_E10S_UPDATED: {
-      const { isMultiE10s } = action;
-      return Object.assign({}, state, { isMultiE10s });
-    }
-
     case NETWORK_LOCATIONS_UPDATED: {
       const { locations } = action;
       return Object.assign({}, state, { networkLocations: locations });
     }
 
     case PAGE_SELECTED: {
       const { page, runtimeId } = action;
       return Object.assign({}, state,
--- a/devtools/client/aboutdebugging-new/src/types/index.js
+++ b/devtools/client/aboutdebugging-new/src/types/index.js
@@ -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/. */
 
 "use strict";
 
 const { debugTarget } = require("./debug-target");
-const { runtime } = require("./runtime");
+const { runtime, runtimeDetails } = require("./runtime");
 
 module.exports = Object.assign({}, {
   debugTarget,
   runtime,
+  runtimeDetails,
 });
--- a/devtools/client/aboutdebugging-new/src/types/runtime.js
+++ b/devtools/client/aboutdebugging-new/src/types/runtime.js
@@ -26,17 +26,22 @@ const runtimeDetails = {
   // ClientWrapper built using a DebuggerClient for the runtime
   clientWrapper: PropTypes.instanceOf(ClientWrapper).isRequired,
 
   // reflect devtools.debugger.prompt-connection preference of this runtime
   connectionPromptEnabled: PropTypes.bool.isRequired,
 
   // runtime information
   info: PropTypes.shape(runtimeInfo).isRequired,
+
+  // True if this runtime supports multiple content processes
+  // This might be undefined when connecting to runtimes older than Fx 66
+  isMultiE10s: PropTypes.bool,
 };
+exports.runtimeDetails = PropTypes.shape(runtimeDetails);
 
 const networkRuntimeConnectionParameter = {
   // host name of debugger server to connect
   host: PropTypes.string.isRequired,
 
   // port number of debugger server to connect
   port: PropTypes.number.isRequired,
 };
--- 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
@@ -71,16 +71,18 @@ function createClientMock() {
     listTabs: () => [],
     // Empty arrays of workers
     listWorkers: () => ({
       otherWorkers: [],
       serviceWorkers: [],
       sharedWorkers: [],
     }),
     // no-op
+    getFront: () => {},
+    // no-op
     onFront: () => {},
     // stores the preference locally (doesn't update about:config)
     setPreference: function(prefName, value) {
       this._preferences[prefName] = value;
     },
   };
 }
 
--- a/devtools/client/aboutdebugging/components/workers/Panel.js
+++ b/devtools/client/aboutdebugging/components/workers/Panel.js
@@ -10,17 +10,17 @@ loader.lazyImporter(this, "PrivateBrowsi
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const Services = require("Services");
 const {
   addMultiE10sListener,
   isMultiE10s,
   removeMultiE10sListener,
-} = require("devtools/client/shared/multi-e10s-helper");
+} = require("devtools/shared/multi-e10s-helper");
 
 const PanelHeader = createFactory(require("../PanelHeader"));
 const TargetList = createFactory(require("../TargetList"));
 const WorkerTarget = createFactory(require("./Target"));
 const MultiE10SWarning = createFactory(require("./MultiE10sWarning"));
 const ServiceWorkerTarget = createFactory(require("./ServiceWorkerTarget"));
 
 loader.lazyImporter(this, "PrivateBrowsingUtils",
--- a/devtools/client/shared/moz.build
+++ b/devtools/client/shared/moz.build
@@ -32,17 +32,16 @@ DevToolsModules(
     'enum.js',
     'file-saver.js',
     'focus.js',
     'getjson.js',
     'inplace-editor.js',
     'key-shortcuts.js',
     'keycodes.js',
     'link.js',
-    'multi-e10s-helper.js',
     'natural-sort.js',
     'node-attribute-parser.js',
     'options-view.js',
     'output-parser.js',
     'poller.js',
     'prefs.js',
     'react-utils.js',
     'scroll.js',
--- a/devtools/server/actors/device.js
+++ b/devtools/server/actors/device.js
@@ -3,43 +3,57 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Ci, Cc} = require("chrome");
 const Services = require("Services");
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
+const {
+  addMultiE10sListener,
+  isMultiE10s,
+  removeMultiE10sListener,
+} = require("devtools/shared/multi-e10s-helper");
+
 const {DebuggerServer} = require("devtools/server/main");
 const {getSystemInfo} = require("devtools/shared/system");
 const {deviceSpec} = require("devtools/shared/specs/device");
 const {AppConstants} = require("resource://gre/modules/AppConstants.jsm");
 
 exports.DeviceActor = protocol.ActorClassWithSpec(deviceSpec, {
   initialize: function(conn) {
     protocol.Actor.prototype.initialize.call(this, conn);
     // pageshow and pagehide event release wake lock, so we have to acquire
     // wake lock again by pageshow event
     this._onPageShow = this._onPageShow.bind(this);
     if (this._window) {
       this._window.addEventListener("pageshow", this._onPageShow, true);
     }
     this._acquireWakeLock();
+
+    this._onMultiE10sUpdated = this._onMultiE10sUpdated.bind(this);
+    addMultiE10sListener(this._onMultiE10sUpdated);
   },
 
   destroy: function() {
     protocol.Actor.prototype.destroy.call(this);
     this._releaseWakeLock();
     if (this._window) {
       this._window.removeEventListener("pageshow", this._onPageShow, true);
     }
+    removeMultiE10sListener(this._onMultiE10sUpdated);
+  },
+
+  _onMultiE10sUpdated: function() {
+    this.emit("multi-e10s-updated", isMultiE10s());
   },
 
   getDescription: function() {
-    return getSystemInfo();
+    return Object.assign({}, getSystemInfo(), { isMultiE10s: isMultiE10s() });
   },
 
   screenshotToDataURL: function() {
     const window = this._window;
     const { devicePixelRatio } = window;
     const canvas = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     const width = window.innerWidth;
     const height = window.innerHeight;
--- a/devtools/shared/moz.build
+++ b/devtools/shared/moz.build
@@ -60,16 +60,17 @@ DevToolsModules(
     'extend.js',
     'flags.js',
     'generate-uuid.js',
     'indentation.js',
     'indexed-db.js',
     'l10n.js',
     'loader-plugin-raw.jsm',
     'Loader.jsm',
+    'multi-e10s-helper.js',
     'Parser.jsm',
     'path.js',
     'plural-form.js',
     'protocol.js',
     'system.js',
     'task.js',
     'ThreadSafeDevToolsUtils.js',
     'throttle.js',
rename from devtools/client/shared/multi-e10s-helper.js
rename to devtools/shared/multi-e10s-helper.js
--- a/devtools/shared/specs/device.js
+++ b/devtools/shared/specs/device.js
@@ -1,17 +1,23 @@
 /* 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 {RetVal, generateActorSpec} = require("devtools/shared/protocol");
-
+const { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");
 const deviceSpec = generateActorSpec({
   typeName: "device",
 
+  events: {
+    "multi-e10s-updated": {
+      type: "multi-e10s-updated",
+      isMultiE10s: Arg(0, "boolean"),
+    },
+  },
+
   methods: {
     getDescription: {request: {}, response: { value: RetVal("json")}},
     screenshotToDataURL: {request: {}, response: { value: RetVal("longstring")}},
   },
 });
 
 exports.deviceSpec = deviceSpec;
--- a/devtools/shared/system.js
+++ b/devtools/shared/system.js
@@ -30,17 +30,17 @@ const APP_MAP = {
   "{3550f703-e582-4d05-9a08-453d09bdfdc6}": "thunderbird",
   "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}": "seamonkey",
   "{718e30fb-e89b-41dd-9da7-e25a45638b28}": "sunbird",
   "{aa3c5121-dab2-40e2-81ca-7ea25febc110}": "mobile/android",
 };
 
 var CACHED_INFO = null;
 
-async function getSystemInfo() {
+function getSystemInfo() {
   if (CACHED_INFO) {
     return CACHED_INFO;
   }
 
   const appInfo = Services.appinfo;
   const win = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
   const [processor, compiler] = appInfo.XPCOMABI.split("-");
   let dpi,