Bug 1471795 - Part 15: Implement worker list. r?jdescottes, r?ladybenko draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Thu, 19 Jul 2018 18:38:12 +0900
changeset 820264 18422f41f9f6667e3c959f80f335e7275a127283
parent 820263 020e51245bb7c04bae1190d31764bd2732ed52d3
child 820265 5e9a780dc74ccca49c16a66d2999d96e1237beac
push id116772
push userbmo:dakatsuka@mozilla.com
push dateThu, 19 Jul 2018 09:51:56 +0000
reviewersjdescottes, ladybenko
bugs1471795
milestone63.0a1
Bug 1471795 - Part 15: Implement worker list. r?jdescottes, r?ladybenko MozReview-Commit-ID: EIGrBTAHlwl
devtools/client/aboutdebugging-new/aboutdebugging.css
devtools/client/aboutdebugging-new/src/components/DebugTargetsPane.js
devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerItem.css
devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerItem.js
devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerItem.js
devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
devtools/client/aboutdebugging-new/src/runtimes/runtime.js
devtools/client/aboutdebugging-new/src/runtimes/this-firefox.js
--- a/devtools/client/aboutdebugging-new/aboutdebugging.css
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.css
@@ -6,15 +6,16 @@
 @import "resource://devtools/client/themes/variables.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/DebugTargetsPane.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/RuntimesPane.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionItem.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerItem.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/TemporaryExtensionInstaller.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/runtime/ThisFirefoxItem.css";
 
 #mount {
   height: 100%;
   width: 100%;
 }
--- a/devtools/client/aboutdebugging-new/src/components/DebugTargetsPane.js
+++ b/devtools/client/aboutdebugging-new/src/components/DebugTargetsPane.js
@@ -8,19 +8,21 @@ const { createFactory, PureComponent } =
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const Runtime = require("../runtimes/runtime");
 
 const DebugTargetList = createFactory(require("./debugtarget/DebugTargetList"));
 const ExtensionItem = createFactory(require("./debugtarget/ExtensionItem"));
 const RuntimeInfo = createFactory(require("./RuntimeInfo"));
+const ServiceWorkerItem = createFactory(require("./debugtarget/ServiceWorkerItem"));
 const TabItem = createFactory(require("./debugtarget/TabItem"));
 const TemporaryExtensionInstaller =
   createFactory(require("./debugtarget/TemporaryExtensionInstaller"));
+const WorkerItem = createFactory(require("./debugtarget/WorkerItem"));
 
 class DebugTargetsPane extends PureComponent {
   static get propTypes() {
     return {
       runtime: PropTypes.instanceOf(Runtime).isRequired,
     };
   }
 
@@ -28,18 +30,21 @@ class DebugTargetsPane extends PureCompo
     super(props);
 
     this.onExtensionsUpdated = this.onExtensionsUpdated.bind(this);
     this.onTabsUpdated = this.onTabsUpdated.bind(this);
 
     this.state = {
       info: {},
       installedExtensions: [],
+      otherWorkers: [],
       tabs: [],
       temporaryExtensions: [],
+      serviceWorkers: [],
+      sharedWorkers: [],
     };
     this.update(props.runtime);
   }
 
   componentDidUpdate(prevProps) {
     if (prevProps.runtime !== this.props.runtime) {
       const { runtime } = prevProps;
       runtime.removeExtensionsUpdateListener(this.onExtensionsUpdated);
@@ -53,16 +58,17 @@ class DebugTargetsPane extends PureCompo
     runtime.removeExtensionsUpdateListener(this.onExtensionsUpdated);
     runtime.removeTabsUpdateListener(this.onTabsUpdated);
   }
 
   update(runtime) {
     this.updateRuntimeInfo(runtime);
     this.updateTabs(runtime);
     this.updateExtensions(runtime);
+    this.updateWorkers(runtime);
 
     runtime.addExtensionsUpdateListener(this.onExtensionsUpdated);
     runtime.addTabsUpdateListener(this.onTabsUpdated);
   }
 
   async updateExtensions(runtime) {
     const extensions = (await runtime.getExtensions()).filter(t => t.debuggable);
     const installedExtensions = extensions.filter(t => !t.temporarilyInstalled);
@@ -76,31 +82,43 @@ class DebugTargetsPane extends PureCompo
   }
 
   async updateTabs(runtime) {
     // Filter out closed tabs (represented as `null`).
     const tabs = (await runtime.getTabs()).filter(t => !!t);
     this.setState({ tabs });
   }
 
+  async updateWorkers(runtime) {
+    const {
+      other: otherWorkers,
+      service: serviceWorkers,
+      shared: sharedWorkers,
+    } = await runtime.getWorkers();
+    this.setState({ otherWorkers, serviceWorkers, sharedWorkers });
+  }
+
   onExtensionsUpdated() {
     this.updateExtensions(this.props.runtime);
   }
 
   onTabsUpdated() {
     this.updateTabs(this.props.runtime);
   }
 
   render() {
     const { runtime } = this.props;
     const {
       info,
       installedExtensions,
+      otherWorkers,
       tabs,
       temporaryExtensions,
+      serviceWorkers,
+      sharedWorkers,
     } = this.state;
 
     return dom.div(
       {
         className: "debug-targets-pane",
       },
       RuntimeInfo({ info }),
       DebugTargetList({
@@ -119,14 +137,33 @@ class DebugTargetsPane extends PureCompo
         title: "Extensions",
       }),
       DebugTargetList({
         className: "debug-target-list--tabs",
         debugTargetItemComponent: TabItem,
         debugTargets: tabs,
         runtime,
         title: "Tabs",
+      }),
+      DebugTargetList({
+        className: "debug-target-list--service-workers",
+        debugTargetItemComponent: ServiceWorkerItem,
+        debugTargets: serviceWorkers,
+        runtime,
+        title: "Service Workers",
+      }),
+      DebugTargetList({
+        className: "debug-target-list--shared-workers",
+        debugTargetItemComponent: WorkerItem,
+        debugTargets: sharedWorkers,
+        title: "Shared Workers",
+      }),
+      DebugTargetList({
+        className: "debug-target-list--other-workers",
+        debugTargetItemComponent: WorkerItem,
+        debugTargets: otherWorkers,
+        title: "Other Workers",
       })
     );
   }
 }
 
 module.exports = DebugTargetsPane;
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerItem.css
@@ -0,0 +1,41 @@
+/* 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/. */
+
+.debug-target-item__info__detail__service-worker__info {
+  display: grid;
+  grid-template-columns: 100px 1fr;
+  margin-block-start: 4px;
+}
+
+.debug-target-item__info__detail__service-worker__info__label {
+}
+
+.debug-target-item__info__detail__service-worker__info__content {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.debug-target-item__info__detail__service-worker__status {
+  border-style: solid;
+  border-width: 1px;
+  box-sizing: border-box;
+  display: inline-block;
+  font-size: 10px;
+  margin-block-start: 6px;
+  min-width: 50px;
+  padding-block-start: 2px;
+  padding-block-end: 2px;
+  text-align: center;
+}
+
+.debug-target-item__info__detail__service-worker__status--running {
+  border-color: limegreen;
+  background-color: palegreen;
+}
+
+.debug-target-item__info__detail__service-worker__status--stopped {
+  border-color: grey;
+  background-color: lightgrey;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerItem.js
@@ -0,0 +1,159 @@
+/* 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 dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+const WorkerItem = require("./WorkerItem");
+
+class ServiceWorkerItem extends WorkerItem {
+  constructor(props) {
+    super(props);
+    this.state = {
+      pushSubscription: null,
+    };
+    this.updateState(props);
+  }
+
+  componentDidUpdate(prevProps) {
+    if (prevProps.runtime !== this.props.runtime ||
+        prevProps.debugTarget !== this.props.debugTarget) {
+      this.updateState(this.props);
+    }
+  }
+
+  getStatus() {
+    if (this.isActive() && this.isRunning()) {
+      return "running";
+    } else if (this.isActive()) {
+      return "stopped";
+    }
+    // We cannot get service worker registrations unless the registration is in
+    // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we
+    // display a custom state "registering" for now. See Bug 1153292.
+    return "registering";
+  }
+
+  isRunning() {
+    const { debugTarget } = this.props;
+    // We know the target is running if it has a worker actor.
+    return !!debugTarget.workerTargetActor;
+  }
+
+  isActive() {
+    const { debugTarget } = this.props;
+    return debugTarget.active;
+  }
+
+  renderActionComponents() {
+    return null;
+  }
+
+  renderDetailComponents() {
+    return [
+      dom.div(
+        {
+          className: "debug-target-item__info__detail__service-worker__info",
+        },
+        this.renderFetch(),
+        this.renderScope(),
+      ),
+      this.renderStatus(),
+    ];
+  }
+
+  renderFetch() {
+    const { debugTarget } = this.props;
+    const fetch = debugTarget.fetch ? "Listening for fetch events."
+                                    : "Not listening for fetch events.";
+
+    return [
+      dom.dt(
+        {
+          className: "debug-target-item__info__detail__service-worker__info__label",
+        },
+        "Fetch"
+      ),
+      dom.dd(
+        {
+          className: "debug-target-item__info__detail__service-worker__info__content",
+          title: fetch,
+        },
+        fetch
+      ),
+    ];
+  }
+
+  renderPushService() {
+    const { pushSubscription } = this.state;
+
+    if (!pushSubscription) {
+      return null;
+    }
+
+    return [
+      dom.dt(
+        {
+          className: "debug-target-item__info__detail__service-worker__info__label",
+        },
+        "Push Service"
+      ),
+      dom.dd(
+        {
+          className: "debug-target-item__info__detail__service-worker__info__content",
+          title: pushSubscription.endpoint,
+        },
+        pushSubscription.endpoint
+      ),
+    ];
+  }
+
+  renderScope() {
+    const { debugTarget } = this.props;
+
+    return [
+      dom.dt(
+        {
+          className: "debug-target-item__info__detail__service-worker__info__label",
+        },
+        "Scope"
+      ),
+      dom.dd(
+        {
+          className: "debug-target-item__info__detail__service-worker__info__content",
+          title: debugTarget.scope,
+        },
+        debugTarget.scope
+      ),
+    ];
+  }
+
+  renderStatus() {
+    const status = this.getStatus();
+
+    return dom.div(
+      {
+        className: "debug-target-item__info__detail__service-worker__status " +
+                   `debug-target-item__info__detail__service-worker__status--${ status }`,
+      },
+      status
+    );
+  }
+
+  async updateState({ debugTarget, runtime }) {
+    if (!debugTarget) {
+      // A valid registrationActor is needed to retrieve the push subscription.
+      return;
+    }
+
+    const { subscription } = await runtime.sendRequest({
+      to: debugTarget.registrationActor,
+      type: "getPushSubscription",
+    });
+    this.setState({ pushSubscription: subscription });
+  }
+}
+
+module.exports = ServiceWorkerItem;
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerItem.js
@@ -0,0 +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 DebugTargetItem = require("./DebugTargetItem");
+
+class WorkerItem extends DebugTargetItem {
+  getIcon() {
+    return "chrome://devtools/skin/images/debugging-workers.svg";
+  }
+
+  getName() {
+    const { debugTarget } = this.props;
+    return debugTarget.name;
+  }
+
+  renderActionComponents() {
+    return null;
+  }
+
+  renderDetailComponents() {
+    return null;
+  }
+}
+
+module.exports = WorkerItem;
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
@@ -4,12 +4,15 @@
 
 DevToolsModules(
     'DebugTargetItem.css',
     'DebugTargetItem.js',
     'DebugTargetList.css',
     'DebugTargetList.js',
     'ExtensionItem.css',
     'ExtensionItem.js',
+    'ServiceWorkerItem.css',
+    'ServiceWorkerItem.js',
     'TabItem.js',
     'TemporaryExtensionInstaller.css',
-    'TemporaryExtensionInstaller.js'
+    'TemporaryExtensionInstaller.js',
+    'WorkerItem.js'
 )
--- a/devtools/client/aboutdebugging-new/src/runtimes/runtime.js
+++ b/devtools/client/aboutdebugging-new/src/runtimes/runtime.js
@@ -81,16 +81,25 @@ class Runtime {
    * Subclass should override this method.
    * @return {Array}
    */
   async getTabs() {
     throw new Error("Subclass of Runtime should override getTabs()");
   }
 
   /**
+   * Return workers on this runtime.
+   * Subclass should override this method.
+   * @return {Array}
+   */
+  async getWorkers() {
+    throw new Error("Subclass of Runtime should override getWorkers()");
+  }
+
+  /**
    * Inspect the provided extension target which can get by getExtensions().
    * Subclass should override this method.
    * @param {Object} - debug target
    */
   async inspectExtension(_) {
     throw new Error("Subclass of Runtime should override inspectExtension()");
   }
 
--- a/devtools/client/aboutdebugging-new/src/runtimes/this-firefox.js
+++ b/devtools/client/aboutdebugging-new/src/runtimes/this-firefox.js
@@ -67,16 +67,20 @@ class ThisFirefox extends Runtime {
     };
   }
 
   async getTabs() {
     const { tabs } = await this.client.listTabs({ favicons: true });
     return tabs;
   }
 
+  async getWorkers() {
+    return this.client.mainRoot.listAllWorkers();
+  }
+
   async inspectExtension(debugTarget) {
     // Close previous addon debugging toolbox.
     if (this.browserToolboxProcess) {
       this.browserToolboxProcess.close();
     }
 
     this.browserToolboxProcess = BrowserToolboxProcess.init({
       addonID: debugTarget.id,