Bug 1498469 - Part 1: Wait until adb process is ready to show the 'Usb enabled' message r=jdescottes,daisuke
authorBelén Albeza <balbeza@mozilla.com>
Tue, 23 Apr 2019 11:44:44 +0000
changeset 470473 913c397010c2c9ba8ba0f28a1eae5e30e410dbe9
parent 470472 515cfc853f2a8372dcc9139f8b90a4e7fd038744
child 470474 512231282d5f08fbf997840fc66c968a9ebaac75
push id35906
push useraciure@mozilla.com
push dateTue, 23 Apr 2019 22:14:56 +0000
treeherdermozilla-central@0ce3633f8b80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes, daisuke
bugs1498469
milestone68.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 1498469 - Part 1: Wait until adb process is ready to show the 'Usb enabled' message r=jdescottes,daisuke Differential Revision: https://phabricator.services.mozilla.com/D27296
devtools/client/aboutdebugging-new/aboutdebugging.js
devtools/client/aboutdebugging-new/src/actions/ui.js
devtools/client/aboutdebugging-new/src/components/App.js
devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
devtools/client/aboutdebugging-new/src/constants.js
devtools/client/aboutdebugging-new/src/reducers/ui-state.js
devtools/shared/adb/adb-process.js
--- a/devtools/client/aboutdebugging-new/aboutdebugging.js
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.js
@@ -31,29 +31,31 @@ const {
 } = require("./src/modules/network-locations");
 const {
   addUSBRuntimesObserver,
   getUSBRuntimes,
   removeUSBRuntimesObserver,
 } = require("./src/modules/usb-runtimes");
 
 loader.lazyRequireGetter(this, "adbAddon", "devtools/shared/adb/adb-addon", true);
+loader.lazyRequireGetter(this, "adbProcess", "devtools/shared/adb/adb-process", 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.onAdbProcessReady = this.onAdbProcessReady.bind(this);
     this.onNetworkLocationsUpdated = this.onNetworkLocationsUpdated.bind(this);
     this.onUSBRuntimesUpdated = this.onUSBRuntimesUpdated.bind(this);
 
     this.store = configureStore();
     this.actions = bindActionCreators(actions, this.store.dispatch);
 
     const width = this.getRoundedViewportWidth();
     this.actions.recordTelemetryEvent("open_adbg", { width });
@@ -84,25 +86,32 @@ const AboutDebugging = {
     addNetworkLocationsObserver(this.onNetworkLocationsUpdated);
 
     // Listen to USB runtime updates and retrieve the initial list of runtimes.
     this.onUSBRuntimesUpdated();
     addUSBRuntimesObserver(this.onUSBRuntimesUpdated);
 
     adbAddon.on("update", this.onAdbAddonUpdated);
     this.onAdbAddonUpdated();
+    adbProcess.on("adb-ready", this.onAdbProcessReady);
+    // get the initial status of adb process, in case it's already started
+    this.onAdbProcessReady();
 
     // Remove deprecated remote debugging extensions.
     await adbAddon.uninstallUnsupportedExtensions();
   },
 
   onAdbAddonUpdated() {
     this.actions.updateAdbAddonStatus(adbAddon.status);
   },
 
+  onAdbProcessReady() {
+    this.actions.updateAdbReady(adbProcess.ready);
+  },
+
   onNetworkLocationsUpdated() {
     this.actions.updateNetworkLocations(getNetworkLocations());
   },
 
   async onUSBRuntimesUpdated() {
     const runtimes = await getUSBRuntimes();
     this.actions.updateUSBRuntimes(runtimes);
   },
@@ -118,16 +127,17 @@ const AboutDebugging = {
     }
 
     // Remove all client listeners.
     this.actions.removeRuntimeListeners();
 
     removeNetworkLocationsObserver(this.onNetworkLocationsUpdated);
     removeUSBRuntimesObserver(this.onUSBRuntimesUpdated);
     adbAddon.off("update", this.onAdbAddonUpdated);
+    adbProcess.off("adb-ready", this.onAdbProcessReady);
     setDebugTargetCollapsibilities(state.ui.debugTargetCollapsibilities);
     unmountComponentAtNode(this.mount);
   },
 
   get mount() {
     return document.getElementById("mount");
   },
 
--- a/devtools/client/aboutdebugging-new/src/actions/ui.js
+++ b/devtools/client/aboutdebugging-new/src/actions/ui.js
@@ -7,16 +7,17 @@
 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,
+  ADB_READY_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
   HIDE_PROFILER_DIALOG,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_TYPES,
   SELECT_PAGE_FAILURE,
   SELECT_PAGE_START,
   SELECT_PAGE_SUCCESS,
   SELECTED_RUNTIME_ID_UPDATED,
@@ -107,16 +108,20 @@ function showProfilerDialog() {
 function hideProfilerDialog() {
   return { type: HIDE_PROFILER_DIALOG };
 }
 
 function updateAdbAddonStatus(adbAddonStatus) {
   return { type: ADB_ADDON_STATUS_UPDATED, adbAddonStatus };
 }
 
+function updateAdbReady(isAdbReady) {
+  return { type: ADB_READY_UPDATED, isAdbReady };
+}
+
 function updateNetworkLocations(locations) {
   return (dispatch, getState) => {
     dispatch(Actions.updateNetworkRuntimes(locations));
     dispatch({ type: NETWORK_LOCATIONS_UPDATED, locations });
   };
 }
 
 function installAdbAddon() {
@@ -165,11 +170,12 @@ module.exports = {
   hideProfilerDialog,
   installAdbAddon,
   removeNetworkLocation,
   scanUSBRuntimes,
   selectPage,
   showProfilerDialog,
   uninstallAdbAddon,
   updateAdbAddonStatus,
+  updateAdbReady,
   updateDebugTargetCollapsibility,
   updateNetworkLocations,
 };
--- a/devtools/client/aboutdebugging-new/src/components/App.js
+++ b/devtools/client/aboutdebugging-new/src/components/App.js
@@ -28,16 +28,17 @@ class App extends PureComponent {
     return {
       adbAddonStatus: Types.adbAddonStatus,
       // The "dispatch" helper is forwarded to the App component via connect.
       // From that point, components are responsible for forwarding the dispatch
       // property to all components who need to dispatch actions.
       dispatch: PropTypes.func.isRequired,
       // getString prop is injected by the withLocalization wrapper
       getString: PropTypes.func.isRequired,
+      isAdbReady: PropTypes.bool.isRequired,
       isScanningUsb: PropTypes.bool.isRequired,
       networkLocations: PropTypes.arrayOf(Types.location).isRequired,
       networkRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
       selectedPage: Types.page,
       selectedRuntimeId: PropTypes.string,
       usbRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
     };
   }
@@ -144,46 +145,49 @@ class App extends PureComponent {
       })
     );
   }
 
   render() {
     const {
       adbAddonStatus,
       dispatch,
+      isAdbReady,
       isScanningUsb,
       networkRuntimes,
       selectedPage,
       selectedRuntimeId,
       usbRuntimes,
     } = this.props;
 
     return Localized(
       { },
       dom.div(
         { className: "app" },
         Sidebar({
           adbAddonStatus,
           className: "app__sidebar",
           dispatch,
+          isAdbReady,
           isScanningUsb,
           networkRuntimes,
           selectedPage,
           selectedRuntimeId,
           usbRuntimes,
         }),
         dom.main({ className: "app__content" }, this.renderRoutes())
       )
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
     adbAddonStatus: state.ui.adbAddonStatus,
+    isAdbReady: state.ui.isAdbReady,
     isScanningUsb: state.ui.isScanningUsb,
     networkLocations: state.ui.networkLocations,
     networkRuntimes: state.runtimes.networkRuntimes,
     selectedPage: state.ui.selectedPage,
     selectedRuntimeId: state.runtimes.selectedRuntimeId,
     usbRuntimes: state.runtimes.usbRuntimes,
   };
 };
--- a/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
+++ b/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
@@ -26,28 +26,30 @@ const GLOBE_ICON = "chrome://devtools/sk
 const USB_ICON = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
 
 class Sidebar extends PureComponent {
   static get propTypes() {
     return {
       adbAddonStatus: Types.adbAddonStatus,
       className: PropTypes.string,
       dispatch: PropTypes.func.isRequired,
+      isAdbReady: PropTypes.bool.isRequired,
       isScanningUsb: PropTypes.bool.isRequired,
       networkRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
       selectedPage: Types.page,
       selectedRuntimeId: PropTypes.string,
       usbRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
     };
   }
 
-  renderAdbAddonStatus() {
-    const isAddonInstalled = this.props.adbAddonStatus === ADB_ADDON_STATES.INSTALLED;
-    const localizationId = isAddonInstalled ? "about-debugging-sidebar-usb-enabled" :
-                                              "about-debugging-sidebar-usb-disabled";
+  renderAdbStatus() {
+    const isUsbEnabled = this.props.isAdbReady &&
+      this.props.adbAddonStatus === ADB_ADDON_STATES.INSTALLED;
+    const localizationId = isUsbEnabled ? "about-debugging-sidebar-usb-enabled" :
+                                          "about-debugging-sidebar-usb-disabled";
     return Message(
       {
           level: MESSAGE_LEVEL.INFO,
           isCloseable: true,
       },
         Localized(
           {
             id: localizationId,
@@ -203,17 +205,17 @@ class Sidebar extends PureComponent {
             to: `/runtime/${RUNTIMES.THIS_FIREFOX}`,
           })
         ),
         SidebarItem(
           {
             className: "sidebar-item--overflow sidebar-item--full-width",
           },
           dom.hr({ className: "separator separator--breathe" }),
-          this.renderAdbAddonStatus(),
+          this.renderAdbStatus(),
         ),
         this.renderDevices(),
         SidebarItem(
           {
             className: "sidebar-item--breathe sidebar__refresh-usb",
             key: "refresh-devices",
           },
           RefreshDevicesButton({
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -10,16 +10,17 @@ const { CONNECTION_TYPES, DEBUG_TARGET_T
 const actionTypes = {
   ADB_ADDON_INSTALL_START: "ADB_ADDON_INSTALL_START",
   ADB_ADDON_INSTALL_SUCCESS: "ADB_ADDON_INSTALL_SUCCESS",
   ADB_ADDON_INSTALL_FAILURE: "ADB_ADDON_INSTALL_FAILURE",
   ADB_ADDON_UNINSTALL_START: "ADB_ADDON_UNINSTALL_START",
   ADB_ADDON_UNINSTALL_SUCCESS: "ADB_ADDON_UNINSTALL_SUCCESS",
   ADB_ADDON_UNINSTALL_FAILURE: "ADB_ADDON_UNINSTALL_FAILURE",
   ADB_ADDON_STATUS_UPDATED: "ADB_ADDON_STATUS_UPDATED",
+  ADB_READY_UPDATED: "ADB_READY_UPDATED",
   CONNECT_RUNTIME_CANCEL: "CONNECT_RUNTIME_CANCEL",
   CONNECT_RUNTIME_FAILURE: "CONNECT_RUNTIME_FAILURE",
   CONNECT_RUNTIME_NOT_RESPONDING: "CONNECT_RUNTIME_NOT_RESPONDING",
   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",
--- a/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
@@ -1,48 +1,55 @@
 /* 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 {
   ADB_ADDON_STATUS_UPDATED,
+  ADB_READY_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
   HIDE_PROFILER_DIALOG,
   NETWORK_LOCATIONS_UPDATED,
   SELECT_PAGE_SUCCESS,
   SHOW_PROFILER_DIALOG,
   TEMPORARY_EXTENSION_INSTALL_FAILURE,
   TEMPORARY_EXTENSION_INSTALL_SUCCESS,
   USB_RUNTIMES_SCAN_START,
   USB_RUNTIMES_SCAN_SUCCESS,
 } = require("../constants");
 
 function UiState(locations = [], debugTargetCollapsibilities = {},
                  showSystemAddons = false) {
   return {
     adbAddonStatus: null,
     debugTargetCollapsibilities,
+    isAdbReady: false,
     isScanningUsb: false,
     networkLocations: locations,
     selectedPage: null,
     showProfilerDialog: false,
     showSystemAddons,
     temporaryInstallError: null,
   };
 }
 
 function uiReducer(state = UiState(), action) {
   switch (action.type) {
     case ADB_ADDON_STATUS_UPDATED: {
       const { adbAddonStatus } = action;
       return Object.assign({}, state, { adbAddonStatus });
     }
 
+    case ADB_READY_UPDATED: {
+      const { isAdbReady } = action;
+      return Object.assign({}, state, { isAdbReady });
+    }
+
     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 NETWORK_LOCATIONS_UPDATED: {
--- a/devtools/shared/adb/adb-process.js
+++ b/devtools/shared/adb/adb-process.js
@@ -1,19 +1,19 @@
 /* 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 { Cc, Ci } = require("chrome");
 const { dumpn } = require("devtools/shared/DevToolsUtils");
+const EventEmitter = require("devtools/shared/event-emitter");
 const { getFileForBinary } = require("./adb-binary");
 const { setTimeout } = require("resource://gre/modules/Timer.jsm");
-const { Services } = require("resource://gre/modules/Services.jsm");
 
 loader.lazyRequireGetter(this, "runCommand", "devtools/shared/adb/commands/index", true);
 loader.lazyRequireGetter(this, "check", "devtools/shared/adb/adb-running-checker", true);
 
 // Waits until a predicate returns true or re-tries the predicate calls
 // |retry| times, we wait for 100ms between each calls.
 async function waitUntil(predicate, retry = 20) {
   let count = 0;
@@ -24,18 +24,20 @@ async function waitUntil(predicate, retr
     // Wait for 100 milliseconds.
     await new Promise(resolve => setTimeout(resolve, 100));
   }
   // Timed out after trying too many times.
   return false;
 }
 
 // Class representing the ADB process.
-class AdbProcess {
+class AdbProcess extends EventEmitter {
   constructor() {
+    super();
+
     this._ready = false;
     this._didRunInitially = false;
   }
 
   get ready() {
     return this._ready;
   }
 
@@ -64,18 +66,18 @@ class AdbProcess {
     });
   }
 
   // We startup by launching adb in server mode, and setting
   // the tcp socket preference to |true|
   async start() {
     return new Promise(async (resolve, reject) => {
       const onSuccessfulStart = () => {
-        Services.obs.notifyObservers(null, "adb-ready");
         this._ready = true;
+        this.emit("adb-ready");
         resolve();
       };
 
       const isAdbRunning = await check();
       if (isAdbRunning) {
         dumpn("Found ADB process running, not restarting");
         onSuccessfulStart();
         return;