Bug 1495665 - Add performance panel as modal dialog in about:debugging;r=ladybenko
authorJulian Descottes <jdescottes@mozilla.com>
Tue, 12 Feb 2019 10:16:20 +0000
changeset 458664 b096b883636d8fd8430b6405d8c88744ee6e7202
parent 458663 0b7949b3d6d3c99a32a1d76d889048807ec8aa42
child 458665 106bb3f48bebd07605f24bbd69028f8a530af75f
push id35543
push userccoroiu@mozilla.com
push dateTue, 12 Feb 2019 16:27:27 +0000
treeherdermozilla-central@4ad4e42bcb99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersladybenko
bugs1495665
milestone67.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 1495665 - Add performance panel as modal dialog in about:debugging;r=ladybenko Depends on D18697 Differential Revision: https://phabricator.services.mozilla.com/D18698
devtools/client/aboutdebugging-new/aboutdebugging.css
devtools/client/aboutdebugging-new/src/actions/ui.js
devtools/client/aboutdebugging-new/src/base.css
devtools/client/aboutdebugging-new/src/components/ProfilerDialog.css
devtools/client/aboutdebugging-new/src/components/ProfilerDialog.js
devtools/client/aboutdebugging-new/src/components/RuntimePage.js
devtools/client/aboutdebugging-new/src/components/moz.build
devtools/client/aboutdebugging-new/src/constants.js
devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
devtools/client/aboutdebugging-new/src/reducers/ui-state.js
devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
--- a/devtools/client/aboutdebugging-new/aboutdebugging.css
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.css
@@ -7,16 +7,17 @@
 */
 @import "resource://devtools/client/themes/variables.css";
 @import "resource://devtools/client/aboutdebugging-new/src/base.css";
 
 /*
 * Components
 */
 @import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/ProfilerDialog.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/RuntimePage.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectSteps.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsForm.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsList.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/DebugTargetPane.css";
--- a/devtools/client/aboutdebugging-new/src/actions/ui.js
+++ b/devtools/client/aboutdebugging-new/src/actions/ui.js
@@ -8,22 +8,24 @@ const {
   ADB_ADDON_INSTALL_START,
   ADB_ADDON_INSTALL_SUCCESS,
   ADB_ADDON_INSTALL_FAILURE,
   ADB_ADDON_UNINSTALL_START,
   ADB_ADDON_UNINSTALL_SUCCESS,
   ADB_ADDON_UNINSTALL_FAILURE,
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
+  HIDE_PROFILER_DIALOG,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_TYPES,
   SELECT_PAGE_FAILURE,
   SELECT_PAGE_START,
   SELECT_PAGE_SUCCESS,
   SELECTED_RUNTIME_ID_UPDATED,
+  SHOW_PROFILER_DIALOG,
   USB_RUNTIMES_SCAN_START,
   USB_RUNTIMES_SCAN_SUCCESS,
 } = require("../constants");
 
 const NetworkLocationsModule = require("../modules/network-locations");
 const { adbAddon } = require("devtools/shared/adb/adb-addon");
 const { refreshUSBRuntimes } = require("../modules/usb-runtimes");
 
@@ -46,16 +48,21 @@ function selectPage(page, runtimeId) {
       }
 
       const currentPage = getState().ui.selectedPage;
       // Nothing to dispatch if the page is the same as the current page
       if (isSamePage(currentPage, page)) {
         return;
       }
 
+      // Stop showing the profiler dialog if we are navigating to another page.
+      if (getState().ui.showProfilerDialog) {
+        await dispatch({ type: HIDE_PROFILER_DIALOG });
+      }
+
       // Stop watching current runtime, if currently on a RUNTIME page.
       if (currentPage === PAGE_TYPES.RUNTIME) {
         const currentRuntimeId = getState().runtimes.selectedRuntimeId;
         await dispatch(Actions.unwatchRuntime(currentRuntimeId));
       }
 
       // Always update the selected runtime id.
       // If we are navigating to a non-runtime page, the Runtime page components are no
@@ -88,16 +95,24 @@ function addNetworkLocation(location) {
 }
 
 function removeNetworkLocation(location) {
   return (dispatch, getState) => {
     NetworkLocationsModule.removeNetworkLocation(location);
   };
 }
 
+function showProfilerDialog() {
+  return { type: SHOW_PROFILER_DIALOG };
+}
+
+function hideProfilerDialog() {
+  return { type: HIDE_PROFILER_DIALOG };
+}
+
 function updateAdbAddonStatus(adbAddonStatus) {
   return { type: ADB_ADDON_STATUS_UPDATED, adbAddonStatus };
 }
 
 function updateNetworkLocations(locations) {
   return (dispatch, getState) => {
     dispatch(Actions.updateNetworkRuntimes(locations));
     dispatch({ type: NETWORK_LOCATIONS_UPDATED, locations });
@@ -142,17 +157,19 @@ function scanUSBRuntimes() {
     dispatch({ type: USB_RUNTIMES_SCAN_START });
     await refreshUSBRuntimes();
     dispatch({ type: USB_RUNTIMES_SCAN_SUCCESS });
   };
 }
 
 module.exports = {
   addNetworkLocation,
+  hideProfilerDialog,
   installAdbAddon,
   removeNetworkLocation,
   scanUSBRuntimes,
   selectPage,
+  showProfilerDialog,
   uninstallAdbAddon,
   updateAdbAddonStatus,
   updateDebugTargetCollapsibility,
   updateNetworkLocations,
 };
--- a/devtools/client/aboutdebugging-new/src/base.css
+++ b/devtools/client/aboutdebugging-new/src/base.css
@@ -29,17 +29,20 @@
 
   /* Colors from Photon */
   --success-50: #30e60b;
   --warning-50: #ffe900;
   --warning-90: #3e2800;
   --error-50: #ff0039;
   --error-60: #d70022;
   --highlight-50: #0a84ff;
+  --grey-20: #ededf0;
   --grey-30: #d7d7db; /* for ui, no special semantic */
+  --grey-90-a60: rgba(12, 12, 13, 0.6);
+  --grey-90-a80: rgba(12, 12, 13, 0.8);
   --white-100: #fff; /* for ui, no special semantic */
 
   /* Global layout vars */
   --page-width: 664px;
   --base-unit: 4px;
 
   /* Global styles */
   --base-font-style: message-box;
@@ -259,16 +262,36 @@ Form controls
 /* smaller size for a default button */
 .default-button--micro {
   padding-inline-start: calc(2 * var(--base-unit));
   padding-inline-end: calc(2 * var(--base-unit));
   font-size: var(--micro-font-size);
   height: calc(var(--base-unit) * 6);
 }
 
+/* ghost button. icon button with no background from Photon guidelines */
+.ghost-button {
+  border: none;
+  border-radius: calc(var(--base-unit) / 2);
+  fill: var(--grey-90-a80);
+  height: calc(var(--base-unit) * 8);
+  padding: calc(var(--base-unit) * 2);
+  width: calc(var(--base-unit) * 8);
+
+  -moz-context-properties: fill;
+}
+
+.ghost-button:hover {
+  background-color: var(--grey-30);
+}
+
+.ghost-button:active {
+  background-color: var(--grey-40);
+}
+
 /* standard inputs */
 .default-input {
   line-height: unset;
   padding: 0 calc(var(--base-unit) * 2);
   height: 100%;
 
   border: 1px solid var(--box-border-color);
   border-radius: 2px;
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/ProfilerDialog.css
@@ -0,0 +1,55 @@
+/* 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/. */
+
+.profiler-dialog__frame {
+  border: none;
+  height: 100%;
+  width: 100%;
+}
+
+/*
+ * The current layout of the dialog header is
+ *
+ *  +-----------------------------+---+
+ *  | dialog title (auto)         | X |
+ *  +-----------------------------+---+
+ */
+.profiler-dialog__header {
+  align-items: center;
+  background-color: var(--grey-20);
+  display: grid;
+  grid-template-columns: 1fr max-content;
+  padding: var(--base-unit);
+}
+
+.profiler-dialog__header__title {
+  margin: 0;
+  margin-inline-start: calc(var(--base-unit) * 2);
+
+  /* Reset <h1> styles */
+  font-size: 15px;
+  font-weight: normal;
+}
+
+.profiler-dialog__inner {
+  background-color: var(--white-100);
+  display: grid;
+  height: calc(var(--base-unit) * 150); /* 600px */
+  grid-template-rows: max-content auto;
+  max-height: calc(100% - calc(var(--base-unit) * 25)); /* 100% - 100px */
+  position: fixed;
+  width: calc(var(--base-unit) * 150); /* 600px */
+}
+
+.profiler-dialog__mask {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: var(--grey-90-a60);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/ProfilerDialog.js
@@ -0,0 +1,83 @@
+/* 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 { 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 FluentReact = require("devtools/client/shared/vendor/fluent-react");
+const Localized = createFactory(FluentReact.Localized);
+
+const Actions = require("../actions/index");
+const Types = require("../types/index");
+
+/**
+ * This component a modal dialog containing the performance profiler UI.
+ */
+class ProfilerDialog extends PureComponent {
+  static get propTypes() {
+    return {
+      runtimeDetails: Types.runtimeDetails.isRequired,
+      dispatch: PropTypes.func.isRequired,
+    };
+  }
+
+  hide() {
+    this.props.dispatch(Actions.hideProfilerDialog());
+  }
+
+  render() {
+    return dom.div(
+      {
+        className: "profiler-dialog__mask",
+        onClick: () => this.hide(),
+      },
+      dom.article(
+        {
+          className: "profiler-dialog__inner",
+          onClick: e => e.stopPropagation(),
+        },
+        dom.header(
+          {
+            className: "profiler-dialog__header",
+          },
+          Localized(
+            {
+              id: "about-debugging-profiler-dialog-title",
+            },
+            dom.h1(
+              {
+                className: "profiler-dialog__header__title",
+              },
+              "Performance Profiler",
+            )
+          ),
+          dom.button(
+            {
+              className: "ghost-button",
+              onClick: () => this.hide(),
+            },
+            dom.img(
+              {
+                src: "chrome://devtools/skin/images/close.svg",
+              }
+            )
+          )
+        ),
+        dom.iframe({
+          className: "profiler-dialog__frame",
+          src: "chrome://devtools/content/performance-new/index.xhtml",
+          onLoad: (e) => {
+            const { clientWrapper } = this.props.runtimeDetails;
+            clientWrapper.loadPerformanceProfiler(e.target.contentWindow);
+          },
+        })
+      )
+    );
+  }
+}
+
+module.exports = ProfilerDialog;
--- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
@@ -11,16 +11,17 @@ const PropTypes = require("devtools/clie
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const Localized = createFactory(FluentReact.Localized);
 
 const ConnectionPromptSetting = createFactory(require("./ConnectionPromptSetting"));
 const DebugTargetPane = createFactory(require("./debugtarget/DebugTargetPane"));
 const ExtensionDetail = createFactory(require("./debugtarget/ExtensionDetail"));
 const InspectAction = createFactory(require("./debugtarget/InspectAction"));
+const ProfilerDialog = createFactory(require("./ProfilerDialog"));
 const RuntimeInfo = createFactory(require("./RuntimeInfo"));
 const ServiceWorkerAction = createFactory(require("./debugtarget/ServiceWorkerAction"));
 const ServiceWorkersWarning = createFactory(require("./ServiceWorkersWarning"));
 const TabDetail = createFactory(require("./debugtarget/TabDetail"));
 const TemporaryExtensionAction = createFactory(require("./debugtarget/TemporaryExtensionAction"));
 const TemporaryExtensionDetail = createFactory(require("./debugtarget/TemporaryExtensionDetail"));
 const TemporaryExtensionInstaller =
   createFactory(require("./debugtarget/TemporaryExtensionInstaller"));
@@ -39,32 +40,32 @@ class RuntimePage extends PureComponent 
       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,
       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.string,
     };
   }
 
   // TODO: avoid the use of this method
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1508688
   componentWillMount() {
     const { dispatch, runtimeId } = this.props;
     dispatch(Actions.selectPage(PAGE_TYPES.RUNTIME, runtimeId));
   }
 
   onProfilerButtonClick() {
-    // TODO
-    // this.props.dispatch(Actions.showProfilerDialog());
+    this.props.dispatch(Actions.showProfilerDialog());
   }
 
   renderRemoteRuntimeActions() {
     const { runtimeDetails, runtimeId, dispatch } = this.props;
     const { connectionPromptEnabled } = runtimeDetails;
 
     if (runtimeId === RUNTIMES.THIS_FIREFOX) {
       // Connection prompt and Profiling are only available on remote runtimes.
@@ -126,16 +127,17 @@ class RuntimePage extends PureComponent 
   render() {
     const {
       dispatch,
       installedExtensions,
       otherWorkers,
       runtimeDetails,
       serviceWorkers,
       sharedWorkers,
+      showProfilerDialog,
       tabs,
       temporaryExtensions,
       temporaryInstallError,
     } = this.props;
 
     if (!runtimeDetails) {
       // runtimeInfo can be null when the selectPage action navigates from a runtime A
       // to a runtime B (between unwatchRuntime and watchRuntime).
@@ -186,27 +188,30 @@ class RuntimePage extends PureComponent 
                                  DEBUG_TARGET_PANE.SHARED_WORKER,
                                  "about-debugging-runtime-shared-workers"),
       this.renderDebugTargetPane("Other Workers",
                                  otherWorkers,
                                  InspectAction,
                                  WorkerDetail,
                                  DEBUG_TARGET_PANE.OTHER_WORKER,
                                  "about-debugging-runtime-other-workers"),
+
+      showProfilerDialog ? ProfilerDialog({ dispatch, runtimeDetails }) : null,
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
     collapsibilities: state.ui.debugTargetCollapsibilities,
     installedExtensions: state.debugTargets.installedExtensions,
     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,
   };
 };
 
 module.exports = connect(mapStateToProps)(RuntimePage);
--- a/devtools/client/aboutdebugging-new/src/components/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/moz.build
@@ -8,13 +8,15 @@ DIRS += [
     'shared',
     'sidebar',
 ]
 
 DevToolsModules(
     'App.css',
     'App.js',
     'ConnectionPromptSetting.js',
+    'ProfilerDialog.css',
+    'ProfilerDialog.js',
     'RuntimeInfo.js',
     'RuntimePage.css',
     'RuntimePage.js',
     'ServiceWorkersWarning.js',
 )
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -17,31 +17,33 @@ const actionTypes = {
   ADB_ADDON_STATUS_UPDATED: "ADB_ADDON_STATUS_UPDATED",
   CONNECT_RUNTIME_FAILURE: "CONNECT_RUNTIME_FAILURE",
   CONNECT_RUNTIME_START: "CONNECT_RUNTIME_START",
   CONNECT_RUNTIME_SUCCESS: "CONNECT_RUNTIME_SUCCESS",
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED: "DEBUG_TARGET_COLLAPSIBILITY_UPDATED",
   DISCONNECT_RUNTIME_FAILURE: "DISCONNECT_RUNTIME_FAILURE",
   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_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",
   SELECT_PAGE_SUCCESS: "SELECT_PAGE_SUCCESS",
   SELECTED_RUNTIME_ID_UPDATED: "SELECTED_RUNTIME_ID_UPDATED",
+  SHOW_PROFILER_DIALOG: "SHOW_PROFILER_DIALOG",
   TELEMETRY_RECORD: "TELEMETRY_RECORD",
   TEMPORARY_EXTENSION_INSTALL_FAILURE: "TEMPORARY_EXTENSION_INSTALL_FAILURE",
   TEMPORARY_EXTENSION_INSTALL_START: "TEMPORARY_EXTENSION_INSTALL_START",
   TEMPORARY_EXTENSION_INSTALL_SUCCESS: "TEMPORARY_EXTENSION_INSTALL_SUCCESS",
   THIS_FIREFOX_RUNTIME_CREATED: "THIS_FIREFOX_RUNTIME_CREATED",
   UNWATCH_RUNTIME_FAILURE: "UNWATCH_RUNTIME_FAILURE",
   UNWATCH_RUNTIME_START: "UNWATCH_RUNTIME_START",
   UNWATCH_RUNTIME_SUCCESS: "UNWATCH_RUNTIME_SUCCESS",
--- a/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
+++ b/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
@@ -149,11 +149,18 @@ class ClientWrapper {
 
   async close() {
     return this.client.close();
   }
 
   isClosed() {
     return this.client._closed;
   }
+
+  async loadPerformanceProfiler(win) {
+    const preferenceFront = await this.getFront("preference");
+    const perfFront = await this.getFront("perf");
+    const perfActorVersion = this.client.mainRoot.traits.perfActorVersion;
+    win.gInit(perfFront, preferenceFront, perfActorVersion);
+  }
 }
 
 exports.ClientWrapper = ClientWrapper;
--- a/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
@@ -2,34 +2,37 @@
  * 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 {
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
+  HIDE_PROFILER_DIALOG,
   NETWORK_LOCATIONS_UPDATED,
   SELECT_PAGE_SUCCESS,
+  SHOW_PROFILER_DIALOG,
   TEMPORARY_EXTENSION_INSTALL_FAILURE,
   TEMPORARY_EXTENSION_INSTALL_SUCCESS,
   USB_RUNTIMES_SCAN_START,
   USB_RUNTIMES_SCAN_SUCCESS,
 } = require("../constants");
 
 function UiState(locations = [], debugTargetCollapsibilities = {},
                  networkEnabled = false, wifiEnabled = false,
                  showSystemAddons = false) {
   return {
     adbAddonStatus: null,
     debugTargetCollapsibilities,
     isScanningUsb: false,
     networkEnabled,
     networkLocations: locations,
     selectedPage: null,
+    showProfilerDialog: false,
     showSystemAddons,
     temporaryInstallError: null,
     wifiEnabled,
   };
 }
 
 function uiReducer(state = UiState(), action) {
   switch (action.type) {
@@ -50,16 +53,24 @@ function uiReducer(state = UiState(), ac
       return Object.assign({}, state, { networkLocations: locations });
     }
 
     case SELECT_PAGE_SUCCESS: {
       const { page } = action;
       return Object.assign({}, state, { selectedPage: page });
     }
 
+    case SHOW_PROFILER_DIALOG: {
+      return Object.assign({}, state, { showProfilerDialog: true });
+    }
+
+    case HIDE_PROFILER_DIALOG: {
+      return Object.assign({}, state, { showProfilerDialog: false });
+    }
+
     case USB_RUNTIMES_SCAN_START: {
       return Object.assign({}, state, { isScanningUsb: true });
     }
 
     case USB_RUNTIMES_SCAN_SUCCESS: {
       return Object.assign({}, state, { isScanningUsb: false });
     }
 
--- a/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
+++ b/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
@@ -278,8 +278,11 @@ about-debugging-page-title-selected-page
   }
 
 # Page title with the runtime displayed in the tab
 # { $selectedRuntimeId } is the id of the current runtime, such as "this-firefox", "localhost:6080", ...
 about-debugging-page-title-with-runtime = { -application-title } - { about-debugging-page-title-selected-page } / { $selectedRuntimeId }
 
 # Page title without the runtime displayed in the tab
 about-debugging-page-title = { -application-title } - { about-debugging-page-title-selected-page }
+
+# Title of a modal dialog displayed on remote runtime pages after clicking on the Profile Runtime button.
+about-debugging-profiler-dialog-title = Performance Profiler