Bug 1522062 - Add Processes category to debug the main process on remote runtimes r=ladybenko
authorJulian Descottes <jdescottes@mozilla.com>
Wed, 20 Mar 2019 00:47:46 +0000
changeset 465152 7f85aba0b4abcf3da06bdbd410aacb318695c7e4
parent 465151 993bbf845ef0d2bb00817a112683c84df4b08cd6
child 465153 94948ac7f019cef011458bdb19d100200ec836b6
push id35732
push useropoprus@mozilla.com
push dateWed, 20 Mar 2019 10:52:37 +0000
treeherdermozilla-central@708979f9c3f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersladybenko
bugs1522062
milestone68.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 1522062 - Add Processes category to debug the main process on remote runtimes r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D21695
devtools/client/aboutdebugging-new/src/actions/debug-targets.js
devtools/client/aboutdebugging-new/src/actions/runtimes.js
devtools/client/aboutdebugging-new/src/components/RuntimePage.js
devtools/client/aboutdebugging-new/src/components/debugtarget/ProcessDetail.js
devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
devtools/client/aboutdebugging-new/src/constants.js
devtools/client/aboutdebugging-new/src/create-store.js
devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
devtools/client/aboutdebugging-new/src/middleware/moz.build
devtools/client/aboutdebugging-new/src/middleware/process-component-data.js
devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
devtools/client/aboutdebugging-new/src/modules/debug-target-support.js
devtools/client/aboutdebugging-new/src/reducers/debug-targets-state.js
devtools/client/aboutdebugging-new/src/types/debug-target.js
devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
devtools/client/preferences/devtools-client.js
--- a/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
+++ b/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
@@ -1,16 +1,18 @@
 /* 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 { gDevTools } = require("devtools/client/framework/devtools");
 const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
+const { Toolbox } = require("devtools/client/framework/toolbox");
 const { remoteClientManager } =
   require("devtools/client/shared/remote-debugging/remote-client-manager");
 
 const { l10n } = require("../modules/l10n");
 
 const {
   debugAddon,
   openTemporaryExtension,
@@ -22,16 +24,19 @@ const {
   getCurrentRuntime,
 } = require("../modules/runtimes-state-helper");
 
 const {
   DEBUG_TARGETS,
   REQUEST_EXTENSIONS_FAILURE,
   REQUEST_EXTENSIONS_START,
   REQUEST_EXTENSIONS_SUCCESS,
+  REQUEST_PROCESSES_FAILURE,
+  REQUEST_PROCESSES_START,
+  REQUEST_PROCESSES_SUCCESS,
   REQUEST_TABS_FAILURE,
   REQUEST_TABS_START,
   REQUEST_TABS_SUCCESS,
   REQUEST_WORKERS_FAILURE,
   REQUEST_WORKERS_START,
   REQUEST_WORKERS_SUCCESS,
   TEMPORARY_EXTENSION_INSTALL_FAILURE,
   TEMPORARY_EXTENSION_INSTALL_START,
@@ -58,16 +63,33 @@ function inspectDebugTarget(type, id) {
           window.open(`about:devtools-toolbox?type=tab&id=${id}`);
         }
         break;
       }
       case DEBUG_TARGETS.EXTENSION: {
         await debugAddon(id, runtimeDetails.clientWrapper.client);
         break;
       }
+      case DEBUG_TARGETS.PROCESS: {
+        const devtoolsClient = runtimeDetails.clientWrapper.client;
+        const processTargetFront = devtoolsClient.getActor(id);
+        const toolbox = await gDevTools.showToolbox(processTargetFront, null,
+          Toolbox.HostType.WINDOW);
+
+        // Once the target is destroyed after closing the toolbox, the front is gone and
+        // can no longer be used. Extensions don't have the issue because we don't list
+        // the target fronts directly, but proxies on which we call connect to create a
+        // target front when we want to inspect them. Local workers don't have this issue
+        // because closing the toolbox stops several workers which will indirectly trigger
+        // a requestWorkers action. However workers on a remote runtime have the exact
+        // same issue.
+        // To workaround the issue we request processes after the toolbox is closed.
+        toolbox.once("destroy", () => dispatch(Actions.requestProcesses()));
+        break;
+      }
       case DEBUG_TARGETS.WORKER: {
         // Open worker toolbox in new window.
         const devtoolsClient = runtimeDetails.clientWrapper.client;
         const front = devtoolsClient.getActor(id);
         gDevToolsBrowser.openWorkerToolbox(front);
         break;
       }
       default: {
@@ -184,16 +206,38 @@ function requestExtensions() {
         temporaryExtensions,
       });
     } catch (e) {
       dispatch({ type: REQUEST_EXTENSIONS_FAILURE, error: e });
     }
   };
 }
 
+function requestProcesses() {
+  return async (dispatch, getState) => {
+    dispatch({ type: REQUEST_PROCESSES_START });
+
+    const clientWrapper = getCurrentClient(getState().runtimes);
+
+    try {
+      const mainProcessFront = await clientWrapper.getMainProcess();
+
+      dispatch({
+        type: REQUEST_PROCESSES_SUCCESS,
+        mainProcess: {
+          id: 0,
+          processFront: mainProcessFront,
+        },
+      });
+    } catch (e) {
+      dispatch({ type: REQUEST_PROCESSES_FAILURE, error: e });
+    }
+  };
+}
+
 function requestWorkers() {
   return async (dispatch, getState) => {
     dispatch({ type: REQUEST_WORKERS_START });
 
     const clientWrapper = getCurrentClient(getState().runtimes);
 
     try {
       const {
@@ -247,12 +291,13 @@ function unregisterServiceWorker(registr
 module.exports = {
   inspectDebugTarget,
   installTemporaryExtension,
   pushServiceWorker,
   reloadTemporaryExtension,
   removeTemporaryExtension,
   requestTabs,
   requestExtensions,
+  requestProcesses,
   requestWorkers,
   startServiceWorker,
   unregisterServiceWorker,
 };
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -9,25 +9,29 @@ const Actions = require("./index");
 const {
   getAllRuntimes,
   getCurrentRuntime,
   findRuntimeById,
 } = require("../modules/runtimes-state-helper");
 
 const { l10n } = require("../modules/l10n");
 const { createClientForRuntime } = require("../modules/runtime-client-factory");
-const { isExtensionDebugSettingNeeded } = require("../modules/debug-target-support");
+const {
+  isExtensionDebugSettingNeeded,
+  isSupportedDebugTargetPane,
+} = require("../modules/debug-target-support");
 
 const { remoteClientManager } =
   require("devtools/client/shared/remote-debugging/remote-client-manager");
 
 const {
   CONNECT_RUNTIME_FAILURE,
   CONNECT_RUNTIME_START,
   CONNECT_RUNTIME_SUCCESS,
+  DEBUG_TARGET_PANE,
   DISCONNECT_RUNTIME_FAILURE,
   DISCONNECT_RUNTIME_START,
   DISCONNECT_RUNTIME_SUCCESS,
   PAGE_TYPES,
   REMOTE_RUNTIMES_UPDATED,
   RUNTIME_PREFERENCE,
   RUNTIMES,
   THIS_FIREFOX_RUNTIME_CREATED,
@@ -255,16 +259,21 @@ function watchRuntime(id) {
 
       // The selected runtime should already have a connected client assigned.
       const runtime = findRuntimeById(id, getState().runtimes);
       await dispatch({ type: WATCH_RUNTIME_SUCCESS, runtime });
 
       dispatch(Actions.requestExtensions());
       dispatch(Actions.requestTabs());
       dispatch(Actions.requestWorkers());
+
+      if (isSupportedDebugTargetPane(runtime.runtimeDetails.info.type,
+                                     DEBUG_TARGET_PANE.PROCESSES)) {
+        dispatch(Actions.requestProcesses());
+      }
     } catch (e) {
       dispatch({ type: WATCH_RUNTIME_FAILURE, error: e });
     }
   };
 }
 
 function unwatchRuntime(id) {
   return async (dispatch, getState) => {
--- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
@@ -19,16 +19,17 @@ const ExtensionDetail = createFactory(re
 const InspectAction = createFactory(require("./debugtarget/InspectAction"));
 const ProfilerDialog = createFactory(require("./ProfilerDialog"));
 const RuntimeActions = createFactory(require("./RuntimeActions"));
 const RuntimeInfo = createFactory(require("./RuntimeInfo"));
 const ServiceWorkerAction = createFactory(require("./debugtarget/ServiceWorkerAction"));
 const ServiceWorkerAdditionalActions =
   createFactory(require("./debugtarget/ServiceWorkerAdditionalActions"));
 const ServiceWorkersWarning = createFactory(require("./ServiceWorkersWarning"));
+const ProcessDetail = createFactory(require("./debugtarget/ProcessDetail"));
 const TabDetail = createFactory(require("./debugtarget/TabDetail"));
 const TemporaryExtensionAdditionalActions =
   createFactory(require("./debugtarget/TemporaryExtensionAdditionalActions"));
 const TemporaryExtensionDetail = createFactory(require("./debugtarget/TemporaryExtensionDetail"));
 const TemporaryExtensionInstallSection =
   createFactory(require("./debugtarget/TemporaryExtensionInstallSection"));
 const WorkerDetail = createFactory(require("./debugtarget/WorkerDetail"));
 
@@ -43,16 +44,17 @@ class RuntimePage extends PureComponent 
   static get propTypes() {
     return {
       collapsibilities: Types.collapsibilities.isRequired,
       dispatch: PropTypes.func.isRequired,
       installedExtensions: PropTypes.arrayOf(PropTypes.object).isRequired,
       otherWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
       runtimeDetails: Types.runtimeDetails,
       runtimeId: PropTypes.string.isRequired,
+      processes: PropTypes.arrayOf(PropTypes.object).isRequired,
       serviceWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
       sharedWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
       showProfilerDialog: PropTypes.bool.isRequired,
       tabs: PropTypes.arrayOf(PropTypes.object).isRequired,
       temporaryExtensions: PropTypes.arrayOf(PropTypes.object).isRequired,
       temporaryInstallError: PropTypes.object,
     };
   }
@@ -63,16 +65,18 @@ class RuntimePage extends PureComponent 
     const { dispatch, runtimeId } = this.props;
     dispatch(Actions.selectPage(PAGE_TYPES.RUNTIME, runtimeId));
   }
 
   getIconByType(type) {
     switch (type) {
       case DEBUG_TARGETS.EXTENSION:
         return "chrome://devtools/skin/images/debugging-addons.svg";
+      case DEBUG_TARGETS.PROCESS:
+        return "chrome://devtools/skin/images/settings.svg";
       case DEBUG_TARGETS.TAB:
         return "chrome://devtools/skin/images/debugging-tabs.svg";
       case DEBUG_TARGETS.WORKER:
         return "chrome://devtools/skin/images/debugging-workers.svg";
     }
 
     throw new Error(`Unsupported type [${ type }]`);
   }
@@ -118,16 +122,17 @@ class RuntimePage extends PureComponent 
     return TemporaryExtensionInstallSection({ dispatch, temporaryInstallError });
   }
 
   render() {
     const {
       dispatch,
       installedExtensions,
       otherWorkers,
+      processes,
       runtimeDetails,
       runtimeId,
       serviceWorkers,
       sharedWorkers,
       showProfilerDialog,
       tabs,
       temporaryExtensions,
     } = this.props;
@@ -197,26 +202,36 @@ class RuntimePage extends PureComponent 
                                  this.getIconByType(DEBUG_TARGETS.WORKER),
                                  otherWorkers,
                                  null,
                                  InspectAction,
                                  null,
                                  WorkerDetail,
                                  DEBUG_TARGET_PANE.OTHER_WORKER,
                                  "about-debugging-runtime-other-workers"),
+      this.renderDebugTargetPane("Processes",
+                                 this.getIconByType(DEBUG_TARGETS.PROCESS),
+                                 processes,
+                                 null,
+                                 InspectAction,
+                                 null,
+                                 ProcessDetail,
+                                 DEBUG_TARGET_PANE.PROCESSES,
+                                 "about-debugging-runtime-processes"),
 
       showProfilerDialog ? ProfilerDialog({ dispatch, runtimeDetails }) : null,
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
     collapsibilities: state.ui.debugTargetCollapsibilities,
     installedExtensions: state.debugTargets.installedExtensions,
+    processes: state.debugTargets.processes,
     otherWorkers: state.debugTargets.otherWorkers,
     runtimeDetails: getCurrentRuntimeDetails(state.runtimes),
     serviceWorkers: state.debugTargets.serviceWorkers,
     sharedWorkers: state.debugTargets.sharedWorkers,
     showProfilerDialog: state.ui.showProfilerDialog,
     tabs: state.debugTargets.tabs,
     temporaryExtensions: state.debugTargets.temporaryExtensions,
     temporaryInstallError: state.ui.temporaryInstallError,
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/ProcessDetail.js
@@ -0,0 +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 { PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const Types = require("../../types/index");
+
+/**
+ * This component displays detail information for a process.
+ */
+class ProcessDetail extends PureComponent {
+  static get propTypes() {
+    return {
+      target: Types.debugTarget.isRequired,
+    };
+  }
+
+  render() {
+    const { description } = this.props.target.details;
+    return dom.p({ className: "debug-target-item__subname ellipsis-text" }, description);
+  }
+}
+
+module.exports = ProcessDetail;
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
@@ -9,16 +9,17 @@ DevToolsModules(
     'DebugTargetList.js',
     'DebugTargetPane.css',
     'DebugTargetPane.js',
     'ExtensionAction.js',
     'ExtensionDetail.js',
     'FieldPair.css',
     'FieldPair.js',
     'InspectAction.js',
+    'ProcessDetail.js',
     'ServiceWorkerAction.css',
     'ServiceWorkerAction.js',
     'ServiceWorkerAdditionalActions.js',
     'TabDetail.js',
     'TemporaryExtensionAdditionalActions.js',
     'TemporaryExtensionDetail.js',
     'TemporaryExtensionInstaller.js',
     'TemporaryExtensionInstallSection.css',
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -23,16 +23,19 @@ const actionTypes = {
   DISCONNECT_RUNTIME_START: "DISCONNECT_RUNTIME_START",
   DISCONNECT_RUNTIME_SUCCESS: "DISCONNECT_RUNTIME_SUCCESS",
   HIDE_PROFILER_DIALOG: "HIDE_PROFILER_DIALOG",
   NETWORK_LOCATIONS_UPDATED: "NETWORK_LOCATIONS_UPDATED",
   REMOTE_RUNTIMES_UPDATED: "REMOTE_RUNTIMES_UPDATED",
   REQUEST_EXTENSIONS_FAILURE: "REQUEST_EXTENSIONS_FAILURE",
   REQUEST_EXTENSIONS_START: "REQUEST_EXTENSIONS_START",
   REQUEST_EXTENSIONS_SUCCESS: "REQUEST_EXTENSIONS_SUCCESS",
+  REQUEST_PROCESSES_FAILURE: "REQUEST_PROCESSES_FAILURE",
+  REQUEST_PROCESSES_START: "REQUEST_PROCESSES_START",
+  REQUEST_PROCESSES_SUCCESS: "REQUEST_PROCESSES_SUCCESS",
   REQUEST_TABS_FAILURE: "REQUEST_TABS_FAILURE",
   REQUEST_TABS_START: "REQUEST_TABS_START",
   REQUEST_TABS_SUCCESS: "REQUEST_TABS_SUCCESS",
   REQUEST_WORKERS_FAILURE: "REQUEST_WORKERS_FAILURE",
   REQUEST_WORKERS_START: "REQUEST_WORKERS_START",
   REQUEST_WORKERS_SUCCESS: "REQUEST_WORKERS_SUCCESS",
   SELECT_PAGE_FAILURE: "SELECT_PAGE_FAILURE",
   SELECT_PAGE_START: "SELECT_PAGE_START",
@@ -60,22 +63,24 @@ const actionTypes = {
   USB_RUNTIMES_SCAN_SUCCESS: "USB_RUNTIMES_SCAN_SUCCESS",
   WATCH_RUNTIME_FAILURE: "WATCH_RUNTIME_FAILURE",
   WATCH_RUNTIME_START: "WATCH_RUNTIME_START",
   WATCH_RUNTIME_SUCCESS: "WATCH_RUNTIME_SUCCESS",
 };
 
 const DEBUG_TARGETS = {
   EXTENSION: "EXTENSION",
+  PROCESS: "PROCESS",
   TAB: "TAB",
   WORKER: "WORKER",
 };
 
 const DEBUG_TARGET_PANE = {
   INSTALLED_EXTENSION: "installedExtension",
+  PROCESSES: "processes",
   OTHER_WORKER: "otherWorker",
   SERVICE_WORKER: "serviceWorker",
   SHARED_WORKER: "sharedWorker",
   TAB: "tab",
   TEMPORARY_EXTENSION: "temporaryExtension",
 };
 
 const MESSAGE_LEVEL = {
@@ -87,16 +92,18 @@ const MESSAGE_LEVEL = {
 const PAGE_TYPES = {
   RUNTIME: "runtime",
   CONNECT: "connect",
 };
 
 const PREFERENCES = {
   // Temporary preference without any default value until network locations are enabled.
   NETWORK_ENABLED: "devtools.aboutdebugging.network",
+  // Preference that drives the display of the "Processes" debug target category.
+  PROCESS_DEBUGGING_ENABLED: "devtools.aboutdebugging.process-debugging",
   // Preference that drives the display of system addons in about:debugging.
   SHOW_SYSTEM_ADDONS: "devtools.aboutdebugging.showSystemAddons",
   // Temporary preference without any default value until wifi is enabled.
   WIFI_ENABLED: "devtools.aboutdebugging.wifi",
 };
 
 const RUNTIME_PREFERENCE = {
   CHROME_DEBUG_ENABLED: "devtools.chrome.enabled",
--- a/devtools/client/aboutdebugging-new/src/create-store.js
+++ b/devtools/client/aboutdebugging-new/src/create-store.js
@@ -13,16 +13,17 @@ const { waitUntilService } = require("de
 const rootReducer = require("./reducers/index");
 const { DebugTargetsState } = require("./reducers/debug-targets-state");
 const { RuntimesState } = require("./reducers/runtimes-state");
 const { UiState } = require("./reducers/ui-state");
 const debugTargetListenerMiddleware = require("./middleware/debug-target-listener");
 const errorLoggingMiddleware = require("./middleware/error-logging");
 const eventRecordingMiddleware = require("./middleware/event-recording");
 const extensionComponentDataMiddleware = require("./middleware/extension-component-data");
+const processComponentDataMiddleware = require("./middleware/process-component-data");
 const tabComponentDataMiddleware = require("./middleware/tab-component-data");
 const workerComponentDataMiddleware = require("./middleware/worker-component-data");
 const { getDebugTargetCollapsibilities } = require("./modules/debug-target-collapsibilities");
 const { getNetworkLocations } = require("./modules/network-locations");
 
 const { PREFERENCES } = require("./constants");
 
 function configureStore() {
@@ -32,16 +33,17 @@ function configureStore() {
     ui: getUiState(),
   };
 
   const middleware = applyMiddleware(thunk,
                                      debugTargetListenerMiddleware,
                                      errorLoggingMiddleware,
                                      eventRecordingMiddleware,
                                      extensionComponentDataMiddleware,
+                                     processComponentDataMiddleware,
                                      tabComponentDataMiddleware,
                                      workerComponentDataMiddleware,
                                      waitUntilService);
 
   return createStore(rootReducer, initialState, middleware);
 }
 
 function getUiState() {
--- a/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
+++ b/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
@@ -32,32 +32,30 @@ function debugTargetListenerMiddleware(s
         // Tabs
         clientWrapper.addListener("tabListChanged", onTabsUpdated);
 
         // Addons
         clientWrapper.addListener("addonListChanged", onExtensionsUpdated);
 
         // Workers
         clientWrapper.addListener("workersUpdated", onWorkersUpdated);
-
         break;
       }
       case UNWATCH_RUNTIME_START: {
         const { runtime } = action;
         const { clientWrapper } = runtime.runtimeDetails;
 
         // Tabs
         clientWrapper.removeListener("tabListChanged", onTabsUpdated);
 
         // Addons
         clientWrapper.removeListener("addonListChanged", onExtensionsUpdated);
 
         // Workers
         clientWrapper.removeListener("workersUpdated", onWorkersUpdated);
-
         break;
       }
     }
 
     return next(action);
   };
 }
 
--- a/devtools/client/aboutdebugging-new/src/middleware/moz.build
+++ b/devtools/client/aboutdebugging-new/src/middleware/moz.build
@@ -2,11 +2,12 @@
 # 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(
     'debug-target-listener.js',
     'error-logging.js',
     'event-recording.js',
     'extension-component-data.js',
+    'process-component-data.js',
     'tab-component-data.js',
     'worker-component-data.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/middleware/process-component-data.js
@@ -0,0 +1,49 @@
+/* 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 { l10n } = require("../modules/l10n");
+
+const {
+  DEBUG_TARGETS,
+  REQUEST_PROCESSES_SUCCESS,
+} = require("../constants");
+
+/**
+ * This middleware converts tabs object that get from DebuggerClient.listProcesses() to
+ * data which is used in DebugTargetItem.
+ */
+const processComponentDataMiddleware = store => next => action => {
+  switch (action.type) {
+    case REQUEST_PROCESSES_SUCCESS: {
+      const mainProcessComponentData = toMainProcessComponentData(action.mainProcess);
+      action.processes = [mainProcessComponentData];
+      break;
+    }
+  }
+
+  return next(action);
+};
+
+function toMainProcessComponentData(process) {
+  const type = DEBUG_TARGETS.PROCESS;
+  const id = process.processFront.actorID;
+  const icon = "chrome://devtools/skin/images/settings.svg";
+  const name = l10n.getString("about-debugging-main-process-name");
+  const description = l10n.getString("about-debugging-main-process-description");
+
+  return {
+    name,
+    icon,
+    id,
+    type,
+    details: {
+      description,
+      processId: process.id,
+    },
+  };
+}
+
+module.exports = processComponentDataMiddleware;
--- a/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
+++ b/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
@@ -135,16 +135,20 @@ class ClientWrapper {
   async listAddons(options) {
     return this.client.mainRoot.listAddons(options);
   }
 
   async getAddon({ id }) {
     return this.client.mainRoot.getAddon({ id });
   }
 
+  async getMainProcess() {
+    return this.client.mainRoot.getMainProcess();
+  }
+
   async getServiceWorkerFront({ id }) {
     const { serviceWorkers } = await this.listWorkers();
     const workerFronts = serviceWorkers.map(sw => sw.workerTargetFront);
     return workerFronts.find(front => front && front.actorID === id);
   }
 
   async listWorkers() {
     const { other, service, shared } = await this.client.mainRoot.listAllWorkers();
--- a/devtools/client/aboutdebugging-new/src/modules/debug-target-support.js
+++ b/devtools/client/aboutdebugging-new/src/modules/debug-target-support.js
@@ -1,31 +1,45 @@
 /* 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 { DEBUG_TARGET_PANE, RUNTIMES } = require("../constants");
+const { DEBUG_TARGET_PANE, PREFERENCES, RUNTIMES } = require("../constants");
+
+const Services = require("Services");
+
+// Process target debugging is disabled by default.
+function isProcessDebuggingSupported() {
+  return Services.prefs.getBoolPref(PREFERENCES.PROCESS_DEBUGGING_ENABLED, false);
+}
 
 const ALL_DEBUG_TARGET_PANES = [
   DEBUG_TARGET_PANE.INSTALLED_EXTENSION,
+  ...(isProcessDebuggingSupported() ? [DEBUG_TARGET_PANE.PROCESSES] : []),
   DEBUG_TARGET_PANE.OTHER_WORKER,
   DEBUG_TARGET_PANE.SERVICE_WORKER,
   DEBUG_TARGET_PANE.SHARED_WORKER,
   DEBUG_TARGET_PANE.TAB,
   DEBUG_TARGET_PANE.TEMPORARY_EXTENSION,
 ];
 
 // All debug target panes except temporary extensions
 const REMOTE_DEBUG_TARGET_PANES = ALL_DEBUG_TARGET_PANES.filter(p =>
   p !== DEBUG_TARGET_PANE.TEMPORARY_EXTENSION);
 
+// Main process debugging is not available for This Firefox.
+// At the moment only the main process is listed under processes, so remove the category
+// for this runtime.
+const THIS_FIREFOX_DEBUG_TARGET_PANES = ALL_DEBUG_TARGET_PANES.filter(p =>
+  p !== DEBUG_TARGET_PANE.PROCESSES);
+
 const SUPPORTED_TARGET_PANE_BY_RUNTIME = {
-  [RUNTIMES.THIS_FIREFOX]: ALL_DEBUG_TARGET_PANES,
+  [RUNTIMES.THIS_FIREFOX]: THIS_FIREFOX_DEBUG_TARGET_PANES,
   [RUNTIMES.USB]: REMOTE_DEBUG_TARGET_PANES,
   [RUNTIMES.NETWORK]: REMOTE_DEBUG_TARGET_PANES,
 };
 
 /**
  * If extension debug setting is needed for given runtime type, return true.
  *
  * @param {String} runtimeType
--- a/devtools/client/aboutdebugging-new/src/reducers/debug-targets-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/debug-targets-state.js
@@ -1,41 +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/. */
 
 "use strict";
 
 const {
   REQUEST_EXTENSIONS_SUCCESS,
+  REQUEST_PROCESSES_SUCCESS,
   REQUEST_TABS_SUCCESS,
   REQUEST_WORKERS_SUCCESS,
   UNWATCH_RUNTIME_SUCCESS,
 } = require("../constants");
 
 function DebugTargetsState() {
   return {
     installedExtensions: [],
     otherWorkers: [],
+    processes: [],
     serviceWorkers: [],
     sharedWorkers: [],
     tabs: [],
     temporaryExtensions: [],
   };
 }
 
 function debugTargetsReducer(state = DebugTargetsState(), action) {
   switch (action.type) {
     case UNWATCH_RUNTIME_SUCCESS: {
       return DebugTargetsState();
     }
     case REQUEST_EXTENSIONS_SUCCESS: {
       const { installedExtensions, temporaryExtensions } = action;
       return Object.assign({}, state, { installedExtensions, temporaryExtensions });
     }
+    case REQUEST_PROCESSES_SUCCESS: {
+      const { processes } = action;
+      return Object.assign({}, state, { processes });
+    }
     case REQUEST_TABS_SUCCESS: {
       const { tabs } = action;
       return Object.assign({}, state, { tabs });
     }
     case REQUEST_WORKERS_SUCCESS: {
       const { otherWorkers, serviceWorkers, sharedWorkers } = action;
       return Object.assign({}, state, { otherWorkers, serviceWorkers, sharedWorkers });
     }
--- a/devtools/client/aboutdebugging-new/src/types/debug-target.js
+++ b/devtools/client/aboutdebugging-new/src/types/debug-target.js
@@ -14,16 +14,23 @@ const extensionTargetDetails = {
   // local extensions so it might be null.
   manifestURL: PropTypes.string,
   // unique extension id.
   uuid: PropTypes.string.isRequired,
   // warning messages forwarded from the addon manager.
   warnings: PropTypes.arrayOf(PropTypes.string).isRequired,
 };
 
+const processTargetDetails = {
+  // Description for the process.
+  description: PropTypes.string.isRequired,
+  // The id for the process. #0 is the main/parent process, #1++ are parent processes
+  processId: PropTypes.number.isRequired,
+};
+
 const tabTargetDetails = {
   // the url of the tab.
   url: PropTypes.string.isRequired,
 };
 
 const workerTargetDetails = {
   // (service worker specific) one of "LISTENING", "NOT_LISTENING". undefined otherwise.
   fetch: PropTypes.string,
@@ -34,16 +41,17 @@ const workerTargetDetails = {
   // (service worker specific) one of "RUNNING", "REGISTERING", "STOPPED".
   status: PropTypes.string,
 };
 
 const debugTarget = {
   // details property will contain a type-specific object.
   details: PropTypes.oneOfType([
     PropTypes.shape(extensionTargetDetails),
+    PropTypes.shape(processTargetDetails),
     PropTypes.shape(tabTargetDetails),
     PropTypes.shape(workerTargetDetails),
   ]).isRequired,
   // icon to display for the debug target.
   icon: PropTypes.string.isRequired,
   // unique id for the target (unique in the scope of the application lifecycle).
   // - extensions: {String} extension id (for instance "someextension@mozilla.org")
   // - tabs: {Number} outerWindowID
--- a/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
+++ b/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
@@ -65,16 +65,18 @@ function createClientMock() {
       }
       if (prefName in DEFAULT_PREFERENCES) {
         return DEFAULT_PREFERENCES[prefName];
       }
       return null;
     },
     // Empty array of addons
     listAddons: () => [],
+    // Empty array of processes
+    listProcesses: () => [],
     // Empty array of tabs
     listTabs: () => [],
     // Empty arrays of workers
     listWorkers: () => ({
       otherWorkers: [],
       serviceWorkers: [],
       sharedWorkers: [],
     }),
--- a/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
+++ b/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
@@ -302,8 +302,12 @@ about-debugging-profiler-dialog-title = 
 # The "Learn more" link points to MDN.
 # https://developer.mozilla.org/docs/Tools/about:debugging#Enabling_add-on_debugging
 about-debugging-extension-debug-setting-label = Enable extension debugging <a>Learn more</a>
 
 # Clicking on the header of a debug target category will expand or collapse the debug
 # target items in the category. This text is used as ’title’ attribute of the header,
 # to describe this feature.
 about-debugging-collapse-expand-debug-targets = Collapse / expand
+
+about-debugging-main-process-name = Main Process
+about-debugging-main-process-description = Main Process for the target runtime
+
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -340,16 +340,18 @@ pref("devtools.responsive.showUserAgentI
 #endif
 
 // Enable new about:debugging.
 pref("devtools.aboutdebugging.new-enabled", false);
 // Enable the network location feature.
 pref("devtools.aboutdebugging.network", false);
 // Enable the wifi feature.
 pref("devtools.aboutdebugging.wifi", false);
+// Show process debug targets.
+pref("devtools.aboutdebugging.process-debugging", false);
 // Stringified array of network locations that users can connect to.
 pref("devtools.aboutdebugging.network-locations", "[]");
 // Debug target pane collapse/expand settings.
 pref("devtools.aboutdebugging.collapsibilities.installedExtension", false);
 pref("devtools.aboutdebugging.collapsibilities.otherWorker", false);
 pref("devtools.aboutdebugging.collapsibilities.serviceWorker", false);
 pref("devtools.aboutdebugging.collapsibilities.sharedWorker", false);
 pref("devtools.aboutdebugging.collapsibilities.tab", false);