Bug 1336384 - Implement top level NetworkMonitor component r=gasolin,Honza
authorRicky Chien <rchien@mozilla.com>
Mon, 13 Feb 2017 13:16:50 +0800
changeset 343052 53698c71b19066259c1b033569ddf1cf64710421
parent 343051 620f06960400e4d95da0ed5c9682edda3aa136ec
child 343053 79bce9013f54c19278ef9f5bc92b34b843f9cb07
push id37425
push userrchien@mozilla.com
push dateWed, 15 Feb 2017 15:22:50 +0000
treeherderautoland@53698c71b190 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgasolin, Honza
bugs1336384
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 1336384 - Implement top level NetworkMonitor component r=gasolin,Honza MozReview-Commit-ID: 6E3his8E20A
devtools/client/netmonitor/actions/requests.js
devtools/client/netmonitor/actions/ui.js
devtools/client/netmonitor/components/monitor-panel.js
devtools/client/netmonitor/components/moz.build
devtools/client/netmonitor/components/network-monitor.js
devtools/client/netmonitor/components/request-list-content.js
devtools/client/netmonitor/components/request-list-empty.js
devtools/client/netmonitor/components/request-list-header.js
devtools/client/netmonitor/components/statistics-panel.js
devtools/client/netmonitor/l10n.js
devtools/client/netmonitor/moz.build
devtools/client/netmonitor/netmonitor-controller.js
devtools/client/netmonitor/netmonitor-view.js
devtools/client/netmonitor/netmonitor.js
devtools/client/netmonitor/netmonitor.xul
devtools/client/netmonitor/panel.js
devtools/client/netmonitor/prefs.js
devtools/client/netmonitor/reducers/ui.js
devtools/client/netmonitor/request-list-context-menu.js
devtools/client/netmonitor/request-list-tooltip.js
devtools/client/netmonitor/shared/components/headers-panel.js
devtools/client/netmonitor/test/browser_net_copy_as_curl.js
devtools/client/netmonitor/test/browser_net_copy_headers.js
devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js
devtools/client/netmonitor/test/browser_net_copy_params.js
devtools/client/netmonitor/test/browser_net_copy_response.js
devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js
devtools/client/netmonitor/test/browser_net_copy_url.js
devtools/client/netmonitor/test/browser_net_image-tooltip.js
devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
devtools/client/netmonitor/test/browser_net_prefs-reload.js
devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
devtools/client/netmonitor/test/browser_net_simple-init.js
devtools/client/netmonitor/test/browser_net_statistics-01.js
devtools/client/netmonitor/test/browser_net_statistics-02.js
devtools/client/netmonitor/test/head.js
devtools/client/themes/netmonitor.css
--- a/devtools/client/netmonitor/actions/requests.js
+++ b/devtools/client/netmonitor/actions/requests.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals NetMonitorController */
-
 "use strict";
 
 const {
   ADD_REQUEST,
   CLEAR_REQUESTS,
   CLONE_SELECTED_REQUEST,
   REMOVE_SELECTED_CUSTOM_REQUEST,
   SEND_CUSTOM_REQUEST,
@@ -43,17 +41,17 @@ function cloneSelectedRequest() {
     type: CLONE_SELECTED_REQUEST
   };
 }
 
 /**
  * Send a new HTTP request using the data in the custom request form.
  */
 function sendCustomRequest() {
-  if (!NetMonitorController.supportsCustomRequest) {
+  if (!window.NetMonitorController.supportsCustomRequest) {
     return cloneSelectedRequest();
   }
 
   return (dispatch, getState) => {
     const selected = getSelectedRequest(getState());
 
     if (!selected) {
       return;
@@ -67,17 +65,17 @@ function sendCustomRequest() {
     };
     if (selected.requestHeaders) {
       data.headers = selected.requestHeaders.headers;
     }
     if (selected.requestPostData) {
       data.body = selected.requestPostData.postData.text;
     }
 
-    NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
+    window.NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
       return dispatch({
         type: SEND_CUSTOM_REQUEST,
         id: response.eventActor.actor,
       });
     });
   };
 }
 
--- a/devtools/client/netmonitor/actions/ui.js
+++ b/devtools/client/netmonitor/actions/ui.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
+  ACTIVITY_TYPE,
   OPEN_NETWORK_DETAILS,
   OPEN_STATISTICS,
   SELECT_DETAILS_PANEL_TAB,
   WATERFALL_RESIZE,
 } = require("../constants");
 
 /**
  * Change network details panel.
@@ -24,16 +25,19 @@ function openNetworkDetails(open) {
 }
 
 /**
  * Change performance statistics panel open state.
  *
  * @param {boolean} visible - expected performance statistics panel open state
  */
 function openStatistics(open) {
+  if (open) {
+    window.NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
+  }
   return {
     type: OPEN_STATISTICS,
     open,
   };
 }
 
 /**
  * Waterfall width has changed (likely on window resize). Update the UI.
--- a/devtools/client/netmonitor/components/monitor-panel.js
+++ b/devtools/client/netmonitor/components/monitor-panel.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals gNetwork, NetMonitorController */
-
 "use strict";
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
@@ -50,40 +48,33 @@ const MonitorPanel = createClass({
   },
 
   componentDidMount() {
     MediaQueryList.addListener(this.onLayoutChange);
   },
 
   componentWillReceiveProps(nextProps) {
     let {
-      openNetworkDetails,
       request = {},
       updateRequest,
     } = nextProps;
     let {
       formDataSections,
       requestHeaders,
       requestHeadersFromUploadStream,
       requestPostData,
     } = request;
 
-    if (nextProps.isEmpty) {
-      openNetworkDetails(false);
-    } else if (nextProps.request && nextProps.request !== this.props.request) {
-      openNetworkDetails(true);
-    }
-
     if (!formDataSections && requestHeaders &&
         requestHeadersFromUploadStream && requestPostData) {
       getFormDataSections(
         requestHeaders,
         requestHeadersFromUploadStream,
         requestPostData,
-        gNetwork.getString.bind(gNetwork),
+        window.gNetwork.getString.bind(window.gNetwork),
       ).then((newFormDataSections) => {
         updateRequest(
           request.id,
           { formDataSections: newFormDataSections },
           true,
         );
       });
     }
@@ -117,17 +108,17 @@ const MonitorPanel = createClass({
           initialHeight: `${Prefs.networkDetailsHeight}px`,
           minSize: "50px",
           maxSize: "80%",
           splitterSize: "1px",
           startPanel: RequestList({ isEmpty }),
           endPanel: networkDetailsOpen ?
             NetworkDetailsPanel({
               ref: "networkDetailsPanel",
-              toolbox: NetMonitorController._toolbox,
+              toolbox: window.NetMonitorController._toolbox,
             }) : null,
           endPanelControl: true,
           vert: this.state.isVerticalSpliter,
         }),
       )
     );
   }
 });
--- a/devtools/client/netmonitor/components/moz.build
+++ b/devtools/client/netmonitor/components/moz.build
@@ -1,14 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'monitor-panel.js',
+    'network-monitor.js',
     'request-list-content.js',
     'request-list-empty.js',
     'request-list-header.js',
     'request-list-item.js',
     'request-list.js',
     'statistics-panel.js',
     'toolbar.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/components/network-monitor.js
@@ -0,0 +1,39 @@
+/* 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 {
+  createFactory,
+  DOM,
+  PropTypes,
+} = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+// Components
+const MonitoPanel = createFactory(require("./monitor-panel"));
+const StatisticsPanel = createFactory(require("./statistics-panel"));
+
+const { div } = DOM;
+
+/*
+ * Network monitor component
+ */
+function NetworkMonitor({ statisticsOpen }) {
+  return (
+    div({ className: "network-monitor theme-sidebar" },
+      !statisticsOpen ? MonitoPanel() : StatisticsPanel()
+    )
+  );
+}
+
+NetworkMonitor.displayName = "NetworkMonitor";
+
+NetworkMonitor.propTypes = {
+  statisticsOpen: PropTypes.bool.isRequired,
+};
+
+module.exports = connect(
+  (state) => ({ statisticsOpen: state.ui.statisticsOpen }),
+)(NetworkMonitor);
--- a/devtools/client/netmonitor/components/request-list-content.js
+++ b/devtools/client/netmonitor/components/request-list-content.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals NetMonitorController */
-
 "use strict";
 
 const { KeyCodes } = require("devtools/client/shared/keycodes");
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
@@ -53,17 +51,20 @@ const RequestListContent = createClass({
   },
 
   componentWillMount() {
     const { dispatch } = this.props;
     this.contextMenu = new RequestListContextMenu({
       cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
       openStatistics: (open) => dispatch(Actions.openStatistics(open)),
     });
-    this.tooltip = new HTMLTooltip(NetMonitorController._toolbox.doc, { type: "arrow" });
+    this.tooltip = new HTMLTooltip(
+      window.NetMonitorController._toolbox.doc,
+      { type: "arrow" }
+     );
   },
 
   componentDidMount() {
     // Set the CSS variables for waterfall scaling
     this.setScalingStyles();
 
     // Install event handler for displaying a tooltip
     this.tooltip.startTogglingOnHover(this.refs.contentEl, this.onHover, {
--- a/devtools/client/netmonitor/components/request-list-empty.js
+++ b/devtools/client/netmonitor/components/request-list-empty.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals NetMonitorController */
-
 "use strict";
 
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
@@ -64,11 +62,12 @@ const RequestListEmptyNotice = createCla
   }
 });
 
 module.exports = connect(
   undefined,
   dispatch => ({
     onPerfClick: () => dispatch(Actions.openStatistics(true)),
     onReloadClick: () =>
-      NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
+      window.NetMonitorController
+        .triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
   })
 )(RequestListEmptyNotice);
--- a/devtools/client/netmonitor/components/request-list-header.js
+++ b/devtools/client/netmonitor/components/request-list-header.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals document */
-
 "use strict";
 
 const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
 const { div, button } = DOM;
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
 const { L10N } = require("../l10n");
 const { getWaterfallScale } = require("../selectors/index");
--- a/devtools/client/netmonitor/components/statistics-panel.js
+++ b/devtools/client/netmonitor/components/statistics-panel.js
@@ -38,17 +38,17 @@ const StatisticsPanel = createClass({
   propTypes: {
     closeStatistics: PropTypes.func.isRequired,
     enableRequestFilterTypeOnly: PropTypes.func.isRequired,
     requests: PropTypes.object,
   },
 
   componentDidUpdate(prevProps) {
     const { requests } = this.props;
-    let ready = requests && requests.every((req) =>
+    let ready = requests && !requests.isEmpty() && 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,
--- a/devtools/client/netmonitor/l10n.js
+++ b/devtools/client/netmonitor/l10n.js
@@ -1,13 +1,13 @@
 /* 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 {LocalizationHelper} = require("devtools/shared/l10n");
+const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const NET_STRINGS_URI = "devtools/client/locales/netmonitor.properties";
 const WEBCONSOLE_STRINGS_URI = "devtools/client/locales/webconsole.properties";
 
 exports.L10N = new LocalizationHelper(NET_STRINGS_URI);
 exports.WEBCONSOLE_L10N = new LocalizationHelper(WEBCONSOLE_STRINGS_URI);
--- a/devtools/client/netmonitor/moz.build
+++ b/devtools/client/netmonitor/moz.build
@@ -14,17 +14,16 @@ DIRS += [
 ]
 
 DevToolsModules(
     'constants.js',
     'events.js',
     'filter-predicates.js',
     'l10n.js',
     'netmonitor-controller.js',
-    'netmonitor-view.js',
     'panel.js',
     'prefs.js',
     'request-list-context-menu.js',
     'request-list-tooltip.js',
     'request-utils.js',
     'sort-predicates.js',
     'store.js',
     'waterfall-background.js',
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals window, NetMonitorView, gStore, gNetwork */
-
 "use strict";
 
 const promise = require("promise");
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { TimelineFront } = require("devtools/shared/fronts/timeline");
 const { CurlUtils } = require("devtools/client/shared/curl");
 const { Task } = require("devtools/shared/task");
@@ -20,60 +18,53 @@ const {
   fetchHeaders,
   formDataURI,
 } = require("./request-utils");
 const {
   getRequestById,
   getDisplayedRequestById,
 } = require("./selectors/index");
 
-// Initialize the global Redux store
-window.gStore = configureStore();
+const gStore = window.gStore = configureStore();
 
 /**
  * Object defining the network monitor controller components.
  */
 var NetMonitorController = {
   /**
    * Initializes the view and connects the monitor client.
    *
    * @return object
    *         A promise that is resolved when the monitor finishes startup.
    */
   startupNetMonitor: Task.async(function* () {
     if (this._startup) {
       return this._startup.promise;
     }
     this._startup = promise.defer();
-    {
-      NetMonitorView.initialize();
-      yield this.connect();
-    }
+    yield this.connect();
     this._startup.resolve();
     return undefined;
   }),
 
   /**
    * Destroys the view and disconnects the monitor client from the server.
    *
    * @return object
    *         A promise that is resolved when the monitor finishes shutdown.
    */
   shutdownNetMonitor: Task.async(function* () {
     if (this._shutdown) {
       return this._shutdown.promise;
     }
     this._shutdown = promise.defer();
-    {
-      gStore.dispatch(Actions.batchReset());
-      NetMonitorView.destroy();
-      this.TargetEventsHandler.disconnect();
-      this.NetworkEventsHandler.disconnect();
-      yield this.disconnect();
-    }
+    gStore.dispatch(Actions.batchReset());
+    this.TargetEventsHandler.disconnect();
+    this.NetworkEventsHandler.disconnect();
+    yield this.disconnect();
     this._shutdown.resolve();
     return undefined;
   }),
 
   /**
    * Initiates remote or chrome network monitoring based on the current target,
    * wiring event handlers as necessary. Since the TabTarget will have already
    * started listening to network requests by now, this is largely
@@ -441,16 +432,17 @@ TargetEventsHandler.prototype = {
 };
 
 /**
  * Functions handling target network events.
  */
 function NetworkEventsHandler() {
   this.addRequest = this.addRequest.bind(this);
   this.updateRequest = this.updateRequest.bind(this);
+  this.getString = this.getString.bind(this);
   this._onNetworkEvent = this._onNetworkEvent.bind(this);
   this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
   this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this);
   this._onRequestHeaders = this._onRequestHeaders.bind(this);
   this._onRequestCookies = this._onRequestCookies.bind(this);
   this._onRequestPostData = this._onRequestPostData.bind(this);
   this._onResponseHeaders = this._onResponseHeaders.bind(this);
   this._onResponseCookies = this._onResponseCookies.bind(this);
@@ -583,43 +575,41 @@ NetworkEventsHandler.prototype = {
       responseHeaders,
       requestCookies,
       requestHeaders,
       requestPostData,
     } = action.data;
     let request = getRequestById(gStore.getState(), action.id);
 
     if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
-      let headers = yield fetchHeaders(
-        requestHeaders, gNetwork.getString.bind(gNetwork));
+      let headers = yield fetchHeaders(requestHeaders, this.getString);
       if (headers) {
         yield gStore.dispatch(Actions.updateRequest(
           action.id,
           { requestHeaders: headers },
           true,
         ));
       }
     }
 
     if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
-      let headers = yield fetchHeaders(
-        responseHeaders, gNetwork.getString.bind(gNetwork));
+      let headers = yield fetchHeaders(responseHeaders, this.getString);
       if (headers) {
         yield gStore.dispatch(Actions.updateRequest(
           action.id,
           { responseHeaders: headers },
           true,
         ));
       }
     }
 
     if (request && responseContent && responseContent.content) {
       let { mimeType } = request;
       let { text, encoding } = responseContent.content;
-      let response = yield gNetwork.getString(text);
+      let response = yield this.getString(text);
       let payload = {};
 
       if (mimeType.includes("image/")) {
         payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
       }
 
       responseContent.content.text = response;
       payload.responseContent = responseContent;
@@ -630,17 +620,17 @@ NetworkEventsHandler.prototype = {
         window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
       }
     }
 
     // Search the POST data upload stream for request headers and add
     // them as a separate property, different from the classic headers.
     if (requestPostData && requestPostData.postData) {
       let { text } = requestPostData.postData;
-      let postData = yield gNetwork.getString(text);
+      let postData = yield this.getString(text);
       const headers = CurlUtils.getHeadersFromMultipartText(postData);
       const headersSize = headers.reduce((acc, { name, value }) => {
         return acc + name.length + value.length + 2;
       }, 0);
       let payload = {};
       requestPostData.postData.text = postData;
       payload.requestPostData = Object.assign({}, requestPostData);
       payload.requestHeadersFromUploadStream = { headers, headersSize };
@@ -655,17 +645,17 @@ NetworkEventsHandler.prototype = {
       let reqCookies = [];
       // request store cookies in requestCookies or requestCookies.cookies
       let cookies = requestCookies.cookies ?
         requestCookies.cookies : requestCookies;
       // make sure cookies is iterable
       if (typeof cookies[Symbol.iterator] === "function") {
         for (let cookie of cookies) {
           reqCookies.push(Object.assign({}, cookie, {
-            value: yield gNetwork.getString(cookie.value),
+            value: yield this.getString(cookie.value),
           }));
         }
         if (reqCookies.length) {
           yield gStore.dispatch(Actions.updateRequest(
             action.id,
             { requestCookies: reqCookies },
             true));
         }
@@ -676,17 +666,17 @@ NetworkEventsHandler.prototype = {
       let resCookies = [];
       // response store cookies in responseCookies or responseCookies.cookies
       let cookies = responseCookies.cookies ?
         responseCookies.cookies : responseCookies;
       // make sure cookies is iterable
       if (typeof cookies[Symbol.iterator] === "function") {
         for (let cookie of cookies) {
           resCookies.push(Object.assign({}, cookie, {
-            value: yield gNetwork.getString(cookie.value),
+            value: yield this.getString(cookie.value),
           }));
         }
         if (resCookies.length) {
           yield gStore.dispatch(Actions.updateRequest(
             action.id,
             { responseCookies: resCookies },
             true));
         }
deleted file mode 100644
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/* 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/. */
-
-/* globals gStore, NetMonitorController */
-
-"use strict";
-
-const { ACTIVITY_TYPE } = require("./constants");
-const { createFactory } = require("devtools/client/shared/vendor/react");
-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
-const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
-
-// Components
-const MonitorPanel = createFactory(require("./components/monitor-panel"));
-const StatisticsPanel = createFactory(require("./components/statistics-panel"));
-
-/**
- * Object defining the network monitor view components.
- */
-exports.NetMonitorView = {
-  /**
-   * Initializes the network monitor view.
-   */
-  initialize: function () {
-    this._body = document.querySelector("#body");
-
-    this.monitorPanel = document.querySelector("#react-monitor-panel-hook");
-    ReactDOM.render(Provider(
-      { store: gStore },
-      MonitorPanel(),
-    ), this.monitorPanel);
-
-    this.statisticsPanel = document.querySelector("#react-statistics-panel-hook");
-    ReactDOM.render(Provider(
-      { store: gStore },
-      StatisticsPanel(),
-    ), this.statisticsPanel);
-
-    // Store watcher here is for observing the statisticsOpen state change.
-    // It should be removed once we migrate to react and apply react/redex binding.
-    this.unsubscribeStore = gStore.subscribe(storeWatcher(
-      false,
-      () => gStore.getState().ui.statisticsOpen,
-      this.toggleFrontendMode.bind(this)
-    ));
-  },
-
-  /**
-   * Destroys the network monitor view.
-   */
-  destroy: function () {
-    ReactDOM.unmountComponentAtNode(this.monitorPanel);
-    ReactDOM.unmountComponentAtNode(this.statisticsPanel);
-    this.unsubscribeStore();
-  },
-
-  toggleFrontendMode: function () {
-    if (gStore.getState().ui.statisticsOpen) {
-      this._body.selectedPanel = this.statisticsPanel;
-      NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
-    } else {
-      this._body.selectedPanel = this.monitorPanel;
-    }
-  },
-};
-
-// A smart store watcher to notify store changes as necessary
-function storeWatcher(initialValue, reduceValue, onChange) {
-  let currentValue = initialValue;
-
-  return () => {
-    const newValue = reduceValue();
-    if (newValue !== currentValue) {
-      onChange();
-      currentValue = newValue;
-    }
-  };
-}
--- a/devtools/client/netmonitor/netmonitor.js
+++ b/devtools/client/netmonitor/netmonitor.js
@@ -1,40 +1,52 @@
 /* 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/. */
 
-/* globals window, document, NetMonitorController, NetMonitorView */
-/* exported Netmonitor, NetMonitorController, NetMonitorView */
+/* exported Netmonitor, NetMonitorController */
 
 "use strict";
 
 const Cu = Components.utils;
 const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
 
 function Netmonitor(toolbox) {
-  const { require } = BrowserLoader({
+  const require = window.windowRequire = BrowserLoader({
     baseURI: "resource://devtools/client/netmonitor/",
     window,
     commonLibRequire: toolbox.browserRequire,
-  });
-
-  window.windowRequire = require;
+  }).require;
 
-  const { NetMonitorController } = require("./netmonitor-controller.js");
-  const { NetMonitorView } = require("./netmonitor-view.js");
-
-  window.NetMonitorController = NetMonitorController;
-  window.NetMonitorView = NetMonitorView;
-
-  NetMonitorController._toolbox = toolbox;
-  NetMonitorController._target = toolbox.target;
+  window.NetMonitorController = require("./netmonitor-controller").NetMonitorController;
+  window.NetMonitorController._toolbox = toolbox;
+  window.NetMonitorController._target = toolbox.target;
 }
 
 Netmonitor.prototype = {
   init() {
+    const require = window.windowRequire;
+    const { createFactory } = require("devtools/client/shared/vendor/react");
+    const { render } = require("devtools/client/shared/vendor/react-dom");
+    const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
+
+    // Components
+    const NetworkMonitor = createFactory(require("./components/network-monitor"));
+
+    this.networkMonitor = document.querySelector("#react-network-monitor-hook");
+
+    render(Provider(
+      { store: window.gStore },
+      NetworkMonitor({ toolbox: window.NetMonitorController._toolbox }),
+    ), this.networkMonitor);
+
     return window.NetMonitorController.startupNetMonitor();
   },
 
   destroy() {
+    const require = window.windowRequire;
+    const { unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
+
+    unmountComponentAtNode(this.networkMonitor);
+
     return window.NetMonitorController.shutdownNetMonitor();
   }
 };
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -9,21 +9,11 @@
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml">
 
   <script type="application/javascript;version=1.8"
           src="chrome://devtools/content/shared/theme-switching.js"/>
   <script type="text/javascript" src="netmonitor.js"/>
 
-  <deck id="body"
-        class="theme-sidebar"
-        flex="1"
-        data-localization-bundle="devtools/client/locales/netmonitor.properties">
-    <html:div xmlns="http://www.w3.org/1999/xhtml"
-              id="react-monitor-panel-hook">
-    </html:div>
-    <html:div xmlns="http://www.w3.org/1999/xhtml"
-              id="react-statistics-panel-hook">
-    </html:div>
-  </deck>
-
+  <html:div xmlns="http://www.w3.org/1999/xhtml"
+               id="react-network-monitor-hook"/>
 </window>
--- a/devtools/client/netmonitor/panel.js
+++ b/devtools/client/netmonitor/panel.js
@@ -1,74 +1,32 @@
 /* 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 promise = require("promise");
-const EventEmitter = require("devtools/shared/event-emitter");
-const { Task } = require("devtools/shared/task");
-const { localizeMarkup } = require("devtools/shared/l10n");
-
 function NetMonitorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this.panelDoc = iframeWindow.document;
-  this._toolbox = toolbox;
-
-  this._netmonitor = new iframeWindow.Netmonitor(toolbox);
-
-  EventEmitter.decorate(this);
+  this.toolbox = toolbox;
+  this.netmonitor = new iframeWindow.Netmonitor(toolbox);
 }
 
-exports.NetMonitorPanel = NetMonitorPanel;
-
 NetMonitorPanel.prototype = {
-  /**
-   * Open is effectively an asynchronous constructor.
-   *
-   * @return object
-   *         A promise that is resolved when the NetMonitor completes opening.
-   */
-  open: Task.async(function* () {
-    if (this._opening) {
-      return this._opening;
+  open: async function () {
+    if (!this.toolbox.target.isRemote) {
+      await this.toolbox.target.makeRemote();
     }
-    // Localize all the nodes containing a data-localization attribute.
-    localizeMarkup(this.panelDoc);
-
-    let deferred = promise.defer();
-    this._opening = deferred.promise;
-
-    // Local monitoring needs to make the target remote.
-    if (!this.target.isRemote) {
-      yield this.target.makeRemote();
-    }
-
-    yield this._netmonitor.init();
-
+    await this.netmonitor.init();
+    this.emit("ready");
     this.isReady = true;
-    this.emit("ready");
-
-    deferred.resolve(this);
-    return this._opening;
-  }),
-
-  // DevToolPanel API
-
-  get target() {
-    return this._toolbox.target;
+    return this;
   },
 
-  destroy: Task.async(function* () {
-    if (this._destroying) {
-      return this._destroying;
-    }
-    let deferred = promise.defer();
-    this._destroying = deferred.promise;
+  destroy: async function () {
+    await this.netmonitor.destroy();
+    this.emit("destroyed");
+    return this;
+  },
+};
 
-    yield this._netmonitor.destroy();
-    this.emit("destroyed");
-
-    deferred.resolve();
-    return this._destroying;
-  })
-};
+exports.NetMonitorPanel = NetMonitorPanel;
--- a/devtools/client/netmonitor/prefs.js
+++ b/devtools/client/netmonitor/prefs.js
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {PrefsHelper} = require("devtools/client/shared/prefs");
+const { PrefsHelper } = require("devtools/client/shared/prefs");
 
 /**
  * Shortcuts for accessing various network monitor preferences.
  */
-
 exports.Prefs = new PrefsHelper("devtools.netmonitor", {
   networkDetailsWidth: ["Int", "panes-network-details-width"],
   networkDetailsHeight: ["Int", "panes-network-details-height"],
   filters: ["Json", "filters"]
 });
--- a/devtools/client/netmonitor/reducers/ui.js
+++ b/devtools/client/netmonitor/reducers/ui.js
@@ -1,21 +1,23 @@
 /* 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 I = require("devtools/client/shared/vendor/immutable");
 const {
+  CLEAR_REQUESTS,
   OPEN_NETWORK_DETAILS,
   OPEN_STATISTICS,
   REMOVE_SELECTED_CUSTOM_REQUEST,
   SELECT_DETAILS_PANEL_TAB,
   SEND_CUSTOM_REQUEST,
+  SELECT_REQUEST,
   WATERFALL_RESIZE,
 } = require("../constants");
 
 const UI = I.Record({
   detailsPanelSelectedTab: "headers",
   networkDetailsOpen: false,
   statisticsOpen: false,
   waterfallWidth: null,
@@ -37,25 +39,29 @@ function openStatistics(state, action) {
 }
 
 function setDetailsPanelTab(state, action) {
   return state.set("detailsPanelSelectedTab", action.id);
 }
 
 function ui(state = new UI(), action) {
   switch (action.type) {
+    case CLEAR_REQUESTS:
+      return openNetworkDetails(state, { open: false });
     case OPEN_NETWORK_DETAILS:
       return openNetworkDetails(state, action);
     case OPEN_STATISTICS:
       return openStatistics(state, action);
     case REMOVE_SELECTED_CUSTOM_REQUEST:
     case SEND_CUSTOM_REQUEST:
       return openNetworkDetails(state, { open: false });
     case SELECT_DETAILS_PANEL_TAB:
       return setDetailsPanelTab(state, action);
+    case SELECT_REQUEST:
+      return openNetworkDetails(state, { open: true });
     case WATERFALL_RESIZE:
       return resizeWaterfall(state, action);
     default:
       return state;
   }
 }
 
 module.exports = ui;
--- a/devtools/client/netmonitor/request-list-context-menu.js
+++ b/devtools/client/netmonitor/request-list-context-menu.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals NetMonitorController, gNetwork, gStore */
-
 "use strict";
 
 const Services = require("Services");
 const { Task } = require("devtools/shared/task");
 const { Curl } = require("devtools/client/shared/curl");
 const { gDevTools } = require("devtools/client/framework/devtools");
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
@@ -35,21 +33,21 @@ function RequestListContextMenu({
   openStatistics,
 }) {
   this.cloneSelectedRequest = cloneSelectedRequest;
   this.openStatistics = openStatistics;
 }
 
 RequestListContextMenu.prototype = {
   get selectedRequest() {
-    return getSelectedRequest(gStore.getState());
+    return getSelectedRequest(window.gStore.getState());
   },
 
   get sortedRequests() {
-    return getSortedRequests(gStore.getState());
+    return getSortedRequests(window.gStore.getState());
   },
 
   /**
    * Handle the context menu opening. Hide items if no request is selected.
    * Since visible attribute only accept boolean value but the method call may
    * return undefined, we use !! to force convert any object to boolean
    */
   open({ screenX = 0, screenY = 0 } = {}) {
@@ -155,17 +153,17 @@ RequestListContextMenu.prototype = {
       type: "separator",
       visible: !!selectedRequest,
     }));
 
     menu.append(new MenuItem({
       id: "request-menu-context-resend",
       label: L10N.getStr("netmonitor.context.editAndResend"),
       accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
-      visible: !!(NetMonitorController.supportsCustomRequest &&
+      visible: !!(window.NetMonitorController.supportsCustomRequest &&
                selectedRequest && !selectedRequest.isCustom),
       click: this.cloneSelectedRequest,
     }));
 
     menu.append(new MenuItem({
       type: "separator",
       visible: !!selectedRequest,
     }));
@@ -177,21 +175,21 @@ RequestListContextMenu.prototype = {
       visible: !!selectedRequest,
       click: () => this.openRequestInTab()
     }));
 
     menu.append(new MenuItem({
       id: "request-menu-context-perf",
       label: L10N.getStr("netmonitor.context.perfTools"),
       accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"),
-      visible: !!NetMonitorController.supportsPerfStats,
+      visible: !!window.NetMonitorController.supportsPerfStats,
       click: () => this.openStatistics(true)
     }));
 
-    menu.popup(screenX, screenY, NetMonitorController._toolbox);
+    menu.popup(screenX, screenY, window.NetMonitorController._toolbox);
     return menu;
   },
 
   /**
    * Opens selected item in a new tab.
    */
   openRequestInTab() {
     let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
@@ -223,34 +221,34 @@ RequestListContextMenu.prototype = {
   copyPostData: Task.async(function* () {
     let selected = this.selectedRequest;
 
     // Try to extract any form data parameters.
     let formDataSections = yield getFormDataSections(
       selected.requestHeaders,
       selected.requestHeadersFromUploadStream,
       selected.requestPostData,
-      gNetwork.getString.bind(gNetwork));
+      window.gNetwork.getString.bind(window.gNetwork));
 
     let params = [];
     formDataSections.forEach(section => {
       let paramsArray = parseQueryString(section);
       if (paramsArray) {
         params = [...params, ...paramsArray];
       }
     });
 
     let string = params
       .map(param => param.name + (param.value ? "=" + param.value : ""))
       .join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
 
     // Fall back to raw payload.
     if (!string) {
       let postData = selected.requestPostData.postData.text;
-      string = yield gNetwork.getString(postData);
+      string = yield window.gNetwork.getString(postData);
       if (Services.appinfo.OS !== "WINNT") {
         string = string.replace(/\r/g, "");
       }
     }
 
     clipboardHelper.copyString(string);
   }),
 
@@ -266,24 +264,24 @@ RequestListContextMenu.prototype = {
       method: selected.method,
       headers: [],
       httpVersion: selected.httpVersion,
       postDataText: null
     };
 
     // Fetch header values.
     for (let { name, value } of selected.requestHeaders.headers) {
-      let text = yield gNetwork.getString(value);
+      let text = yield window.gNetwork.getString(value);
       data.headers.push({ name: name, value: text });
     }
 
     // Fetch the request payload.
     if (selected.requestPostData) {
       let postData = selected.requestPostData.postData.text;
-      data.postDataText = yield gNetwork.getString(postData);
+      data.postDataText = yield window.gNetwork.getString(postData);
     }
 
     clipboardHelper.copyString(Curl.generateCommand(data));
   }),
 
   /**
    * Copy the raw request headers from the currently selected item.
    */
@@ -307,29 +305,29 @@ RequestListContextMenu.prototype = {
   },
 
   /**
    * Copy image as data uri.
    */
   copyImageAsDataUri() {
     const { mimeType, text, encoding } = this.selectedRequest.responseContent.content;
 
-    gNetwork.getString(text).then(string => {
+    window.gNetwork.getString(text).then(string => {
       let data = formDataURI(mimeType, encoding, string);
       clipboardHelper.copyString(data);
     });
   },
 
   /**
    * Copy response data as a string.
    */
   copyResponse() {
     const { text } = this.selectedRequest.responseContent.content;
 
-    gNetwork.getString(text).then(string => {
+    window.gNetwork.getString(text).then(string => {
       clipboardHelper.copyString(string);
     });
   },
 
   /**
    * Copy HAR from the network panel content to the clipboard.
    */
   copyAllAsHar() {
@@ -341,20 +339,20 @@ RequestListContextMenu.prototype = {
    * Save HAR from the network panel content to a file.
    */
   saveAllAsHar() {
     let options = this.getDefaultHarOptions();
     return HarExporter.save(options);
   },
 
   getDefaultHarOptions() {
-    let form = NetMonitorController._target.form;
+    let form = window.NetMonitorController._target.form;
     let title = form.title || form.url;
 
     return {
-      getString: gNetwork.getString.bind(gNetwork),
+      getString: window.gNetwork.getString.bind(window.gNetwork),
       items: this.sortedRequests,
       title: title
     };
   }
 };
 
 module.exports = RequestListContextMenu;
--- a/devtools/client/netmonitor/request-list-tooltip.js
+++ b/devtools/client/netmonitor/request-list-tooltip.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals gNetwork, NetMonitorController */
-
 "use strict";
 
 const { Task } = require("devtools/shared/task");
 const {
   setImageTooltip,
   getImageDimensions,
 } = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
 const { WEBCONSOLE_L10N } = require("./l10n");
@@ -23,17 +21,17 @@ const HTML_NS = "http://www.w3.org/1999/
 
 const setTooltipImageContent = Task.async(function* (tooltip, itemEl, requestItem) {
   let { mimeType, text, encoding } = requestItem.responseContent.content;
 
   if (!mimeType || !mimeType.includes("image/")) {
     return false;
   }
 
-  let string = yield gNetwork.getString(text);
+  let string = yield window.gNetwork.getString(text);
   let src = formDataURI(mimeType, encoding, string);
   let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
   let { naturalWidth, naturalHeight } = yield getImageDimensions(tooltip.doc, src);
   let options = { maxDim, naturalWidth, naturalHeight };
   setImageTooltip(tooltip, tooltip.doc, src, options);
 
   return itemEl.querySelector(".requests-menu-icon");
 });
@@ -87,17 +85,17 @@ const setTooltipStackTraceContent = Task
     let lineEl = doc.createElementNS(HTML_NS, "span");
     lineEl.className = "stack-frame-line";
     lineEl.textContent = `:${lineNumber}:${columnNumber}`;
     sourceInnerEl.appendChild(lineEl);
 
     frameEl.addEventListener("click", () => {
       // hide the tooltip immediately, not after delay
       tooltip.hide();
-      NetMonitorController.viewSourceInDebugger(filename, lineNumber);
+      window.NetMonitorController.viewSourceInDebugger(filename, lineNumber);
     });
 
     el.appendChild(frameEl);
   }
 
   tooltip.setContent(el, {width: REQUESTS_TOOLTIP_STACK_TRACE_WIDTH});
 
   return true;
--- a/devtools/client/netmonitor/shared/components/headers-panel.js
+++ b/devtools/client/netmonitor/shared/components/headers-panel.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals NetMonitorController */
-
 "use strict";
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
@@ -157,17 +155,17 @@ const HeadersPanel = createClass({
             className: "requests-menu-status-icon",
             "data-code": code,
           }),
           input({
             className: "tabpanel-summary-value textbox-input devtools-monospace",
             readOnly: true,
             value: `${status} ${statusText}`,
           }),
-          NetMonitorController.supportsCustomRequest && button({
+          window.NetMonitorController.supportsCustomRequest && button({
             className: "devtools-button",
             onClick: cloneSelectedRequest,
           }, EDIT_AND_RESEND),
           button({
             className: "devtools-button",
             onClick: this.toggleRawHeaders,
           }, RAW_HEADERS),
         )
--- a/devtools/client/netmonitor/test/browser_net_copy_as_curl.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_as_curl.js
@@ -52,18 +52,18 @@ add_task(function* () {
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelectorAll(".request-list-item")[0]);
 
   yield waitForClipboardPromise(function setup() {
     // Context menu is appending in XUL document, we must select it from
-    // _toolbox.doc
-    monitor._toolbox.doc
+    // toolbox.doc
+    monitor.toolbox.doc
       .querySelector("#request-menu-context-copy-as-curl").click();
   }, function validate(result) {
     if (typeof result !== "string") {
       return false;
     }
 
     // Different setups may produce the same command, but with the
     // parameters in a different order in the commandline (which is fine).
--- a/devtools/client/netmonitor/test/browser_net_copy_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_headers.js
@@ -37,18 +37,18 @@ add_task(function* () {
     "Connection: keep-alive",
     "Upgrade-Insecure-Requests: 1",
     "Pragma: no-cache",
     "Cache-Control: no-cache"
   ].join("\n");
 
   yield waitForClipboardPromise(function setup() {
     // Context menu is appending in XUL document, we must select it from
-    // _toolbox.doc
-    monitor._toolbox.doc
+    // toolbox.doc
+    monitor.toolbox.doc
       .querySelector("#request-menu-context-copy-request-headers").click();
   }, function validate(result) {
     // Sometimes, a "Cookie" header is left over from other tests. Remove it:
     result = String(result).replace(/Cookie: [^\n]+\n/, "");
     return result === EXPECTED_REQUEST_HEADERS;
   });
   info("Clipboard contains the currently selected item's request headers.");
 
@@ -62,18 +62,18 @@ add_task(function* () {
     "Date: Sun, 3 May 2015 11:11:11 GMT"
   ].join("\n");
 
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelectorAll(".request-list-item")[0]);
 
   yield waitForClipboardPromise(function setup() {
     // Context menu is appending in XUL document, we must select it from
-    // _toolbox.doc
-    monitor._toolbox.doc
+    // _oolbox.doc
+    monitor.toolbox.doc
       .querySelector("#response-menu-context-copy-response-headers").click();
   }, function validate(result) {
     // Fake the "Last-Modified" and "Date" headers because they will vary:
     result = String(result)
       .replace(/Last-Modified: [^\n]+ GMT/, "Last-Modified: Sun, 3 May 2015 11:11:11 GMT")
       .replace(/Date: [^\n]+ GMT/, "Date: Sun, 3 May 2015 11:11:11 GMT");
     return result === EXPECTED_RESPONSE_HEADERS;
   });
--- a/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js
@@ -21,17 +21,17 @@ add_task(function* () {
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[5]);
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelectorAll(".request-list-item")[5]);
 
   yield waitForClipboardPromise(function setup() {
     // Context menu is appending in XUL document, we must select it from
-    // _toolbox.doc
-    monitor._toolbox.doc
+    // toolbox.doc
+    monitor.toolbox.doc
       .querySelector("#request-menu-context-copy-image-as-data-uri").click();
   }, TEST_IMAGE_DATA_URI);
 
   ok(true, "Clipboard contains the currently selected image as data uri.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_copy_params.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_params.js
@@ -57,51 +57,51 @@ add_task(function* () {
 
   return teardown(monitor);
 
   function testCopyUrlParamsHidden(index, hidden) {
     EventUtils.sendMouseEvent({ type: "mousedown" },
       document.querySelectorAll(".request-list-item")[index]);
     EventUtils.sendMouseEvent({ type: "contextmenu" },
       document.querySelectorAll(".request-list-item")[index]);
-    let copyUrlParamsNode = monitor._toolbox.doc
+    let copyUrlParamsNode = monitor.toolbox.doc
       .querySelector("#request-menu-context-copy-url-params");
     is(!!copyUrlParamsNode, !hidden,
       "The \"Copy URL Parameters\" context menu item should" + (hidden ? " " : " not ") +
         "be hidden.");
   }
 
   function* testCopyUrlParams(index, queryString) {
     EventUtils.sendMouseEvent({ type: "mousedown" },
       document.querySelectorAll(".request-list-item")[index]);
     EventUtils.sendMouseEvent({ type: "contextmenu" },
       document.querySelectorAll(".request-list-item")[index]);
     yield waitForClipboardPromise(function setup() {
-      monitor._toolbox.doc
+      monitor.toolbox.doc
         .querySelector("#request-menu-context-copy-url-params").click();
     }, queryString);
     ok(true, "The url query string copied from the selected item is correct.");
   }
 
   function testCopyPostDataHidden(index, hidden) {
     EventUtils.sendMouseEvent({ type: "mousedown" },
       document.querySelectorAll(".request-list-item")[index]);
     EventUtils.sendMouseEvent({ type: "contextmenu" },
       document.querySelectorAll(".request-list-item")[index]);
-    let copyPostDataNode = monitor._toolbox.doc
+    let copyPostDataNode = monitor.toolbox.doc
       .querySelector("#request-menu-context-copy-post-data");
     is(!!copyPostDataNode, !hidden,
       "The \"Copy POST Data\" context menu item should" + (hidden ? " " : " not ") +
         "be hidden.");
   }
 
   function* testCopyPostData(index, postData) {
     EventUtils.sendMouseEvent({ type: "mousedown" },
       document.querySelectorAll(".request-list-item")[index]);
     EventUtils.sendMouseEvent({ type: "contextmenu" },
       document.querySelectorAll(".request-list-item")[index]);
     yield waitForClipboardPromise(function setup() {
-      monitor._toolbox.doc
+      monitor.toolbox.doc
         .querySelector("#request-menu-context-copy-post-data").click();
     }, postData);
     ok(true, "The post data string copied from the selected item is correct.");
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_copy_response.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_response.js
@@ -23,15 +23,15 @@ add_task(function* () {
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[3]);
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelectorAll(".request-list-item")[3]);
 
   yield waitForClipboardPromise(function setup() {
     // Context menu is appending in XUL document, we must select it from
-    // _toolbox.doc
-    monitor._toolbox.doc
+    // toolbox.doc
+    monitor.toolbox.doc
       .querySelector("#request-menu-context-copy-response").click();
   }, EXPECTED_RESULT);
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js
@@ -23,17 +23,17 @@ add_task(function* () {
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelectorAll(".request-list-item")[0]);
 
   yield waitForClipboardPromise(function setup() {
     // Context menu is appending in XUL document, we must select it from
-    // _toolbox.doc
-    monitor._toolbox.doc
+    // toolbox.doc
+    monitor.toolbox.doc
       .querySelector("#request-menu-context-copy-image-as-data-uri").click();
   }, function check(text) {
     return text.startsWith("data:") && !/undefined/.test(text);
   });
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_copy_url.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_url.js
@@ -24,15 +24,15 @@ add_task(function* () {
     document.querySelectorAll(".request-list-item")[0]);
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelectorAll(".request-list-item")[0]);
 
   let requestItem = getSortedRequests(gStore.getState()).get(0);
 
   yield waitForClipboardPromise(function setup() {
     // Context menu is appending in XUL document, we must select it from
-    // _toolbox.doc
-    monitor._toolbox.doc
+    // toolbox.doc
+    monitor.toolbox.doc
       .querySelector("#request-menu-context-copy-url").click();
   }, requestItem.url);
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js
+++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js
@@ -16,34 +16,34 @@ add_task(function* test() {
   let { document, gStore, windowRequire, NetMonitorController } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/constants");
   let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/selectors/index");
-  let toolboxDoc = monitor._toolbox.doc;
+  let toolboxDoc = monitor.toolbox.doc;
 
   gStore.dispatch(Actions.batchEnable(false));
 
   let onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS);
   let onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
   yield performRequests();
   yield onEvents;
   yield onThumbnail;
 
   info("Checking the image thumbnail after a few requests were made...");
   yield showTooltipAndVerify(toolboxDoc,
     document.querySelectorAll(".request-list-item")[0]);
 
   // Hide tooltip before next test, to avoid the situation that tooltip covers
   // the icon for the request of the next test.
   info("Checking the image thumbnail gets hidden...");
-  yield hideTooltipAndVerify(monitor._toolbox.doc,
+  yield hideTooltipAndVerify(monitor.toolbox.doc,
     document.querySelectorAll(".request-list-item")[0]);
 
   // +1 extra document reload
   onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS + 1);
   onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
 
   info("Reloading the debuggee and performing all requests again...");
   yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
--- a/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
+++ b/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
@@ -28,18 +28,18 @@ add_task(function* () {
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelectorAll(".request-list-item")[0]);
 
   let onTabOpen = once(gBrowser.tabContainer, "TabOpen", false);
   // Context menu is appending in XUL document, we must select it from
-  // _toolbox.doc
-  monitor._toolbox.doc
+  // toolbox.doc
+  monitor.toolbox.doc
     .querySelector("#request-menu-context-newtab").click();
   yield onTabOpen;
 
   ok(true, "A new tab has been opened");
 
   yield teardown(monitor);
 
   gBrowser.removeCurrentTab();
--- a/devtools/client/netmonitor/test/browser_net_prefs-reload.js
+++ b/devtools/client/netmonitor/test/browser_net_prefs-reload.js
@@ -57,17 +57,17 @@ add_task(function* () {
     /* add more prefs here... */
   };
 
   yield testBottom();
   yield testSide();
   yield testWindow();
 
   info("Moving toolbox back to the bottom...");
-  yield monitor._toolbox.switchHost(Toolbox.HostType.BOTTOM);
+  yield monitor.toolbox.switchHost(Toolbox.HostType.BOTTOM);
   return teardown(monitor);
 
   function storeFirstPrefValues() {
     info("Caching initial pref values.");
 
     for (let name in prefsToCheck) {
       let currentValue = getPrefs()[name];
       prefsToCheck[name].firstValue = currentValue;
@@ -207,17 +207,17 @@ add_task(function* () {
     validateFirstPrefValues(true);
   }
 
   function* testSide() {
     yield restartNetMonitorAndSetupEnv();
 
     info("Moving toolbox to the side...");
 
-    yield monitor._toolbox.switchHost(Toolbox.HostType.SIDE);
+    yield monitor.toolbox.switchHost(Toolbox.HostType.SIDE);
     info("Testing prefs reload for a side host.");
     storeFirstPrefValues();
 
     // Validate and modify frontend while toolbox is on the side.
     validateFirstPrefValues(false);
     modifyFrontend(false);
 
     yield restartNetMonitorAndSetupEnv();
@@ -232,17 +232,17 @@ add_task(function* () {
     validateFirstPrefValues(false);
   }
 
   function* testWindow() {
     yield restartNetMonitorAndSetupEnv();
 
     info("Moving toolbox into a window...");
 
-    yield monitor._toolbox.switchHost(Toolbox.HostType.WINDOW);
+    yield monitor.toolbox.switchHost(Toolbox.HostType.WINDOW);
     info("Testing prefs reload for a window host.");
     storeFirstPrefValues();
 
     // Validate and modify frontend while toolbox is in a window.
     validateFirstPrefValues(true);
     modifyFrontend(true);
 
     yield restartNetMonitorAndSetupEnv();
--- a/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
+++ b/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
@@ -27,23 +27,23 @@ add_task(function* () {
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequest(0);
 
   // Switch to the webconsole.
-  let onWebConsole = monitor._toolbox.once("webconsole-selected");
-  monitor._toolbox.selectTool("webconsole");
+  let onWebConsole = monitor.toolbox.once("webconsole-selected");
+  monitor.toolbox.selectTool("webconsole");
   yield onWebConsole;
 
   // Switch back to the netmonitor.
-  let onNetMonitor = monitor._toolbox.once("netmonitor-selected");
-  monitor._toolbox.selectTool("netmonitor");
+  let onNetMonitor = monitor.toolbox.once("netmonitor-selected");
+  monitor.toolbox.selectTool("netmonitor");
   yield onNetMonitor;
 
   // Reload debugee.
   wait = waitForNetworkEvents(monitor, 1);
   tab.linkedBrowser.reload();
   yield wait;
 
   // Perform another batch of requests.
--- a/devtools/client/netmonitor/test/browser_net_simple-init.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-init.js
@@ -17,18 +17,16 @@ function test() {
     info("Starting test... ");
 
     is(tab.linkedBrowser.currentURI.spec, SIMPLE_URL,
       "The current tab's location is the correct one.");
 
     function checkIfInitialized(tag) {
       info(`Checking if initialization is ok (${tag}).`);
 
-      ok(monitor.panelWin.NetMonitorView,
-        `The network monitor view object exists (${tag}).`);
       ok(monitor.panelWin.NetMonitorController,
         `The network monitor controller object exists (${tag}).`);
       ok(monitor.panelWin.NetMonitorController._startup,
         `The network monitor controller object exists and is initialized (${tag}).`);
 
       ok(monitor.isReady,
         `The network monitor panel appears to be ready (${tag}).`);
 
@@ -38,18 +36,16 @@ function test() {
         `There should be a webConsoleClient available at this point (${tag}).`);
       ok(monitor.panelWin.NetMonitorController.timelineFront,
         `There should be a timelineFront available at this point (${tag}).`);
     }
 
     function checkIfDestroyed(tag) {
       gInfo("Checking if destruction is ok.");
 
-      gOk(monitor.panelWin.NetMonitorView,
-        `The network monitor view object still exists (${tag}).`);
       gOk(monitor.panelWin.NetMonitorController,
         `The network monitor controller object still exists (${tag}).`);
       gOk(monitor.panelWin.NetMonitorController._shutdown,
         `The network monitor controller object still exists and is destroyed (${tag}).`);
 
       gOk(!monitor.panelWin.NetMonitorController.tabClient,
         `There shouldn't be a tabClient available after destruction (${tag}).`);
       gOk(!monitor.panelWin.NetMonitorController.webConsoleClient,
--- a/devtools/client/netmonitor/test/browser_net_statistics-01.js
+++ b/devtools/client/netmonitor/test/browser_net_statistics-01.js
@@ -1,55 +1,46 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
- * Tests if the statistics view is populated correctly.
+ * Tests if the statistics panel displays correctly.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(STATISTICS_URL);
   info("Starting test... ");
 
   let panel = monitor.panelWin;
   let { document, gStore, windowRequire } = panel;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
 
-  let body = document.querySelector("#body");
-
-  is(body.selectedPanel.id, "react-monitor-panel-hook",
+  ok(document.querySelector(".monitor-panel"),
     "The current main panel is correct.");
 
-  info("Displaying statistics view");
+  info("Displaying statistics panel");
   gStore.dispatch(Actions.openStatistics(true));
 
-  is(body.selectedPanel.id, "react-statistics-panel-hook",
+  ok(document.querySelector(".statistics-panel"),
     "The current main panel is correct.");
 
   info("Waiting for placeholder to display");
 
-  is(document.querySelector(".primed-cache-chart").childNodes.length, 1,
-    "There should be a placeholder primed cache chart created now.");
-  is(document.querySelector(".empty-cache-chart").childNodes.length, 1,
-    "There should be a placeholder empty cache chart created now.");
+  yield waitUntil(
+    () => document.querySelectorAll(".pie-chart-container[placeholder=true]").length == 2);
+  ok(true, "Two placeholder pie charts appear to be rendered correctly.");
 
-  is(document.querySelectorAll(".pie-chart-container[placeholder=true]").length, 2,
-    "Two placeholder pie chart appear to be rendered correctly.");
-  is(document.querySelectorAll(".table-chart-container[placeholder=true]").length, 2,
-    "Two placeholder table chart appear to be rendered correctly.");
+  yield waitUntil(
+    () => document.querySelectorAll(".table-chart-container[placeholder=true]").length == 2);
+  ok(true, "Two placeholde table charts appear to be rendered correctly.");
 
   info("Waiting for chart to display");
 
-  is(document.querySelector(".primed-cache-chart").childNodes.length, 1,
-    "There should be a real primed cache chart created now.");
-  is(document.querySelector(".empty-cache-chart").childNodes.length, 1,
-    "There should be a real empty cache chart created now.");
-
   yield waitUntil(
     () => document.querySelectorAll(".pie-chart-container:not([placeholder=true])").length == 2);
   ok(true, "Two real pie charts appear to be rendered correctly.");
 
   yield waitUntil(
     () => document.querySelectorAll(".table-chart-container:not([placeholder=true])").length == 2);
   ok(true, "Two real table charts appear to be rendered correctly.");
 
--- a/devtools/client/netmonitor/test/browser_net_statistics-02.js
+++ b/devtools/client/netmonitor/test/browser_net_statistics-02.js
@@ -26,28 +26,26 @@ add_task(function* () {
     document.querySelector("#requests-menu-filter-ws-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-menu-filter-other-button"));
   testFilterButtonsCustom(monitor, [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1]);
   info("The correct filtering predicates are used before entering perf. analysis mode.");
 
   gStore.dispatch(Actions.openStatistics(true));
 
-  let body = document.querySelector("#body");
-
-  is(body.selectedPanel.id, "react-statistics-panel-hook",
+  ok(document.querySelector(".statistics-panel"),
     "The main panel is switched to the statistics panel.");
 
   yield waitUntil(
     () => document.querySelectorAll(".pie-chart-container:not([placeholder=true])").length == 2);
   ok(true, "Two real pie charts appear to be rendered correctly.");
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".pie-chart-slice"));
 
-  is(body.selectedPanel.id, "react-monitor-panel-hook",
-    "The main panel is switched back to the inspector panel.");
+  ok(document.querySelector(".monitor-panel"),
+    "The main panel is switched back to the monitor panel.");
 
   testFilterButtons(monitor, "html");
   info("The correct filtering predicate is used when exiting perf. analysis mode.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -147,32 +147,32 @@ function initNetMonitor(aUrl, aWindow, a
     return {tab, monitor};
   });
 }
 
 function restartNetMonitor(monitor, newUrl) {
   info("Restarting the specified network monitor.");
 
   return Task.spawn(function* () {
-    let tab = monitor.target.tab;
+    let tab = monitor.toolbox.target.tab;
     let url = newUrl || tab.linkedBrowser.currentURI.spec;
 
     let onDestroyed = monitor.once("destroyed");
     yield removeTab(tab);
     yield onDestroyed;
 
     return initNetMonitor(url);
   });
 }
 
 function teardown(monitor) {
   info("Destroying the specified network monitor.");
 
   return Task.spawn(function* () {
-    let tab = monitor.target.tab;
+    let tab = monitor.toolbox.target.tab;
 
     let onDestroyed = monitor.once("destroyed");
     yield removeTab(tab);
     yield onDestroyed;
   });
 }
 
 function waitForNetworkEvents(aMonitor, aGetRequests, aPostRequests = 0) {
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -1265,8 +1265,13 @@
   width: 100%;
   height: 100%;
   overflow: hidden;
 }
 
 .split-box {
   width: 100vw;
 }
+
+.network-monitor {
+  width: 100vw;
+  height: 100vh;
+}