Bug 1324334 - Migrate Chart.jsm to js and remove xul components in the chart, r=Honza,rickychien
authorSteve Chung <schung@mozilla.com>
Fri, 16 Dec 2016 15:47:03 +0800
changeset 456674 8ee4d028ba89f98b4cbafc8116ec4e0ea0c3b6a6
parent 456673 a98cb2d15ba0e75f6c53fce9021abcd0e0a9ad0f
child 456675 a09e3d9416898c6812da3358cf0a3cf2a971c286
push id40575
push userjwwang@mozilla.com
push dateFri, 06 Jan 2017 02:27:46 +0000
reviewersHonza, rickychien
bugs1324334
milestone53.0a1
Bug 1324334 - Migrate Chart.jsm to js and remove xul components in the chart, r=Honza,rickychien MozReview-Commit-ID: CvVJlolZ8pZ
devtools/client/netmonitor/netmonitor-controller.js
devtools/client/netmonitor/netmonitor.xul
devtools/client/netmonitor/statistics-view.js
devtools/client/netmonitor/test/browser_net_charts-01.js
devtools/client/netmonitor/test/browser_net_charts-02.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-06.js
devtools/client/netmonitor/test/browser_net_charts-07.js
devtools/client/shared/widgets/Chart.js
devtools/client/shared/widgets/Chart.jsm
devtools/client/shared/widgets/moz.build
devtools/client/themes/netmonitor.css
devtools/shared/gcli/commands/csscoverage.js
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -20,18 +20,16 @@ const { configureStore } = require("./st
 const Actions = require("./actions/index");
 const { getDisplayedRequestById } = require("./selectors/index");
 const { Prefs } = require("./prefs");
 
 XPCOMUtils.defineConstant(window, "EVENTS", EVENTS);
 XPCOMUtils.defineConstant(window, "ACTIVITY_TYPE", ACTIVITY_TYPE);
 XPCOMUtils.defineConstant(window, "Editor", Editor);
 XPCOMUtils.defineConstant(window, "Prefs", Prefs);
-XPCOMUtils.defineLazyModuleGetter(window, "Chart",
-  "resource://devtools/client/shared/widgets/Chart.jsm");
 
 // Initialize the global Redux store
 window.gStore = configureStore();
 
 /**
  * Object defining the network monitor controller components.
  */
 var NetMonitorController = {
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -279,24 +279,23 @@
           </tabbox>
         </deck>
       </hbox>
 
     </vbox>
 
     <html:div id="network-statistics-view">
       <html:div id="network-statistics-toolbar"
-               class="devtools-toolbar">
+                class="devtools-toolbar">
         <html:div xmlns="http://www.w3.org/1999/xhtml"
                   id="react-statistics-back-hook"/>
       </html:div>
-      <box id="network-statistics-charts"
-           class="devtools-responsive-container"
-           flex="1">
-        <vbox id="primed-cache-chart" pack="center" flex="1"/>
-        <splitter id="network-statistics-view-splitter"
-                  class="devtools-side-splitter"/>
-        <vbox id="empty-cache-chart" pack="center" flex="1"/>
-      </box>
+      <html:div id="network-statistics-charts"
+                class="devtools-responsive-container">
+        <html:div id="primed-cache-chart"/>
+        <html:div id="network-statistics-view-splitter"
+                  class="split-box devtools-side-splitter"/>
+        <html:div id="empty-cache-chart"/>
+      </html:div>
     </html:div>
   </deck>
 
 </window>
--- a/devtools/client/netmonitor/statistics-view.js
+++ b/devtools/client/netmonitor/statistics-view.js
@@ -2,28 +2,25 @@
  * 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/. */
 
 /* eslint-disable mozilla/reject-some-requires */
 /* globals $, window, document */
 
 "use strict";
 
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const { PluralForm } = require("devtools/shared/plural-form");
 const { Filters } = require("./filter-predicates");
 const { L10N } = require("./l10n");
 const { EVENTS } = require("./events");
 const { DOM } = require("devtools/client/shared/vendor/react");
 const { button } = DOM;
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const Actions = require("./actions/index");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Chart",
-  "resource://devtools/client/shared/widgets/Chart.jsm");
+const { Chart } = require("devtools/client/shared/widgets/Chart");
 
 const REQUEST_TIME_DECIMALS = 2;
 const CONTENT_SIZE_DECIMALS = 2;
 
 // px
 const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
 
 /**
@@ -33,16 +30,17 @@ function StatisticsView() {
 }
 
 StatisticsView.prototype = {
   /**
    * Initialization function, called when the statistics view is started.
    */
   initialize: function (store) {
     this.store = store;
+    this.Chart = Chart;
     this._backButton = $("#react-statistics-back-hook");
 
     let backStr = L10N.getStr("netmonitor.backButton");
     ReactDOM.render(button({
       id: "network-statistics-back-button",
       className: "devtools-toolbarbutton",
       "data-text-only": "true",
       title: backStr,
@@ -144,28 +142,28 @@ StatisticsView.prototype = {
   },
 
   /**
    * Adds a specific chart to this container.
    *
    * @param object
    *        An object containing all or some the following properties:
    *          - id: either "#primed-cache-chart" or "#empty-cache-chart"
-   *          - title/data/strings/totals/sorted: @see Chart.jsm for details
+   *          - title/data/strings/totals/sorted: @see Chart.js for details
    */
   _createChart: function ({ id, title, data, strings, totals, sorted }) {
     let container = $(id);
 
     // Nuke all existing charts of the specified type.
     while (container.hasChildNodes()) {
       container.firstChild.remove();
     }
 
     // Create a new chart.
-    let chart = Chart.PieTable(document, {
+    let chart = this.Chart.PieTable(document, {
       diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
       title: L10N.getStr(title),
       data: data,
       strings: strings,
       totals: totals,
       sorted: sorted
     });
 
@@ -175,17 +173,17 @@ StatisticsView.prototype = {
       this.store.dispatch(Actions.openStatistics(false));
     });
 
     container.appendChild(chart.node);
   },
 
   /**
    * Sanitizes the data source used for creating charts, to follow the
-   * data format spec defined in Chart.jsm.
+   * data format spec defined in Chart.js.
    *
    * @param array items
    *        A collection of request items used as the data source for the chart.
    * @param boolean emptyCache
    *        True if the cache is considered enabled, false for disabled.
    */
   _sanitizeChartDataSource: function (items, emptyCache) {
     let data = [
--- a/devtools/client/netmonitor/test/browser_net_charts-01.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-01.js
@@ -6,17 +6,18 @@
 /**
  * Makes sure Pie Charts have the right internal structure.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  const { Chart } = NetMonitorView.Statistics;
 
   let pie = Chart.Pie(document, {
     width: 100,
     height: 100,
     data: [{
       size: 1,
       label: "foo"
     }, {
--- a/devtools/client/netmonitor/test/browser_net_charts-02.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-02.js
@@ -9,17 +9,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let pie = Chart.Pie(document, {
     data: null,
     width: 100,
     height: 100
   });
 
   let node = pie.node;
--- a/devtools/client/netmonitor/test/browser_net_charts-03.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-03.js
@@ -8,17 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let table = Chart.Table(document, {
     title: "Table title",
     data: [{
       label1: 1,
       label2: 11.1
     }, {
       label1: 2,
@@ -43,64 +44,64 @@ add_task(function* () {
   let rows = grid.querySelectorAll(".table-chart-row");
   let sums = node.querySelectorAll(".table-chart-summary-label");
 
   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.getAttribute("value"), "Table title",
+  is(title.textContent, "Table title",
     "The title node displays the correct text.");
 
   is(rows.length, 3, "There should be 3 table chart rows created.");
 
   ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the firt row.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "label1",
+  is(rows[0].querySelectorAll("span")[0].getAttribute("name"), "label1",
     "The first column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label2",
+  is(rows[0].querySelectorAll("span")[1].getAttribute("name"), "label2",
     "The second column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "1",
+  is(rows[0].querySelectorAll("span")[0].textContent, "1",
     "The first column of the first row displays the correct text.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("value"), "11.1foo",
+  is(rows[0].querySelectorAll("span")[1].textContent, "11.1foo",
     "The second column of the first row displays the correct text.");
 
   ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the second row.");
-  is(rows[1].querySelectorAll("label")[0].getAttribute("name"), "label1",
+  is(rows[1].querySelectorAll("span")[0].getAttribute("name"), "label1",
     "The first column of the second row exists.");
-  is(rows[1].querySelectorAll("label")[1].getAttribute("name"), "label2",
+  is(rows[1].querySelectorAll("span")[1].getAttribute("name"), "label2",
     "The second column of the second row exists.");
-  is(rows[1].querySelectorAll("label")[0].getAttribute("value"), "2",
+  is(rows[1].querySelectorAll("span")[0].textContent, "2",
     "The first column of the second row displays the correct text.");
-  is(rows[1].querySelectorAll("label")[1].getAttribute("value"), "12.2bar",
+  is(rows[1].querySelectorAll("span")[1].textContent, "12.2bar",
     "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.");
-  is(rows[2].querySelectorAll("label")[0].getAttribute("name"), "label1",
+  is(rows[2].querySelectorAll("span")[0].getAttribute("name"), "label1",
     "The first column of the third row exists.");
-  is(rows[2].querySelectorAll("label")[1].getAttribute("name"), "label2",
+  is(rows[2].querySelectorAll("span")[1].getAttribute("name"), "label2",
     "The second column of the third row exists.");
-  is(rows[2].querySelectorAll("label")[0].getAttribute("value"), "3",
+  is(rows[2].querySelectorAll("span")[0].textContent, "3",
     "The first column of the third row displays the correct text.");
-  is(rows[2].querySelectorAll("label")[1].getAttribute("value"), "13.3baz",
+  is(rows[2].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].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[0].textContent,
     "Hello 6",
     "The first sum's value is correct.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"),
     "label2",
     "The second sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[1].textContent,
     "World 36.60",
     "The second sum's value is correct.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_charts-04.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-04.js
@@ -9,17 +9,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   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)
     }
@@ -32,44 +33,44 @@ add_task(function* () {
   let rows = grid.querySelectorAll(".table-chart-row");
   let sums = node.querySelectorAll(".table-chart-summary-label");
 
   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.getAttribute("value"), "Table title",
+  is(title.textContent, "Table title",
     "The title node displays the correct text.");
 
   is(rows.length, 1, "There should be 1 table chart row created.");
 
   ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the firt row.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "size",
+  is(rows[0].querySelectorAll("span")[0].getAttribute("name"), "size",
     "The first column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label",
+  is(rows[0].querySelectorAll("span")[1].getAttribute("name"), "label",
     "The second column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "",
+  is(rows[0].querySelectorAll("span")[0].textContent, "",
     "The first column of the first row displays the correct text.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("value"),
+  is(rows[0].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",
     "The first sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[0].textContent,
     "Hello 0",
     "The first sum's value is correct.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"),
     "label2",
     "The second sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[1].textContent,
     "World 0",
     "The second sum's value is correct.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_charts-05.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-05.js
@@ -8,17 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let chart = Chart.PieTable(document, {
     title: "Table title",
     data: [{
       size: 1,
       label: 11.1
     }, {
       size: 2,
--- a/devtools/client/netmonitor/test/browser_net_charts-06.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-06.js
@@ -8,17 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let pie = Chart.Pie(document, {
     data: [],
     width: 100,
     height: 100
   });
 
   let node = pie.node;
--- a/devtools/client/netmonitor/test/browser_net_charts-07.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-07.js
@@ -8,17 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let table = Chart.Table(document, {
     data: [],
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
     }
   });
@@ -28,36 +29,36 @@ add_task(function* () {
   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.");
 
   ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the firt row.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "size",
+  is(rows[0].querySelectorAll("span")[0].getAttribute("name"), "size",
     "The first column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label",
+  is(rows[0].querySelectorAll("span")[1].getAttribute("name"), "label",
     "The second column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "",
+  is(rows[0].querySelectorAll("span")[0].textContent, "",
     "The first column of the first row displays the correct text.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("value"),
+  is(rows[0].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.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[0].textContent,
     "Hello 0",
     "The first sum's value is correct.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"),
     "label2",
     "The second sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[1].textContent,
     "World 0",
     "The second sum's value is correct.");
 
   yield teardown(monitor);
 });
rename from devtools/client/shared/widgets/Chart.jsm
rename to devtools/client/shared/widgets/Chart.js
--- a/devtools/client/shared/widgets/Chart.jsm
+++ b/devtools/client/shared/widgets/Chart.js
@@ -1,37 +1,25 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 Cu = Components.utils;
-
 const NET_STRINGS_URI = "devtools/client/locales/netmonitor.properties";
 const SVG_NS = "http://www.w3.org/2000/svg";
 const PI = Math.PI;
 const TAU = PI * 2;
 const EPSILON = 0.0000001;
 const NAMED_SLICE_MIN_ANGLE = TAU / 8;
 const NAMED_SLICE_TEXT_DISTANCE_RATIO = 1.9;
 const HOVERED_SLICE_TRANSLATE_DISTANCE_RATIO = 20;
 
-const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { LocalizationHelper } = require("devtools/shared/l10n");
-
-this.EXPORTED_SYMBOLS = ["Chart"];
-
-/**
- * Localization convenience methods.
- */
-var L10N = new LocalizationHelper(NET_STRINGS_URI);
+const L10N = new LocalizationHelper(NET_STRINGS_URI);
 
 /**
  * A factory for creating charts.
  * Example usage: let myChart = Chart.Pie(document, { ... });
  */
 var Chart = {
   Pie: createPieChart,
   Table: createTableChart,
@@ -100,34 +88,35 @@ function PieTableChart(node, pie, table)
  *          - sorted: a flag specifying if the `data` should be sorted
  *                    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 }) {
+function createPieTableChart(document,
+                             { title, diameter, data, strings, totals, sorted }) {
   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
   });
 
-  let container = document.createElement("hbox");
+  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);
 
   pie.on("click", (event, item) => {
     proxy.emit(event, item);
@@ -202,21 +191,21 @@ function createPieChart(document, { data
   radius = radius || (width + height) / 4;
   let isPlaceholder = false;
 
   // Filter out very small sizes, as they'll just render invisible slices.
   data = data ? data.filter(e => e.size > EPSILON) : null;
 
   // If there's no data available, display an empty placeholder.
   if (!data) {
-    data = loadingPieChartData;
+    data = loadingPieChartData();
     isPlaceholder = true;
   }
   if (!data.length) {
-    data = emptyPieChartData;
+    data = emptyPieChartData();
     isPlaceholder = true;
   }
 
   let container = document.createElementNS(SVG_NS, "svg");
   container.setAttribute("class", "generic-chart-container pie-chart-container");
   container.setAttribute("pack", "center");
   container.setAttribute("flex", "1");
   container.setAttribute("width", width);
@@ -340,100 +329,103 @@ function createPieChart(document, { data
  */
 function createTableChart(document, { title, data, strings, totals }) {
   strings = strings || {};
   totals = totals || {};
   let isPlaceholder = false;
 
   // If there's no data available, display an empty placeholder.
   if (!data) {
-    data = loadingTableChartData;
+    data = loadingTableChartData();
     isPlaceholder = true;
   }
   if (!data.length) {
-    data = emptyTableChartData;
+    data = emptyTableChartData();
     isPlaceholder = true;
   }
 
-  let container = document.createElement("vbox");
+  let container = document.createElement("div");
   container.className = "generic-chart-container table-chart-container";
   container.setAttribute("pack", "center");
   container.setAttribute("flex", "1");
   container.setAttribute("rows", data.length);
   container.setAttribute("placeholder", isPlaceholder);
+  container.setAttribute("style", "-moz-box-orient: vertical");
 
   let proxy = new TableChart(container);
 
-  let titleNode = document.createElement("label");
+  let titleNode = document.createElement("span");
   titleNode.className = "plain table-chart-title";
-  titleNode.setAttribute("value", title);
+  titleNode.textContent = title;
   container.appendChild(titleNode);
 
-  let tableNode = document.createElement("vbox");
+  let tableNode = document.createElement("div");
   tableNode.className = "plain table-chart-grid";
+  tableNode.setAttribute("style", "-moz-box-orient: vertical");
   container.appendChild(tableNode);
 
   for (let rowInfo of data) {
-    let rowNode = document.createElement("hbox");
+    let rowNode = document.createElement("div");
     rowNode.className = "table-chart-row";
     rowNode.setAttribute("align", "center");
 
-    let boxNode = document.createElement("hbox");
+    let boxNode = document.createElement("div");
     boxNode.className = "table-chart-row-box chart-colored-blob";
     boxNode.setAttribute("name", rowInfo.label);
     rowNode.appendChild(boxNode);
 
     for (let [key, value] of Object.entries(rowInfo)) {
       let index = data.indexOf(rowInfo);
       let stringified = strings[key] ? strings[key](value, index) : value;
-      let labelNode = document.createElement("label");
+      let labelNode = document.createElement("span");
       labelNode.className = "plain table-chart-row-label";
       labelNode.setAttribute("name", key);
-      labelNode.setAttribute("value", stringified);
+      labelNode.textContent = stringified;
       rowNode.appendChild(labelNode);
     }
 
     proxy.rows.set(rowInfo, rowNode);
     delegate(proxy, ["click", "mouseover", "mouseout"], rowNode, rowInfo);
     tableNode.appendChild(rowNode);
   }
 
-  let totalsNode = document.createElement("vbox");
+  let totalsNode = document.createElement("div");
   totalsNode.className = "table-chart-totals";
+  totalsNode.setAttribute("style", "-moz-box-orient: vertical");
 
   for (let [key, value] of Object.entries(totals)) {
     let total = data.reduce((acc, e) => acc + e[key], 0);
-    let stringified = totals[key] ? totals[key](total || 0) : total;
-    let labelNode = document.createElement("label");
+    let stringified = value ? value(total || 0) : total;
+    let labelNode = document.createElement("span");
     labelNode.className = "plain table-chart-summary-label";
     labelNode.setAttribute("name", key);
-    labelNode.setAttribute("value", stringified);
+    labelNode.textContent = stringified;
     totalsNode.appendChild(labelNode);
   }
 
   container.appendChild(totalsNode);
 
   return proxy;
 }
 
-XPCOMUtils.defineLazyGetter(this, "loadingPieChartData", () => {
+function loadingPieChartData() {
   return [{ size: 1, label: L10N.getStr("pieChart.loading") }];
-});
+}
 
-XPCOMUtils.defineLazyGetter(this, "emptyPieChartData", () => {
+function emptyPieChartData() {
   return [{ size: 1, label: L10N.getStr("pieChart.unavailable") }];
-});
+}
 
-XPCOMUtils.defineLazyGetter(this, "loadingTableChartData", () => {
+function loadingTableChartData() {
   return [{ size: "", label: L10N.getStr("tableChart.loading") }];
-});
+}
 
-XPCOMUtils.defineLazyGetter(this, "emptyTableChartData", () => {
+function emptyTableChartData() {
   return [{ size: "", label: L10N.getStr("tableChart.unavailable") }];
-});
+}
 
 /**
  * Delegates DOM events emitted by an nsIDOMNode to an EventEmitter proxy.
  *
  * @param EventEmitter emitter
  *        The event emitter proxy instance.
  * @param array events
  *        An array of events, e.g. ["mouseover", "mouseout"].
@@ -442,8 +434,10 @@ XPCOMUtils.defineLazyGetter(this, "empty
  * @param any args
  *        The arguments passed when emitting events through the proxy.
  */
 function delegate(emitter, events, node, args) {
   for (let event of events) {
     node.addEventListener(event, emitter.emit.bind(emitter, event, args));
   }
 }
+
+exports.Chart = Chart;
--- a/devtools/client/shared/widgets/moz.build
+++ b/devtools/client/shared/widgets/moz.build
@@ -7,17 +7,17 @@
 DIRS += [
     'tooltip',
 ]
 
 DevToolsModules(
     'AbstractTreeItem.jsm',
     'BarGraphWidget.js',
     'BreadcrumbsWidget.jsm',
-    'Chart.jsm',
+    'Chart.js',
     'CubicBezierPresets.js',
     'CubicBezierWidget.js',
     'FastListWidget.js',
     'FilterWidget.js',
     'FlameGraph.js',
     'Graphs.js',
     'GraphsWorker.js',
     'LineGraphWidget.js',
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -1137,17 +1137,33 @@
 
 .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
   cursor: default;
   text-decoration: none;
 }
 
 /*
  * FIXME: normal html block element cannot fill outer XUL element
- * This workaround should be removed after sidebar is migrated to react
+ * This workaround should be removed after netmonitor is migrated to react
  */
 #react-preview-tabpanel-hook,
 #react-security-tabpanel-hook,
-#react-timings-tabpanel-hook {
+#react-timings-tabpanel-hook,
+#network-statistics-charts,
+#primed-cache-chart,
+#empty-cache-chart {
   display: -moz-box;
-  -moz-box-orient: vertical;
   -moz-box-flex: 1;
 }
+
+/* For vbox */
+#react-preview-tabpanel-hook,
+#react-security-tabpanel-hook,
+#react-timings-tabpanel-hook,
+#primed-cache-chart,
+#empty-cache-chart {
+  -moz-box-orient: vertical;
+}
+
+#primed-cache-chart,
+#empty-cache-chart {
+  -moz-box-pack: center;
+}
--- a/devtools/shared/gcli/commands/csscoverage.js
+++ b/devtools/shared/gcli/commands/csscoverage.js
@@ -5,17 +5,17 @@
 "use strict";
 
 const domtemplate = require("gcli/util/domtemplate");
 const csscoverage = require("devtools/shared/fronts/csscoverage");
 const l10n = csscoverage.l10n;
 
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 
-loader.lazyImporter(this, "Chart", "resource://devtools/client/shared/widgets/Chart.jsm");
+const { Chart } = require("devtools/client/shared/widgets/Chart");
 
 /**
  * The commands/converters for GCLI
  */
 exports.items = [
   {
     name: "csscoverage",
     hidden: true,