Bug 1505289 - Show unplugged USB devices in about:debugging r=Ola,ladybenko,flod
authorJulian Descottes <jdescottes@mozilla.com>
Tue, 16 Apr 2019 06:36:09 +0000
changeset 469620 d864f9d6ce00
parent 469619 88f37063a1ff
child 469621 3b2597e84231
push id35878
push userapavel@mozilla.com
push dateTue, 16 Apr 2019 15:43:40 +0000
treeherdermozilla-central@258af4e91151 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersOla, ladybenko, flod
bugs1505289
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 1505289 - Show unplugged USB devices in about:debugging r=Ola,ladybenko,flod Depends on D25779 To avoid confusion with connection/disconnection to runtimes, I use "unplugged" here. Makes sense for USB, might not make sense of we extend it to other connection types Differential Revision: https://phabricator.services.mozilla.com/D25780
devtools/client/aboutdebugging-new/src/actions/runtimes.js
devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
devtools/client/aboutdebugging-new/src/components/sidebar/SidebarRuntimeItem.js
devtools/client/aboutdebugging-new/src/modules/usb-runtimes.js
devtools/client/aboutdebugging-new/src/types/runtime.js
devtools/client/aboutdebugging-new/test/browser/helper-mocks.js
devtools/client/locales/en-US/aboutdebugging.ftl
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -155,16 +155,17 @@ function connectRuntime(id) {
 function createThisFirefoxRuntime() {
   return (dispatch, getState) => {
     const thisFirefoxRuntime = {
       id: RUNTIMES.THIS_FIREFOX,
       isConnecting: false,
       isConnectionFailed: false,
       isConnectionNotResponding: false,
       isUnknown: false,
+      isUnplugged: false,
       name: l10n.getString("about-debugging-this-firefox-runtime-name"),
       type: RUNTIMES.THIS_FIREFOX,
     };
     dispatch({ type: THIS_FIREFOX_RUNTIME_CREATED, runtime: thisFirefoxRuntime });
   };
 }
 
 function disconnectRuntime(id, shouldRedirect = false) {
@@ -316,16 +317,17 @@ function updateNetworkRuntimes(locations
       id: location,
       extra: {
         connectionParameters: { host, port: parseInt(port, 10) },
       },
       isConnecting: false,
       isConnectionFailed: false,
       isConnectionNotResponding: false,
       isUnknown: false,
+      isUnplugged: false,
       name: location,
       type: RUNTIMES.NETWORK,
     };
   });
   return updateRemoteRuntimes(runtimes, RUNTIMES.NETWORK);
 }
 
 function updateUSBRuntimes(adbRuntimes) {
@@ -339,16 +341,17 @@ function updateUSBRuntimes(adbRuntimes) 
       extra: {
         connectionParameters,
         deviceName: adbRuntime.deviceName,
       },
       isConnecting: false,
       isConnectionFailed: false,
       isConnectionNotResponding: false,
       isUnknown: adbRuntime.isUnknown,
+      isUnplugged: adbRuntime.isUnplugged,
       name: adbRuntime.shortName,
       type: RUNTIMES.USB,
     };
   });
   return updateRemoteRuntimes(runtimes, RUNTIMES.USB);
 }
 
 /**
--- a/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
+++ b/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
@@ -113,16 +113,17 @@ class Sidebar extends PureComponent {
         icon,
         key: keyId,
         isConnected: runtimeHasDetails,
         isConnecting: runtime.isConnecting,
         isConnectionFailed: runtime.isConnectionFailed,
         isConnectionNotResponding: runtime.isConnectionNotResponding,
         isSelected,
         isUnknown: runtime.isUnknown,
+        isUnplugged: runtime.isUnplugged,
         name,
         runtimeId: runtime.id,
       });
     });
   }
 
   renderFooter() {
     const HELP_ICON_SRC = "chrome://global/skin/icons/help.svg";
--- a/devtools/client/aboutdebugging-new/src/components/sidebar/SidebarRuntimeItem.js
+++ b/devtools/client/aboutdebugging-new/src/components/sidebar/SidebarRuntimeItem.js
@@ -28,16 +28,17 @@ class SidebarRuntimeItem extends PureCom
       getString: PropTypes.func.isRequired,
       icon: PropTypes.string.isRequired,
       isConnected: PropTypes.bool.isRequired,
       isConnecting: PropTypes.bool.isRequired,
       isConnectionFailed: PropTypes.bool.isRequired,
       isConnectionNotResponding: PropTypes.bool.isRequired,
       isSelected: PropTypes.bool.isRequired,
       isUnknown: PropTypes.bool.isRequired,
+      isUnplugged: PropTypes.bool.isRequired,
       name: PropTypes.string.isRequired,
       runtimeId: PropTypes.string.isRequired,
     };
   }
 
   renderConnectButton() {
     const { isConnecting } = this.props;
     const localizationId = isConnecting
@@ -107,20 +108,26 @@ class SidebarRuntimeItem extends PureCom
           id: localizationId,
         },
         dom.p({ className: "word-wrap-anywhere" }, localizationId)
       )
     );
   }
 
   renderName() {
-    const { deviceName, getString, isUnknown, name } = this.props;
+    const { deviceName, getString, isUnknown, isUnplugged, name } = this.props;
 
-    const displayName = isUnknown ?
-      getString("about-debugging-sidebar-runtime-item-waiting-for-browser") : name;
+    let displayName;
+    if (isUnplugged) {
+      displayName = getString("about-debugging-sidebar-runtime-item-unplugged");
+    } else if (isUnknown) {
+      displayName = getString("about-debugging-sidebar-runtime-item-waiting-for-browser");
+    } else {
+      displayName = name;
+    }
 
     const localizationId = deviceName
       ? "about-debugging-sidebar-runtime-item-name"
       : "about-debugging-sidebar-runtime-item-name-no-device";
 
     const className = "ellipsis-text sidebar-runtime-item__runtime";
 
     function renderWithDevice() {
--- a/devtools/client/aboutdebugging-new/src/modules/usb-runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/modules/usb-runtimes.js
@@ -33,16 +33,38 @@ class UnknownUsbRuntime {
     this.shortName = "Unknown runtime";
     this.socketPath = null;
     this.isUnknown = true;
     this.isUnplugged = false;
   }
 }
 
 /**
+ * Used to represent USB devices that were previously connected but are now missing
+ * (presumably after being unplugged/disconnected from the computer).
+ */
+class UnpluggedUsbRuntime {
+  constructor(deviceId, deviceName) {
+    this.id = deviceId + "|unplugged";
+    this.deviceId = deviceId;
+    this.deviceName = deviceName;
+    this.shortName = "Unplugged runtime";
+    this.socketPath = null;
+    this.isUnknown = true;
+    this.isUnplugged = true;
+  }
+}
+
+/**
+ * Map used to keep track of discovered usb devices. Will be used to create the unplugged
+ * usb runtimes.
+ */
+const devices = new Map();
+
+/**
  * This module provides a collection of helper methods to detect USB runtimes whom Firefox
  * is running on.
  */
 function addUSBRuntimesObserver(listener) {
   adb.registerListener(listener);
 }
 exports.addUSBRuntimesObserver = addUSBRuntimesObserver;
 
@@ -51,17 +73,32 @@ async function getUSBRuntimes() {
   const runtimes = adb.getRuntimes().map(r => new UsbRuntime(r));
 
   // Get devices found by ADB, but without any available runtime.
   const runtimeDevices = runtimes.map(r => r.deviceId);
   const unknownRuntimes = adb.getDevices()
     .filter(d => !runtimeDevices.includes(d.id))
     .map(d => new UnknownUsbRuntime(d));
 
-  return runtimes.concat(unknownRuntimes);
+  // Add all devices to the map of known devices.
+  const allRuntimes = runtimes.concat(unknownRuntimes);
+  for (const runtime of allRuntimes) {
+    devices.set(runtime.deviceId, runtime.deviceName);
+  }
+
+  // Get devices previously found by ADB but no longer available.
+  const currentDevices = allRuntimes.map(r => r.deviceId);
+  const knownDevices = [...devices.keys()];
+  const unpluggedDevices = knownDevices.filter(id => !currentDevices.includes(id));
+  const unpluggedRuntimes = unpluggedDevices.map(deviceId => {
+    const deviceName = devices.get(deviceId);
+    return new UnpluggedUsbRuntime(deviceId, deviceName);
+  });
+
+  return allRuntimes.concat(unpluggedRuntimes);
 }
 exports.getUSBRuntimes = getUSBRuntimes;
 
 function removeUSBRuntimesObserver(listener) {
   adb.unregisterListener(listener);
 }
 exports.removeUSBRuntimesObserver = removeUSBRuntimesObserver;
 
--- a/devtools/client/aboutdebugging-new/src/types/runtime.js
+++ b/devtools/client/aboutdebugging-new/src/types/runtime.js
@@ -125,16 +125,20 @@ const runtime = {
   // will be true if connecting to runtime is taking time, will be false after connecting
   // or failing.
   isConnectionNotResponding: PropTypes.bool.isRequired,
 
   // unknown runtimes are placeholders for devices where the runtime has not been started
   // yet. For instance an ADB device connected without a compatible runtime running.
   isUnknown: PropTypes.bool.isRequired,
 
+  // unplugged runtimes are placeholders for devices that are no longer available. For
+  // instance a USB device that was unplugged from the computer.
+  isUnplugged: PropTypes.bool.isRequired,
+
   // display name of the runtime
   name: PropTypes.string.isRequired,
 
   // available after the connection to the runtime is established
   // unavailable on disconnected runtimes
   runtimeDetails: PropTypes.shape(runtimeDetails),
 
   // runtime type, for instance "network", "usb" ...
--- a/devtools/client/aboutdebugging-new/test/browser/helper-mocks.js
+++ b/devtools/client/aboutdebugging-new/test/browser/helper-mocks.js
@@ -128,16 +128,19 @@ class Mocks {
     // Add a new runtime to the list of scanned runtimes.
     this._usbRuntimes.push({
       id: id,
       socketPath: runtimeInfo.socketPath || "test/path",
       deviceName: runtimeInfo.deviceName || "test device name",
       get isUnknown() {
         return runtimeInfo.isUnknown || false;
       },
+      get isUnplugged() {
+        return runtimeInfo.isUnplugged || false;
+      },
       shortName: runtimeInfo.shortName || "testshort",
     });
 
     // Add a valid client that can be returned for this particular runtime id.
     const mockUsbClient = createClientMock();
     mockUsbClient.getDeviceDescription = () => {
       return {
         channel: runtimeInfo.channel || "release",
--- a/devtools/client/locales/en-US/aboutdebugging.ftl
+++ b/devtools/client/locales/en-US/aboutdebugging.ftl
@@ -60,16 +60,20 @@ about-debugging-sidebar-item-connect-but
 # successfully connecting to them. Temporary UI, do not localize.
 about-debugging-sidebar-item-connected-label = Connected
 
 # Text displayed in sidebar items for remote devices where a compatible browser (eg
 # Firefox) has not been detected yet. Typically, Android phones connected via USB with
 # USB debugging enabled, but where Firefox is not started.
 about-debugging-sidebar-runtime-item-waiting-for-browser = Waiting for browser…
 
+# Text displayed in sidebar items for remote devices that have been disconnected from the
+# computer.
+about-debugging-sidebar-runtime-item-unplugged = Unplugged
+
 # Title for runtime sidebar items that are related to a specific device (USB, WiFi).
 about-debugging-sidebar-runtime-item-name =
   .title = { $displayName } ({ $deviceName })
 # Title for runtime sidebar items where we cannot get device information (network
 # locations).
 about-debugging-sidebar-runtime-item-name-no-device =
   .title = { $displayName }