Bug 1477602 - Part 3: Implement UI for workers. r=ladybenko
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Fri, 17 Aug 2018 09:58:52 +0900
changeset 481596 3cab1552392997546f16cd0a087cbbeeb63716de
parent 481595 f6b8a0ca7823e20126c727c2add7c64afc98c275
child 481597 ab2638b0ed51338efd498612dfcf3c20df219588
push id232
push userfmarier@mozilla.com
push dateWed, 05 Sep 2018 20:45:54 +0000
reviewersladybenko
bugs1477602
milestone63.0a1
Bug 1477602 - Part 3: Implement UI for workers. r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D3754 Depends on D3571
devtools/client/aboutdebugging-new/aboutdebugging.css
devtools/client/aboutdebugging-new/src/components/RuntimePage.js
devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js
devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css
devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.js
devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
devtools/client/aboutdebugging-new/src/constants.js
devtools/client/aboutdebugging-new/src/reducers/runtime-state.js
--- a/devtools/client/aboutdebugging-new/aboutdebugging.css
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.css
@@ -6,16 +6,17 @@
 @import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/ConnectPage.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/Sidebar.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/SidebarItem.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/ExtensionDetail.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css";
 
 :root {
   /* Import css variables from common.css */
   --text-color: var(--in-content-page-color);
 }
 
 html, body {
   margin: 0;
--- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
@@ -60,29 +60,31 @@ class RuntimePage extends PureComponent 
         name: "Extensions",
         targets: installedExtensions,
       }),
       DebugTargetPane({
         dispatch,
         name: "Tabs",
         targets: tabs
       }),
-      // Temporary implementation
-      dom.ul(
-        {},
-        serviceWorkers.map(e => dom.li({}, e.name))
-      ),
-      dom.ul(
-        {},
-        sharedWorkers.map(e => dom.li({}, e.name))
-      ),
-      dom.ul(
-        {},
-        otherWorkers.map(e => dom.li({}, e.name))
-      ),
+      DebugTargetPane({
+        dispatch,
+        name: "Service Workers",
+        targets: serviceWorkers
+      }),
+      DebugTargetPane({
+        dispatch,
+        name: "Shared Workers",
+        targets: sharedWorkers
+      }),
+      DebugTargetPane({
+        dispatch,
+        name: "Other Workers",
+        targets: otherWorkers
+      }),
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
     installedExtensions: state.runtime.installedExtensions,
     otherWorkers: state.runtime.otherWorkers,
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js
@@ -6,16 +6,17 @@
 
 const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const ExtensionDetail = createFactory(require("./ExtensionDetail"));
 const TabDetail = createFactory(require("./TabDetail"));
 const TemporaryExtensionAction = createFactory(require("./TemporaryExtensionAction"));
+const WorkerDetail = createFactory(require("./WorkerDetail"));
 
 const Actions = require("../../actions/index");
 const { DEBUG_TARGETS } = require("../../constants");
 
 /**
  * This component displays debug target.
  */
 class DebugTargetItem extends PureComponent {
@@ -51,16 +52,18 @@ class DebugTargetItem extends PureCompon
   renderDetail() {
     const { target } = this.props;
 
     switch (target.type) {
       case DEBUG_TARGETS.EXTENSION:
         return ExtensionDetail({ target });
       case DEBUG_TARGETS.TAB:
         return TabDetail({ target });
+      case DEBUG_TARGETS.WORKER:
+        return WorkerDetail({ target });
 
       default:
         return null;
     }
   }
 
   renderIcon() {
     return dom.img({
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css
@@ -0,0 +1,52 @@
+/* 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/. */
+
+.worker-detail {
+  --worker-status-font-size: 10px;
+}
+
+/*
+ * The current layout of worker detail is
+ *
+ *  +----------------+--------------------+
+ *  | detail name dt | detail value dd    |
+ *  | (60px )        | (auto)             |
+ *  +----------------+--------------------+
+ *  | detail name dt | detail value dd    |
+ *  +----------------+--------------------+
+ *  | detail name dt | detail value dd    |
+ *  +----------------+--------------------+
+ */
+.worker-detail {
+  display: grid;
+  grid-template-columns: 60px auto;
+  margin-block-start: 4px;
+}
+
+/*
+ * worker-detail__status has a ui like badge and the color change by the status.
+ * For now, the background-color of running status is palegreen, stopped is lightgrey
+ * though, might be changed since this is not Photon color.
+ */
+.worker-detail__status {
+  border-style: solid;
+  border-width: 1px;
+  box-sizing: border-box;
+  display: inline-block;
+  font-size: var(--worker-status-font-size);
+  margin-block-start: 6px;
+  padding-block-start: 2px;
+  padding-block-end: 2px;
+  text-align: center;
+}
+
+.worker-detail__status--running {
+  border-color: limegreen;
+  background-color: palegreen;
+}
+
+.worker-detail__status--stopped {
+  border-color: grey;
+  background-color: lightgrey;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.js
@@ -0,0 +1,71 @@
+/* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+const {
+  SERVICE_WORKER_FETCH_STATES,
+} = require("../../constants");
+
+/**
+ * This component displays detail information for worker.
+ */
+class WorkerDetail extends PureComponent {
+  static get propTypes() {
+    return {
+      target: PropTypes.object.isRequired,
+    };
+  }
+
+  renderFetch() {
+    const { fetch } = this.props.target.details;
+    const label = fetch === SERVICE_WORKER_FETCH_STATES.LISTENING
+                    ? "Listening for fetch events"
+                    : "Not listening for fetch events";
+    return this.renderField("Fetch", label);
+  }
+
+  renderField(name, value) {
+    return [
+      dom.dt({}, name),
+      dom.dd(
+        {
+          className: "ellipsis-text",
+          title: value,
+        },
+        value,
+      ),
+    ];
+  }
+
+  renderStatus() {
+    const status = this.props.target.details.status.toLowerCase();
+
+    return dom.div(
+      {
+        className: `worker-detail__status worker-detail__status--${ status }`,
+      },
+      status
+    );
+  }
+
+  render() {
+    const { fetch, scope, status } = this.props.target.details;
+
+    return dom.dl(
+      {
+        className: "worker-detail",
+      },
+      fetch ? this.renderFetch() : null,
+      scope ? this.renderField("Scope", scope) : null,
+      status ? this.renderStatus() : null,
+    );
+  }
+}
+
+module.exports = WorkerDetail;
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
@@ -8,9 +8,11 @@ DevToolsModules(
     'DebugTargetList.css',
     'DebugTargetList.js',
     'DebugTargetPane.js',
     'ExtensionDetail.css',
     'ExtensionDetail.js',
     'TabDetail.js',
     'TemporaryExtensionAction.js',
     'TemporaryExtensionInstaller.js',
+    'WorkerDetail.css',
+    'WorkerDetail.js',
 )
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -22,20 +22,34 @@ const actionTypes = {
   REQUEST_WORKERS_FAILURE: "REQUEST_WORKERS_FAILURE",
   REQUEST_WORKERS_START: "REQUEST_WORKERS_START",
   REQUEST_WORKERS_SUCCESS: "REQUEST_WORKERS_SUCCESS",
 };
 
 const DEBUG_TARGETS = {
   EXTENSION: "EXTENSION",
   TAB: "TAB",
+  WORKER: "WORKER",
 };
 
 const PAGES = {
   THIS_FIREFOX: "this-firefox",
   CONNECT: "connect",
 };
 
+const SERVICE_WORKER_FETCH_STATES = {
+  LISTENING: "LISTENING",
+  NOT_LISTENING: "NOT_LISTENING",
+};
+
+const SERVICE_WORKER_STATUSES = {
+  RUNNING: "RUNNING",
+  REGISTERING: "REGISTERING",
+  STOPPED: "STOPPED",
+};
+
 // flatten constants
 module.exports = Object.assign({}, {
   DEBUG_TARGETS,
   PAGES,
+  SERVICE_WORKER_FETCH_STATES,
+  SERVICE_WORKER_STATUSES,
 }, actionTypes);
--- a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js
@@ -6,16 +6,18 @@
 
 const {
   CONNECT_RUNTIME_SUCCESS,
   DEBUG_TARGETS,
   DISCONNECT_RUNTIME_SUCCESS,
   REQUEST_EXTENSIONS_SUCCESS,
   REQUEST_TABS_SUCCESS,
   REQUEST_WORKERS_SUCCESS,
+  SERVICE_WORKER_FETCH_STATES,
+  SERVICE_WORKER_STATUSES,
 } = require("../constants");
 
 function RuntimeState() {
   return {
     client: null,
     installedExtensions: [],
     otherWorkers: [],
     serviceWorkers: [],
@@ -43,19 +45,19 @@ function runtimeReducer(state = RuntimeS
     }
     case REQUEST_TABS_SUCCESS: {
       const { tabs } = action;
       return Object.assign({}, state, { tabs: toTabComponentData(tabs) });
     }
     case REQUEST_WORKERS_SUCCESS: {
       const { otherWorkers, serviceWorkers, sharedWorkers } = action;
       return Object.assign({}, state, {
-        otherWorkers,
-        serviceWorkers,
-        sharedWorkers,
+        otherWorkers: toWorkerComponentData(otherWorkers),
+        serviceWorkers: toWorkerComponentData(serviceWorkers, true),
+        sharedWorkers: toWorkerComponentData(sharedWorkers),
       });
     }
 
     default:
       return state;
   }
 }
 
@@ -118,12 +120,50 @@ function toTabComponentData(tabs) {
       type,
       details: {
         url,
       },
     };
   });
 }
 
+function getServiceWorkerStatus(worker) {
+  if (worker.active && !!worker.workerTargetActor) {
+    return SERVICE_WORKER_STATUSES.RUNNING;
+  } else if (worker.active) {
+    return SERVICE_WORKER_STATUSES.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 SERVICE_WORKER_STATUSES.REGISTERING;
+}
+
+function toWorkerComponentData(workers, isServiceWorker) {
+  return workers.map(worker => {
+    const type = DEBUG_TARGETS.WORKER;
+    const icon = "chrome://devtools/skin/images/debugging-workers.svg";
+    let { fetch, name, scope } = worker;
+    let status = null;
+
+    if (isServiceWorker) {
+      fetch = fetch ? SERVICE_WORKER_FETCH_STATES.LISTENING
+                    : SERVICE_WORKER_FETCH_STATES.NOT_LISTENING;
+      status = getServiceWorkerStatus(worker);
+    }
+
+    return {
+      name,
+      icon,
+      type,
+      details: {
+        fetch,
+        scope,
+        status,
+      },
+    };
+  });
+}
+
 module.exports = {
   RuntimeState,
   runtimeReducer,
 };