Bug 975591 - Part 2: Show DevTools devices in WebIDE. r=paul
authorJ. Ryan Stinnett <jryans@gmail.com>
Thu, 26 Jun 2014 16:41:00 +0200
changeset 191169 82a75a2df1c249ad088e4498b5c55dac4111cd43
parent 191168 57f870a3e9c11f6d7a26d9f90e58a64eda156fba
child 191170 9ea6813cf5cca68c0a8aa88bd4541b2b61aae8f5
push id8436
push usercbook@mozilla.com
push dateFri, 27 Jun 2014 13:56:57 +0000
treeherderb2g-inbound@22ea396750e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaul
bugs975591
milestone33.0a1
Bug 975591 - Part 2: Show DevTools devices in WebIDE. r=paul
browser/devtools/webide/content/webide.js
browser/devtools/webide/content/webide.xul
browser/devtools/webide/locales/en-US/webide.dtd
browser/devtools/webide/modules/app-manager.js
browser/devtools/webide/modules/runtimes.js
browser/devtools/webide/themes/webide.css
modules/libpref/src/init/all.js
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -228,17 +228,25 @@ let UI = {
     nbox.removeAllNotifications(true);
     nbox.appendNotification(text, "webide:errornotification", null,
                             nbox.PRIORITY_WARNING_LOW, buttons);
   },
 
   /********** RUNTIME **********/
 
   updateRuntimeList: function() {
+    let wifiHeaderNode = document.querySelector("#runtime-header-wifi-devices");
+    if (AppManager.isWiFiScanningEnabled) {
+      wifiHeaderNode.removeAttribute("hidden");
+    } else {
+      wifiHeaderNode.setAttribute("hidden", "true");
+    }
+
     let USBListNode = document.querySelector("#runtime-panel-usbruntime");
+    let WiFiListNode = document.querySelector("#runtime-panel-wifi-devices");
     let simulatorListNode = document.querySelector("#runtime-panel-simulators");
     let customListNode = document.querySelector("#runtime-panel-custom");
 
     let noHelperNode = document.querySelector("#runtime-panel-noadbhelper");
     let noUSBNode = document.querySelector("#runtime-panel-nousbdevice");
     let noSimulatorNode = document.querySelector("#runtime-panel-nosimulator");
 
     if (Devices.helperAddonInstalled) {
@@ -256,16 +264,17 @@ let UI = {
     if (AppManager.runtimeList.simulator.length > 0) {
       noSimulatorNode.setAttribute("hidden", "true");
     } else {
       noSimulatorNode.removeAttribute("hidden");
     }
 
     for (let [type, parent] of [
       ["usb", USBListNode],
+      ["wifi", WiFiListNode],
       ["simulator", simulatorListNode],
       ["custom", customListNode],
     ]) {
       while (parent.hasChildNodes()) {
         parent.firstChild.remove();
       }
       for (let runtime of AppManager.runtimeList[type]) {
         let panelItemNode = document.createElement("toolbarbutton");
@@ -770,16 +779,18 @@ let Cmds = {
         };
       }, true);
     }
 
     return deferred.promise;
   },
 
   showRuntimePanel: function() {
+    AppManager.scanForWiFiRuntimes();
+
     let panel = document.querySelector("#runtime-panel");
     let anchor = document.querySelector("#runtime-panel-button > .panel-button-anchor");
 
     let deferred = promise.defer();
     function onPopupShown() {
       panel.removeEventListener("popupshown", onPopupShown);
       deferred.resolve();
     }
--- a/browser/devtools/webide/content/webide.xul
+++ b/browser/devtools/webide/content/webide.xul
@@ -139,16 +139,18 @@
 
     <!-- Runtime panel -->
     <panel id="runtime-panel" type="arrow" position="bottomcenter topright" consumeoutsideclicks="true" animate="false">
       <vbox flex="1">
         <label class="panel-header">&runtimePanel_USBDevices;</label>
         <toolbarbutton class="panel-item-help" label="&runtimePanel_nousbdevice;" id="runtime-panel-nousbdevice" command="cmd_showTroubleShooting"/>
         <toolbarbutton class="panel-item-help" label="&runtimePanel_noadbhelper;" id="runtime-panel-noadbhelper" command="cmd_showAddons"/>
         <vbox id="runtime-panel-usbruntime"></vbox>
+        <label class="panel-header" id="runtime-header-wifi-devices">&runtimePanel_WiFiDevices;</label>
+        <vbox id="runtime-panel-wifi-devices"></vbox>
         <label class="panel-header">&runtimePanel_simulators;</label>
         <toolbarbutton class="panel-item-help" label="&runtimePanel_nosimulator;" id="runtime-panel-nosimulator" command="cmd_showAddons"/>
         <vbox id="runtime-panel-simulators"></vbox>
         <label class="panel-header">&runtimePanel_custom;</label>
         <vbox id="runtime-panel-custom"></vbox>
         <vbox flex="1" id="runtime-actions" hidden="true">
           <toolbarbutton class="panel-item" id="runtime-details" command="cmd_showRuntimeDetails"/>
           <toolbarbutton class="panel-item" id="runtime-permissions" command="cmd_showPermissionsTable"/>
--- a/browser/devtools/webide/locales/en-US/webide.dtd
+++ b/browser/devtools/webide/locales/en-US/webide.dtd
@@ -54,16 +54,17 @@
 <!-- show toolbox -->
 <!ENTITY key_toggleToolbox "VK_F12">
 <!-- toggle sidebar -->
 <!ENTITY key_toggleEditor "B">
 
 <!ENTITY projectPanel_myProjects "My Projects">
 <!ENTITY projectPanel_runtimeApps "Runtime Apps">
 <!ENTITY runtimePanel_USBDevices "USB Devices">
+<!ENTITY runtimePanel_WiFiDevices "WiFi Devices">
 <!ENTITY runtimePanel_simulators "Simulators">
 <!ENTITY runtimePanel_custom "Custom">
 <!ENTITY runtimePanel_nosimulator "Install Simulator">
 <!ENTITY runtimePanel_noadbhelper "Install ADB Helper">
 <!ENTITY runtimePanel_nousbdevice "Can't see your device?">
 
 <!-- Lense -->
 <!ENTITY details_valid_header "valid">
--- a/browser/devtools/webide/modules/app-manager.js
+++ b/browser/devtools/webide/modules/app-manager.js
@@ -15,20 +15,24 @@ const {TextEncoder, OS}  = Cu.import("re
 const {AppProjects} = require("devtools/app-manager/app-projects");
 const WebappsStore = require("devtools/app-manager/webapps-store");
 const {AppValidator} = require("devtools/app-manager/app-validator");
 const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
 const AppActorFront = require("devtools/app-actor-front");
 const {getDeviceFront} = require("devtools/server/actors/device");
 const {setTimeout} = require("sdk/timers");
 const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
-const {USBRuntime, SimulatorRuntime, gLocalRuntime, gRemoteRuntime} = require("devtools/webide/runtimes");
+const {USBRuntime, WiFiRuntime, SimulatorRuntime,
+       gLocalRuntime, gRemoteRuntime} = require("devtools/webide/runtimes");
+const discovery = require("devtools/toolkit/discovery/discovery");
 
 const Strings = Services.strings.createBundle("chrome://webide/content/webide.properties");
 
+const WIFI_SCANNING_PREF = "devtools.remote.wifi.scan";
+
 exports.AppManager = AppManager = {
 
   // FIXME: will break when devtools/app-manager will be removed:
   DEFAULT_PROJECT_ICON: "chrome://browser/skin/devtools/app-manager/default-app-icon.png",
   DEFAULT_PROJECT_NAME: "--",
 
   init: function() {
     let host = Services.prefs.getCharPref("devtools.debugger.remote-host");
@@ -37,39 +41,60 @@ exports.AppManager = AppManager = {
     this.connection = ConnectionManager.createConnection("localhost", port);
     this.onConnectionChanged = this.onConnectionChanged.bind(this);
     this.connection.on(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
 
     this.onWebAppsStoreready = this.onWebAppsStoreready.bind(this);
     this.webAppsStore = new WebappsStore(this.connection);
     this.webAppsStore.on("store-ready", this.onWebAppsStoreready);
 
-    this.runtimeList = {usb: [], simulator: [], custom: [gRemoteRuntime]};
+    this.runtimeList = {
+      usb: [],
+      wifi: [],
+      simulator: [],
+      custom: [gRemoteRuntime]
+    };
     if (Services.prefs.getBoolPref("devtools.webide.enableLocalRuntime")) {
       this.runtimeList.custom.push(gLocalRuntime);
     }
     this.trackUSBRuntimes();
+    this.trackWiFiRuntimes();
     this.trackSimulatorRuntimes();
+
+    this.observe = this.observe.bind(this);
+    Services.prefs.addObserver(WIFI_SCANNING_PREF, this, false);
   },
 
   uninit: function() {
     this._unlistenToApps();
     this.selectedProject = null;
     this.selectedRuntime = null;
     this.untrackUSBRuntimes();
+    this.untrackWiFiRuntimes();
     this.untrackSimulatorRuntimes();
     this._runningApps.clear();
     this.runtimeList = null;
     this.webAppsStore.off("store-ready", this.onWebAppsStoreready);
     this.webAppsStore.destroy();
     this.webAppsStore = null;
     this.connection.off(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
     this._listTabsResponse = null;
     this.connection.disconnect();
     this.connection = null;
+    Services.prefs.removeObserver(WIFI_SCANNING_PREF, this);
+  },
+
+  observe: function(subject, topic, data) {
+    if (data !== WIFI_SCANNING_PREF) {
+      return;
+    }
+    // Cycle WiFi tracking to reflect the new value
+    this.untrackWiFiRuntimes();
+    this.trackWiFiRuntimes();
+    this._updateWiFiRuntimes();
   },
 
   update: function(what, details) {
     // Anything we want to forward to the UI
     this.emit("app-manager-update", what, details);
   },
 
   reportError: function(l10nProperty, ...l10nArgs) {
@@ -500,16 +525,50 @@ exports.AppManager = AppManager = {
   _updateUSBRuntimes: function() {
     this.runtimeList.usb = [];
     for (let id of Devices.available()) {
       this.runtimeList.usb.push(new USBRuntime(id));
     }
     this.update("runtimelist");
   },
 
+  get isWiFiScanningEnabled() {
+    return Services.prefs.getBoolPref(WIFI_SCANNING_PREF);
+  },
+  scanForWiFiRuntimes: function() {
+    if (!this.isWiFiScanningEnabled) {
+      return;
+    }
+    discovery.scan();
+  },
+  trackWiFiRuntimes: function() {
+    if (!this.isWiFiScanningEnabled) {
+      return;
+    }
+    this._updateWiFiRuntimes = this._updateWiFiRuntimes.bind(this);
+    discovery.on("devtools-device-added", this._updateWiFiRuntimes);
+    discovery.on("devtools-device-updated", this._updateWiFiRuntimes);
+    discovery.on("devtools-device-removed", this._updateWiFiRuntimes);
+  },
+  untrackWiFiRuntimes: function() {
+    if (!this.isWiFiScanningEnabled) {
+      return;
+    }
+    discovery.off("devtools-device-added", this._updateWiFiRuntimes);
+    discovery.off("devtools-device-updated", this._updateWiFiRuntimes);
+    discovery.off("devtools-device-removed", this._updateWiFiRuntimes);
+  },
+  _updateWiFiRuntimes: function() {
+    this.runtimeList.wifi = [];
+    for (let device of discovery.getRemoteDevicesWithService("devtools")) {
+      this.runtimeList.wifi.push(new WiFiRuntime(device));
+    }
+    this.update("runtimelist");
+  },
+
   trackSimulatorRuntimes: function() {
     this._updateSimulatorRuntimes = this._updateSimulatorRuntimes.bind(this);
     Simulator.on("register", this._updateSimulatorRuntimes);
     Simulator.on("unregister", this._updateSimulatorRuntimes);
     this._updateSimulatorRuntimes();
   },
   untrackSimulatorRuntimes: function() {
     Simulator.off("register", this._updateSimulatorRuntimes);
--- a/browser/devtools/webide/modules/runtimes.js
+++ b/browser/devtools/webide/modules/runtimes.js
@@ -3,53 +3,77 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {Cu} = require("chrome");
 const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
 const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
 const {DebuggerServer} = require("resource://gre/modules/devtools/dbg-server.jsm");
+const discovery = require("devtools/toolkit/discovery/discovery");
 
 const Strings = Services.strings.createBundle("chrome://webide/content/webide.properties");
 
 function USBRuntime(id) {
   this.id = id;
 }
 
 USBRuntime.prototype = {
   connect: function(connection) {
     let device = Devices.getByName(this.id);
     if (!device) {
-      return promise.reject("Can't find device: " + id);
+      return promise.reject("Can't find device: " + this.getName());
     }
     return device.connect().then((port) => {
       connection.host = "localhost";
       connection.port = port;
       connection.connect();
     });
   },
   getID: function() {
     return this.id;
   },
   getName: function() {
     return this.id;
   },
 }
 
+function WiFiRuntime(deviceName) {
+  this.deviceName = deviceName;
+}
+
+WiFiRuntime.prototype = {
+  connect: function(connection) {
+    let service = discovery.getRemoteService("devtools", this.deviceName);
+    if (!service) {
+      return promise.reject("Can't find device: " + this.getName());
+    }
+    connection.host = service.host;
+    connection.port = service.port;
+    connection.connect();
+    return promise.resolve();
+  },
+  getID: function() {
+    return this.deviceName;
+  },
+  getName: function() {
+    return this.deviceName;
+  },
+}
+
 function SimulatorRuntime(version) {
   this.version = version;
 }
 
 SimulatorRuntime.prototype = {
   connect: function(connection) {
     let port = ConnectionManager.getFreeTCPPort();
     let simulator = Simulator.getByVersion(this.version);
     if (!simulator || !simulator.launch) {
-      return promise.reject("Can't find simulator: " + this.version);
+      return promise.reject("Can't find simulator: " + this.getName());
     }
     return simulator.launch({port: port}).then(() => {
       connection.port = port;
       connection.keepConnecting = true;
       connection.connect();
     });
   },
   getID: function() {
@@ -97,11 +121,12 @@ let gRemoteRuntime = {
     return promise.resolve();
   },
   getName: function() {
     return Strings.GetStringFromName("remote_runtime");
   },
 }
 
 exports.USBRuntime = USBRuntime;
+exports.WiFiRuntime = WiFiRuntime;
 exports.SimulatorRuntime = SimulatorRuntime;
 exports.gRemoteRuntime = gRemoteRuntime;
 exports.gLocalRuntime = gLocalRuntime;
--- a/browser/devtools/webide/themes/webide.css
+++ b/browser/devtools/webide/themes/webide.css
@@ -182,24 +182,26 @@ panel > .panel-arrowcontainer > .panel-a
 
 #runtime-panel-custom {
   margin-bottom: 12px;
 }
 
 #runtime-permissions,
 #runtime-screenshot,
 .runtime-panel-item-usb,
+.runtime-panel-item-wifi,
 .runtime-panel-item-custom,
 .runtime-panel-item-simulator {
   list-style-image: url("icons.png");
 }
 
 #runtime-screenshot             { -moz-image-region: rect(200px, 640px, 240px, 600px) }
 #runtime-permissions            { -moz-image-region: rect(100px, 840px, 140px, 800px) }
 .runtime-panel-item-usb         { -moz-image-region: rect(100px, 640px, 140px, 600px) }
+.runtime-panel-item-wifi        { -moz-image-region: rect(100px, 640px, 140px, 600px) }
 .runtime-panel-item-custom      { -moz-image-region: rect(100px, 640px, 140px, 600px) }
 .runtime-panel-item-simulator   { -moz-image-region: rect(100px, 740px, 140px, 700px) }
 
 #runtime-actions {
   border-top: 1px solid rgba(221,221,221,1);
 }
 
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -635,16 +635,18 @@ pref("devtools.debugger.forbid-certified
 // DevTools default color unit
 pref("devtools.defaultColorUnit", "hex");
 
 // Used for devtools debugging
 pref("devtools.dump.emit", false);
 
 // Disable device discovery logging
 pref("devtools.discovery.log", false);
+// Disable scanning for DevTools devices via WiFi
+pref("devtools.remote.wifi.scan", false);
 
 // view source
 pref("view_source.syntax_highlight", true);
 pref("view_source.wrap_long_lines", false);
 pref("view_source.editor.external", false);
 pref("view_source.editor.path", "");
 // allows to add further arguments to the editor; use the %LINE% placeholder
 // for jumping to a specific line (e.g. "/line:%LINE%" or "--goto %LINE%")