Bug 1308425 - Move statistics view into standalone module. r=Honza
authorSteve Chung <schung@mozilla.com>
Wed, 19 Oct 2016 18:25:00 +0800
changeset 319901 61627f78c095d7af063ed465c545b8f5233c32e3
parent 319900 42550d2fd6711cc42eb955bb794f556b5a73a237
child 319902 90409a433f31784609b4bcb1f421ec3963c816ed
push id33661
push userryanvm@gmail.com
push dateFri, 28 Oct 2016 19:33:20 +0000
treeherderautoland@61627f78c095 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersHonza
bugs1308425
milestone52.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 1308425 - Move statistics view into standalone module. r=Honza MozReview-Commit-ID: 2iqKSHWpEWw
devtools/client/netmonitor/moz.build
devtools/client/netmonitor/netmonitor-controller.js
devtools/client/netmonitor/netmonitor-view.js
devtools/client/netmonitor/performance-statistics-view.js
--- a/devtools/client/netmonitor/moz.build
+++ b/devtools/client/netmonitor/moz.build
@@ -13,16 +13,17 @@ DIRS += [
 
 DevToolsModules(
     'constants.js',
     'custom-request-view.js',
     'events.js',
     'filter-predicates.js',
     'l10n.js',
     'panel.js',
+    'performance-statistics-view.js',
     'prefs.js',
     'request-utils.js',
     'requests-menu-view.js',
     'sort-predicates.js',
     'store.js',
     'toolbar-view.js',
 )
 
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -38,16 +38,17 @@ const Services = require("Services");
 /* eslint-disable mozilla/reject-some-requires */
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const EventEmitter = require("devtools/shared/event-emitter");
 const Editor = require("devtools/client/sourceeditor/editor");
 const {TimelineFront} = require("devtools/shared/fronts/timeline");
 const {Task} = require("devtools/shared/task");
 const {Prefs} = require("./prefs");
 const {EVENTS} = require("./events");
+const Actions = require("./actions/index");
 
 XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);
 XPCOMUtils.defineConstant(this, "ACTIVITY_TYPE", ACTIVITY_TYPE);
 XPCOMUtils.defineConstant(this, "Editor", Editor);
 XPCOMUtils.defineConstant(this, "Prefs", Prefs);
 
 XPCOMUtils.defineLazyModuleGetter(this, "Chart",
   "resource://devtools/client/shared/widgets/Chart.jsm");
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -14,46 +14,43 @@ XPCOMUtils.defineLazyGetter(this, "Netwo
 
 /* eslint-disable mozilla/reject-some-requires */
 const {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
 /* eslint-disable mozilla/reject-some-requires */
 const {VariablesViewController} = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 const {ToolSidebar} = require("devtools/client/framework/sidebar");
 const {testing: isTesting} = require("devtools/shared/flags");
 const {ViewHelpers, Heritage} = require("devtools/client/shared/widgets/view-helpers");
-const {PluralForm} = require("devtools/shared/plural-form");
 const {Filters} = require("./filter-predicates");
 const {getFormDataSections,
        formDataURI,
        getUriHostPort} = require("./request-utils");
 const {L10N} = require("./l10n");
 const {RequestsMenuView} = require("./requests-menu-view");
 const {CustomRequestView} = require("./custom-request-view");
 const {ToolbarView} = require("./toolbar-view");
 const {configureStore} = require("./store");
-const Actions = require("./actions/index");
+const {PerformanceStatisticsView} = require("./performance-statistics-view");
 
 // Initialize the global redux variables
 var gStore = configureStore();
 
 // ms
 const WDA_DEFAULT_VERIFY_INTERVAL = 50;
 
 // Use longer timeout during testing as the tests need this process to succeed
 // and two seconds is quite short on slow debug builds. The timeout here should
 // be at least equal to the general mochitest timeout of 45 seconds so that this
 // never gets hit during testing.
 // ms
 const WDA_DEFAULT_GIVE_UP_TIMEOUT = isTesting ? 45000 : 2000;
 
 // 100 KB in bytes
 const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 102400;
-const REQUEST_TIME_DECIMALS = 2;
 const HEADERS_SIZE_DECIMALS = 3;
-const CONTENT_SIZE_DECIMALS = 2;
 const CONTENT_MIME_TYPE_MAPPINGS = {
   "/ecmascript": Editor.modes.js,
   "/javascript": Editor.modes.js,
   "/x-javascript": Editor.modes.js,
   "/html": Editor.modes.html,
   "/xhtml": Editor.modes.html,
   "/xml": Editor.modes.html,
   "/atom": Editor.modes.html,
@@ -75,33 +72,32 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = 
   lazyEmptyDelay: 10,
   searchEnabled: true,
   editableValueTooltip: "",
   editableNameTooltip: "",
   preventDisableOnChange: true,
   preventDescriptorModifiers: true,
   eval: () => {}
 };
-// px
-const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
 
 /**
  * Object defining the network monitor view components.
  */
 var NetMonitorView = {
   /**
    * Initializes the network monitor view.
    */
   initialize: function () {
     this._initializePanes();
 
     this.Toolbar.initialize(gStore);
     this.RequestsMenu.initialize(gStore);
     this.NetworkDetails.initialize();
     this.CustomRequest.initialize();
+    this.PerformanceStatistics.initialize(gStore);
   },
 
   /**
    * Destroys the network monitor view.
    */
   destroy: function () {
     this._isDestroyed = true;
     this.Toolbar.destroy();
@@ -1192,259 +1188,23 @@ NetworkDetailsView.prototype = {
   _paramsPostPayload: "",
   _requestHeaders: "",
   _responseHeaders: "",
   _requestCookies: "",
   _responseCookies: ""
 };
 
 /**
- * Functions handling the performance statistics view.
- */
-function PerformanceStatisticsView() {
-}
-
-PerformanceStatisticsView.prototype = {
-  /**
-   * Initializes and displays empty charts in this container.
-   */
-  displayPlaceholderCharts: function () {
-    this._createChart({
-      id: "#primed-cache-chart",
-      title: "charts.cacheEnabled"
-    });
-    this._createChart({
-      id: "#empty-cache-chart",
-      title: "charts.cacheDisabled"
-    });
-    window.emit(EVENTS.PLACEHOLDER_CHARTS_DISPLAYED);
-  },
-
-  /**
-   * Populates and displays the primed cache chart in this container.
-   *
-   * @param array items
-   *        @see this._sanitizeChartDataSource
-   */
-  createPrimedCacheChart: function (items) {
-    this._createChart({
-      id: "#primed-cache-chart",
-      title: "charts.cacheEnabled",
-      data: this._sanitizeChartDataSource(items),
-      strings: this._commonChartStrings,
-      totals: this._commonChartTotals,
-      sorted: true
-    });
-    window.emit(EVENTS.PRIMED_CACHE_CHART_DISPLAYED);
-  },
-
-  /**
-   * Populates and displays the empty cache chart in this container.
-   *
-   * @param array items
-   *        @see this._sanitizeChartDataSource
-   */
-  createEmptyCacheChart: function (items) {
-    this._createChart({
-      id: "#empty-cache-chart",
-      title: "charts.cacheDisabled",
-      data: this._sanitizeChartDataSource(items, true),
-      strings: this._commonChartStrings,
-      totals: this._commonChartTotals,
-      sorted: true
-    });
-    window.emit(EVENTS.EMPTY_CACHE_CHART_DISPLAYED);
-  },
-
-  /**
-   * Common stringifier predicates used for items and totals in both the
-   * "primed" and "empty" cache charts.
-   */
-  _commonChartStrings: {
-    size: value => {
-      let string = L10N.numberWithDecimals(value / 1024, CONTENT_SIZE_DECIMALS);
-      return L10N.getFormatStr("charts.sizeKB", string);
-    },
-    time: value => {
-      let string = L10N.numberWithDecimals(value / 1000, REQUEST_TIME_DECIMALS);
-      return L10N.getFormatStr("charts.totalS", string);
-    }
-  },
-  _commonChartTotals: {
-    size: total => {
-      let string = L10N.numberWithDecimals(total / 1024, CONTENT_SIZE_DECIMALS);
-      return L10N.getFormatStr("charts.totalSize", string);
-    },
-    time: total => {
-      let seconds = total / 1000;
-      let string = L10N.numberWithDecimals(seconds, REQUEST_TIME_DECIMALS);
-      return PluralForm.get(seconds,
-        L10N.getStr("charts.totalSeconds")).replace("#1", string);
-    },
-    cached: total => {
-      return L10N.getFormatStr("charts.totalCached", total);
-    },
-    count: total => {
-      return L10N.getFormatStr("charts.totalCount", total);
-    }
-  },
-
-  /**
-   * 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
-   */
-  _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, {
-      diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
-      title: L10N.getStr(title),
-      data: data,
-      strings: strings,
-      totals: totals,
-      sorted: sorted
-    });
-
-    chart.on("click", (_, item) => {
-      // Reset FilterButtons and enable one filter exclusively
-      gStore.dispatch(Actions.enableFilterOnly(item.label));
-      NetMonitorView.showNetworkInspectorView();
-    });
-
-    container.appendChild(chart.node);
-  },
-
-  /**
-   * Sanitizes the data source used for creating charts, to follow the
-   * data format spec defined in Chart.jsm.
-   *
-   * @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 = [
-      "html", "css", "js", "xhr", "fonts", "images", "media", "flash", "ws", "other"
-    ].map(e => ({
-      cached: 0,
-      count: 0,
-      label: e,
-      size: 0,
-      time: 0
-    }));
-
-    for (let requestItem of items) {
-      let details = requestItem.attachment;
-      let type;
-
-      if (Filters.html(details)) {
-        // "html"
-        type = 0;
-      } else if (Filters.css(details)) {
-        // "css"
-        type = 1;
-      } else if (Filters.js(details)) {
-        // "js"
-        type = 2;
-      } else if (Filters.fonts(details)) {
-        // "fonts"
-        type = 4;
-      } else if (Filters.images(details)) {
-        // "images"
-        type = 5;
-      } else if (Filters.media(details)) {
-        // "media"
-        type = 6;
-      } else if (Filters.flash(details)) {
-        // "flash"
-        type = 7;
-      } else if (Filters.ws(details)) {
-        // "ws"
-        type = 8;
-      } else if (Filters.xhr(details)) {
-        // Verify XHR last, to categorize other mime types in their own blobs.
-        // "xhr"
-        type = 3;
-      } else {
-        // "other"
-        type = 9;
-      }
-
-      if (emptyCache || !responseIsFresh(details)) {
-        data[type].time += details.totalTime || 0;
-        data[type].size += details.contentSize || 0;
-      } else {
-        data[type].cached++;
-      }
-      data[type].count++;
-    }
-
-    return data.filter(e => e.count > 0);
-  },
-};
-
-/**
  * DOM query helper.
+ * TODO: Move it into "dom-utils.js" module and "require" it when needed.
  */
 var $ = (selector, target = document) => target.querySelector(selector);
 var $all = (selector, target = document) => target.querySelectorAll(selector);
 
 /**
- * Checks if the "Expiration Calculations" defined in section 13.2.4 of the
- * "HTTP/1.1: Caching in HTTP" spec holds true for a collection of headers.
- *
- * @param object
- *        An object containing the { responseHeaders, status } properties.
- * @return boolean
- *         True if the response is fresh and loaded from cache.
- */
-function responseIsFresh({ responseHeaders, status }) {
-  // Check for a "304 Not Modified" status and response headers availability.
-  if (status != 304 || !responseHeaders) {
-    return false;
-  }
-
-  let list = responseHeaders.headers;
-  let cacheControl = list.filter(e => {
-    return e.name.toLowerCase() == "cache-control";
-  })[0];
-
-  let expires = list.filter(e => e.name.toLowerCase() == "expires")[0];
-
-  // Check the "Cache-Control" header for a maximum age value.
-  if (cacheControl) {
-    let maxAgeMatch =
-      cacheControl.value.match(/s-maxage\s*=\s*(\d+)/) ||
-      cacheControl.value.match(/max-age\s*=\s*(\d+)/);
-
-    if (maxAgeMatch && maxAgeMatch.pop() > 0) {
-      return true;
-    }
-  }
-
-  // Check the "Expires" header for a valid date.
-  if (expires && Date.parse(expires.value)) {
-    return true;
-  }
-
-  return false;
-}
-
-/**
  * Makes sure certain properties are available on all objects in a data store.
  *
  * @param array dataStore
  *        A list of objects for which to check the availability of properties.
  * @param array mandatoryFields
  *        A list of strings representing properties of objects in dataStore.
  * @return object
  *         A promise resolved when all objects in dataStore contain the
copy from devtools/client/netmonitor/netmonitor-view.js
copy to devtools/client/netmonitor/performance-statistics-view.js
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/performance-statistics-view.js
@@ -1,1214 +1,43 @@
 /* -*- 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/. */
 /* import-globals-from ./netmonitor-controller.js */
-/* globals Prefs, gNetwork, setInterval, setTimeout, clearInterval, clearTimeout, btoa */
-/* exported $, $all */
+/* globals $ */
 "use strict";
 
-XPCOMUtils.defineLazyGetter(this, "NetworkHelper", function () {
-  return require("devtools/shared/webconsole/network-helper");
-});
-
-/* eslint-disable mozilla/reject-some-requires */
-const {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
-/* eslint-disable mozilla/reject-some-requires */
-const {VariablesViewController} = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
-const {ToolSidebar} = require("devtools/client/framework/sidebar");
-const {testing: isTesting} = require("devtools/shared/flags");
-const {ViewHelpers, Heritage} = require("devtools/client/shared/widgets/view-helpers");
 const {PluralForm} = require("devtools/shared/plural-form");
 const {Filters} = require("./filter-predicates");
-const {getFormDataSections,
-       formDataURI,
-       getUriHostPort} = require("./request-utils");
 const {L10N} = require("./l10n");
-const {RequestsMenuView} = require("./requests-menu-view");
-const {CustomRequestView} = require("./custom-request-view");
-const {ToolbarView} = require("./toolbar-view");
-const {configureStore} = require("./store");
 const Actions = require("./actions/index");
 
-// Initialize the global redux variables
-var gStore = configureStore();
-
-// ms
-const WDA_DEFAULT_VERIFY_INTERVAL = 50;
-
-// Use longer timeout during testing as the tests need this process to succeed
-// and two seconds is quite short on slow debug builds. The timeout here should
-// be at least equal to the general mochitest timeout of 45 seconds so that this
-// never gets hit during testing.
-// ms
-const WDA_DEFAULT_GIVE_UP_TIMEOUT = isTesting ? 45000 : 2000;
-
-// 100 KB in bytes
-const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 102400;
 const REQUEST_TIME_DECIMALS = 2;
-const HEADERS_SIZE_DECIMALS = 3;
 const CONTENT_SIZE_DECIMALS = 2;
-const CONTENT_MIME_TYPE_MAPPINGS = {
-  "/ecmascript": Editor.modes.js,
-  "/javascript": Editor.modes.js,
-  "/x-javascript": Editor.modes.js,
-  "/html": Editor.modes.html,
-  "/xhtml": Editor.modes.html,
-  "/xml": Editor.modes.html,
-  "/atom": Editor.modes.html,
-  "/soap": Editor.modes.html,
-  "/vnd.mpeg.dash.mpd": Editor.modes.html,
-  "/rdf": Editor.modes.css,
-  "/rss": Editor.modes.css,
-  "/css": Editor.modes.css
-};
 
-const DEFAULT_EDITOR_CONFIG = {
-  mode: Editor.modes.text,
-  readOnly: true,
-  lineNumbers: true
-};
-const GENERIC_VARIABLES_VIEW_SETTINGS = {
-  lazyEmpty: true,
-  // ms
-  lazyEmptyDelay: 10,
-  searchEnabled: true,
-  editableValueTooltip: "",
-  editableNameTooltip: "",
-  preventDisableOnChange: true,
-  preventDescriptorModifiers: true,
-  eval: () => {}
-};
 // px
 const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
 
 /**
- * Object defining the network monitor view components.
- */
-var NetMonitorView = {
-  /**
-   * Initializes the network monitor view.
-   */
-  initialize: function () {
-    this._initializePanes();
-
-    this.Toolbar.initialize(gStore);
-    this.RequestsMenu.initialize(gStore);
-    this.NetworkDetails.initialize();
-    this.CustomRequest.initialize();
-  },
-
-  /**
-   * Destroys the network monitor view.
-   */
-  destroy: function () {
-    this._isDestroyed = true;
-    this.Toolbar.destroy();
-    this.RequestsMenu.destroy();
-    this.NetworkDetails.destroy();
-    this.CustomRequest.destroy();
-
-    this._destroyPanes();
-  },
-
-  /**
-   * Initializes the UI for all the displayed panes.
-   */
-  _initializePanes: function () {
-    dumpn("Initializing the NetMonitorView panes");
-
-    this._body = $("#body");
-    this._detailsPane = $("#details-pane");
-    this._detailsPaneToggleButton = $("#details-pane-toggle");
-
-    this._collapsePaneString = L10N.getStr("collapseDetailsPane");
-    this._expandPaneString = L10N.getStr("expandDetailsPane");
-
-    this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth);
-    this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight);
-    this.toggleDetailsPane({ visible: false });
-
-    // Disable the performance statistics mode.
-    if (!Prefs.statistics) {
-      $("#request-menu-context-perf").hidden = true;
-      $("#notice-perf-message").hidden = true;
-      $("#requests-menu-network-summary-button").hidden = true;
-    }
-  },
-
-  /**
-   * Destroys the UI for all the displayed panes.
-   */
-  _destroyPanes: Task.async(function* () {
-    dumpn("Destroying the NetMonitorView panes");
-
-    Prefs.networkDetailsWidth = this._detailsPane.getAttribute("width");
-    Prefs.networkDetailsHeight = this._detailsPane.getAttribute("height");
-
-    this._detailsPane = null;
-    this._detailsPaneToggleButton = null;
-
-    for (let p of this._editorPromises.values()) {
-      let editor = yield p;
-      editor.destroy();
-    }
-  }),
-
-  /**
-   * Gets the visibility state of the network details pane.
-   * @return boolean
-   */
-  get detailsPaneHidden() {
-    return this._detailsPane.classList.contains("pane-collapsed");
-  },
-
-  /**
-   * Sets the network details pane hidden or visible.
-   *
-   * @param object flags
-   *        An object containing some of the following properties:
-   *        - visible: true if the pane should be shown, false to hide
-   *        - animated: true to display an animation on toggle
-   *        - delayed: true to wait a few cycles before toggle
-   *        - callback: a function to invoke when the toggle finishes
-   * @param number tabIndex [optional]
-   *        The index of the intended selected tab in the details pane.
-   */
-  toggleDetailsPane: function (flags, tabIndex) {
-    let pane = this._detailsPane;
-    let button = this._detailsPaneToggleButton;
-
-    ViewHelpers.togglePane(flags, pane);
-
-    if (flags.visible) {
-      this._body.classList.remove("pane-collapsed");
-      button.classList.remove("pane-collapsed");
-      button.setAttribute("tooltiptext", this._collapsePaneString);
-    } else {
-      this._body.classList.add("pane-collapsed");
-      button.classList.add("pane-collapsed");
-      button.setAttribute("tooltiptext", this._expandPaneString);
-    }
-
-    if (tabIndex !== undefined) {
-      $("#event-details-pane").selectedIndex = tabIndex;
-    }
-  },
-
-  /**
-   * Gets the current mode for this tool.
-   * @return string (e.g, "network-inspector-view" or "network-statistics-view")
-   */
-  get currentFrontendMode() {
-    // The getter may be called from a timeout after the panel is destroyed.
-    if (!this._body.selectedPanel) {
-      return null;
-    }
-    return this._body.selectedPanel.id;
-  },
-
-  /**
-   * Toggles between the frontend view modes ("Inspector" vs. "Statistics").
-   */
-  toggleFrontendMode: function () {
-    if (this.currentFrontendMode != "network-inspector-view") {
-      this.showNetworkInspectorView();
-    } else {
-      this.showNetworkStatisticsView();
-    }
-  },
-
-  /**
-   * Switches to the "Inspector" frontend view mode.
-   */
-  showNetworkInspectorView: function () {
-    this._body.selectedPanel = $("#network-inspector-view");
-    this.RequestsMenu._flushWaterfallViews(true);
-  },
-
-  /**
-   * Switches to the "Statistics" frontend view mode.
-   */
-  showNetworkStatisticsView: function () {
-    this._body.selectedPanel = $("#network-statistics-view");
-
-    let controller = NetMonitorController;
-    let requestsView = this.RequestsMenu;
-    let statisticsView = this.PerformanceStatistics;
-
-    Task.spawn(function* () {
-      statisticsView.displayPlaceholderCharts();
-      yield controller.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
-
-      try {
-        // • The response headers and status code are required for determining
-        // whether a response is "fresh" (cacheable).
-        // • The response content size and request total time are necessary for
-        // populating the statistics view.
-        // • The response mime type is used for categorization.
-        yield whenDataAvailable(requestsView.attachments, [
-          "responseHeaders", "status", "contentSize", "mimeType", "totalTime"
-        ]);
-      } catch (ex) {
-        // Timed out while waiting for data. Continue with what we have.
-        console.error(ex);
-      }
-
-      statisticsView.createPrimedCacheChart(requestsView.items);
-      statisticsView.createEmptyCacheChart(requestsView.items);
-    });
-  },
-
-  reloadPage: function () {
-    NetMonitorController.triggerActivity(
-      ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT);
-  },
-
-  /**
-   * Lazily initializes and returns a promise for a Editor instance.
-   *
-   * @param string id
-   *        The id of the editor placeholder node.
-   * @return object
-   *         A promise that is resolved when the editor is available.
-   */
-  editor: function (id) {
-    dumpn("Getting a NetMonitorView editor: " + id);
-
-    if (this._editorPromises.has(id)) {
-      return this._editorPromises.get(id);
-    }
-
-    let deferred = promise.defer();
-    this._editorPromises.set(id, deferred.promise);
-
-    // Initialize the source editor and store the newly created instance
-    // in the ether of a resolved promise's value.
-    let editor = new Editor(DEFAULT_EDITOR_CONFIG);
-    editor.appendTo($(id)).then(() => deferred.resolve(editor));
-
-    return deferred.promise;
-  },
-
-  _body: null,
-  _detailsPane: null,
-  _detailsPaneToggleButton: null,
-  _collapsePaneString: "",
-  _expandPaneString: "",
-  _editorPromises: new Map()
-};
-
-/**
- * Functions handling the sidebar details view.
- */
-function SidebarView() {
-  dumpn("SidebarView was instantiated");
-}
-
-SidebarView.prototype = {
-  /**
-   * Sets this view hidden or visible. It's visible by default.
-   *
-   * @param boolean visibleFlag
-   *        Specifies the intended visibility.
-   */
-  toggle: function (visibleFlag) {
-    NetMonitorView.toggleDetailsPane({ visible: visibleFlag });
-    NetMonitorView.RequestsMenu._flushWaterfallViews(true);
-  },
-
-  /**
-   * Populates this view with the specified data.
-   *
-   * @param object data
-   *        The data source (this should be the attachment of a request item).
-   * @return object
-   *        Returns a promise that resolves upon population of the subview.
-   */
-  populate: Task.async(function* (data) {
-    let isCustom = data.isCustom;
-    let view = isCustom ?
-      NetMonitorView.CustomRequest :
-      NetMonitorView.NetworkDetails;
-
-    yield view.populate(data);
-    $("#details-pane").selectedIndex = isCustom ? 0 : 1;
-
-    window.emit(EVENTS.SIDEBAR_POPULATED);
-  })
-};
-
-/**
- * Functions handling the requests details view.
- */
-function NetworkDetailsView() {
-  dumpn("NetworkDetailsView was instantiated");
-
-  // The ToolSidebar requires the panel object to be able to emit events.
-  EventEmitter.decorate(this);
-
-  this._onTabSelect = this._onTabSelect.bind(this);
-}
-
-NetworkDetailsView.prototype = {
-  /**
-   * An object containing the state of tabs.
-   */
-  _viewState: {
-    // if updating[tab] is true a task is currently updating the given tab.
-    updating: [],
-    // if dirty[tab] is true, the tab needs to be repopulated once current
-    // update task finishes
-    dirty: [],
-    // the most recently received attachment data for the request
-    latestData: null,
-  },
-
-  /**
-   * Initialization function, called when the network monitor is started.
-   */
-  initialize: function () {
-    dumpn("Initializing the NetworkDetailsView");
-
-    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, {
-        emptyText: L10N.getStr("headersEmptyText"),
-        searchPlaceholder: L10N.getStr("headersFilterText")
-      }));
-    this._cookies = new VariablesView($("#all-cookies"),
-      Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
-        emptyText: L10N.getStr("cookiesEmptyText"),
-        searchPlaceholder: L10N.getStr("cookiesFilterText")
-      }));
-    this._params = new VariablesView($("#request-params"),
-      Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
-        emptyText: L10N.getStr("paramsEmptyText"),
-        searchPlaceholder: L10N.getStr("paramsFilterText")
-      }));
-    this._json = new VariablesView($("#response-content-json"),
-      Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
-        onlyEnumVisible: true,
-        searchPlaceholder: L10N.getStr("jsonFilterText")
-      }));
-    VariablesViewController.attach(this._json);
-
-    this._paramsQueryString = L10N.getStr("paramsQueryString");
-    this._paramsFormData = L10N.getStr("paramsFormData");
-    this._paramsPostPayload = L10N.getStr("paramsPostPayload");
-    this._requestHeaders = L10N.getStr("requestHeaders");
-    this._requestHeadersFromUpload = L10N.getStr("requestHeadersFromUpload");
-    this._responseHeaders = L10N.getStr("responseHeaders");
-    this._requestCookies = L10N.getStr("requestCookies");
-    this._responseCookies = L10N.getStr("responseCookies");
-
-    $("tabpanels", this.widget).addEventListener("select", this._onTabSelect);
-  },
-
-  /**
-   * Destruction function, called when the network monitor is closed.
-   */
-  destroy: function () {
-    dumpn("Destroying the NetworkDetailsView");
-    this.sidebar.destroy();
-    $("tabpanels", this.widget).removeEventListener("select",
-      this._onTabSelect);
-  },
-
-  /**
-   * Populates this view with the specified data.
-   *
-   * @param object data
-   *        The data source (this should be the attachment of a request item).
-   * @return object
-   *        Returns a promise that resolves upon population the view.
-   */
-  populate: function (data) {
-    $("#request-params-box").setAttribute("flex", "1");
-    $("#request-params-box").hidden = false;
-    $("#request-post-data-textarea-box").hidden = true;
-    $("#response-content-info-header").hidden = true;
-    $("#response-content-json-box").hidden = true;
-    $("#response-content-textarea-box").hidden = true;
-    $("#raw-headers").hidden = true;
-    $("#response-content-image-box").hidden = true;
-
-    let isHtml = Filters.html(data);
-
-    // Show the "Preview" tabpanel only for plain HTML responses.
-    this.sidebar.toggleTab(isHtml, "preview-tab");
-
-    // Show the "Security" tab only for requests that
-    //   1) are https (state != insecure)
-    //   2) come from a target that provides security information.
-    let hasSecurityInfo = data.securityState &&
-                          data.securityState !== "insecure";
-    this.sidebar.toggleTab(hasSecurityInfo, "security-tab");
-
-    // Switch to the "Headers" tabpanel if the "Preview" previously selected
-    // and this is not an HTML response or "Security" was selected but this
-    // request has no security information.
-
-    if (!isHtml && this.widget.selectedPanel === $("#preview-tabpanel") ||
-        !hasSecurityInfo && this.widget.selectedPanel ===
-          $("#security-tabpanel")) {
-      this.widget.selectedIndex = 0;
-    }
-
-    this._headers.empty();
-    this._cookies.empty();
-    this._params.empty();
-    this._json.empty();
-
-    this._dataSrc = { src: data, populated: [] };
-    this._onTabSelect();
-    window.emit(EVENTS.NETWORKDETAILSVIEW_POPULATED);
-
-    return promise.resolve();
-  },
-
-  /**
-   * Listener handling the tab selection event.
-   */
-  _onTabSelect: function () {
-    let { src, populated } = this._dataSrc || {};
-    let tab = this.widget.selectedIndex;
-    let view = this;
-
-    // Make sure the data source is valid and don't populate the same tab twice.
-    if (!src || populated[tab]) {
-      return;
-    }
-
-    let viewState = this._viewState;
-    if (viewState.updating[tab]) {
-      // A task is currently updating this tab. If we started another update
-      // task now it would result in a duplicated content as described in bugs
-      // 997065 and 984687. As there's no way to stop the current task mark the
-      // tab dirty and refresh the panel once the current task finishes.
-      viewState.dirty[tab] = true;
-      viewState.latestData = src;
-      return;
-    }
-
-    Task.spawn(function* () {
-      viewState.updating[tab] = true;
-      switch (tab) {
-        // "Headers"
-        case 0:
-          yield view._setSummary(src);
-          yield view._setResponseHeaders(src.responseHeaders);
-          yield view._setRequestHeaders(
-            src.requestHeaders,
-            src.requestHeadersFromUploadStream);
-          break;
-        // "Cookies"
-        case 1:
-          yield view._setResponseCookies(src.responseCookies);
-          yield view._setRequestCookies(src.requestCookies);
-          break;
-        // "Params"
-        case 2:
-          yield view._setRequestGetParams(src.url);
-          yield view._setRequestPostParams(
-            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;
-      }
-      viewState.updating[tab] = false;
-    }).then(() => {
-      if (tab == this.widget.selectedIndex) {
-        if (viewState.dirty[tab]) {
-          // The request information was updated while the task was running.
-          viewState.dirty[tab] = false;
-          view.populate(viewState.latestData);
-        } else {
-          // Tab is selected but not dirty. We're done here.
-          populated[tab] = true;
-          window.emit(EVENTS.TAB_UPDATED);
-
-          if (NetMonitorController.isConnected()) {
-            NetMonitorView.RequestsMenu.ensureSelectedItemIsVisible();
-          }
-        }
-      } else if (viewState.dirty[tab]) {
-        // Tab is dirty but no longer selected. Don't refresh it now, it'll be
-        // done if the tab is shown again.
-        viewState.dirty[tab] = false;
-      }
-    }, e => console.error(e));
-  },
-
-  /**
-   * Sets the network request summary shown in this view.
-   *
-   * @param object data
-   *        The data source (this should be the attachment of a request item).
-   */
-  _setSummary: function (data) {
-    if (data.url) {
-      let unicodeUrl = NetworkHelper.convertToUnicode(unescape(data.url));
-      $("#headers-summary-url-value").setAttribute("value", unicodeUrl);
-      $("#headers-summary-url-value").setAttribute("tooltiptext", unicodeUrl);
-      $("#headers-summary-url").removeAttribute("hidden");
-    } else {
-      $("#headers-summary-url").setAttribute("hidden", "true");
-    }
-
-    if (data.method) {
-      $("#headers-summary-method-value").setAttribute("value", data.method);
-      $("#headers-summary-method").removeAttribute("hidden");
-    } else {
-      $("#headers-summary-method").setAttribute("hidden", "true");
-    }
-
-    if (data.remoteAddress) {
-      let address = data.remoteAddress;
-      if (address.indexOf(":") != -1) {
-        address = `[${address}]`;
-      }
-      if (data.remotePort) {
-        address += `:${data.remotePort}`;
-      }
-      $("#headers-summary-address-value").setAttribute("value", address);
-      $("#headers-summary-address-value").setAttribute("tooltiptext", address);
-      $("#headers-summary-address").removeAttribute("hidden");
-    } else {
-      $("#headers-summary-address").setAttribute("hidden", "true");
-    }
-
-    if (data.status) {
-      // "code" attribute is only used by css to determine the icon color
-      let code;
-      if (data.fromCache) {
-        code = "cached";
-      } else if (data.fromServiceWorker) {
-        code = "service worker";
-      } else {
-        code = data.status;
-      }
-      $("#headers-summary-status-circle").setAttribute("code", code);
-      $("#headers-summary-status-value").setAttribute("value",
-        data.status + " " + data.statusText);
-      $("#headers-summary-status").removeAttribute("hidden");
-    } else {
-      $("#headers-summary-status").setAttribute("hidden", "true");
-    }
-
-    if (data.httpVersion) {
-      $("#headers-summary-version-value").setAttribute("value",
-        data.httpVersion);
-      $("#headers-summary-version").removeAttribute("hidden");
-    } else {
-      $("#headers-summary-version").setAttribute("hidden", "true");
-    }
-  },
-
-  /**
-   * Sets the network request headers shown in this view.
-   *
-   * @param object headers
-   *        The "requestHeaders" message received from the server.
-   * @param object uploadHeaders
-   *        The "requestHeadersFromUploadStream" inferred from the POST payload.
-   * @return object
-   *        A promise that resolves when request headers are set.
-   */
-  _setRequestHeaders: Task.async(function* (headers, uploadHeaders) {
-    if (headers && headers.headers.length) {
-      yield this._addHeaders(this._requestHeaders, headers);
-    }
-    if (uploadHeaders && uploadHeaders.headers.length) {
-      yield this._addHeaders(this._requestHeadersFromUpload, uploadHeaders);
-    }
-  }),
-
-  /**
-   * Sets the network response headers shown in this view.
-   *
-   * @param object response
-   *        The message received from the server.
-   * @return object
-   *        A promise that resolves when response headers are set.
-   */
-  _setResponseHeaders: Task.async(function* (response) {
-    if (response && response.headers.length) {
-      response.headers.sort((a, b) => a.name > b.name);
-      yield this._addHeaders(this._responseHeaders, response);
-    }
-  }),
-
-  /**
-   * Populates the headers container in this view with the specified data.
-   *
-   * @param string name
-   *        The type of headers to populate (request or response).
-   * @param object response
-   *        The message received from the server.
-   * @return object
-   *        A promise that resolves when headers are added.
-   */
-  _addHeaders: Task.async(function* (name, response) {
-    let kb = response.headersSize / 1024;
-    let size = L10N.numberWithDecimals(kb, HEADERS_SIZE_DECIMALS);
-    let text = L10N.getFormatStr("networkMenu.sizeKB", size);
-
-    let headersScope = this._headers.addScope(name + " (" + text + ")");
-    headersScope.expanded = true;
-
-    for (let header of response.headers) {
-      let headerVar = headersScope.addItem(header.name, {}, {relaxed: true});
-      let headerValue = yield gNetwork.getString(header.value);
-      headerVar.setGrip(headerValue);
-    }
-  }),
-
-  /**
-   * Sets the network request cookies shown in this view.
-   *
-   * @param object response
-   *        The message received from the server.
-   * @return object
-   *        A promise that is resolved when the request cookies are set.
-   */
-  _setRequestCookies: Task.async(function* (response) {
-    if (response && response.cookies.length) {
-      response.cookies.sort((a, b) => a.name > b.name);
-      yield this._addCookies(this._requestCookies, response);
-    }
-  }),
-
-  /**
-   * Sets the network response cookies shown in this view.
-   *
-   * @param object response
-   *        The message received from the server.
-   * @return object
-   *        A promise that is resolved when the response cookies are set.
-   */
-  _setResponseCookies: Task.async(function* (response) {
-    if (response && response.cookies.length) {
-      yield this._addCookies(this._responseCookies, response);
-    }
-  }),
-
-  /**
-   * Populates the cookies container in this view with the specified data.
-   *
-   * @param string name
-   *        The type of cookies to populate (request or response).
-   * @param object response
-   *        The message received from the server.
-   * @return object
-   *        Returns a promise that resolves upon the adding of cookies.
-   */
-  _addCookies: Task.async(function* (name, response) {
-    let cookiesScope = this._cookies.addScope(name);
-    cookiesScope.expanded = true;
-
-    for (let cookie of response.cookies) {
-      let cookieVar = cookiesScope.addItem(cookie.name, {}, {relaxed: true});
-      let cookieValue = yield gNetwork.getString(cookie.value);
-      cookieVar.setGrip(cookieValue);
-
-      // By default the cookie name and value are shown. If this is the only
-      // information available, then nothing else is to be displayed.
-      let cookieProps = Object.keys(cookie);
-      if (cookieProps.length == 2) {
-        continue;
-      }
-
-      // Display any other information other than the cookie name and value
-      // which may be available.
-      let rawObject = Object.create(null);
-      let otherProps = cookieProps.filter(e => e != "name" && e != "value");
-      for (let prop of otherProps) {
-        rawObject[prop] = cookie[prop];
-      }
-      cookieVar.populate(rawObject);
-      cookieVar.twisty = true;
-      cookieVar.expanded = true;
-    }
-  }),
-
-  /**
-   * Sets the network request get params shown in this view.
-   *
-   * @param string url
-   *        The request's url.
-   */
-  _setRequestGetParams: function (url) {
-    let query = NetworkHelper.nsIURL(url).query;
-    if (query) {
-      this._addParams(this._paramsQueryString, query);
-    }
-  },
-
-  /**
-   * Sets the network request post params shown in this view.
-   *
-   * @param object headers
-   *        The "requestHeaders" message received from the server.
-   * @param object uploadHeaders
-   *        The "requestHeadersFromUploadStream" inferred from the POST payload.
-   * @param object postData
-   *        The "requestPostData" message received from the server.
-   * @return object
-   *        A promise that is resolved when the request post params are set.
-   */
-  _setRequestPostParams: Task.async(function* (headers, uploadHeaders,
-    postData) {
-    if (!headers || !uploadHeaders || !postData) {
-      return;
-    }
-
-    let formDataSections = yield getFormDataSections(
-      headers,
-      uploadHeaders,
-      postData,
-      gNetwork.getString.bind(gNetwork));
-
-    this._params.onlyEnumVisible = false;
-
-    // Handle urlencoded form data sections (e.g. "?foo=bar&baz=42").
-    if (formDataSections.length > 0) {
-      formDataSections.forEach(section => {
-        this._addParams(this._paramsFormData, section);
-      });
-    } else {
-      // Handle JSON and actual forms ("multipart/form-data" content type).
-      let postDataLongString = postData.postData.text;
-      let text = yield gNetwork.getString(postDataLongString);
-      let jsonVal = null;
-      try {
-        jsonVal = JSON.parse(text);
-      } catch (ex) { // eslint-disable-line
-      }
-
-      if (jsonVal) {
-        this._params.onlyEnumVisible = true;
-        let jsonScopeName = L10N.getStr("jsonScopeName");
-        let jsonScope = this._params.addScope(jsonScopeName);
-        jsonScope.expanded = true;
-        let jsonItem = jsonScope.addItem(undefined, { enumerable: true });
-        jsonItem.populate(jsonVal, { sorted: true });
-      } else {
-        // This is really awkward, but hey, it works. Let's show an empty
-        // scope in the params view and place the source editor containing
-        // the raw post data directly underneath.
-        $("#request-params-box").removeAttribute("flex");
-        let paramsScope = this._params.addScope(this._paramsPostPayload);
-        paramsScope.expanded = true;
-        paramsScope.locked = true;
-
-        $("#request-post-data-textarea-box").hidden = false;
-        let editor = yield NetMonitorView.editor("#request-post-data-textarea");
-        editor.setMode(Editor.modes.text);
-        editor.setText(text);
-      }
-    }
-
-    window.emit(EVENTS.REQUEST_POST_PARAMS_DISPLAYED);
-  }),
-
-  /**
-   * Populates the params container in this view with the specified data.
-   *
-   * @param string name
-   *        The type of params to populate (get or post).
-   * @param string queryString
-   *        A query string of params (e.g. "?foo=bar&baz=42").
-   */
-  _addParams: function (name, queryString) {
-    let paramsArray = NetworkHelper.parseQueryString(queryString);
-    if (!paramsArray) {
-      return;
-    }
-    let paramsScope = this._params.addScope(name);
-    paramsScope.expanded = true;
-
-    for (let param of paramsArray) {
-      let paramVar = paramsScope.addItem(param.name, {}, {relaxed: true});
-      paramVar.setGrip(param.value);
-    }
-  },
-
-  /**
-   * Sets the network response body shown in this view.
-   *
-   * @param string url
-   *        The request's url.
-   * @param object response
-   *        The message received from the server.
-   * @return object
-   *         A promise that is resolved when the response body is set.
-   */
-  _setResponseBody: Task.async(function* (url, response) {
-    if (!response) {
-      return;
-    }
-    let { mimeType, text, encoding } = response.content;
-    let responseBody = yield gNetwork.getString(text);
-
-    // Handle json, which we tentatively identify by checking the MIME type
-    // for "json" after any word boundary. This works for the standard
-    // "application/json", and also for custom types like "x-bigcorp-json".
-    // Additionally, we also directly parse the response text content to
-    // verify whether it's json or not, to handle responses incorrectly
-    // labeled as text/plain instead.
-    let jsonMimeType, jsonObject, jsonObjectParseError;
-    try {
-      jsonMimeType = /\bjson/.test(mimeType);
-      jsonObject = JSON.parse(responseBody);
-    } catch (e) {
-      jsonObjectParseError = e;
-    }
-    if (jsonMimeType || jsonObject) {
-      // Extract the actual json substring in case this might be a "JSONP".
-      // This regex basically parses a function call and captures the
-      // function name and arguments in two separate groups.
-      let jsonpRegex = /^\s*([\w$]+)\s*\(\s*([^]*)\s*\)\s*;?\s*$/;
-      let [_, callbackPadding, jsonpString] = // eslint-disable-line
-        responseBody.match(jsonpRegex) || [];
-
-      // Make sure this is a valid JSON object first. If so, nicely display
-      // the parsing results in a variables view. Otherwise, simply show
-      // the contents as plain text.
-      if (callbackPadding && jsonpString) {
-        try {
-          jsonObject = JSON.parse(jsonpString);
-        } catch (e) {
-          jsonObjectParseError = e;
-        }
-      }
-
-      // Valid JSON or JSONP.
-      if (jsonObject) {
-        $("#response-content-json-box").hidden = false;
-        let jsonScopeName = callbackPadding
-          ? L10N.getFormatStr("jsonpScopeName", callbackPadding)
-          : L10N.getStr("jsonScopeName");
-
-        let jsonVar = { label: jsonScopeName, rawObject: jsonObject };
-        yield this._json.controller.setSingleVariable(jsonVar).expanded;
-      } else {
-        // Malformed JSON.
-        $("#response-content-textarea-box").hidden = false;
-        let infoHeader = $("#response-content-info-header");
-        infoHeader.setAttribute("value", jsonObjectParseError);
-        infoHeader.setAttribute("tooltiptext", jsonObjectParseError);
-        infoHeader.hidden = false;
-
-        let editor = yield NetMonitorView.editor("#response-content-textarea");
-        editor.setMode(Editor.modes.js);
-        editor.setText(responseBody);
-      }
-    } else if (mimeType.includes("image/")) {
-      // Handle images.
-      $("#response-content-image-box").setAttribute("align", "center");
-      $("#response-content-image-box").setAttribute("pack", "center");
-      $("#response-content-image-box").hidden = false;
-      $("#response-content-image").src = formDataURI(mimeType, encoding, responseBody);
-
-      // Immediately display additional information about the image:
-      // file name, mime type and encoding.
-      $("#response-content-image-name-value").setAttribute("value",
-        NetworkHelper.nsIURL(url).fileName);
-      $("#response-content-image-mime-value").setAttribute("value", mimeType);
-
-      // Wait for the image to load in order to display the width and height.
-      $("#response-content-image").onload = e => {
-        // XUL images are majestic so they don't bother storing their dimensions
-        // in width and height attributes like the rest of the folk. Hack around
-        // this by getting the bounding client rect and subtracting the margins.
-        let { width, height } = e.target.getBoundingClientRect();
-        let dimensions = (width - 2) + " \u00D7 " + (height - 2);
-        $("#response-content-image-dimensions-value").setAttribute("value",
-          dimensions);
-      };
-    } else {
-      $("#response-content-textarea-box").hidden = false;
-      let editor = yield NetMonitorView.editor("#response-content-textarea");
-      editor.setMode(Editor.modes.text);
-      editor.setText(responseBody);
-
-      // Maybe set a more appropriate mode in the Source Editor if possible,
-      // but avoid doing this for very large files.
-      if (responseBody.length < SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
-        let mapping = Object.keys(CONTENT_MIME_TYPE_MAPPINGS).find(key => {
-          return mimeType.includes(key);
-        });
-
-        if (mapping) {
-          editor.setMode(CONTENT_MIME_TYPE_MAPPINGS[mapping]);
-        }
-      }
-    }
-
-    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) {
-    if (!response) {
-      return promise.resolve();
-    }
-    let { text } = response.content;
-    let responseBody = yield gNetwork.getString(text);
-
-    // Always disable JS when previewing HTML responses.
-    let iframe = $("#response-preview");
-    iframe.contentDocument.docShell.allowJavascript = false;
-    iframe.contentDocument.documentElement.innerHTML = responseBody;
-
-    window.emit(EVENTS.RESPONSE_HTML_PREVIEW_DISPLAYED);
-    return undefined;
-  }),
-
-  /**
-   * Sets the security information shown in this view.
-   *
-   * @param object securityInfo
-   *        The data received from server
-   * @param string url
-   *        The URL of this request
-   * @return object
-   *        A promise that is resolved when the security info is rendered.
-   */
-  _setSecurityInfo: Task.async(function* (securityInfo, url) {
-    if (!securityInfo) {
-      // We don't have security info. This could mean one of two things:
-      // 1) This connection is not secure and this tab is not visible and thus
-      //    we shouldn't be here.
-      // 2) We have already received securityState and the tab is visible BUT
-      //    the rest of the information is still on its way. Once it arrives
-      //    this method is called again.
-      return;
-    }
-
-    /**
-     * A helper that sets value and tooltiptext attributes of an element to
-     * specified value.
-     *
-     * @param string selector
-     *        A selector for the element.
-     * @param string value
-     *        The value to set. If this evaluates to false a placeholder string
-     *        <Not Available> is used instead.
-     */
-    function setValue(selector, value) {
-      let label = $(selector);
-      if (!value) {
-        label.setAttribute("value", L10N.getStr(
-          "netmonitor.security.notAvailable"));
-        label.setAttribute("tooltiptext", label.getAttribute("value"));
-      } else {
-        label.setAttribute("value", value);
-        label.setAttribute("tooltiptext", value);
-      }
-    }
-
-    let errorbox = $("#security-error");
-    let infobox = $("#security-information");
-
-    if (securityInfo.state === "secure" || securityInfo.state === "weak") {
-      infobox.hidden = false;
-      errorbox.hidden = true;
-
-      // Warning icons
-      let cipher = $("#security-warning-cipher");
-
-      if (securityInfo.state === "weak") {
-        cipher.hidden = securityInfo.weaknessReasons.indexOf("cipher") === -1;
-      } else {
-        cipher.hidden = true;
-      }
-
-      let enabledLabel = L10N.getStr("netmonitor.security.enabled");
-      let disabledLabel = L10N.getStr("netmonitor.security.disabled");
-
-      // Connection parameters
-      setValue("#security-protocol-version-value",
-        securityInfo.protocolVersion);
-      setValue("#security-ciphersuite-value", securityInfo.cipherSuite);
-
-      // Host header
-      let domain = getUriHostPort(url);
-      let hostHeader = L10N.getFormatStr("netmonitor.security.hostHeader",
-        domain);
-      setValue("#security-info-host-header", hostHeader);
-
-      // Parameters related to the domain
-      setValue("#security-http-strict-transport-security-value",
-                securityInfo.hsts ? enabledLabel : disabledLabel);
-
-      setValue("#security-public-key-pinning-value",
-                securityInfo.hpkp ? enabledLabel : disabledLabel);
-
-      // Certificate parameters
-      let cert = securityInfo.cert;
-      setValue("#security-cert-subject-cn", cert.subject.commonName);
-      setValue("#security-cert-subject-o", cert.subject.organization);
-      setValue("#security-cert-subject-ou", cert.subject.organizationalUnit);
-
-      setValue("#security-cert-issuer-cn", cert.issuer.commonName);
-      setValue("#security-cert-issuer-o", cert.issuer.organization);
-      setValue("#security-cert-issuer-ou", cert.issuer.organizationalUnit);
-
-      setValue("#security-cert-validity-begins", cert.validity.start);
-      setValue("#security-cert-validity-expires", cert.validity.end);
-
-      setValue("#security-cert-sha1-fingerprint", cert.fingerprint.sha1);
-      setValue("#security-cert-sha256-fingerprint", cert.fingerprint.sha256);
-    } else {
-      infobox.hidden = true;
-      errorbox.hidden = false;
-
-      // Strip any HTML from the message.
-      let plain = new DOMParser().parseFromString(securityInfo.errorMessage,
-        "text/html");
-      setValue("#security-error-message", plain.body.textContent);
-    }
-  }),
-
-  _dataSrc: null,
-  _headers: null,
-  _cookies: null,
-  _params: null,
-  _json: null,
-  _paramsQueryString: "",
-  _paramsFormData: "",
-  _paramsPostPayload: "",
-  _requestHeaders: "",
-  _responseHeaders: "",
-  _requestCookies: "",
-  _responseCookies: ""
-};
-
-/**
  * Functions handling the performance statistics view.
  */
 function PerformanceStatisticsView() {
 }
 
 PerformanceStatisticsView.prototype = {
   /**
+   * Initialization function, called when the debugger is started.
+   */
+  initialize: function (store) {
+    this.store = store;
+  },
+
+  /**
    * Initializes and displays empty charts in this container.
    */
   displayPlaceholderCharts: function () {
     this._createChart({
       id: "#primed-cache-chart",
       title: "charts.cacheEnabled"
     });
     this._createChart({
@@ -1310,17 +139,17 @@ PerformanceStatisticsView.prototype = {
       data: data,
       strings: strings,
       totals: totals,
       sorted: sorted
     });
 
     chart.on("click", (_, item) => {
       // Reset FilterButtons and enable one filter exclusively
-      gStore.dispatch(Actions.enableFilterOnly(item.label));
+      this.store.dispatch(Actions.enableFilterOnly(item.label));
       NetMonitorView.showNetworkInspectorView();
     });
 
     container.appendChild(chart.node);
   },
 
   /**
    * Sanitizes the data source used for creating charts, to follow the
@@ -1388,22 +217,16 @@ PerformanceStatisticsView.prototype = {
       data[type].count++;
     }
 
     return data.filter(e => e.count > 0);
   },
 };
 
 /**
- * DOM query helper.
- */
-var $ = (selector, target = document) => target.querySelector(selector);
-var $all = (selector, target = document) => target.querySelectorAll(selector);
-
-/**
  * Checks if the "Expiration Calculations" defined in section 13.2.4 of the
  * "HTTP/1.1: Caching in HTTP" spec holds true for a collection of headers.
  *
  * @param object
  *        An object containing the { responseHeaders, status } properties.
  * @return boolean
  *         True if the response is fresh and loaded from cache.
  */
@@ -1434,49 +257,9 @@ function responseIsFresh({ responseHeade
   // Check the "Expires" header for a valid date.
   if (expires && Date.parse(expires.value)) {
     return true;
   }
 
   return false;
 }
 
-/**
- * Makes sure certain properties are available on all objects in a data store.
- *
- * @param array dataStore
- *        A list of objects for which to check the availability of properties.
- * @param array mandatoryFields
- *        A list of strings representing properties of objects in dataStore.
- * @return object
- *         A promise resolved when all objects in dataStore contain the
- *         properties defined in mandatoryFields.
- */
-function whenDataAvailable(dataStore, mandatoryFields) {
-  let deferred = promise.defer();
-
-  let interval = setInterval(() => {
-    if (dataStore.every(item => {
-      return mandatoryFields.every(field => field in item);
-    })) {
-      clearInterval(interval);
-      clearTimeout(timer);
-      deferred.resolve();
-    }
-  }, WDA_DEFAULT_VERIFY_INTERVAL);
-
-  let timer = setTimeout(() => {
-    clearInterval(interval);
-    deferred.reject(new Error("Timed out while waiting for data"));
-  }, WDA_DEFAULT_GIVE_UP_TIMEOUT);
-
-  return deferred.promise;
-}
-
-/**
- * Preliminary setup for the NetMonitorView object.
- */
-NetMonitorView.Toolbar = new ToolbarView();
-NetMonitorView.RequestsMenu = new RequestsMenuView();
-NetMonitorView.Sidebar = new SidebarView();
-NetMonitorView.CustomRequest = new CustomRequestView();
-NetMonitorView.NetworkDetails = new NetworkDetailsView();
-NetMonitorView.PerformanceStatistics = new PerformanceStatisticsView();
+exports.PerformanceStatisticsView = PerformanceStatisticsView;