Bug 993533: Add remote add-ons to the list of targets on a remote device. r=panos
authorDave Townsend <dtownsend@oxymoronical.com>
Thu, 17 Apr 2014 08:35:20 -0700
changeset 179064 67c1405cd110853a2664fe3365158cfd9648c17b
parent 179063 5a9d701731a1143118ad6258517545ab17615029
child 179065 9e163c842e3b74902725392cdaf15d9296bcaea9
push id26606
push userryanvm@gmail.com
push dateFri, 18 Apr 2014 02:20:00 +0000
treeherdermozilla-central@ec728bfdbb79 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspanos
bugs993533
milestone31.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 993533: Add remote add-ons to the list of targets on a remote device. r=panos
browser/devtools/framework/connect/connect.js
browser/devtools/framework/connect/connect.xhtml
browser/locales/en-US/chrome/browser/devtools/connection-screen.dtd
mobile/android/chrome/content/dbg-browser-actors.js
toolkit/devtools/server/actors/webbrowser.js
--- a/browser/devtools/framework/connect/connect.js
+++ b/browser/devtools/framework/connect/connect.js
@@ -4,19 +4,21 @@
  * 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 Cu = Components.utils;
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 let gClient;
 let gConnectionTimeout;
 
 XPCOMUtils.defineLazyGetter(window, 'l10n', function () {
   return Services.strings.createBundle('chrome://browser/locale/devtools/connection-screen.properties');
 });
 
@@ -70,69 +72,107 @@ function submit() {
   let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout");
   gConnectionTimeout = setTimeout(handleConnectionTimeout, delay);
   gClient.connect(onConnectionReady);
 }
 
 /**
  * Connection is ready. List actors and build buttons.
  */
-function onConnectionReady(aType, aTraits) {
+let onConnectionReady = Task.async(function*(aType, aTraits) {
   clearTimeout(gConnectionTimeout);
-  gClient.listTabs(function(aResponse) {
-    document.body.classList.remove("connecting");
-    document.body.classList.add("actors-mode");
 
-    let parent = document.getElementById("tabActors");
+  let deferred = promise.defer();
+  gClient.listAddons(deferred.resolve);
+  let response = yield deferred.promise;
 
-    // Add Global Process debugging...
-    let globals = JSON.parse(JSON.stringify(aResponse));
-    delete globals.tabs;
-    delete globals.selected;
-    // ...only if there are appropriate actors (a 'from' property will always
-    // be there).
+  let parent = document.getElementById("addonActors")
+  if (!response.error && response.addons.length > 0) {
+    // Add one entry for each add-on.
+    for (let addon of response.addons) {
+      if (!addon.debuggable) {
+        continue;
+      }
+      buildAddonLink(addon, parent);
+    }
+  }
+  else {
+    // Hide the section when there are no add-ons
+    parent.previousElementSibling.remove();
+    parent.remove();
+  }
 
-    // Add one entry for each open tab.
-    for (let i = 0; i < aResponse.tabs.length; i++) {
-      buildLink(aResponse.tabs[i], parent, i == aResponse.selected);
-    }
+  deferred = promise.defer();
+  gClient.listTabs(deferred.resolve);
+  response = yield deferred.promise;
+
+  parent = document.getElementById("tabActors");
 
-    let gParent = document.getElementById("globalActors");
+  // Add Global Process debugging...
+  let globals = JSON.parse(JSON.stringify(response));
+  delete globals.tabs;
+  delete globals.selected;
+  // ...only if there are appropriate actors (a 'from' property will always
+  // be there).
+
+  // Add one entry for each open tab.
+  for (let i = 0; i < response.tabs.length; i++) {
+    buildTabLink(response.tabs[i], parent, i == response.selected);
+  }
+
+  let gParent = document.getElementById("globalActors");
 
-    // Build the Remote Process button
-    if (Object.keys(globals).length > 1) {
-      let a = document.createElement("a");
-      a.onclick = function() {
-        openToolbox(globals, true);
+  // Build the Remote Process button
+  if (Object.keys(globals).length > 1) {
+    let a = document.createElement("a");
+    a.onclick = function() {
+      openToolbox(globals, true);
 
-      }
-      a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
-      a.className = "remote-process";
-      a.href = "#";
-      gParent.appendChild(a);
     }
-    // Move the selected tab on top
-    let selectedLink = parent.querySelector("a.selected");
-    if (selectedLink) {
-      parent.insertBefore(selectedLink, parent.firstChild);
-    }
+    a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
+    a.className = "remote-process";
+    a.href = "#";
+    gParent.appendChild(a);
+  }
+  // Move the selected tab on top
+  let selectedLink = parent.querySelector("a.selected");
+  if (selectedLink) {
+    parent.insertBefore(selectedLink, parent.firstChild);
+  }
+
+  document.body.classList.remove("connecting");
+  document.body.classList.add("actors-mode");
 
-    // Ensure the first link is focused
-    let firstLink = parent.querySelector("a:first-of-type");
-    if (firstLink) {
-      firstLink.focus();
-    }
+  // Ensure the first link is focused
+  let firstLink = parent.querySelector("a:first-of-type");
+  if (firstLink) {
+    firstLink.focus();
+  }
+});
 
-  });
+/**
+ * Build one button for an add-on actor.
+ */
+function buildAddonLink(addon, parent) {
+  let a = document.createElement("a");
+  a.onclick = function() {
+    openToolbox({ addonActor: addon.actor, title: addon.name }, true, "jsdebugger");
+  }
+
+  a.textContent = addon.name;
+  a.title = addon.id;
+  a.href = "#";
+
+  parent.appendChild(a);
 }
 
 /**
- * Build one button for an actor.
+ * Build one button for a tab actor.
  */
-function buildLink(tab, parent, selected) {
+function buildTabLink(tab, parent, selected) {
   let a = document.createElement("a");
   a.onclick = function() {
     openToolbox(tab);
   }
 
   a.textContent = tab.title;
   a.title = tab.url;
   if (!a.textContent) {
@@ -168,24 +208,24 @@ function showError(type) {
 function handleConnectionTimeout() {
   showError("timeout");
 }
 
 /**
  * The user clicked on one of the buttons.
  * Opens the toolbox.
  */
-function openToolbox(form, chrome=false) {
+function openToolbox(form, chrome=false, tool="webconsole") {
   let options = {
     form: form,
     client: gClient,
     chrome: chrome
   };
   devtools.TargetFactory.forRemoteTab(options).then((target) => {
     let hostType = devtools.Toolbox.HostType.WINDOW;
-    gDevTools.showToolbox(target, "webconsole", hostType).then((toolbox) => {
+    gDevTools.showToolbox(target, tool, hostType).then((toolbox) => {
       toolbox.once("destroyed", function() {
         gClient.close();
       });
     });
     window.close();
   });
 }
--- a/browser/devtools/framework/connect/connect.xhtml
+++ b/browser/devtools/framework/connect/connect.xhtml
@@ -34,16 +34,18 @@
       </form>
       <p class="error-message error-timeout">&errorTimeout;</p>
       <p class="error-message error-refused">&errorRefused;</p>
       <p class="error-message error-unexpected">&errorUnexpected;</p>
     </section>
     <section id="actors-list">
       <p>&availableTabs;</p>
       <ul class="actors" id="tabActors"></ul>
+      <p>&availableAddons;</p>
+      <ul class="actors" id="addonActors"></ul>
       <p>&availableProcesses;</p>
       <ul class="actors" id="globalActors"></ul>
     </section>
     <section id="connecting">
       <p><img src="chrome://browser/skin/tabbrowser/loading.png"></img> &connecting;</p>
     </section>
     <footer>&remoteHelp;<a target='_' href='https://developer.mozilla.org/docs/Tools/Remote_Debugging'>&remoteDocumentation;</a>&remoteHelpSuffix;</footer>
   </body>
--- a/browser/locales/en-US/chrome/browser/devtools/connection-screen.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/connection-screen.dtd
@@ -8,16 +8,17 @@
   - -->
 
 <!ENTITY title      "Connect">
 <!ENTITY header     "Connect to remote device">
 <!ENTITY host       "Host:">
 <!ENTITY port       "Port:">
 <!ENTITY connect    "Connect">
 <!ENTITY connecting "Connecting…">
+<!ENTITY availableAddons "Available remote add-ons:">
 <!ENTITY availableTabs "Available remote tabs:">
 <!ENTITY availableProcesses "Available remote processes:">
 <!ENTITY connectionError "Error:">
 <!ENTITY errorTimeout "Error: connection timeout.">
 <!ENTITY errorRefused "Error: connection refused.">
 <!ENTITY errorUnexpected "Unexpected error.">
 
 <!-- LOCALIZATION NOTE (remoteHelp, remoteDocumentation, remoteHelpSuffix):
--- a/mobile/android/chrome/content/dbg-browser-actors.js
+++ b/mobile/android/chrome/content/dbg-browser-actors.js
@@ -18,16 +18,17 @@
  *
  * * @param aConnection DebuggerServerConnection
  *        The conection to the client.
  */
 function createRootActor(aConnection)
 {
   let parameters = {
     tabList: new MobileTabList(aConnection),
+    addonList: new BrowserAddonList(aConnection),
     globalActorFactories: DebuggerServer.globalActorFactories,
     onShutdown: sendShutdownEvent
   };
   return new RootActor(aConnection, parameters);
 }
 
 /**
  * A live list of BrowserTabActors representing the current browser tabs,
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -1142,17 +1142,18 @@ BrowserAddonActor.prototype = {
 
   form: function BAA_form() {
     dbg_assert(this.actorID, "addon should have an actorID.");
 
     return {
       actor: this.actorID,
       id: this.id,
       name: this._addon.name,
-      url: this.url
+      url: this.url,
+      debuggable: this._addon.isDebuggable,
     };
   },
 
   disconnect: function BAA_disconnect() {
     this._addon = null;
     this._global = null;
     AddonManager.removeAddonListener(this);
   },