Bug 1419369 - NetMonitor: Stop using ImmutableJS in ui reducer; r=Honza draft
authorRussell <miller.time.baby@gmail.com>
Fri, 24 Nov 2017 15:28:26 -0800
changeset 707593 c39fa317d25d137ad756216d5015e9739b568f01
parent 707503 b4cef8d1dff06a1ec2b9bb17211c0c3c7f5b76fa
child 742993 41c6639f3dd5c373890cac63aa28236839664cd9
push id92173
push userbmo:miller.time.baby@gmail.com
push dateTue, 05 Dec 2017 15:40:18 +0000
reviewersHonza
bugs1419369
milestone59.0a1
Bug 1419369 - NetMonitor: Stop using ImmutableJS in ui reducer; r=Honza MozReview-Commit-ID: IxxOmakX4Fr
devtools/client/netmonitor/src/components/RequestListHeader.js
devtools/client/netmonitor/src/components/RequestListItem.js
devtools/client/netmonitor/src/middleware/prefs.js
devtools/client/netmonitor/src/reducers/ui.js
devtools/client/netmonitor/src/request-list-header-context-menu.js
devtools/client/netmonitor/src/utils/create-store.js
devtools/client/netmonitor/test/browser_net_columns_last_column.js
devtools/client/netmonitor/test/browser_net_columns_pref.js
devtools/client/netmonitor/test/browser_net_columns_reset.js
devtools/client/netmonitor/test/browser_net_columns_showhide.js
--- a/devtools/client/netmonitor/src/components/RequestListHeader.js
+++ b/devtools/client/netmonitor/src/components/RequestListHeader.js
@@ -102,17 +102,17 @@ class RequestListHeader extends Componen
     let { columns, scale, sort, sortBy, waterfallWidth } = this.props;
 
     return (
       div({ className: "devtools-toolbar requests-list-headers-wrapper" },
         div({
           className: "devtools-toolbar requests-list-headers",
           onContextMenu: this.onContextMenu
         },
-          HEADERS.filter((header) => columns.get(header.name)).map((header) => {
+          HEADERS.filter((header) => columns[header.name]).map((header) => {
             let name = header.name;
             let boxName = header.boxName || name;
             let label = header.noLocalization
               ? name : L10N.getStr(`netmonitor.toolbar.${header.label || name}`);
             let sorted, sortedTitle;
             let active = sort.type == name ? true : undefined;
 
             if (active) {
--- a/devtools/client/netmonitor/src/components/RequestListItem.js
+++ b/devtools/client/netmonitor/src/components/RequestListItem.js
@@ -2,17 +2,16 @@
  * 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 I = require("devtools/client/shared/vendor/immutable");
 const { propertiesEqual } = require("../utils/request-utils");
 const { RESPONSE_HEADERS } = require("../constants");
 
 // Components
 const RequestListColumnCause = createFactory(require("./RequestListColumnCause"));
 const RequestListColumnContentSize = createFactory(require("./RequestListColumnContentSize"));
 const RequestListColumnCookies = createFactory(require("./RequestListColumnCookies"));
 const RequestListColumnDomain = createFactory(require("./RequestListColumnDomain"));
@@ -95,17 +94,17 @@ class RequestListItem extends Component 
     if (this.props.isSelected) {
       this.refs.listItem.focus();
     }
   }
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_REQ_ITEM_PROPS, this.props.item, nextProps.item) ||
       !propertiesEqual(UPDATED_REQ_PROPS, this.props, nextProps) ||
-      !I.is(this.props.columns, nextProps.columns);
+      this.props.columns !== nextProps.columns;
   }
 
   componentDidUpdate(prevProps) {
     if (!prevProps.isSelected && this.props.isSelected) {
       this.refs.listItem.focus();
       if (this.props.onFocusedNodeChange) {
         this.props.onFocusedNodeChange();
       }
@@ -136,42 +135,42 @@ class RequestListItem extends Component 
       div({
         ref: "listItem",
         className: classList.join(" "),
         "data-id": item.id,
         tabIndex: 0,
         onContextMenu,
         onMouseDown,
       },
-        columns.get("status") && RequestListColumnStatus({ item }),
-        columns.get("method") && RequestListColumnMethod({ item }),
-        columns.get("file") && RequestListColumnFile({ item }),
-        columns.get("protocol") && RequestListColumnProtocol({ item }),
-        columns.get("scheme") && RequestListColumnScheme({ item }),
-        columns.get("domain") && RequestListColumnDomain({ item,
-                                                           onSecurityIconMouseDown }),
-        columns.get("remoteip") && RequestListColumnRemoteIP({ item }),
-        columns.get("cause") && RequestListColumnCause({ item, onCauseBadgeMouseDown }),
-        columns.get("type") && RequestListColumnType({ item }),
-        columns.get("cookies") && RequestListColumnCookies({ connector, item }),
-        columns.get("setCookies") && RequestListColumnSetCookies({ connector, item }),
-        columns.get("transferred") && RequestListColumnTransferredSize({ item }),
-        columns.get("contentSize") && RequestListColumnContentSize({ item }),
-        columns.get("startTime") &&
+        columns.status && RequestListColumnStatus({ item }),
+        columns.method && RequestListColumnMethod({ item }),
+        columns.file && RequestListColumnFile({ item }),
+        columns.protocol && RequestListColumnProtocol({ item }),
+        columns.scheme && RequestListColumnScheme({ item }),
+        columns.domain && RequestListColumnDomain({ item,
+                                                    onSecurityIconMouseDown }),
+        columns.remoteip && RequestListColumnRemoteIP({ item }),
+        columns.cause && RequestListColumnCause({ item, onCauseBadgeMouseDown }),
+        columns.type && RequestListColumnType({ item }),
+        columns.cookies && RequestListColumnCookies({ connector, item }),
+        columns.setCookies && RequestListColumnSetCookies({ connector, item }),
+        columns.transferred && RequestListColumnTransferredSize({ item }),
+        columns.contentSize && RequestListColumnContentSize({ item }),
+        columns.startTime &&
           RequestListColumnStartTime({ item, firstRequestStartedMillis }),
-        columns.get("endTime") &&
+        columns.endTime &&
           RequestListColumnEndTime({ item, firstRequestStartedMillis }),
-        columns.get("responseTime") &&
+        columns.responseTime &&
           RequestListColumnResponseTime({ item, firstRequestStartedMillis }),
-        columns.get("duration") && RequestListColumnDuration({ item }),
-        columns.get("latency") && RequestListColumnLatency({ item }),
-        ...RESPONSE_HEADERS.filter(header => columns.get(header)).map(
+        columns.duration && RequestListColumnDuration({ item }),
+        columns.latency && RequestListColumnLatency({ item }),
+        ...RESPONSE_HEADERS.filter(header => columns[header]).map(
           header => RequestListColumnResponseHeader({ item, header }),
         ),
-        columns.get("waterfall") &&
+        columns.waterfall &&
           RequestListColumnWaterfall({ item, firstRequestStartedMillis,
                                        onWaterfallMouseDown }),
       )
     );
   }
 }
 
 module.exports = RequestListItem;
--- a/devtools/client/netmonitor/src/middleware/prefs.js
+++ b/devtools/client/netmonitor/src/middleware/prefs.js
@@ -37,19 +37,23 @@ function prefsMiddleware(store) {
           "devtools.netmonitor.persistlog", store.getState().ui.persistentLogsEnabled);
         break;
       case DISABLE_BROWSER_CACHE:
         Services.prefs.setBoolPref(
           "devtools.cache.disabled", store.getState().ui.browserCacheDisabled);
         break;
       case TOGGLE_COLUMN:
       case RESET_COLUMNS:
-        let visibleColumns = [...store.getState().ui.columns]
-          .filter(([column, shown]) => shown)
-          .map(([column, shown]) => column);
+        let visibleColumns = [];
+        let columns = store.getState().ui.columns;
+        for (let column in columns) {
+          if (columns[column]) {
+            visibleColumns.push(column);
+          }
+        }
         Services.prefs.setCharPref(
           "devtools.netmonitor.visibleColumns", JSON.stringify(visibleColumns));
         break;
     }
     return res;
   };
 }
 
--- a/devtools/client/netmonitor/src/reducers/ui.js
+++ b/devtools/client/netmonitor/src/reducers/ui.js
@@ -1,15 +1,14 @@
 /* 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 Services = require("Services");
 const {
   CLEAR_REQUESTS,
   OPEN_NETWORK_DETAILS,
   ENABLE_PERSISTENT_LOGS,
   DISABLE_BROWSER_CACHE,
   OPEN_STATISTICS,
   REMOVE_SELECTED_CUSTOM_REQUEST,
@@ -39,75 +38,102 @@ const cols = {
   contentSize: true,
   startTime: false,
   endTime: false,
   responseTime: false,
   duration: false,
   latency: false,
   waterfall: true,
 };
-const Columns = I.Record(
-  Object.assign(
+function Columns() {
+  return Object.assign(
     cols,
     RESPONSE_HEADERS.reduce((acc, header) => Object.assign(acc, { [header]: false }), {})
-  )
-);
+  );
+}
 
-const UI = I.Record({
-  columns: new Columns(),
-  detailsPanelSelectedTab: PANELS.HEADERS,
-  networkDetailsOpen: false,
-  persistentLogsEnabled: Services.prefs.getBoolPref("devtools.netmonitor.persistlog"),
-  browserCacheDisabled: Services.prefs.getBoolPref("devtools.cache.disabled"),
-  statisticsOpen: false,
-  waterfallWidth: null,
-});
+function UI(initialState = {}) {
+  return {
+    columns: Columns(),
+    detailsPanelSelectedTab: PANELS.HEADERS,
+    networkDetailsOpen: false,
+    persistentLogsEnabled: Services.prefs.getBoolPref("devtools.netmonitor.persistlog"),
+    browserCacheDisabled: Services.prefs.getBoolPref("devtools.cache.disabled"),
+    statisticsOpen: false,
+    waterfallWidth: null,
+    ...initialState,
+  };
+}
 
 function resetColumns(state) {
-  return state.set("columns", new Columns());
+  return {
+    ...state,
+    columns: Columns()
+  };
 }
 
 function resizeWaterfall(state, action) {
-  return state.set("waterfallWidth", action.width);
+  return {
+    ...state,
+    waterfallWidth: action.width
+  };
 }
 
 function openNetworkDetails(state, action) {
-  return state.set("networkDetailsOpen", action.open);
+  return {
+    ...state,
+    networkDetailsOpen: action.open
+  };
 }
 
 function enablePersistentLogs(state, action) {
-  return state.set("persistentLogsEnabled", action.enabled);
+  return {
+    ...state,
+    persistentLogsEnabled: action.enabled
+  };
 }
 
 function disableBrowserCache(state, action) {
-  return state.set("browserCacheDisabled", action.disabled);
+  return {
+    ...state,
+    browserCacheDisabled: action.disabled
+  };
 }
 
 function openStatistics(state, action) {
-  return state.set("statisticsOpen", action.open);
+  return {
+    ...state,
+    statisticsOpen: action.open
+  };
 }
 
 function setDetailsPanelTab(state, action) {
-  return state.set("detailsPanelSelectedTab", action.id);
+  return {
+    ...state,
+    detailsPanelSelectedTab: action.id
+  };
 }
 
 function toggleColumn(state, action) {
   let { column } = action;
 
-  if (!state.has(column)) {
+  if (!state.columns.hasOwnProperty(column)) {
     return state;
   }
 
-  let newState = state.withMutations(columns => {
-    columns.set(column, !state.get(column));
-  });
-  return newState;
+  return {
+    ...state,
+    columns: {
+      ...state.columns,
+      [column]: !state.columns[column]
+    }
+  };
 }
 
-function ui(state = new UI(), action) {
+function ui(state = UI(), action) {
   switch (action.type) {
     case CLEAR_REQUESTS:
       return openNetworkDetails(state, { open: false });
     case OPEN_NETWORK_DETAILS:
       return openNetworkDetails(state, action);
     case ENABLE_PERSISTENT_LOGS:
       return enablePersistentLogs(state, action);
     case DISABLE_BROWSER_CACHE:
@@ -119,17 +145,17 @@ function ui(state = new UI(), 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 TOGGLE_COLUMN:
-      return state.set("columns", toggleColumn(state.columns, action));
+      return toggleColumn(state, action);
     case WATERFALL_RESIZE:
       return resizeWaterfall(state, action);
     default:
       return state;
   }
 }
 
 module.exports = {
--- a/devtools/client/netmonitor/src/request-list-header-context-menu.js
+++ b/devtools/client/netmonitor/src/request-list-header-context-menu.js
@@ -28,28 +28,35 @@ class RequestListHeaderContextMenu {
 
   get columns() {
     // FIXME: Bug 1362059 - Implement RequestListHeaderContextMenu React component
     // Remove window.store
     return window.store.getState().ui.columns;
   }
 
   get visibleColumns() {
-    return [...this.columns].filter(([_, shown]) => shown);
+    let visible = [];
+    for (let column in this.columns) {
+      if (this.columns[column]) {
+        visible.push(column);
+      }
+    }
+    return visible;
   }
 
   /**
    * Handle the context menu opening.
    */
   open(event = {}) {
     let menu = [];
     let subMenu = { timings: [], responseHeaders: [] };
     let onlyOneColumn = this.visibleColumns.length === 1;
 
-    for (let [column, shown] of this.columns) {
+    for (let column in this.columns) {
+      let shown = this.columns[column];
       let label = nonLocalizedHeaders.includes(column)
           ? stringMap[column] || column
           : L10N.getStr(`netmonitor.toolbar.${stringMap[column] || column}`);
       let entry = {
         id: `request-list-header-${column}-toggle`,
         label,
         type: "checkbox",
         checked: shown,
--- a/devtools/client/netmonitor/src/utils/create-store.js
+++ b/devtools/client/netmonitor/src/utils/create-store.js
@@ -28,17 +28,17 @@ function configureStore(connector) {
   // Prepare initial state.
   const initialState = {
     filters: new Filters({
       requestFilterTypes: getFilterState()
     }),
     requests: new Requests(),
     sort: new Sort(),
     timingMarkers: new TimingMarkers(),
-    ui: new UI({
+    ui: UI({
       columns: getColumnState()
     }),
   };
 
   // Prepare middleware.
   let middleware = applyMiddleware(
     thunk,
     prefs,
@@ -50,26 +50,25 @@ function configureStore(connector) {
 }
 
 // Helpers
 
 /**
  * Get column state from preferences.
  */
 function getColumnState() {
-  let columns = new Columns();
+  let columns = Columns();
   let visibleColumns = getPref("devtools.netmonitor.visibleColumns");
 
-  for (let [col] of columns) {
-    columns = columns.withMutations((state) => {
-      state.set(col, visibleColumns.includes(col));
-    });
+  const state = {};
+  for (let col in columns) {
+    state[col] = visibleColumns.includes(col);
   }
 
-  return columns;
+  return state;
 }
 
 /**
  * Get filter state from preferences.
  */
 function getFilterState() {
   let activeFilters = {};
   let filters = getPref("devtools.netmonitor.filters");
--- a/devtools/client/netmonitor/test/browser_net_columns_last_column.js
+++ b/devtools/client/netmonitor/test/browser_net_columns_last_column.js
@@ -8,19 +8,27 @@
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, store, parent } = monitor.panelWin;
 
-  for (let [column, shown] of store.getState().ui.columns) {
-    let visibleColumns = [...store.getState().ui.columns]
-      .filter(([_, visible]) => visible);
+  let initialColumns = store.getState().ui.columns;
+  for (let column in initialColumns) {
+    let shown = initialColumns[column];
+
+    let columns = store.getState().ui.columns;
+    let visibleColumns = [];
+    for (let c in columns) {
+      if (columns[c]) {
+        visibleColumns.push(c);
+      }
+    }
 
     if (visibleColumns.length === 1) {
       if (!shown) {
         continue;
       }
       yield testLastMenuItem(column);
       break;
     }
--- a/devtools/client/netmonitor/test/browser_net_columns_pref.js
+++ b/devtools/client/netmonitor/test/browser_net_columns_pref.js
@@ -19,25 +19,33 @@ add_task(function* () {
   ok(document.querySelector("#requests-list-status-button"),
      "Status column should be shown");
   ok(document.querySelector("#requests-list-contentSize-button"),
      "Content size column should be shown");
 
   yield hideColumn("status");
   yield hideColumn("contentSize");
 
-  ok(!Services.prefs.getCharPref("devtools.netmonitor.visibleColumns").includes("status"),
-    "Pref should be synced for status");
-  ok(!Services.prefs.getCharPref("devtools.netmonitor.visibleColumns")
-    .includes("contentSize"), "Pref should be synced for contentSize");
+  let visibleColumns = JSON.parse(
+    Services.prefs.getCharPref("devtools.netmonitor.visibleColumns")
+  );
+
+  ok(!visibleColumns.includes("status"),
+     "Pref should be synced for status");
+  ok(!visibleColumns.includes("contentSize"),
+    "Pref should be synced for contentSize");
 
   yield showColumn("status");
 
-  ok(Services.prefs.getCharPref("devtools.netmonitor.visibleColumns").includes("status"),
-  "Pref should be synced for status");
+  visibleColumns = JSON.parse(
+    Services.prefs.getCharPref("devtools.netmonitor.visibleColumns")
+  );
+
+  ok(visibleColumns.includes("status"),
+    "Pref should be synced for status");
 
   function* hideColumn(column) {
     info(`Clicking context-menu item for ${column}`);
     EventUtils.sendMouseEvent({ type: "contextmenu" },
       document.querySelector("#requests-list-status-button") ||
       document.querySelector("#requests-list-waterfall-button"));
 
     let onHeaderRemoved = waitForDOM(document, `#requests-list-${column}-button`, 0);
--- a/devtools/client/netmonitor/test/browser_net_columns_reset.js
+++ b/devtools/client/netmonitor/test/browser_net_columns_reset.js
@@ -19,17 +19,17 @@ add_task(function* () {
   hideColumn("status");
   hideColumn("waterfall");
 
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelector("#requests-list-contentSize-button"));
 
   parent.document.querySelector("#request-list-header-reset-columns").click();
 
-  is(JSON.stringify(prefBefore), JSON.stringify(Prefs.visibleColumns),
+  ok(JSON.stringify(prefBefore) === JSON.stringify(Prefs.visibleColumns),
      "Reset columns item should reset columns pref");
 
   function* hideColumn(column) {
     info(`Clicking context-menu item for ${column}`);
     EventUtils.sendMouseEvent({ type: "contextmenu" },
       document.querySelector("#requests-list-contentSize-button"));
 
     let onHeaderRemoved = waitForDOM(document, `#requests-list-${column}-button`, 0);
--- a/devtools/client/netmonitor/test/browser_net_columns_showhide.js
+++ b/devtools/client/netmonitor/test/browser_net_columns_showhide.js
@@ -8,28 +8,30 @@
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, store, parent } = monitor.panelWin;
 
-  for (let [column, shown] of store.getState().ui.columns) {
-    if (shown) {
+  let columns = store.getState().ui.columns;
+  for (let column in columns) {
+    if (columns[column]) {
       yield testVisibleColumnContextMenuItem(column, document, parent);
       yield testHiddenColumnContextMenuItem(column, document, parent);
     } else {
       yield testHiddenColumnContextMenuItem(column, document, parent);
       yield testVisibleColumnContextMenuItem(column, document, parent);
     }
   }
 
-  for (let [column, shown] of store.getState().ui.columns) {
-    if (shown) {
+  columns = store.getState().ui.columns;
+  for (let column in columns) {
+    if (columns[column]) {
       yield testVisibleColumnContextMenuItem(column, document, parent);
       // Right click on the white-space for the context menu to appear
       // and toggle column visibility
       yield testWhiteSpaceContextMenuItem(column, document, parent);
     }
   }
 });