Bug 1257873 - Split Addons and Workers into separate Target classes in about:debugging. r=ochameau
authorJan Keromnes <janx@linux.com>
Fri, 18 Mar 2016 09:08:00 -0400
changeset 313368 216d9ae88a315bed4961b121fb96f930411cf4b2
parent 313367 cd5db0d77f5354aa285484d844e5f216b5f9db52
child 313369 3be9cf3907ad5bbcb0b5cde7dfca5597b4b8096e
push id9480
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 17:12:58 +0000
treeherdermozilla-aurora@0d6a91c76a9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau
bugs1257873
milestone48.0a1
Bug 1257873 - Split Addons and Workers into separate Target classes in about:debugging. r=ochameau
devtools/client/aboutdebugging/components/aboutdebugging.js
devtools/client/aboutdebugging/components/addon-target.js
devtools/client/aboutdebugging/components/addons-controls.js
devtools/client/aboutdebugging/components/addons-tab.js
devtools/client/aboutdebugging/components/moz.build
devtools/client/aboutdebugging/components/target-list.js
devtools/client/aboutdebugging/components/target.js
devtools/client/aboutdebugging/components/worker-target.js
devtools/client/aboutdebugging/components/workers-tab.js
devtools/client/aboutdebugging/initializer.js
--- a/devtools/client/aboutdebugging/components/aboutdebugging.js
+++ b/devtools/client/aboutdebugging/components/aboutdebugging.js
@@ -1,22 +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/. */
 
 /* eslint-env browser */
+/* globals AddonsTab, WorkersTab */
 
 "use strict";
 
+const { createFactory, createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
 const Services = require("Services");
 
-const { createFactory, createClass, DOM: dom } =
-  require("devtools/client/shared/vendor/react");
+const TabMenu = createFactory(require("./tab-menu"));
 
-const TabMenu = createFactory(require("./tab-menu"));
 loader.lazyGetter(this, "AddonsTab",
   () => createFactory(require("./addons-tab")));
 loader.lazyGetter(this, "WorkersTab",
   () => createFactory(require("./workers-tab")));
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
@@ -60,18 +61,19 @@ module.exports = createClass({
     let { selectedTabId } = this.state;
     let selectTab = this.selectTab;
 
     let selectedTab = tabs.find(t => t.id == selectedTabId);
 
     return dom.div({ className: "app" },
       TabMenu({ tabs, selectedTabId, selectTab }),
       dom.div({ className: "main-content" },
-        selectedTab.component({ client }))
-      );
+        selectedTab.component({ client })
+      )
+    );
   },
 
   onHashChange() {
     let tabId = window.location.hash.substr(1);
 
     let isValid = tabs.some(t => t.id == tabId);
     if (isValid) {
       this.setState({ selectedTabId: tabId });
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/components/addon-target.js
@@ -0,0 +1,47 @@
+/* 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/. */
+
+/* eslint-env browser */
+/* globals BrowserToolboxProcess */
+
+"use strict";
+
+loader.lazyImporter(this, "BrowserToolboxProcess",
+  "resource://devtools/client/framework/ToolboxProcess.jsm");
+
+const { createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
+const Services = require("Services");
+
+const Strings = Services.strings.createBundle(
+  "chrome://devtools/locale/aboutdebugging.properties");
+
+module.exports = createClass({
+  displayName: "AddonTarget",
+
+  render() {
+    let { target, debugDisabled } = this.props;
+
+    return dom.div({ className: "target" },
+      dom.img({
+        className: "target-icon",
+        role: "presentation",
+        src: target.icon
+      }),
+      dom.div({ className: "target-details" },
+        dom.div({ className: "target-name" }, target.name)
+      ),
+      dom.button({
+        className: "debug-button",
+        onClick: this.debug,
+        disabled: debugDisabled,
+      }, Strings.GetStringFromName("debug"))
+    );
+  },
+
+  debug() {
+    let { target } = this.props;
+    BrowserToolboxProcess.init({ addonID: target.addonID });
+  },
+});
--- a/devtools/client/aboutdebugging/components/addons-controls.js
+++ b/devtools/client/aboutdebugging/components/addons-controls.js
@@ -1,24 +1,25 @@
 /* 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/. */
 
 /* eslint-env browser */
+/* globals AddonManager */
 
 "use strict";
 
 loader.lazyImporter(this, "AddonManager",
   "resource://gre/modules/AddonManager.jsm");
 
 const { Cc, Ci } = require("chrome");
+const { createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
 const Services = require("Services");
 
-const { createClass, DOM: dom } = require("devtools/client/shared/vendor/react");
-
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" +
                       "/about:debugging#Enabling_add-on_debugging";
 
 module.exports = createClass({
   displayName: "AddonsControls",
--- a/devtools/client/aboutdebugging/components/addons-tab.js
+++ b/devtools/client/aboutdebugging/components/addons-tab.js
@@ -1,27 +1,28 @@
 /* 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 { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
+const { createFactory, createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
 const Services = require("Services");
 
-const { createFactory, createClass, DOM: dom } =
-  require("devtools/client/shared/vendor/react");
 const AddonsControls = createFactory(require("./addons-controls"));
+const AddonTarget = createFactory(require("./addon-target"));
 const TabHeader = createFactory(require("./tab-header"));
 const TargetList = createFactory(require("./target-list"));
 
-const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
+const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 const CHROME_ENABLED_PREF = "devtools.chrome.enabled";
 const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled";
 
 module.exports = createClass({
   displayName: "AddonsTab",
 
   getInitialState() {
     return {
@@ -49,48 +50,52 @@ module.exports = createClass({
     Services.prefs.removeObserver(REMOTE_ENABLED_PREF,
       this.updateDebugStatus);
   },
 
   render() {
     let { client } = this.props;
     let { debugDisabled, extensions: targets } = this.state;
     let name = Strings.GetStringFromName("extensions");
+    let targetClass = AddonTarget;
 
     return dom.div({
       id: "tab-addons",
       className: "tab",
       role: "tabpanel",
-      "aria-labelledby": "tab-addons-header-name" },
+      "aria-labelledby": "tab-addons-header-name"
+    },
     TabHeader({
       id: "tab-addons-header-name",
-      name: Strings.GetStringFromName("addons") }),
+      name: Strings.GetStringFromName("addons")
+    }),
     AddonsControls({ debugDisabled }),
     dom.div({ id: "addons" },
-      TargetList({ name, targets, client, debugDisabled })));
+      TargetList({ name, targets, client, debugDisabled, targetClass })
+    ));
   },
 
   updateDebugStatus() {
     let debugDisabled =
       !Services.prefs.getBoolPref(CHROME_ENABLED_PREF) ||
       !Services.prefs.getBoolPref(REMOTE_ENABLED_PREF);
 
     this.setState({ debugDisabled });
   },
 
   updateAddonsList() {
     AddonManager.getAllAddons(addons => {
       let extensions = addons.filter(addon => addon.isDebuggable).map(addon => {
         return {
           name: addon.name,
           icon: addon.iconURL || ExtensionIcon,
-          type: addon.type,
           addonID: addon.id
         };
       });
+
       this.setState({ extensions });
     });
   },
 
   /**
    * Mandatory callback as AddonManager listener.
    */
   onInstalled() {
--- a/devtools/client/aboutdebugging/components/moz.build
+++ b/devtools/client/aboutdebugging/components/moz.build
@@ -1,15 +1,16 @@
 # 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/.
 
 DevToolsModules(
     'aboutdebugging.js',
+    'addon-target.js',
     'addons-controls.js',
     'addons-tab.js',
     'tab-header.js',
     'tab-menu-entry.js',
     'tab-menu.js',
     'target-list.js',
-    'target.js',
+    'worker-target.js',
     'workers-tab.js',
 )
--- a/devtools/client/aboutdebugging/components/target-list.js
+++ b/devtools/client/aboutdebugging/components/target-list.js
@@ -1,38 +1,34 @@
 /* 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 { createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
 const Services = require("Services");
 
-const { createClass, createFactory, DOM: dom } =
-  require("devtools/client/shared/vendor/react");
-const Target = createFactory(require("./target"));
-
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const LocaleCompare = (a, b) => {
   return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
 };
 
 module.exports = createClass({
   displayName: "TargetList",
 
   render() {
-    let { client, debugDisabled } = this.props;
+    let { client, debugDisabled, targetClass } = this.props;
     let targets = this.props.targets.sort(LocaleCompare).map(target => {
-      return Target({ client, target, debugDisabled });
+      return targetClass({ client, target, debugDisabled });
     });
 
-    return (
-      dom.div({ id: this.props.id, className: "targets" },
-        dom.h4(null, this.props.name),
-        targets.length > 0 ?
-          targets :
-          dom.p(null, Strings.GetStringFromName("nothing"))
-      )
+    return dom.div({ id: this.props.id, className: "targets" },
+      dom.h4(null, this.props.name),
+      targets.length > 0 ?
+        targets :
+        dom.p(null, Strings.GetStringFromName("nothing"))
     );
   },
 });
rename from devtools/client/aboutdebugging/components/target.js
rename to devtools/client/aboutdebugging/components/worker-target.js
--- a/devtools/client/aboutdebugging/components/target.js
+++ b/devtools/client/aboutdebugging/components/worker-target.js
@@ -1,115 +1,111 @@
 /* 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/. */
 
 /* eslint-env browser */
+/* globals gDevTools, TargetFactory, Toolbox */
 
 "use strict";
 
+loader.lazyRequireGetter(this, "gDevTools",
+  "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "TargetFactory",
-      "devtools/client/framework/target", true);
-loader.lazyRequireGetter(this, "gDevTools",
-      "devtools/client/framework/devtools", true);
+  "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "Toolbox",
-      "devtools/client/framework/toolbox", true);
+  "devtools/client/framework/toolbox", true);
 
-loader.lazyImporter(this, "BrowserToolboxProcess",
-      "resource://devtools/client/framework/ToolboxProcess.jsm");
-
-const Services = require("Services");
 const { createClass, DOM: dom } =
   require("devtools/client/shared/vendor/react");
+const Services = require("Services");
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 module.exports = createClass({
-  displayName: "Target",
+  displayName: "WorkerTarget",
 
   render() {
     let { target, debugDisabled } = this.props;
-    let isServiceWorker = (target.type === "serviceworker");
-    let isRunning = (!isServiceWorker || target.workerActor);
+    let isRunning = this.isRunning();
+    let isServiceWorker = this.isServiceWorker();
+
     return dom.div({ className: "target" },
       dom.img({
         className: "target-icon",
         role: "presentation",
-        src: target.icon }),
+        src: target.icon
+      }),
       dom.div({ className: "target-details" },
         dom.div({ className: "target-name" }, target.name)
       ),
       (isRunning && isServiceWorker ?
         dom.button({
           className: "push-button",
           onClick: this.push
         }, Strings.GetStringFromName("push")) :
         null
       ),
       (isRunning ?
         dom.button({
           className: "debug-button",
           onClick: this.debug,
-          disabled: debugDisabled,
+          disabled: debugDisabled
         }, Strings.GetStringFromName("debug")) :
         dom.button({
           className: "start-button",
           onClick: this.start
         }, Strings.GetStringFromName("start"))
       )
     );
   },
 
   debug() {
-    let { target } = this.props;
-    switch (target.type) {
-      case "extension":
-        BrowserToolboxProcess.init({ addonID: target.addonID });
-        break;
-      case "serviceworker":
-        if (target.workerActor) {
-          this.openWorkerToolbox(target.workerActor);
-        }
-        break;
-      case "sharedworker":
-        this.openWorkerToolbox(target.workerActor);
-        break;
-      case "worker":
-        this.openWorkerToolbox(target.workerActor);
-        break;
-      default:
-        window.alert("Not implemented yet!");
-        break;
+    let { client, target } = this.props;
+    if (!this.isRunning()) {
+      // If the worker is not running, we can't debug it.
+      return;
     }
-  },
-
-  push() {
-    let { client, target } = this.props;
-    if (target.workerActor) {
-      client.request({
-        to: target.workerActor,
-        type: "push"
-      });
-    }
-  },
-
-  start() {
-    let { client, target } = this.props;
-    if (target.type === "serviceworker" && !target.workerActor) {
-      client.request({
-        to: target.registrationActor,
-        type: "start"
-      });
-    }
-  },
-
-  openWorkerToolbox(workerActor) {
-    let { client } = this.props;
-    client.attachWorker(workerActor, (response, workerClient) => {
-      gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
-        "jsdebugger", Toolbox.HostType.WINDOW)
+    client.attachWorker(target.workerActor, (response, workerClient) => {
+      let workerTarget = TargetFactory.forWorker(workerClient);
+      gDevTools.showToolbox(workerTarget, "jsdebugger", Toolbox.HostType.WINDOW)
         .then(toolbox => {
           toolbox.once("destroy", () => workerClient.detach());
         });
     });
   },
+
+  push() {
+    let { client, target } = this.props;
+    if (!this.isRunning()) {
+      // If the worker is not running, we can't push to it.
+      return;
+    }
+    client.request({
+      to: target.workerActor,
+      type: "push"
+    });
+  },
+
+  start() {
+    let { client, target } = this.props;
+    if (!this.isServiceWorker() || this.isRunning()) {
+      // Either the worker is already running, or it's not a service worker, in
+      // which case we don't know how to start it.
+      return;
+    }
+    client.request({
+      to: target.registrationActor,
+      type: "start"
+    });
+  },
+
+  isRunning() {
+    // We know the target is running if it has a worker actor.
+    return !!this.props.target.workerActor;
+  },
+
+  isServiceWorker() {
+    // We know the target is a service worker if it has a registration actor.
+    return !!this.props.target.registrationActor;
+  },
 });
--- a/devtools/client/aboutdebugging/components/workers-tab.js
+++ b/devtools/client/aboutdebugging/components/workers-tab.js
@@ -1,25 +1,27 @@
 /* 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 { Ci } = require("chrome");
+const { createClass, createFactory, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
 const { Task } = require("resource://gre/modules/Task.jsm");
 const Services = require("Services");
 
-const { createClass, createFactory, DOM: dom } =
-  require("devtools/client/shared/vendor/react");
 const TabHeader = createFactory(require("./tab-header"));
 const TargetList = createFactory(require("./target-list"));
+const WorkerTarget = createFactory(require("./worker-target"));
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
+
 const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
 
 module.exports = createClass({
   displayName: "WorkersTab",
 
   getInitialState() {
     return {
       workers: {
@@ -43,61 +45,69 @@ module.exports = createClass({
     client.removeListener("processListChanged", this.update);
     client.removeListener("serviceWorkerRegistrationListChanged", this.update);
     client.removeListener("workerListChanged", this.update);
   },
 
   render() {
     let { client } = this.props;
     let { workers } = this.state;
+    let targetClass = WorkerTarget;
 
     return dom.div({
       id: "tab-workers",
       className: "tab",
       role: "tabpanel",
-      "aria-labelledby": "tab-workers-header-name" },
+      "aria-labelledby": "tab-workers-header-name"
+    },
     TabHeader({
       id: "tab-workers-header-name",
-      name: Strings.GetStringFromName("workers") }),
+      name: Strings.GetStringFromName("workers")
+    }),
     dom.div({ id: "workers", className: "inverted-icons" },
       TargetList({
+        client,
         id: "service-workers",
         name: Strings.GetStringFromName("serviceWorkers"),
-        targets: workers.service,
-        client }),
+        targetClass,
+        targets: workers.service
+      }),
       TargetList({
+        client,
         id: "shared-workers",
         name: Strings.GetStringFromName("sharedWorkers"),
-        targets: workers.shared,
-        client }),
+        targetClass,
+        targets: workers.shared
+      }),
       TargetList({
+        client,
         id: "other-workers",
         name: Strings.GetStringFromName("otherWorkers"),
-        targets: workers.other,
-        client })));
+        targetClass,
+        targets: workers.other
+      })
+    ));
   },
 
   update() {
     let workers = this.getInitialState().workers;
 
     this.getWorkerForms().then(forms => {
       forms.registrations.forEach(form => {
         workers.service.push({
-          type: "serviceworker",
           icon: WorkerIcon,
           name: form.url,
           url: form.url,
           scope: form.scope,
           registrationActor: form.actor
         });
       });
 
       forms.workers.forEach(form => {
         let worker = {
-          type: "worker",
           icon: WorkerIcon,
           name: form.url,
           url: form.url,
           workerActor: form.actor
         };
         switch (form.type) {
           case Ci.nsIWorkerDebugger.TYPE_SERVICE:
             for (let registration of workers.service) {
@@ -108,17 +118,16 @@ module.exports = createClass({
                   registration.name = registration.url = form.url;
                 }
                 registration.workerActor = form.actor;
                 break;
               }
             }
             break;
           case Ci.nsIWorkerDebugger.TYPE_SHARED:
-            worker.type = "sharedworker";
             workers.shared.push(worker);
             break;
           default:
             workers.other.push(worker);
         }
       });
 
       // XXX: Filter out the service worker registrations for which we couldn't
--- a/devtools/client/aboutdebugging/initializer.js
+++ b/devtools/client/aboutdebugging/initializer.js
@@ -1,46 +1,47 @@
 /* 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/. */
 
 /* eslint-env browser */
+/* globals DebuggerClient, DebuggerServer, Telemetry */
 
 "use strict";
 
 const { loader } = Components.utils.import(
   "resource://devtools/shared/Loader.jsm", {});
+const { BrowserLoader } = Components.utils.import(
+  "resource://devtools/client/shared/browser-loader.js", {});
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "DebuggerServer",
   "devtools/server/main", true);
 loader.lazyRequireGetter(this, "Telemetry",
   "devtools/client/shared/telemetry");
 
-const { BrowserLoader } = Components.utils.import(
-  "resource://devtools/client/shared/browser-loader.js", {});
 const { require } = BrowserLoader({
   baseURI: "resource://devtools/client/aboutdebugging/",
   window
 });
 
-const {
-  createFactory,
-  render,
-  unmountComponentAtNode } = require("devtools/client/shared/vendor/react");
+const { createFactory, render, unmountComponentAtNode } =
+  require("devtools/client/shared/vendor/react");
+
 const AboutDebuggingApp = createFactory(require("./components/aboutdebugging"));
 
 var AboutDebugging = {
   init() {
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
       DebuggerServer.addBrowserActors();
     }
     DebuggerServer.allowChromeProcess = true;
+
     this.client = new DebuggerClient(DebuggerServer.connectPipe());
 
     this.client.connect().then(() => {
       let client = this.client;
       let telemetry = new Telemetry();
 
       render(AboutDebuggingApp({ client, telemetry }),
         document.querySelector("#body"));