Bug 1309187 - Implement Timings Panel r=Honza,jsnajdr
☠☠ backed out by bfa85d23df57 ☠ ☠
authorRicky Chien <rchien@mozilla.com>
Thu, 24 Nov 2016 14:43:21 +0800
changeset 325092 76bbb68f8441b2067e45a75baaac6d7c6a00400b
parent 325061 cf2d377f4a5fc4f7b2402406908abd0ca571e3f8
child 325093 31c6008a6773d09b0f84d6db482d25b88d2c1639
push id84599
push userkwierso@gmail.com
push dateFri, 02 Dec 2016 21:13:20 +0000
treeherdermozilla-inbound@18109e54e6cc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersHonza, jsnajdr
bugs1309187
milestone53.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 1309187 - Implement Timings Panel r=Honza,jsnajdr MozReview-Commit-ID: 1i1wZebHRAk
devtools/client/netmonitor/components/moz.build
devtools/client/netmonitor/components/shared/moz.build
devtools/client/netmonitor/components/shared/timings-panel.js
devtools/client/netmonitor/details-view.js
devtools/client/netmonitor/netmonitor-view.js
devtools/client/netmonitor/netmonitor.xul
devtools/client/themes/netmonitor.css
--- a/devtools/client/netmonitor/components/moz.build
+++ b/devtools/client/netmonitor/components/moz.build
@@ -1,12 +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/.
 
+DIRS += [
+    'shared',
+]
+
 DevToolsModules(
     'clear-button.js',
     'filter-buttons.js',
     'request-list-content.js',
     'request-list-empty.js',
     'request-list-header.js',
     'request-list-item.js',
     'request-list-tooltip.js',
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/components/shared/moz.build
@@ -0,0 +1,7 @@
+# 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(
+    'timings-panel.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/components/shared/timings-panel.js
@@ -0,0 +1,85 @@
+/* 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, PropTypes } = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { L10N } = require("../../l10n");
+const { getSelectedRequest } = require("../../selectors/index");
+
+const { div, span } = DOM;
+const types = ["blocked", "dns", "connect", "send", "wait", "receive"];
+const TIMINGS_END_PADDING = "80px";
+
+/*
+ * Timings panel component
+ * Display timeline bars that shows the total wait time for various stages
+ */
+function TimingsPanel({
+  timings = {},
+  totalTime = 0,
+}) {
+  const timelines = types.map((type, idx) => {
+    // Determine the relative offset for each timings box. For example, the
+    // offset of third timings box will be 0 + blocked offset + dns offset
+    const offset = types
+      .slice(0, idx)
+      .reduce((acc, cur) => (acc + timings[cur] || 0), 0);
+    const offsetScale = offset / totalTime || 0;
+    const timelineScale = timings[type] / totalTime || 0;
+
+    return div({
+      key: type,
+      id: `timings-summary-${type}`,
+      className: "tabpanel-summary-container",
+    },
+      span({ className: "tabpanel-summary-label" },
+        L10N.getStr(`netmonitor.timings.${type}`)
+      ),
+      div({ className: "requests-menu-timings-container" },
+        span({
+          className: "requests-menu-timings-offset",
+          style: {
+            width: `calc(${offsetScale} * (100% - ${TIMINGS_END_PADDING})`,
+          },
+        }),
+        span({
+          className: `requests-menu-timings-box ${type}`,
+          style: {
+            width: `calc(${timelineScale} * (100% - ${TIMINGS_END_PADDING}))`,
+          },
+        }),
+        span({ className: "requests-menu-timings-total" },
+          L10N.getFormatStr("networkMenu.totalMS", timings[type])
+        )
+      ),
+    );
+  });
+
+  return div({}, timelines);
+}
+
+TimingsPanel.displayName = "TimingsPanel";
+
+TimingsPanel.propTypes = {
+  timings: PropTypes.object,
+  totalTime: PropTypes.number,
+};
+
+module.exports = connect(
+  (state) => {
+    const selectedRequest = getSelectedRequest(state);
+
+    if (selectedRequest && selectedRequest.eventTimings) {
+      const { timings, totalTime } = selectedRequest.eventTimings;
+      return {
+        timings,
+        totalTime,
+      };
+    }
+
+    return {};
+  }
+)(TimingsPanel);
--- a/devtools/client/netmonitor/details-view.js
+++ b/devtools/client/netmonitor/details-view.js
@@ -22,16 +22,20 @@ const {
   decodeUnicodeUrl,
   formDataURI,
   getFormDataSections,
   getUrlBaseName,
   getUrlQuery,
   getUrlHost,
   parseQueryString,
 } = require("./request-utils");
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
+const TimingsPanel = createFactory(require("./components/shared/timings-panel"));
 
 // 100 KB in bytes
 const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 102400;
 const HEADERS_SIZE_DECIMALS = 3;
 const CONTENT_MIME_TYPE_MAPPINGS = {
   "/ecmascript": Editor.modes.js,
   "/javascript": Editor.modes.js,
   "/x-javascript": Editor.modes.js,
@@ -81,19 +85,26 @@ DetailsView.prototype = {
     dirty: [],
     // the most recently received attachment data for the request
     latestData: null,
   },
 
   /**
    * Initialization function, called when the network monitor is started.
    */
-  initialize: function () {
+  initialize: function (store) {
     dumpn("Initializing the DetailsView");
 
+    this._timingsPanelNode = $("#react-timings-tabpanel-hook");
+
+    ReactDOM.render(Provider(
+      { store },
+      TimingsPanel()
+    ), this._timingsPanelNode);
+
     this.widget = $("#event-details-pane");
     this.sidebar = new ToolSidebar(this.widget, this, "netmonitor", {
       disableTelemetry: true,
       showAllTabsMenu: true
     });
 
     this._headers = new VariablesView($("#all-headers"),
       Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
@@ -129,16 +140,17 @@ DetailsView.prototype = {
     $("tabpanels", this.widget).addEventListener("select", this._onTabSelect);
   },
 
   /**
    * Destruction function, called when the network monitor is closed.
    */
   destroy: function () {
     dumpn("Destroying the DetailsView");
+    ReactDOM.unmountComponentAtNode(this._timingsPanelNode);
     this.sidebar.destroy();
     $("tabpanels", this.widget).removeEventListener("select",
       this._onTabSelect);
   },
 
   /**
    * Populates this view with the specified data.
    *
@@ -238,20 +250,16 @@ DetailsView.prototype = {
             src.requestHeaders,
             src.requestHeadersFromUploadStream,
             src.requestPostData);
           break;
         // "Response"
         case 3:
           yield view._setResponseBody(src.url, src.responseContent);
           break;
-        // "Timings"
-        case 4:
-          yield view._setTimingsInformation(src.eventTimings);
-          break;
         // "Security"
         case 5:
           yield view._setSecurityInfo(src.securityInfo, src.url);
           break;
         // "Preview"
         case 6:
           yield view._setHtmlPreview(src.responseContent);
           break;
@@ -681,97 +689,16 @@ DetailsView.prototype = {
         }
       }
     }
 
     window.emit(EVENTS.RESPONSE_BODY_DISPLAYED);
   }),
 
   /**
-   * Sets the timings information shown in this view.
-   *
-   * @param object response
-   *        The message received from the server.
-   */
-  _setTimingsInformation: function (response) {
-    if (!response) {
-      return;
-    }
-    let { blocked, dns, connect, send, wait, receive } = response.timings;
-
-    let tabboxWidth = $("#details-pane").getAttribute("width");
-
-    // Other nodes also take some space.
-    let availableWidth = tabboxWidth / 2;
-    let scale = (response.totalTime > 0 ?
-                 Math.max(availableWidth / response.totalTime, 0) :
-                 0);
-
-    $("#timings-summary-blocked .requests-menu-timings-box")
-      .setAttribute("width", blocked * scale);
-    $("#timings-summary-blocked .requests-menu-timings-total")
-      .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", blocked));
-
-    $("#timings-summary-dns .requests-menu-timings-box")
-      .setAttribute("width", dns * scale);
-    $("#timings-summary-dns .requests-menu-timings-total")
-      .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", dns));
-
-    $("#timings-summary-connect .requests-menu-timings-box")
-      .setAttribute("width", connect * scale);
-    $("#timings-summary-connect .requests-menu-timings-total")
-      .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", connect));
-
-    $("#timings-summary-send .requests-menu-timings-box")
-      .setAttribute("width", send * scale);
-    $("#timings-summary-send .requests-menu-timings-total")
-      .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", send));
-
-    $("#timings-summary-wait .requests-menu-timings-box")
-      .setAttribute("width", wait * scale);
-    $("#timings-summary-wait .requests-menu-timings-total")
-      .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", wait));
-
-    $("#timings-summary-receive .requests-menu-timings-box")
-      .setAttribute("width", receive * scale);
-    $("#timings-summary-receive .requests-menu-timings-total")
-      .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", receive));
-
-    $("#timings-summary-dns .requests-menu-timings-box")
-      .style.transform = "translateX(" + (scale * blocked) + "px)";
-    $("#timings-summary-connect .requests-menu-timings-box")
-      .style.transform = "translateX(" + (scale * (blocked + dns)) + "px)";
-    $("#timings-summary-send .requests-menu-timings-box")
-      .style.transform =
-        "translateX(" + (scale * (blocked + dns + connect)) + "px)";
-    $("#timings-summary-wait .requests-menu-timings-box")
-      .style.transform =
-        "translateX(" + (scale * (blocked + dns + connect + send)) + "px)";
-    $("#timings-summary-receive .requests-menu-timings-box")
-      .style.transform =
-        "translateX(" + (scale * (blocked + dns + connect + send + wait)) +
-          "px)";
-
-    $("#timings-summary-dns .requests-menu-timings-total")
-      .style.transform = "translateX(" + (scale * blocked) + "px)";
-    $("#timings-summary-connect .requests-menu-timings-total")
-      .style.transform = "translateX(" + (scale * (blocked + dns)) + "px)";
-    $("#timings-summary-send .requests-menu-timings-total")
-      .style.transform =
-        "translateX(" + (scale * (blocked + dns + connect)) + "px)";
-    $("#timings-summary-wait .requests-menu-timings-total")
-      .style.transform =
-        "translateX(" + (scale * (blocked + dns + connect + send)) + "px)";
-    $("#timings-summary-receive .requests-menu-timings-total")
-      .style.transform =
-        "translateX(" + (scale * (blocked + dns + connect + send + wait)) +
-         "px)";
-  },
-
-  /**
    * Sets the preview for HTML responses shown in this view.
    *
    * @param object response
    *        The message received from the server.
    * @return object
    *        A promise that is resolved when the html preview is rendered.
    */
   _setHtmlPreview: Task.async(function* (response) {
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -45,17 +45,17 @@ var NetMonitorView = {
   /**
    * Initializes the network monitor view.
    */
   initialize: function () {
     this._initializePanes();
 
     this.Toolbar.initialize(gStore);
     this.RequestsMenu.initialize(gStore);
-    this.NetworkDetails.initialize();
+    this.NetworkDetails.initialize(gStore);
     this.CustomRequest.initialize();
     this.PerformanceStatistics.initialize(gStore);
   },
 
   /**
    * Destroys the network monitor view.
    */
   destroy: function () {
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -257,66 +257,18 @@
                              crop="end"
                              flex="1"/>
                     </hbox>
                   </vbox>
                 </vbox>
               </tabpanel>
               <tabpanel id="timings-tabpanel"
                         class="tabpanel-content">
-                <vbox flex="1">
-                  <hbox id="timings-summary-blocked"
-                        class="tabpanel-summary-container"
-                        align="center">
-                    <label class="plain tabpanel-summary-label"
-                           data-localization="content=netmonitor.timings.blocked"/>
-                    <hbox class="requests-menu-timings-box blocked"/>
-                    <label class="plain requests-menu-timings-total"/>
-                  </hbox>
-                  <hbox id="timings-summary-dns"
-                        class="tabpanel-summary-container"
-                        align="center">
-                    <label class="plain tabpanel-summary-label"
-                           data-localization="content=netmonitor.timings.dns"/>
-                    <hbox class="requests-menu-timings-box dns"/>
-                    <label class="plain requests-menu-timings-total"/>
-                  </hbox>
-                  <hbox id="timings-summary-connect"
-                        class="tabpanel-summary-container"
-                        align="center">
-                    <label class="plain tabpanel-summary-label"
-                           data-localization="content=netmonitor.timings.connect"/>
-                    <hbox class="requests-menu-timings-box connect"/>
-                    <label class="plain requests-menu-timings-total"/>
-                  </hbox>
-                  <hbox id="timings-summary-send"
-                        class="tabpanel-summary-container"
-                        align="center">
-                    <label class="plain tabpanel-summary-label"
-                           data-localization="content=netmonitor.timings.send"/>
-                    <hbox class="requests-menu-timings-box send"/>
-                    <label class="plain requests-menu-timings-total"/>
-                  </hbox>
-                  <hbox id="timings-summary-wait"
-                        class="tabpanel-summary-container"
-                        align="center">
-                    <label class="plain tabpanel-summary-label"
-                           data-localization="content=netmonitor.timings.wait"/>
-                    <hbox class="requests-menu-timings-box wait"/>
-                    <label class="plain requests-menu-timings-total"/>
-                  </hbox>
-                  <hbox id="timings-summary-receive"
-                        class="tabpanel-summary-container"
-                        align="center">
-                    <label class="plain tabpanel-summary-label"
-                           data-localization="content=netmonitor.timings.receive"/>
-                    <hbox class="requests-menu-timings-box receive"/>
-                    <label class="plain requests-menu-timings-total"/>
-                  </hbox>
-                </vbox>
+                <html:div xmlns="http://www.w3.org/1999/xhtml"
+                          id="react-timings-tabpanel-hook"/>
               </tabpanel>
               <tabpanel id="security-tabpanel"
                         class="tabpanel-content">
                   <vbox id="security-error"
                         class="tabpanel-summary-container"
                         flex="1">
                     <label class="plain tabpanel-summary-label"
                            data-localization="content=netmonitor.security.error"/>
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -542,25 +542,27 @@
   transform-origin: left center;
 }
 
 .requests-menu-timings-total:-moz-locale-dir(rtl) {
   transform-origin: right center;
 }
 
 .requests-menu-timings-total {
+  display: inline-block;
   padding-inline-start: 4px;
   font-size: 85%;
   font-weight: 600;
   white-space: nowrap;
   /* This node should not be scaled - apply a reversed transformation */
   transform: scaleX(var(--timings-rev-scale));
 }
 
 .requests-menu-timings-box {
+  display: inline-block;
   height: 9px;
 }
 
 .theme-firebug .requests-menu-timings-box {
   background-image: linear-gradient(rgba(255, 255, 255, 0.3), rgba(0, 0, 0, 0.2));
   height: 16px;
 }
 
@@ -675,16 +677,17 @@
 
 /* Summary tabpanel */
 
 .tabpanel-summary-container {
   padding: 1px;
 }
 
 .tabpanel-summary-label {
+  display: inline-block;
   padding-inline-start: 4px;
   padding-inline-end: 3px;
   font-weight: 600;
 }
 
 .tabpanel-summary-value {
   color: inherit;
   padding-inline-start: 3px;
@@ -747,28 +750,38 @@
 
 #response-preview {
   display: -moz-box;
   -moz-box-flex: 1;
 }
 
 /* Timings tabpanel */
 
+#timings-tabpanel .tabpanel-summary-container {
+  display: flex;
+}
+
 #timings-tabpanel .tabpanel-summary-label {
   width: 10em;
 }
 
+#timings-tabpanel .requests-menu-timings-container {
+  display: flex;
+  flex: 1;
+  align-items: center;
+}
+
+#timings-tabpanel .requests-menu-timings-offset {
+  transition: width 0.2s ease-out;
+}
+
 #timings-tabpanel .requests-menu-timings-box {
-  transition: transform 0.2s ease-out;
   border: none;
   min-width: 1px;
-}
-
-#timings-tabpanel .requests-menu-timings-total {
-  transition: transform 0.2s ease-out;
+  transition: width 0.2s ease-out;
 }
 
 .theme-firebug #timings-tabpanel .requests-menu-timings-total {
   color: var(--theme-body-color);
 }
 
 /* Security tabpanel */
 .security-info-section {
@@ -1063,8 +1076,18 @@
 }
 
 /* Responsive sidebar */
 @media (max-width: 700px) {
   :root[platform="linux"] .requests-menu-header-button {
     font-size: 85%;
   }
 }
+
+/*
+ * FIXME: normal html block element cannot fill outer XUL element
+ * This workaround should be removed after sidebar is migrated to react
+ */
+#react-timings-tabpanel-hook {
+  display: -moz-box;
+  -moz-box-orient: vertical;
+  -moz-box-flex: 1;
+}