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 325096 76bbb68f8441b2067e45a75baaac6d7c6a00400b
parent 325063 cf2d377f4a5fc4f7b2402406908abd0ca571e3f8
child 325097 31c6008a6773d09b0f84d6db482d25b88d2c1639
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersHonza, jsnajdr
bugs1309187
milestone53.0a1
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;
+}