Backed out changeset a822f74d410e (bug 1352699)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 07 Apr 2017 10:48:10 +0200
changeset 558349 794b7cd760290fb748fb1a9e2fd5957694dbb5be
parent 558348 9e611e21339d3c36ef904d35c98bbeb01f86eae7
child 558350 6471400d8fbe3579149744cf64a4e060bb353c97
push id52860
push userbmo:walkingice0204@gmail.com
push dateFri, 07 Apr 2017 13:29:26 +0000
bugs1352699
milestone55.0a1
backs outa822f74d410e7253929f8761a221f2b7687f29ff
Backed out changeset a822f74d410e (bug 1352699)
devtools/client/netmonitor/index.html
devtools/client/netmonitor/index.js
devtools/client/netmonitor/src/actions/requests.js
devtools/client/netmonitor/src/actions/ui.js
devtools/client/netmonitor/src/assets/styles/netmonitor.css
devtools/client/netmonitor/src/components/headers-panel.js
devtools/client/netmonitor/src/components/request-list-content.js
devtools/client/netmonitor/src/components/request-list-empty-notice.js
devtools/client/netmonitor/src/components/request-list-item.js
devtools/client/netmonitor/src/netmonitor-controller.js
devtools/client/netmonitor/src/request-list-context-menu.js
devtools/client/netmonitor/src/utils/create-store.js
--- a/devtools/client/netmonitor/index.html
+++ b/devtools/client/netmonitor/index.html
@@ -26,16 +26,20 @@
       const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
       const { configureStore } = require("./src/utils/create-store");
       const store = window.gStore = configureStore();
       const { NetMonitorController } = require("./src/netmonitor-controller");
 
       // Inject EventEmitter into global window.
       EventEmitter.decorate(window);
 
+      // Export NetMonitorController to global window
+      // FIXME: Use module export mechanism instead of this tricky global variables
+      window.NetMonitorController = NetMonitorController;
+
       window.Netmonitor = {
         bootstrap({ toolbox }) {
           this.mount = document.querySelector("#mount");
           const App = createFactory(require("./src/components/app"));
           render(Provider({ store }, App()), this.mount);
           return NetMonitorController.startupNetMonitor({
             client: {
               getTabTarget: () => toolbox.target,
--- a/devtools/client/netmonitor/index.js
+++ b/devtools/client/netmonitor/index.js
@@ -8,17 +8,17 @@
  * This script is the entry point of devtools-launchpad. Make netmonitor possible
  * to run on standalone browser tab without chrome privilege.
  * See README.md for more information.
  */
 const React = require("react");
 const ReactDOM = require("react-dom");
 const { bootstrap } = require("devtools-launchpad");
 const { EventEmitter } = require("devtools-modules");
-const { Services: { appinfo, pref }} = require("devtools-modules");
+const { Services: { pref }} = require("devtools-modules");
 const { configureStore } = require("./src/utils/create-store");
 
 require("./src/assets/styles/netmonitor.css");
 
 EventEmitter.decorate(window);
 
 pref("devtools.netmonitor.enabled", true);
 pref("devtools.netmonitor.filters", "[\"all\"]");
@@ -35,24 +35,17 @@ pref("devtools.netmonitor.har.forceExpor
 pref("devtools.netmonitor.har.pageLoadedTimeout", 1500);
 pref("devtools.netmonitor.har.enableAutoExportToFile", false);
 pref("devtools.webconsole.persistlog", false);
 
 const App = require("./src/components/app");
 const store = window.gStore = configureStore();
 const { NetMonitorController } = require("./src/netmonitor-controller");
 
-window.addEventListener("DOMContentLoaded", () => {
-  if (appinfo.OS === "Darwin") {
-    document.documentElement.setAttribute("platform", "mac");
-  } else if (appinfo.OS === "Linux") {
-    document.documentElement.setAttribute("platform", "linux");
-  } else {
-    document.documentElement.setAttribute("platform", "win");
-  }
-});
+// FIXME: Inject NetMonitorController to global window
+window.NetMonitorController = NetMonitorController;
 
 bootstrap(React, ReactDOM, App, null, store).then(connection => {
   if (!connection || !connection.tab) {
     return;
   }
   NetMonitorController.startupNetMonitor(connection);
 });
--- a/devtools/client/netmonitor/src/actions/requests.js
+++ b/devtools/client/netmonitor/src/actions/requests.js
@@ -7,17 +7,16 @@
 const {
   ADD_REQUEST,
   CLEAR_REQUESTS,
   CLONE_SELECTED_REQUEST,
   REMOVE_SELECTED_CUSTOM_REQUEST,
   SEND_CUSTOM_REQUEST,
   UPDATE_REQUEST,
 } = require("../constants");
-const { NetMonitorController } = require("../netmonitor-controller");
 const { getSelectedRequest } = require("../selectors/index");
 
 function addRequest(id, data, batch) {
   return {
     type: ADD_REQUEST,
     id,
     data,
     meta: { batch },
@@ -42,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;
@@ -66,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/src/actions/ui.js
+++ b/devtools/client/netmonitor/src/actions/ui.js
@@ -8,17 +8,16 @@ const {
   ACTIVITY_TYPE,
   OPEN_NETWORK_DETAILS,
   OPEN_STATISTICS,
   RESET_COLUMNS,
   SELECT_DETAILS_PANEL_TAB,
   TOGGLE_COLUMN,
   WATERFALL_RESIZE,
 } = require("../constants");
-const { NetMonitorController } = require("../netmonitor-controller");
 
 /**
  * Change network details panel.
  *
  * @param {boolean} open - expected network details panel open state
  */
 function openNetworkDetails(open) {
   return {
@@ -29,17 +28,17 @@ function openNetworkDetails(open) {
 
 /**
  * Change performance statistics panel open state.
  *
  * @param {boolean} visible - expected performance statistics panel open state
  */
 function openStatistics(open) {
   if (open) {
-    NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
+    window.NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
   }
   return {
     type: OPEN_STATISTICS,
     open,
   };
 }
 
 /**
--- a/devtools/client/netmonitor/src/assets/styles/netmonitor.css
+++ b/devtools/client/netmonitor/src/assets/styles/netmonitor.css
@@ -1,13 +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/. */
 
-@import "resource://devtools/client/themes/light-theme.css";
 @import "resource://devtools/client/shared/components/splitter/split-box.css";
 @import "resource://devtools/client/shared/components/tree/tree-view.css";
 @import "resource://devtools/client/shared/components/tabs/tabs.css";
 @import "resource://devtools/client/shared/components/tabs/tabbar.css";
 @import "chrome://devtools/skin/components-frame.css";
 
 :root.theme-dark {
   --table-splitter-color: rgba(255,255,255,0.15);
--- a/devtools/client/netmonitor/src/components/headers-panel.js
+++ b/devtools/client/netmonitor/src/components/headers-panel.js
@@ -5,17 +5,16 @@
 "use strict";
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
-const { NetMonitorController } = require("../netmonitor-controller");
 const { getFormattedSize } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const {
   getHeadersURL,
   getHTTPStatusCodeURL,
 } = require("../utils/mdn-utils");
 const { writeHeaderText } = require("../utils/request-utils");
 
@@ -193,17 +192,17 @@ const HeadersPanel = createClass({
               + " status-text",
             readOnly: true,
             value: `${status} ${statusText}`,
             size: `${inputWidth}`,
           }),
           statusCodeDocURL ? MDNLink({
             url: statusCodeDocURL,
           }) : null,
-          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/src/components/request-list-content.js
+++ b/devtools/client/netmonitor/src/components/request-list-content.js
@@ -34,17 +34,17 @@ const REQUESTS_TOOLTIP_TOGGLE_DELAY = 50
 const RequestListContent = createClass({
   displayName: "RequestListContent",
 
   propTypes: {
     columns: PropTypes.object.isRequired,
     dispatch: PropTypes.func.isRequired,
     displayedRequests: PropTypes.object.isRequired,
     firstRequestStartedMillis: PropTypes.number.isRequired,
-    fromCache: PropTypes.bool,
+    fromCache: PropTypes.bool.isRequired,
     onCauseBadgeClick: PropTypes.func.isRequired,
     onItemMouseDown: PropTypes.func.isRequired,
     onSecurityIconClick: PropTypes.func.isRequired,
     onSelectDelta: PropTypes.func.isRequired,
     scale: PropTypes.number,
     selectedRequestId: PropTypes.string,
   },
 
@@ -261,29 +261,29 @@ module.exports = connect(
     columns: state.ui.columns,
     displayedRequests: getDisplayedRequests(state),
     firstRequestStartedMillis: state.requests.firstStartedMillis,
     selectedRequestId: state.requests.selectedId,
     scale: getWaterfallScale(state),
   }),
   (dispatch) => ({
     dispatch,
-    /**
-     * A handler that opens the stack trace tab when a stack trace is available
-     */
-    onCauseBadgeClick: (cause) => {
-      if (cause.stacktrace && cause.stacktrace.length > 0) {
-        dispatch(Actions.selectDetailsPanelTab("stack-trace"));
-      }
-    },
     onItemMouseDown: (id) => dispatch(Actions.selectRequest(id)),
     /**
      * A handler that opens the security tab in the details view if secure or
      * broken security indicator is clicked.
      */
     onSecurityIconClick: (securityState) => {
       if (securityState && securityState !== "insecure") {
         dispatch(Actions.selectDetailsPanelTab("security"));
       }
     },
+    /**
+     * A handler that opens the stack trace tab when a stack trace is available
+     */
+    onCauseBadgeClick: (cause) => {
+      if (cause.stacktrace && cause.stacktrace.length > 0) {
+        dispatch(Actions.selectDetailsPanelTab("stack-trace"));
+      }
+    },
     onSelectDelta: (delta) => dispatch(Actions.selectDelta(delta)),
   }),
 )(RequestListContent);
--- a/devtools/client/netmonitor/src/components/request-list-empty-notice.js
+++ b/devtools/client/netmonitor/src/components/request-list-empty-notice.js
@@ -7,17 +7,16 @@
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const Actions = require("../actions/index");
 const { ACTIVITY_TYPE } = require("../constants");
-const { NetMonitorController } = require("../netmonitor-controller");
 const { L10N } = require("../utils/l10n");
 
 const { button, div, span } = DOM;
 
 /**
  * UI displayed when the request list is empty. Contains instructions on reloading
  * the page and on triggering performance analysis of the page.
  */
@@ -60,12 +59,12 @@ const RequestListEmptyNotice = createCla
   }
 });
 
 module.exports = connect(
   undefined,
   dispatch => ({
     onPerfClick: () => dispatch(Actions.openStatistics(true)),
     onReloadClick: () =>
-      NetMonitorController
+      window.NetMonitorController
         .triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
   })
 )(RequestListEmptyNotice);
--- a/devtools/client/netmonitor/src/components/request-list-item.js
+++ b/devtools/client/netmonitor/src/components/request-list-item.js
@@ -63,17 +63,17 @@ const RequestListItem = createClass({
   displayName: "RequestListItem",
 
   propTypes: {
     columns: PropTypes.object.isRequired,
     item: PropTypes.object.isRequired,
     index: PropTypes.number.isRequired,
     isSelected: PropTypes.bool.isRequired,
     firstRequestStartedMillis: PropTypes.number.isRequired,
-    fromCache: PropTypes.bool,
+    fromCache: PropTypes.bool.isRequired,
     onCauseBadgeClick: PropTypes.func.isRequired,
     onContextMenu: PropTypes.func.isRequired,
     onFocusedNodeChange: PropTypes.func,
     onMouseDown: PropTypes.func.isRequired,
     onSecurityIconClick: PropTypes.func.isRequired,
   },
 
   componentDidMount() {
--- a/devtools/client/netmonitor/src/netmonitor-controller.js
+++ b/devtools/client/netmonitor/src/netmonitor-controller.js
@@ -18,16 +18,18 @@ const {
   onFirefoxConnect,
   onFirefoxDisconnect,
 } = require("./utils/client");
 const {
   getRequestById,
   getDisplayedRequestById,
 } = require("./selectors/index");
 
+const gStore = window.gStore;
+
 /**
  * Object defining the network monitor controller components.
  */
 var NetMonitorController = {
   /**
    * Initializes the view and connects the monitor client.
    *
    * @param {Object} connection connection data wrapper
@@ -50,17 +52,17 @@ var NetMonitorController = {
    * @return object
    *         A promise that is resolved when the monitor finishes shutdown.
    */
   shutdownNetMonitor() {
     if (this._shutdown) {
       return this._shutdown;
     }
     this._shutdown = new Promise(async (resolve) => {
-      window.gStore.dispatch(Actions.batchReset());
+      gStore.dispatch(Actions.batchReset());
       onFirefoxDisconnect(this._target);
       this._target.off("close", this._onTabDetached);
       this.NetworkEventsHandler.disconnect();
       await this.disconnect();
       resolve();
     });
 
     return this._shutdown;
@@ -252,28 +254,28 @@ var NetMonitorController = {
    *         A promise resolved once the task finishes.
    */
   inspectRequest: function (requestId) {
     // Look for the request in the existing ones or wait for it to appear, if
     // the network monitor is still loading.
     return new Promise((resolve) => {
       let request = null;
       let inspector = function () {
-        request = getDisplayedRequestById(window.gStore.getState(), requestId);
+        request = getDisplayedRequestById(gStore.getState(), requestId);
         if (!request) {
           // Reset filters so that the request is visible.
-          window.gStore.dispatch(Actions.toggleRequestFilterType("all"));
-          request = getDisplayedRequestById(window.gStore.getState(), requestId);
+          gStore.dispatch(Actions.toggleRequestFilterType("all"));
+          request = getDisplayedRequestById(gStore.getState(), requestId);
         }
 
         // If the request was found, select it. Otherwise this function will be
         // called again once new requests arrive.
         if (request) {
           window.off(EVENTS.REQUEST_ADDED, inspector);
-          window.gStore.dispatch(Actions.selectRequest(request.id));
+          gStore.dispatch(Actions.selectRequest(request.id));
           resolve();
         }
       };
 
       inspector();
       if (!request) {
         window.on(EVENTS.REQUEST_ADDED, inspector);
       }
@@ -448,17 +450,17 @@ NetworkEventsHandler.prototype = {
   },
 
   /**
    * The "DOMContentLoaded" and "Load" events sent by the timeline actor.
    * @param object marker
    */
   _onDocLoadingMarker: function (marker) {
     window.emit(EVENTS.TIMELINE_EVENT, marker);
-    window.gStore.dispatch(Actions.addTimingMarker(marker));
+    gStore.dispatch(Actions.addTimingMarker(marker));
   },
 
   /**
    * The "networkEvent" message type handler.
    *
    * @param string type
    *        Message type.
    * @param object networkInfo
@@ -479,17 +481,17 @@ NetworkEventsHandler.prototype = {
     );
     window.emit(EVENTS.NETWORK_EVENT, actor);
   },
 
   addRequest(id, data) {
     let { method, url, isXHR, cause, startedDateTime, fromCache,
           fromServiceWorker } = data;
 
-    window.gStore.dispatch(Actions.addRequest(
+    gStore.dispatch(Actions.addRequest(
       id,
       {
         // Convert the received date/time string to a unix timestamp.
         startedMillis: Date.parse(startedDateTime),
         method,
         url,
         isXHR,
         cause,
@@ -498,42 +500,42 @@ NetworkEventsHandler.prototype = {
       },
       true
     ))
     .then(() => window.emit(EVENTS.REQUEST_ADDED, id));
   },
 
   async updateRequest(id, data) {
     const action = Actions.updateRequest(id, data, true);
-    await window.gStore.dispatch(action);
+    await gStore.dispatch(action);
     let {
       responseContent,
       responseCookies,
       responseHeaders,
       requestCookies,
       requestHeaders,
       requestPostData,
     } = action.data;
-    let request = getRequestById(window.gStore.getState(), action.id);
+    let request = getRequestById(gStore.getState(), action.id);
 
     if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
       let headers = await fetchHeaders(requestHeaders, getLongString);
       if (headers) {
-        await window.gStore.dispatch(Actions.updateRequest(
+        await gStore.dispatch(Actions.updateRequest(
           action.id,
           { requestHeaders: headers },
           true,
         ));
       }
     }
 
     if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
       let headers = await fetchHeaders(responseHeaders, getLongString);
       if (headers) {
-        await window.gStore.dispatch(Actions.updateRequest(
+        await gStore.dispatch(Actions.updateRequest(
           action.id,
           { responseHeaders: headers },
           true,
         ));
       }
     }
 
     if (request && responseContent && responseContent.content) {
@@ -544,17 +546,17 @@ NetworkEventsHandler.prototype = {
 
       if (mimeType.includes("image/")) {
         payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
       }
 
       responseContent.content.text = response;
       payload.responseContent = responseContent;
 
-      await window.gStore.dispatch(Actions.updateRequest(action.id, payload, true));
+      await gStore.dispatch(Actions.updateRequest(action.id, payload, true));
 
       if (mimeType.includes("image/")) {
         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.
@@ -565,17 +567,17 @@ NetworkEventsHandler.prototype = {
       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 };
 
-      await window.gStore.dispatch(Actions.updateRequest(action.id, payload, true));
+      await gStore.dispatch(Actions.updateRequest(action.id, payload, true));
     }
 
     // Fetch request and response cookies long value.
     // Actor does not provide full sized cookie value when the value is too long
     // To display values correctly, we need fetch them in each request.
     if (requestCookies) {
       let reqCookies = [];
       // request store cookies in requestCookies or requestCookies.cookies
@@ -584,17 +586,17 @@ NetworkEventsHandler.prototype = {
       // make sure cookies is iterable
       if (typeof cookies[Symbol.iterator] === "function") {
         for (let cookie of cookies) {
           reqCookies.push(Object.assign({}, cookie, {
             value: await getLongString(cookie.value),
           }));
         }
         if (reqCookies.length) {
-          await window.gStore.dispatch(Actions.updateRequest(
+          await gStore.dispatch(Actions.updateRequest(
             action.id,
             { requestCookies: reqCookies },
             true));
         }
       }
     }
 
     if (responseCookies) {
@@ -605,17 +607,17 @@ NetworkEventsHandler.prototype = {
       // make sure cookies is iterable
       if (typeof cookies[Symbol.iterator] === "function") {
         for (let cookie of cookies) {
           resCookies.push(Object.assign({}, cookie, {
             value: await getLongString(cookie.value),
           }));
         }
         if (resCookies.length) {
-          await window.gStore.dispatch(Actions.updateRequest(
+          await gStore.dispatch(Actions.updateRequest(
             action.id,
             { responseCookies: resCookies },
             true));
         }
       }
     }
   },
 
--- a/devtools/client/netmonitor/src/request-list-context-menu.js
+++ b/devtools/client/netmonitor/src/request-list-context-menu.js
@@ -6,17 +6,16 @@
 
 const Services = require("Services");
 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");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 const { HarExporter } = require("./har/har-exporter");
-const { NetMonitorController } = require("./netmonitor-controller");
 const { getLongString } = require("./utils/client");
 const { L10N } = require("./utils/l10n");
 const {
   formDataURI,
   getFormDataSections,
   getUrlQuery,
   parseQueryString,
 } = require("./utils/request-utils");
@@ -152,25 +151,25 @@ RequestListContextMenu.prototype = {
       label: L10N.getStr("netmonitor.context.saveAllAsHar"),
       accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
       visible: this.sortedRequests.size > 0,
       click: () => this.saveAllAsHar(),
     }));
 
     menu.append(new MenuItem({
       type: "separator",
-      visible: !!(NetMonitorController.supportsCustomRequest &&
+      visible: !!(window.NetMonitorController.supportsCustomRequest &&
                selectedRequest && !selectedRequest.isCustom),
     }));
 
     menu.append(new MenuItem({
       id: "request-list-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,
     }));
@@ -182,17 +181,17 @@ RequestListContextMenu.prototype = {
       visible: !!selectedRequest,
       click: () => this.openRequestInTab()
     }));
 
     menu.append(new MenuItem({
       id: "request-list-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, { doc: window.parent.document });
     return menu;
   },
 
   /**
@@ -344,17 +343,17 @@ RequestListContextMenu.prototype = {
   /**
    * Save HAR from the network panel content to a file.
    */
   saveAllAsHar() {
     return HarExporter.save(this.getDefaultHarOptions());
   },
 
   getDefaultHarOptions() {
-    let form = NetMonitorController._target.form;
+    let form = window.NetMonitorController._target.form;
     let title = form.title || form.url;
 
     return {
       getString: getLongString,
       items: this.sortedRequests,
       title: title
     };
   }
--- a/devtools/client/netmonitor/src/utils/create-store.js
+++ b/devtools/client/netmonitor/src/utils/create-store.js
@@ -1,16 +1,17 @@
 /* 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 Services = require("Services");
 const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
+const { thunk } = require("devtools/client/shared/redux/middleware/thunk");
 const batching = require("../middleware/batching");
 const prefs = require("../middleware/prefs");
 const rootReducer = require("../reducers/index");
 const { FilterTypes, Filters } = require("../reducers/filters");
 const { Requests } = require("../reducers/requests");
 const { Sort } = require("../reducers/sort");
 const { TimingMarkers } = require("../reducers/timing-markers");
 const { UI, Columns } = require("../reducers/ui");
@@ -34,12 +35,20 @@ function configureStore() {
     requests: new Requests(),
     sort: new Sort(),
     timingMarkers: new TimingMarkers(),
     ui: new UI({
       columns: new Columns(inactiveColumns)
     }),
   };
 
-  return createStore(rootReducer, initialState, applyMiddleware(prefs, batching));
+  return createStore(
+    rootReducer,
+    initialState,
+    applyMiddleware(
+      thunk,
+      prefs,
+      batching
+    )
+  );
 }
 
 exports.configureStore = configureStore;