Bug 1168376 - Show transferred size in request summary. r=Honza
authorLeonardo Couto <leonardo.couto@protonmail.com>
Thu, 09 Feb 2017 01:30:31 +0100
changeset 342919 266f172505701ffe81f84cc9138db3fc7426dc25
parent 342918 366dc31565e39eaca60c55e3ebac8d821e9693cd
child 342920 064b9835eaec3dd129768a2ad7c6924196b0c7ed
push id31366
push usercbook@mozilla.com
push dateWed, 15 Feb 2017 11:25:19 +0000
treeherdermozilla-central@c0807d6938c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersHonza
bugs1168376
milestone54.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 1168376 - Show transferred size in request summary. r=Honza
devtools/client/locales/en-US/netmonitor.properties
devtools/client/netmonitor/components/statistics-panel.js
devtools/client/netmonitor/components/toolbar.js
devtools/client/netmonitor/selectors/requests.js
devtools/client/netmonitor/test/browser_net_charts-03.js
devtools/client/netmonitor/test/browser_net_charts-04.js
devtools/client/netmonitor/test/browser_net_charts-05.js
devtools/client/netmonitor/test/browser_net_charts-07.js
devtools/client/netmonitor/test/browser_net_footer-summary.js
devtools/client/shared/widgets/Chart.js
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -142,22 +142,22 @@ networkMenu.sortedAsc=Sorted ascending
 # LOCALIZATION NOTE (networkMenu.sortedDesc): This is the tooltip displayed
 # in the network table toolbar, for any column that is sorted descending.
 networkMenu.sortedDesc=Sorted descending
 
 # LOCALIZATION NOTE (networkMenu.empty): This is the label displayed
 # in the network table footer when there are no requests available.
 networkMenu.empty=No requests
 
-# LOCALIZATION NOTE (networkMenu.summary): Semi-colon list of plural forms.
+# LOCALIZATION NOTE (networkMenu.summary2): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # This label is displayed in the network table footer providing concise
 # information about all requests. Parameters: #1 is the number of requests,
-# #2 is the size, #3 is the number of seconds.
-networkMenu.summary=One request, #2 KB, #3 s;#1 requests, #2 KB, #3 s
+# #2 is the size, #3 is the transferred size, #4 is the number of seconds.
+networkMenu.summary2=One request, #2 KB (transferred: #3 KB), #4 s;#1 requests, #2 KB (transferred: #3 KB), #4 s
 
 # LOCALIZATION NOTE (networkMenu.sizeB): This is the label displayed
 # in the network menu specifying the size of a request (in bytes).
 networkMenu.sizeB=%S B
 
 # LOCALIZATION NOTE (networkMenu.sizeKB): This is the label displayed
 # in the network menu specifying the size of a request (in kilobytes).
 networkMenu.sizeKB=%S KB
@@ -220,20 +220,32 @@ tableChart.loading=Please wait…
 # for table charts (e.g., in the performance analysis view) when there is
 # no data available, even after loading it.
 tableChart.unavailable=No data available
 
 # LOCALIZATION NOTE (charts.sizeKB): This is the label displayed
 # in pie or table charts specifying the size of a request (in kilobytes).
 charts.sizeKB=%S KB
 
+# LOCALIZATION NOTE (charts.transferredSizeKB): This is the label displayed
+# in pie or table charts specifying the size of a transferred request (in kilobytes).
+charts.transferredSizeKB=%S KB
+
 # LOCALIZATION NOTE (charts.totalS): This is the label displayed
 # in pie or table charts specifying the time for a request to finish (in seconds).
 charts.totalS=%S s
 
+# LOCALIZATION NOTE (charts.totalS): This is the label displayed
+# in the performance analysis view for total requests size, in kilobytes.
+charts.totalSize=Size: %S KB
+
+# LOCALIZATION NOTE (charts.totalTranferredSize): This is the label displayed
+# in the performance analysis view for total transferred size, in kilobytes.
+charts.totalTransferredSize=Transferred Size: %S KB
+
 # LOCALIZATION NOTE (charts.cacheEnabled): This is the label displayed
 # in the performance analysis view for "cache enabled" charts.
 charts.cacheEnabled=Primed cache
 
 # LOCALIZATION NOTE (charts.cacheDisabled): This is the label displayed
 # in the performance analysis view for "cache disabled" charts.
 charts.cacheDisabled=Empty cache
 
@@ -250,16 +262,33 @@ charts.totalSeconds=Time: #1 second;Time
 # LOCALIZATION NOTE (charts.totalCached): This is the label displayed
 # in the performance analysis view for total cached responses.
 charts.totalCached=Cached responses: %S
 
 # LOCALIZATION NOTE (charts.totalCount): This is the label displayed
 # in the performance analysis view for total requests.
 charts.totalCount=Total requests: %S
 
+# LOCALIZATION NOTE (charts.totalCount): This is the label displayed
+# in the header column in the performance analysis view for size of the request.
+charts.size=Size
+
+# LOCALIZATION NOTE (charts.totalCount): This is the label displayed
+# in the header column in the performance analysis view for type of request.
+charts.type=Type
+
+# LOCALIZATION NOTE (charts.totalCount): This is the label displayed
+# in the header column in the performance analysis view for transferred
+# size of the request.
+charts.transferred=Transferred
+
+# LOCALIZATION NOTE (charts.totalCount): This is the label displayed
+# in the header column in the performance analysis view for time of request.
+charts.time=Time
+
 # LOCALIZATION NOTE (netRequest.headers): A label used for Headers tab
 # This tab displays list of HTTP headers
 netRequest.headers=Headers
 
 # LOCALIZATION NOTE (netRequest.response): A label used for Response tab
 # This tab displays HTTP response body
 netRequest.response=Response
 
@@ -748,8 +777,11 @@ netmonitor.custom.cancel=Cancel
 
 # LOCALIZATION NOTE (netmonitor.backButton): This is the label displayed
 # on the button which exists the performance statistics view
 netmonitor.backButton=Back
 
 # LOCALIZATION NOTE (netmonitor.headers.learnMore): This is the label displayed
 # next to a header list item, with a link to external documentation
 netmonitor.headers.learnMore=Learn More
+
+
+
--- a/devtools/client/netmonitor/components/statistics-panel.js
+++ b/devtools/client/netmonitor/components/statistics-panel.js
@@ -48,43 +48,57 @@ const StatisticsPanel = createClass({
     let ready = requests && requests.every((req) =>
       req.contentSize !== undefined && req.mimeType && req.responseHeaders &&
       req.status !== undefined && req.totalTime !== undefined
     );
 
     this.createChart({
       id: "primedCacheChart",
       title: CHARTS_CACHE_ENABLED,
-      data: ready ? this.sanitizeChartDataSource(requests, false) : null,
+      data: ready ? this.sanitizeChartDataSource(requests, false) : null
     });
 
     this.createChart({
       id: "emptyCacheChart",
       title: CHARTS_CACHE_DISABLED,
-      data: ready ? this.sanitizeChartDataSource(requests, true) : null,
+      data: ready ? this.sanitizeChartDataSource(requests, true) : null
     });
   },
 
   createChart({ id, title, data }) {
     // Create a new chart.
     let chart = Chart.PieTable(document, {
       diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
       title,
+      header: {
+        cached: "",
+        count: "",
+        label: L10N.getStr("charts.type"),
+        size: L10N.getStr("charts.size"),
+        transferredSize: L10N.getStr("charts.transferred"),
+        time: L10N.getStr("charts.time")
+      },
       data,
       strings: {
         size: (value) =>
           L10N.getFormatStr("charts.sizeKB", getSizeWithDecimals(value / 1024)),
+        transferredSize: (value) =>
+          L10N.getFormatStr("charts.transferredSizeKB",
+            getSizeWithDecimals(value / 1024)),
         time: (value) =>
-          L10N.getFormatStr("charts.totalS", getTimeWithDecimals(value / 1000)),
+          L10N.getFormatStr("charts.totalS", getTimeWithDecimals(value / 1000))
       },
       totals: {
         cached: (total) => L10N.getFormatStr("charts.totalCached", total),
         count: (total) => L10N.getFormatStr("charts.totalCount", total),
         size: (total) =>
           L10N.getFormatStr("charts.totalSize", getSizeWithDecimals(total / 1024)),
+        transferredSize: total =>
+          L10N.getFormatStr("charts.totalTransferredSize",
+            getSizeWithDecimals(total / 1024)),
         time: (total) => {
           let seconds = total / 1000;
           let string = getTimeWithDecimals(seconds);
           return PluralForm.get(seconds,
             L10N.getStr("charts.totalSeconds")).replace("#1", string);
         },
       },
       sorted: true,
@@ -102,19 +116,26 @@ const StatisticsPanel = createClass({
     while (container.hasChildNodes()) {
       container.firstChild.remove();
     }
 
     container.appendChild(chart.node);
   },
 
   sanitizeChartDataSource(requests, emptyCache) {
-    let data = [
+    const data = [
       "html", "css", "js", "xhr", "fonts", "images", "media", "flash", "ws", "other"
-    ].map((type) => ({ cached: 0, count: 0, label: type, size: 0, time: 0 }));
+    ].map((type) => ({
+      cached: 0,
+      count: 0,
+      label: type,
+      size: 0,
+      transferredSize: 0,
+      time: 0
+    }));
 
     for (let request of requests) {
       let type;
 
       if (Filters.html(request)) {
         // "html"
         type = 0;
       } else if (Filters.css(request)) {
@@ -145,16 +166,17 @@ const StatisticsPanel = createClass({
       } else {
         // "other"
         type = 9;
       }
 
       if (emptyCache || !this.responseIsFresh(request)) {
         data[type].time += request.totalTime || 0;
         data[type].size += request.contentSize || 0;
+        data[type].transferredSize += request.transferredSize || 0;
       } else {
         data[type].cached++;
       }
       data[type].count++;
     }
 
     return data.filter(e => e.count > 0);
   },
--- a/devtools/client/netmonitor/components/toolbar.js
+++ b/devtools/client/netmonitor/components/toolbar.js
@@ -52,22 +52,23 @@ function Toolbar({
   let toggleButtonClassName = [
     "network-details-panel-toggle",
     "devtools-button",
   ];
   if (!networkDetailsOpen) {
     toggleButtonClassName.push("pane-collapsed");
   }
 
-  let { count, bytes, millis } = summary;
+  let { count, contentSize, transferredSize, millis } = summary;
   const text = (count === 0) ? L10N.getStr("networkMenu.empty") :
-    PluralForm.get(count, L10N.getStr("networkMenu.summary"))
+    PluralForm.get(count, L10N.getStr("networkMenu.summary2"))
     .replace("#1", count)
-    .replace("#2", getSizeWithDecimals(bytes / 1024))
-    .replace("#3", getTimeWithDecimals(millis / 1000));
+    .replace("#2", getSizeWithDecimals(contentSize / 1024))
+    .replace("#3", getSizeWithDecimals(transferredSize / 1024))
+    .replace("#4", getTimeWithDecimals(millis / 1000));
 
   const buttons = requestFilterTypes.entrySeq().map(([type, checked]) => {
     let classList = ["menu-filter-button"];
     checked && classList.push("checked");
 
     return (
       button({
         id: `requests-menu-filter-${type}-button`,
--- a/devtools/client/netmonitor/selectors/requests.js
+++ b/devtools/client/netmonitor/selectors/requests.js
@@ -72,26 +72,32 @@ const getDisplayedRequests = createSelec
 const getDisplayedRequestsSummary = createSelector(
   getDisplayedRequests,
   state => state.requests.lastEndedMillis - state.requests.firstStartedMillis,
   (requests, totalMillis) => {
     if (requests.size == 0) {
       return { count: 0, bytes: 0, millis: 0 };
     }
 
-    const totalBytes = requests.reduce((total, item) => {
+    const totalBytes = requests.reduce((totals, item) => {
       if (typeof item.contentSize == "number") {
-        total += item.contentSize;
+        totals.contentSize += item.contentSize;
       }
-      return total;
-    }, 0);
+
+      if (typeof item.transferredSize == "number") {
+        totals.transferredSize += item.transferredSize;
+      }
+
+      return totals;
+    }, { contentSize: 0, transferredSize: 0 });
 
     return {
       count: requests.size,
-      bytes: totalBytes,
+      contentSize: totalBytes.contentSize,
+      transferredSize: totalBytes.transferredSize,
       millis: totalMillis,
     };
   }
 );
 
 const getSelectedRequest = createSelector(
   state => state.requests,
   ({ selectedId, requests }) => selectedId ? requests.get(selectedId) : undefined
--- a/devtools/client/netmonitor/test/browser_net_charts-03.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-03.js
@@ -29,16 +29,20 @@ add_task(function* () {
       label2: 13.3
     }],
     strings: {
       label2: (value, index) => value + ["foo", "bar", "baz"][index]
     },
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
+    },
+    header: {
+      label1: "label1header",
+      label2: "label2header",
     }
   });
 
   let node = table.node;
   let title = node.querySelector(".table-chart-title");
   let grid = node.querySelector(".table-chart-grid");
   let totals = node.querySelector(".table-chart-totals");
   let rows = grid.querySelectorAll(".table-chart-row");
@@ -47,49 +51,59 @@ add_task(function* () {
   ok(node.classList.contains("table-chart-container") &&
      node.classList.contains("generic-chart-container"),
     "A table chart container was created successfully.");
 
   ok(title, "A title node was created successfully.");
   is(title.textContent, "Table title",
     "The title node displays the correct text.");
 
-  is(rows.length, 3, "There should be 3 table chart rows created.");
+  is(rows.length, 4, "There should be 3 table chart rows and a header created.");
 
-  ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
-    "A colored blob exists for the firt row.");
   is(rows[0].querySelectorAll("span")[0].getAttribute("name"), "label1",
-    "The first column of the first row exists.");
+    "The first column of the header exists.");
   is(rows[0].querySelectorAll("span")[1].getAttribute("name"), "label2",
-    "The second column of the first row exists.");
-  is(rows[0].querySelectorAll("span")[0].textContent, "1",
-    "The first column of the first row displays the correct text.");
-  is(rows[0].querySelectorAll("span")[1].textContent, "11.1foo",
-    "The second column of the first row displays the correct text.");
+    "The second column of the header exists.");
+  is(rows[0].querySelectorAll("span")[0].textContent, "label1header",
+    "The first column of the header displays the correct text.");
+  is(rows[0].querySelectorAll("span")[1].textContent, "label2header",
+    "The second column of the header displays the correct text.");
+
 
   ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
-    "A colored blob exists for the second row.");
+    "A colored blob exists for the firt row.");
   is(rows[1].querySelectorAll("span")[0].getAttribute("name"), "label1",
-    "The first column of the second row exists.");
+    "The first column of the first row exists.");
   is(rows[1].querySelectorAll("span")[1].getAttribute("name"), "label2",
-    "The second column of the second row exists.");
-  is(rows[1].querySelectorAll("span")[0].textContent, "2",
-    "The first column of the second row displays the correct text.");
-  is(rows[1].querySelectorAll("span")[1].textContent, "12.2bar",
+    "The second column of the first row exists.");
+  is(rows[1].querySelectorAll("span")[0].textContent, "1",
+    "The first column of the first row displays the correct text.");
+  is(rows[1].querySelectorAll("span")[1].textContent, "11.1foo",
     "The second column of the first row displays the correct text.");
 
   ok(rows[2].querySelector(".table-chart-row-box.chart-colored-blob"),
-    "A colored blob exists for the third row.");
+    "A colored blob exists for the second row.");
   is(rows[2].querySelectorAll("span")[0].getAttribute("name"), "label1",
-    "The first column of the third row exists.");
+    "The first column of the second row exists.");
   is(rows[2].querySelectorAll("span")[1].getAttribute("name"), "label2",
+    "The second column of the second row exists.");
+  is(rows[2].querySelectorAll("span")[0].textContent, "2",
+    "The first column of the second row displays the correct text.");
+  is(rows[2].querySelectorAll("span")[1].textContent, "12.2bar",
+    "The second column of the first row displays the correct text.");
+
+  ok(rows[3].querySelector(".table-chart-row-box.chart-colored-blob"),
+    "A colored blob exists for the third row.");
+  is(rows[3].querySelectorAll("span")[0].getAttribute("name"), "label1",
+    "The first column of the third row exists.");
+  is(rows[3].querySelectorAll("span")[1].getAttribute("name"), "label2",
     "The second column of the third row exists.");
-  is(rows[2].querySelectorAll("span")[0].textContent, "3",
+  is(rows[3].querySelectorAll("span")[0].textContent, "3",
     "The first column of the third row displays the correct text.");
-  is(rows[2].querySelectorAll("span")[1].textContent, "13.3baz",
+  is(rows[3].querySelectorAll("span")[1].textContent, "13.3baz",
     "The second column of the third row displays the correct text.");
 
   is(sums.length, 2, "There should be 2 total summaries created.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"),
     "label1",
     "The first sum's type is correct.");
   is(totals.querySelectorAll(".table-chart-summary-label")[0].textContent,
--- a/devtools/client/netmonitor/test/browser_net_charts-04.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-04.js
@@ -18,16 +18,20 @@ add_task(function* () {
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let table = Chart.Table(document, {
     title: "Table title",
     data: null,
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
+    },
+    header: {
+      label1: "",
+      label2: ""
     }
   });
 
   let node = table.node;
   let title = node.querySelector(".table-chart-title");
   let grid = node.querySelector(".table-chart-grid");
   let totals = node.querySelector(".table-chart-totals");
   let rows = grid.querySelectorAll(".table-chart-row");
@@ -36,27 +40,27 @@ add_task(function* () {
   ok(node.classList.contains("table-chart-container") &&
      node.classList.contains("generic-chart-container"),
     "A table chart container was created successfully.");
 
   ok(title, "A title node was created successfully.");
   is(title.textContent, "Table title",
     "The title node displays the correct text.");
 
-  is(rows.length, 1, "There should be 1 table chart row created.");
+  is(rows.length, 2, "There should be 1 table chart row and a 1 header created.");
 
-  ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
+  ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the firt row.");
-  is(rows[0].querySelectorAll("span")[0].getAttribute("name"), "size",
+  is(rows[1].querySelectorAll("span")[0].getAttribute("name"), "size",
     "The first column of the first row exists.");
-  is(rows[0].querySelectorAll("span")[1].getAttribute("name"), "label",
+  is(rows[1].querySelectorAll("span")[1].getAttribute("name"), "label",
     "The second column of the first row exists.");
-  is(rows[0].querySelectorAll("span")[0].textContent, "",
+  is(rows[1].querySelectorAll("span")[0].textContent, "",
     "The first column of the first row displays the correct text.");
-  is(rows[0].querySelectorAll("span")[1].textContent,
+  is(rows[1].querySelectorAll("span")[1].textContent,
     L10N.getStr("tableChart.loading"),
     "The second column of the first row displays the correct text.");
 
   is(sums.length, 2,
     "There should be 2 total summaries created.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"),
     "label1",
--- a/devtools/client/netmonitor/test/browser_net_charts-05.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-05.js
@@ -29,16 +29,20 @@ add_task(function* () {
       label: 13.3
     }],
     strings: {
       label2: (value, index) => value + ["foo", "bar", "baz"][index]
     },
     totals: {
       size: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label: value => "World " + L10N.numberWithDecimals(value, 2)
+    },
+    header: {
+      label1: "",
+      label2: ""
     }
   });
 
   ok(chart.pie, "The pie chart proxy is accessible.");
   ok(chart.table, "The table chart proxy is accessible.");
 
   let node = chart.node;
   let rows = node.querySelectorAll(".table-chart-row");
@@ -49,14 +53,14 @@ add_task(function* () {
 
   ok(node.querySelector(".table-chart-title"),
     "A title node was created successfully.");
   ok(node.querySelector(".pie-chart-container"),
     "A pie chart was created successfully.");
   ok(node.querySelector(".table-chart-container"),
     "A table chart was created successfully.");
 
-  is(rows.length, 3, "There should be 3 pie chart slices created.");
-  is(rows.length, 3, "There should be 3 table chart rows created.");
-  is(sums.length, 2, "There should be 2 total summaries created.");
+  is(rows.length, 4, "There should be 3 pie chart slices and 1 header created.");
+  is(rows.length, 4, "There should be 3 table chart rows and 1 header created.");
+  is(sums.length, 2, "There should be 2 total summaries and 1 header created.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_charts-07.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-07.js
@@ -16,36 +16,40 @@ add_task(function* () {
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let table = Chart.Table(document, {
     data: [],
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
+    },
+    header: {
+      label1: "",
+      label2: ""
     }
   });
 
   let node = table.node;
   let grid = node.querySelector(".table-chart-grid");
   let totals = node.querySelector(".table-chart-totals");
   let rows = grid.querySelectorAll(".table-chart-row");
   let sums = node.querySelectorAll(".table-chart-summary-label");
 
-  is(rows.length, 1, "There should be 1 table chart row created.");
+  is(rows.length, 2, "There should be 1 table chart row and 1 header created.");
 
-  ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
+  ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the firt row.");
-  is(rows[0].querySelectorAll("span")[0].getAttribute("name"), "size",
+  is(rows[1].querySelectorAll("span")[0].getAttribute("name"), "size",
     "The first column of the first row exists.");
-  is(rows[0].querySelectorAll("span")[1].getAttribute("name"), "label",
+  is(rows[1].querySelectorAll("span")[1].getAttribute("name"), "label",
     "The second column of the first row exists.");
-  is(rows[0].querySelectorAll("span")[0].textContent, "",
+  is(rows[1].querySelectorAll("span")[0].textContent, "",
     "The first column of the first row displays the correct text.");
-  is(rows[0].querySelectorAll("span")[1].textContent,
+  is(rows[1].querySelectorAll("span")[1].textContent,
     L10N.getStr("tableChart.unavailable"),
     "The second column of the first row displays the correct text.");
 
   is(sums.length, 2, "There should be 2 total summaries created.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"),
     "label1",
     "The first sum's type is correct.");
--- a/devtools/client/netmonitor/test/browser_net_footer-summary.js
+++ b/devtools/client/netmonitor/test/browser_net_footer-summary.js
@@ -56,15 +56,16 @@ add_task(function* () {
       is(value, L10N.getStr("networkMenu.empty"),
         "The current summary text is incorrect, expected an 'empty' label.");
       return;
     }
 
     info(`Computed total bytes: ${requestsSummary.bytes}`);
     info(`Computed total millis: ${requestsSummary.millis}`);
 
-    is(value, PluralForm.get(requestsSummary.count, L10N.getStr("networkMenu.summary"))
+    is(value, PluralForm.get(requestsSummary.count, L10N.getStr("networkMenu.summary2"))
       .replace("#1", requestsSummary.count)
-      .replace("#2", L10N.numberWithDecimals(requestsSummary.bytes / 1024, 2))
-      .replace("#3", L10N.numberWithDecimals(requestsSummary.millis / 1000, 2))
+      .replace("#2", L10N.numberWithDecimals(requestsSummary.contentSize / 1024, 2))
+      .replace("#3", L10N.numberWithDecimals(requestsSummary.transferredSize / 1024, 2))
+      .replace("#4", L10N.numberWithDecimals(requestsSummary.millis / 1000, 2))
     , "The current summary text is correct.");
   }
 });
--- a/devtools/client/shared/widgets/Chart.js
+++ b/devtools/client/shared/widgets/Chart.js
@@ -89,31 +89,32 @@ function PieTableChart(node, pie, table)
  *                    ascending by `size`.
  * @return PieTableChart
  *         A pie+table chart proxy instance, which emits the following events:
  *           - "mouseover", when the mouse enters a slice or a row
  *           - "mouseout", when the mouse leaves a slice or a row
  *           - "click", when the mouse enters a slice or a row
  */
 function createPieTableChart(document,
-                             { title, diameter, data, strings, totals, sorted }) {
+                             { title, diameter, data, strings, totals, sorted, header }) {
   if (data && sorted) {
     data = data.slice().sort((a, b) => +(a.size < b.size));
   }
 
   let pie = Chart.Pie(document, {
     width: diameter,
     data: data
   });
 
   let table = Chart.Table(document, {
     title: title,
     data: data,
     strings: strings,
-    totals: totals
+    totals: totals,
+    header: header,
   });
 
   let container = document.createElement("div");
   container.className = "pie-table-chart-container";
   container.appendChild(pie.node);
   container.appendChild(table.node);
 
   let proxy = new PieTableChart(container, pie, table);
@@ -322,17 +323,17 @@ function createPieChart(document, { data
  *                      label2: total => l10n.getFormatStr("...", total),  // 9
  *                    }
  * @return TableChart
  *         A table chart proxy instance, which emits the following events:
  *           - "mouseover", when the mouse enters a row
  *           - "mouseout", when the mouse leaves a row
  *           - "click", when the mouse clicks a row
  */
-function createTableChart(document, { title, data, strings, totals }) {
+function createTableChart(document, { title, data, strings, totals, header }) {
   strings = strings || {};
   totals = totals || {};
   let isPlaceholder = false;
 
   // If there's no data available, display an empty placeholder.
   if (!data) {
     data = loadingTableChartData();
     isPlaceholder = true;
@@ -357,16 +358,34 @@ function createTableChart(document, { ti
   titleNode.textContent = title;
   container.appendChild(titleNode);
 
   let tableNode = document.createElement("div");
   tableNode.className = "plain table-chart-grid";
   tableNode.setAttribute("style", "-moz-box-orient: vertical");
   container.appendChild(tableNode);
 
+  const headerNode = document.createElement("div");
+  headerNode.className = "table-chart-row";
+
+  const headerBoxNode = document.createElement("div");
+  headerBoxNode.className = "table-chart-row-box";
+  headerNode.appendChild(headerBoxNode);
+
+  for (let [key, value] of Object.entries(header)) {
+    let headerLabelNode = document.createElement("span");
+    headerLabelNode.className = "plain table-chart-row-label";
+    headerLabelNode.setAttribute("name", key);
+    headerLabelNode.textContent = value;
+
+    headerNode.appendChild(headerLabelNode);
+  }
+
+  tableNode.appendChild(headerNode);
+
   for (let rowInfo of data) {
     let rowNode = document.createElement("div");
     rowNode.className = "table-chart-row";
     rowNode.setAttribute("align", "center");
 
     let boxNode = document.createElement("div");
     boxNode.className = "table-chart-row-box chart-colored-blob";
     boxNode.setAttribute("name", rowInfo.label);