Bug 1419350 - Stop doing React updates while netmonitor is in background; r=ochameau draft
authorJan Odvarko <odvarko@gmail.com>
Thu, 01 Mar 2018 10:48:26 +0100
changeset 761742 4e2c4d9cc66e2ae4e8c03f5419922903d717f858
parent 761561 b996cabc7ef54bbe050d647494bf00d668ec52e6
child 761743 b8ccd200f913ffcd93d3b9bb44b77c9555654572
push id100983
push userjodvarko@mozilla.com
push dateThu, 01 Mar 2018 09:49:17 +0000
reviewersochameau
bugs1419350
milestone60.0a1
Bug 1419350 - Stop doing React updates while netmonitor is in background; r=ochameau MozReview-Commit-ID: FvKFDiM8xNB
devtools/client/netmonitor/initializer.js
devtools/client/netmonitor/launchpad.js
devtools/client/netmonitor/src/components/App.js
devtools/client/netmonitor/src/components/CustomRequestPanel.js
devtools/client/netmonitor/src/components/MonitorPanel.js
devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
devtools/client/netmonitor/src/components/ParamsPanel.js
devtools/client/netmonitor/src/components/RequestListContent.js
devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
devtools/client/netmonitor/src/components/RequestListHeader.js
devtools/client/netmonitor/src/components/StatisticsPanel.js
devtools/client/netmonitor/src/components/StatusBar.js
devtools/client/netmonitor/src/components/Toolbar.js
devtools/client/netmonitor/src/create-store.js
devtools/client/netmonitor/src/moz.build
devtools/client/netmonitor/src/utils/create-store.js
devtools/client/netmonitor/src/utils/moz.build
devtools/client/netmonitor/src/utils/redux-connect.js
devtools/client/netmonitor/test/browser.ini
devtools/client/netmonitor/test/browser_net_background_update.js
devtools/client/shared/browser-loader.js
--- a/devtools/client/netmonitor/initializer.js
+++ b/devtools/client/netmonitor/initializer.js
@@ -17,17 +17,17 @@ const require = window.windowRequire = B
 }).require;
 
 const EventEmitter = require("devtools/shared/old-event-emitter");
 const { createFactory } = require("devtools/client/shared/vendor/react");
 const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
 const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
 const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
 const { Connector } = require("./src/connector/index");
-const { configureStore } = require("./src/utils/create-store");
+const { configureStore } = require("./src/create-store");
 const App = createFactory(require("./src/components/App"));
 const { EVENTS } = require("./src/constants");
 const {
   getDisplayedRequestById,
   getSortedRequests
 } = require("./src/selectors/index");
 
 // Inject EventEmitter into global window.
--- a/devtools/client/netmonitor/launchpad.js
+++ b/devtools/client/netmonitor/launchpad.js
@@ -38,17 +38,17 @@ pref("devtools.netmonitor.har.enableAuto
 pref("devtools.netmonitor.persistlog", false);
 pref("devtools.styleeditor.enabled", true);
 
 require("./src/assets/styles/netmonitor.css");
 
 const EventEmitter = require("devtools-modules/src/utils/event-emitter");
 EventEmitter.decorate(window);
 
-const { configureStore } = require("./src/utils/create-store");
+const { configureStore } = require("./src/create-store");
 const App = require("./src/components/App");
 const { Connector } = require("./src/connector/index");
 const connector = new Connector();
 const store = configureStore(connector);
 const actions = bindActionCreators(require("./src/actions"), store.dispatch);
 
 // Inject to global window for testing
 window.store = store;
--- a/devtools/client/netmonitor/src/components/App.js
+++ b/devtools/client/netmonitor/src/components/App.js
@@ -2,17 +2,17 @@
  * 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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 
 // Components
 loader.lazyGetter(this, "MonitorPanel", function () {
   return createFactory(require("./MonitorPanel"));
 });
 loader.lazyGetter(this, "StatisticsPanel", function () {
   return createFactory(require("./StatisticsPanel"));
 });
--- a/devtools/client/netmonitor/src/components/CustomRequestPanel.js
+++ b/devtools/client/netmonitor/src/components/CustomRequestPanel.js
@@ -2,17 +2,17 @@
  * 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 { Component } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { L10N } = require("../utils/l10n");
 const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
 const Actions = require("../actions/index");
 const { getSelectedRequest } = require("../selectors/index");
 const {
   getUrlQuery,
   parseQueryString,
   writeHeaderText,
--- a/devtools/client/netmonitor/src/components/MonitorPanel.js
+++ b/devtools/client/netmonitor/src/components/MonitorPanel.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const Services = require("Services");
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { div } = dom;
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const Actions = require("../actions/index");
 const { updateFormDataSections } = require("../utils/request-utils");
 const {
   getSelectedRequest,
   isSelectedRequestVisible,
 } = require("../selectors/index");
 
--- a/devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
+++ b/devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
@@ -2,17 +2,17 @@
  * 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 } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const Actions = require("../actions/index");
 const { getSelectedRequest } = require("../selectors/index");
 
 // Components
 loader.lazyGetter(this, "CustomRequestPanel", function () {
   return createFactory(require("./CustomRequestPanel"));
 });
 loader.lazyGetter(this, "TabboxPanel", function () {
--- a/devtools/client/netmonitor/src/components/ParamsPanel.js
+++ b/devtools/client/netmonitor/src/components/ParamsPanel.js
@@ -2,17 +2,17 @@
  * 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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { L10N } = require("../utils/l10n");
 const {
   fetchNetworkUpdatePacket,
   getUrlQuery,
   parseQueryString,
   parseFormData,
 } = require("../utils/request-utils");
 const { sortObjectKeys } = require("../utils/sort-utils");
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -2,17 +2,17 @@
  * 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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
 
 const Actions = require("../actions/index");
 const { formDataURI } = require("../utils/request-utils");
 const {
   getDisplayedRequests,
   getSelectedRequest,
   getSortedRequests,
--- a/devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
+++ b/devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
@@ -2,17 +2,17 @@
  * 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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const Actions = require("../actions/index");
 const { ACTIVITY_TYPE } = require("../constants");
 const { L10N } = require("../utils/l10n");
 const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
 
 // Components
 const MDNLink = createFactory(require("./MdnLink"));
 const RequestListHeader = createFactory(require("./RequestListHeader"));
--- a/devtools/client/netmonitor/src/components/RequestListHeader.js
+++ b/devtools/client/netmonitor/src/components/RequestListHeader.js
@@ -2,17 +2,17 @@
  * 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 { Component } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { getTheme, addThemeObserver, removeThemeObserver } =
   require("devtools/client/shared/theme");
 const Actions = require("../actions/index");
 const { HEADERS, REQUESTS_WATERFALL } = require("../constants");
 const { getWaterfallScale } = require("../selectors/index");
 const { getFormattedTime } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const RequestListHeaderContextMenu = require("../widgets/RequestListHeaderContextMenu");
--- a/devtools/client/netmonitor/src/components/StatisticsPanel.js
+++ b/devtools/client/netmonitor/src/components/StatisticsPanel.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { FILTER_TAGS } = require("../constants");
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { Chart } = require("devtools/client/shared/widgets/Chart");
 const { PluralForm } = require("devtools/shared/plural-form");
 const Actions = require("../actions/index");
 const { Filters } = require("../utils/filter-predicates");
 const { getSizeWithDecimals, getTimeWithDecimals } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
 const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
--- a/devtools/client/netmonitor/src/components/StatusBar.js
+++ b/devtools/client/netmonitor/src/components/StatusBar.js
@@ -1,17 +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 PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { PluralForm } = require("devtools/shared/plural-form");
 const Actions = require("../actions/index");
 const {
   getDisplayedRequestsSummary,
   getDisplayedTimingMarker,
 } = require("../selectors/index");
 const {
   getFormattedSize,
--- a/devtools/client/netmonitor/src/components/Toolbar.js
+++ b/devtools/client/netmonitor/src/components/Toolbar.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 
 const Actions = require("../actions/index");
 const { FILTER_SEARCH_DELAY, FILTER_TAGS } = require("../constants");
 const {
   getRecordingState,
   getTypeFilteredRequests,
   isNetworkDetailsToggleButtonDisabled,
 } = require("../selectors/index");
rename from devtools/client/netmonitor/src/utils/create-store.js
rename to devtools/client/netmonitor/src/create-store.js
--- a/devtools/client/netmonitor/src/utils/create-store.js
+++ b/devtools/client/netmonitor/src/create-store.js
@@ -3,28 +3,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const { applyMiddleware, createStore } = require("devtools/client/shared/vendor/redux");
 
 // Middleware
-const batching = require("../middleware/batching");
-const prefs = require("../middleware/prefs");
-const thunk = require("../middleware/thunk");
-const recording = require("../middleware/recording");
+const batching = require("./middleware/batching");
+const prefs = require("./middleware/prefs");
+const thunk = require("./middleware/thunk");
+const recording = require("./middleware/recording");
 
 // Reducers
-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");
+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");
 
 /**
  * Configure state and middleware for the Network monitor tool.
  */
 function configureStore(connector) {
   // Prepare initial state.
   const initialState = {
     filters: new Filters({
--- a/devtools/client/netmonitor/src/moz.build
+++ b/devtools/client/netmonitor/src/moz.build
@@ -11,9 +11,10 @@ DIRS += [
     'reducers',
     'selectors',
     'utils',
     'widgets',
 ]
 
 DevToolsModules(
     'constants.js',
+    'create-store.js',
 )
--- a/devtools/client/netmonitor/src/utils/moz.build
+++ b/devtools/client/netmonitor/src/utils/moz.build
@@ -3,23 +3,23 @@
 # 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/.
 
 DIRS += [
     'firefox',
 ]
 
 DevToolsModules(
-    'create-store.js',
     'filter-autocomplete-provider.js',
     'filter-predicates.js',
     'filter-text-utils.js',
     'format-utils.js',
     'headers-provider.js',
     'l10n.js',
     'mdn-utils.js',
     'menu.js',
     'open-request-in-tab.js',
     'prefs.js',
+    'redux-connect.js',
     'request-utils.js',
     'sort-predicates.js',
     'sort-utils.js'
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/utils/redux-connect.js
@@ -0,0 +1,28 @@
+/* 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, createElement } = require("devtools/client/shared/vendor/react");
+const VisibilityHandler = createFactory(require("devtools/client/shared/components/VisibilityHandler"));
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+/**
+ * This helper is wrapping Redux's connect() method and applying
+ * HOC (VisibilityHandler component) on whatever component is
+ * originally passed in. The HOC is responsible for not causing
+ * rendering if the owner panel runs in the background.
+ */
+function connectWrapper() {
+  let args = [].slice.call(arguments);
+  return component => {
+    return connect(...args)(props => {
+      return VisibilityHandler(null, createElement(component, props));
+    });
+  };
+}
+
+module.exports = {
+  connect: connectWrapper
+};
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -55,16 +55,17 @@ support-files =
   !/devtools/client/framework/test/shared-head.js
   xhr_bundle.js
   xhr_bundle.js.map
   xhr_original.js
 
 [browser_net_accessibility-01.js]
 [browser_net_accessibility-02.js]
 [browser_net_api-calls.js]
+[browser_net_background_update.js]
 [browser_net_autoscroll.js]
 [browser_net_cached-status.js]
 [browser_net_cause.js]
 [browser_net_cause_redirect.js]
 [browser_net_cause_source_map.js]
 [browser_net_service-worker-status.js]
 [browser_net_charts-01.js]
 [browser_net_charts-02.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_background_update.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Check that network logs created when the Net panel is not visible
+ * are displayed when the user shows the panel again.
+ */
+add_task(async () => {
+  let { tab, monitor, toolbox } = await initNetMonitor(CUSTOM_GET_URL);
+  info("Starting test... ");
+
+  let { document, store, windowRequire } = monitor.panelWin;
+  let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+
+  store.dispatch(Actions.batchEnable(false));
+
+  // Execute two requests
+  await performRequests(monitor, tab, 2);
+
+  // Wait for two logs
+  await waitUntil(() => document.querySelectorAll(".request-list-item").length == 2);
+
+  info("Select the inspector");
+  await toolbox.selectTool("inspector");
+
+  info("Wait for Net panel to be hidden");
+  await waitUntil(() => (document.visibilityState == "hidden"));
+
+  // Execute another two requests
+  await performRequests(monitor, tab, 2);
+
+  // The number of rendered requests should be the same since
+  // requests shouldn't be rendered while the net panel is in
+  // background
+  is(document.querySelectorAll(".request-list-item").length, 2,
+    "There should be expected number of requests");
+
+  info("Select the Net panel again");
+  await toolbox.selectTool("netmonitor");
+
+  // Wait for another two logs to be rendered since the panel
+  // is selected now.
+  await waitUntil(() => document.querySelectorAll(".request-list-item").length == 4);
+
+  return teardown(monitor);
+});
+
+async function performRequests(monitor, tab, count) {
+  let wait = waitForNetworkEvents(monitor, count);
+  await ContentTask.spawn(tab.linkedBrowser, count, requestCount => {
+    content.wrappedJSObject.performRequests(requestCount);
+  });
+  await wait;
+}
--- a/devtools/client/shared/browser-loader.js
+++ b/devtools/client/shared/browser-loader.js
@@ -14,16 +14,17 @@ const BROWSER_BASED_DIRS = [
   "resource://devtools/client/inspector/changes",
   "resource://devtools/client/inspector/computed",
   "resource://devtools/client/inspector/events",
   "resource://devtools/client/inspector/flexbox",
   "resource://devtools/client/inspector/fonts",
   "resource://devtools/client/inspector/grids",
   "resource://devtools/client/inspector/layout",
   "resource://devtools/client/jsonview",
+  "resource://devtools/client/netmonitor/src/utils",
   "resource://devtools/client/shared/source-map",
   "resource://devtools/client/shared/redux",
   "resource://devtools/client/shared/vendor",
 ];
 
 const COMMON_LIBRARY_DIRS = [
   "resource://devtools/client/shared/vendor",
 ];