Bug 1309192 - Implement search filter in Net Panel Toolbar r?jsnajdr draft
authorRicky Chien <ricky060709@gmail.com>
Thu, 20 Oct 2016 13:58:35 +0800
changeset 433175 0e1856166db6813ab13fe388168d824d1b898df5
parent 433010 ac55a6776435142feebf3c20bbabfee100686416
child 535814 fb4b63310d2b89c451ef0bfaee83a6f0d16e0ded
push id34496
push userbmo:rchien@mozilla.com
push dateThu, 03 Nov 2016 07:21:46 +0000
reviewersjsnajdr
bugs1309192
milestone52.0a1
Bug 1309192 - Implement search filter in Net Panel Toolbar r?jsnajdr MozReview-Commit-ID: FjVo69jmE7e
devtools/client/netmonitor/actions/filters.js
devtools/client/netmonitor/components/filter-buttons.js
devtools/client/netmonitor/components/moz.build
devtools/client/netmonitor/components/search-box.js
devtools/client/netmonitor/constants.js
devtools/client/netmonitor/netmonitor-controller.js
devtools/client/netmonitor/netmonitor.xul
devtools/client/netmonitor/panel.js
devtools/client/netmonitor/reducers/filters.js
devtools/client/netmonitor/requests-menu-view.js
devtools/client/netmonitor/test/browser_net_filter-01.js
devtools/client/netmonitor/test/browser_net_icon-preview.js
devtools/client/netmonitor/test/browser_net_prefs-reload.js
devtools/client/netmonitor/toolbar-view.js
devtools/client/shared/components/search-box.js
devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js
--- a/devtools/client/netmonitor/actions/filters.js
+++ b/devtools/client/netmonitor/actions/filters.js
@@ -1,43 +1,57 @@
 /* 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 {
-  TOGGLE_FILTER,
-  ENABLE_FILTER_ONLY,
+  TOGGLE_FILTER_TYPE,
+  ENABLE_FILTER_TYPE_ONLY,
+  SET_FILTER_TEXT,
 } = require("../constants");
 
 /**
  * Toggle an existing filter type state.
  * If type 'all' is specified, all the other filter types are set to false.
  * Available filter types are defined in filters reducer.
  *
  * @param {string} filter - A filter type is going to be updated
  */
-function toggleFilter(filter) {
+function toggleFilterType(filter) {
   return {
-    type: TOGGLE_FILTER,
+    type: TOGGLE_FILTER_TYPE,
     filter,
   };
 }
 
 /**
  * Enable filter type exclusively.
  * Except filter type is set to true, all the other filter types are set
  * to false.
  * Available filter types are defined in filters reducer.
  *
  * @param {string} filter - A filter type is going to be updated
  */
-function enableFilterOnly(filter) {
+function enableFilterTypeOnly(filter) {
   return {
-    type: ENABLE_FILTER_ONLY,
+    type: ENABLE_FILTER_TYPE_ONLY,
     filter,
   };
 }
 
+/**
+ * Set filter text.
+ *
+ * @param {string} url - A filter text is going to be set
+ */
+function setFilterText(url) {
+  return {
+    type: SET_FILTER_TEXT,
+    url,
+  };
+}
+
 module.exports = {
-  toggleFilter,
-  enableFilterOnly,
+  toggleFilterType,
+  enableFilterTypeOnly,
+  setFilterText,
 };
--- a/devtools/client/netmonitor/components/filter-buttons.js
+++ b/devtools/client/netmonitor/components/filter-buttons.js
@@ -37,12 +37,12 @@ FilterButtons.PropTypes = {
 
 module.exports = connect(
   (state) => ({ filterTypes: state.filters.types }),
   (dispatch) => ({
     triggerFilterType: (evt) => {
       if (evt.type === "keydown" && (evt.key !== "" || evt.key !== "Enter")) {
         return;
       }
-      dispatch(Actions.toggleFilter(evt.target.dataset.key));
+      dispatch(Actions.toggleFilterType(evt.target.dataset.key));
     },
   })
 )(FilterButtons);
--- a/devtools/client/netmonitor/components/moz.build
+++ b/devtools/client/netmonitor/components/moz.build
@@ -1,9 +1,10 @@
 # vim: set filetype=python:
 # 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(
     'filter-buttons.js',
+    'search-box.js',
     'toggle-button.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/components/search-box.js
@@ -0,0 +1,24 @@
+/* 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 { connect } = require("devtools/client/shared/vendor/react-redux");
+const SearchBox = require("devtools/client/shared/components/search-box");
+const { L10N } = require("../l10n");
+const Actions = require("../actions/index");
+const { FREETEXT_FILTER_SEARCH_DELAY } = require("../constants");
+
+module.exports = connect(
+  (state) => ({
+    delay: FREETEXT_FILTER_SEARCH_DELAY,
+    keyShortcut: L10N.getStr("netmonitor.toolbar.filterFreetext.key"),
+    placeholder: L10N.getStr("netmonitor.toolbar.filterFreetext.label"),
+    type: "filter",
+  }),
+  (dispatch) => ({
+    onChange: (url) => {
+      dispatch(Actions.setFilterText(url));
+    },
+  })
+)(SearchBox);
--- a/devtools/client/netmonitor/constants.js
+++ b/devtools/client/netmonitor/constants.js
@@ -1,14 +1,19 @@
 /* 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 general = {
+  FREETEXT_FILTER_SEARCH_DELAY: 200,
+};
+
 const actionTypes = {
-  TOGGLE_FILTER: "TOGGLE_FILTER",
-  ENABLE_FILTER_ONLY: "ENABLE_FILTER_ONLY",
+  TOGGLE_FILTER_TYPE: "TOGGLE_FILTER_TYPE",
+  ENABLE_FILTER_TYPE_ONLY: "ENABLE_FILTER_TYPE_ONLY",
   TOGGLE_SIDEBAR: "TOGGLE_SIDEBAR",
   SHOW_SIDEBAR: "SHOW_SIDEBAR",
   DISABLE_TOGGLE_BUTTON: "DISABLE_TOGGLE_BUTTON",
+  SET_FILTER_TEXT: "SET_FILTER_TEXT",
 };
 
-module.exports = actionTypes;
+module.exports = Object.assign({}, general, actionTypes);
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -294,17 +294,17 @@ var NetMonitorController = {
     // the network monitor is still loading.
     let deferred = promise.defer();
     let request = null;
     let inspector = function () {
       let predicate = i => i.value === requestId;
       request = NetMonitorView.RequestsMenu.getItemForPredicate(predicate);
       if (!request) {
         // Reset filters so that the request is visible.
-        gStore.dispatch(Actions.toggleFilter("all"));
+        gStore.dispatch(Actions.toggleFilterType("all"));
         request = NetMonitorView.RequestsMenu.getItemForPredicate(predicate);
       }
 
       // 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);
         NetMonitorView.RequestsMenu.selectedItem = request;
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -26,21 +26,20 @@
                   id="react-clear-button-hook"/>
         <html:div xmlns="http://www.w3.org/1999/xhtml"
                   id="react-filter-buttons-hook"/>
         <spacer id="requests-menu-spacer"
                 flex="1"/>
         <toolbarbutton id="requests-menu-network-summary-button"
                        class="devtools-toolbarbutton icon-and-text"
                        data-localization="tooltiptext=netmonitor.toolbar.perf"/>
-        <textbox id="requests-menu-filter-freetext-text"
-                 class="devtools-filterinput"
-                 type="search"
-                 required="true"
-                 data-localization="placeholder=netmonitor.toolbar.filterFreetext.label"/>
+        <html:div xmlns="http://www.w3.org/1999/xhtml"
+                  id="react-details-pane-toggle-hook"/>
+        <html:div xmlns="http://www.w3.org/1999/xhtml"
+                  id="react-search-box-hook"/>
         <html:div xmlns="http://www.w3.org/1999/xhtml"
                   id="react-details-pane-toggle-hook"/>
       </hbox>
       <hbox id="network-table-and-sidebar"
             class="devtools-responsive-container"
             flex="1">
         <vbox id="network-table" flex="1" class="devtools-main-content">
           <toolbar id="requests-menu-toolbar"
--- a/devtools/client/netmonitor/panel.js
+++ b/devtools/client/netmonitor/panel.js
@@ -4,39 +4,28 @@
  * 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");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
-const {L10N} = require("./l10n");
 
 function NetMonitorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this.panelDoc = iframeWindow.document;
   this._toolbox = toolbox;
 
   this._view = this.panelWin.NetMonitorView;
   this._controller = this.panelWin.NetMonitorController;
   this._controller._target = this.target;
   this._controller._toolbox = this._toolbox;
 
   EventEmitter.decorate(this);
-
-  this.shortcuts = new KeyShortcuts({
-    window: this.panelDoc.defaultView,
-  });
-  let key = L10N.getStr("netmonitor.toolbar.filterFreetext.key");
-  this.shortcuts.on(key, (name, event) => {
-    event.preventDefault();
-    this._view.RequestsMenu.freetextFilterBox.focus();
-  });
 }
 
 exports.NetMonitorPanel = NetMonitorPanel;
 
 NetMonitorPanel.prototype = {
   /**
    * Open is effectively an asynchronous constructor.
    *
@@ -74,17 +63,15 @@ NetMonitorPanel.prototype = {
 
   destroy: Task.async(function* () {
     if (this._destroying) {
       return this._destroying;
     }
     let deferred = promise.defer();
     this._destroying = deferred.promise;
 
-    this.shortcuts.destroy();
-
     yield this._controller.shutdownNetMonitor();
     this.emit("destroyed");
 
     deferred.resolve();
     return this._destroying;
   })
 };
--- a/devtools/client/netmonitor/reducers/filters.js
+++ b/devtools/client/netmonitor/reducers/filters.js
@@ -1,79 +1,80 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const I = require("devtools/client/shared/vendor/immutable");
 const {
-  TOGGLE_FILTER,
-  ENABLE_FILTER_ONLY,
+  TOGGLE_FILTER_TYPE,
+  ENABLE_FILTER_TYPE_ONLY,
+  SET_FILTER_TEXT,
 } = require("../constants");
 
-const FiltersTypes = I.Record({
+const FilterTypes = I.Record({
   all: false,
   html: false,
   css: false,
   js: false,
   xhr: false,
   fonts: false,
   images: false,
   media: false,
   flash: false,
   ws: false,
   other: false,
 });
 
 const Filters = I.Record({
-  types: new FiltersTypes({ all: true }),
+  types: new FilterTypes({ all: true }),
+  url: "",
 });
 
-function toggleFilter(state, action) {
+function toggleFilterType(state, action) {
   let { filter } = action;
   let newState;
 
   // Ignore unknown filter type
   if (!state.has(filter)) {
     return state;
   }
   if (filter === "all") {
-    return new FiltersTypes({ all: true });
+    return new FilterTypes({ all: true });
   }
 
   newState = state.withMutations(types => {
     types.set("all", false);
     types.set(filter, !state.get(filter));
   });
 
   if (!newState.includes(true)) {
-    newState = new FiltersTypes({ all: true });
+    newState = new FilterTypes({ all: true });
   }
 
   return newState;
 }
 
-function enableFilterOnly(state, action) {
+function enableFilterTypeOnly(state, action) {
   let { filter } = action;
 
   // Ignore unknown filter type
   if (!state.has(filter)) {
     return state;
   }
 
-  return new FiltersTypes({ [filter]: true });
+  return new FilterTypes({ [filter]: true });
 }
 
 function filters(state = new Filters(), action) {
-  let types;
   switch (action.type) {
-    case TOGGLE_FILTER:
-      types = toggleFilter(state.types, action);
-      return state.set("types", types);
-    case ENABLE_FILTER_ONLY:
-      types = enableFilterOnly(state.types, action);
-      return state.set("types", types);
+    case TOGGLE_FILTER_TYPE:
+      return state.set("types", toggleFilterType(state.types, action));
+    case ENABLE_FILTER_TYPE_ONLY:
+      return state.set("types", enableFilterTypeOnly(state.types, action));
+    case SET_FILTER_TEXT:
+      return state.set("url", action.url);
     default:
       return state;
   }
 }
 
 module.exports = filters;
--- a/devtools/client/netmonitor/requests-menu-view.js
+++ b/devtools/client/netmonitor/requests-menu-view.js
@@ -1,13 +1,13 @@
 /* globals document, window, dumpn, $, gNetwork, EVENTS, Prefs,
            NetMonitorController, NetMonitorView */
 "use strict";
 /* eslint-disable mozilla/reject-some-requires */
-const { Cc, Ci, Cu } = require("chrome");
+const { Cu } = require("chrome");
 const Services = require("Services");
 const {Task} = require("devtools/shared/task");
 const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {});
 /* eslint-disable mozilla/reject-some-requires */
 const {SideMenuWidget} = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
 const {setImageTooltip, getImageDimensions} =
   require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
@@ -65,18 +65,17 @@ const REQUESTS_WATERFALL_BACKGROUND_TICK
 // px
 const REQUESTS_WATERFALL_BACKGROUND_TICKS_SPACING_MIN = 10;
 const REQUESTS_WATERFALL_BACKGROUND_TICKS_COLOR_RGB = [128, 136, 144];
 const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN = 32;
 // byte
 const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD = 32;
 const REQUESTS_WATERFALL_DOMCONTENTLOADED_TICKS_COLOR_RGBA = [255, 0, 0, 128];
 const REQUESTS_WATERFALL_LOAD_TICKS_COLOR_RGBA = [0, 0, 255, 128];
-// ms
-const FREETEXT_FILTER_SEARCH_DELAY = 200;
+
 // Constants for formatting bytes.
 const BYTES_IN_KB = 1024;
 const BYTES_IN_MB = Math.pow(BYTES_IN_KB, 2);
 const BYTES_IN_GB = Math.pow(BYTES_IN_KB, 3);
 const MAX_BYTES_SIZE = 1000;
 const MAX_KB_SIZE = 1000 * BYTES_IN_KB;
 const MAX_MB_SIZE = 1000 * BYTES_IN_MB;
 
@@ -129,18 +128,16 @@ RequestsMenuView.prototype = Heritage.ex
 
     this.store = store;
 
     let widgetParentEl = $("#requests-menu-contents");
     this.widget = new SideMenuWidget(widgetParentEl);
     this._splitter = $("#network-inspector-view-splitter");
     this._summary = $("#requests-menu-network-summary-button");
     this._summary.setAttribute("label", L10N.getStr("networkMenu.empty"));
-    this.userInputTimer = Cc["@mozilla.org/timer;1"]
-      .createInstance(Ci.nsITimer);
 
     // Create a tooltip for the newly appended network request item.
     this.tooltip = new HTMLTooltip(NetMonitorController._toolbox.doc, { type: "arrow" });
     this.tooltip.startTogglingOnHover(widgetParentEl, this._onHover, {
       toggleDelay: REQUESTS_TOOLTIP_TOGGLE_DELAY,
       interactive: true
     });
 
@@ -169,49 +166,43 @@ RequestsMenuView.prototype = Heritage.ex
     this._flushRequestsTask = new DeferredTask(this._flushRequests,
       REQUESTS_REFRESH_RATE);
 
     this.sendCustomRequestEvent = this.sendCustomRequest.bind(this);
     this.closeCustomRequestEvent = this.closeCustomRequest.bind(this);
     this.cloneSelectedRequestEvent = this.cloneSelectedRequest.bind(this);
     this.toggleRawHeadersEvent = this.toggleRawHeaders.bind(this);
 
-    this.requestsFreetextFilterEvent =
-      this.requestsFreetextFilterEvent.bind(this);
     this.reFilterRequests = this.reFilterRequests.bind(this);
 
-    this.freetextFilterBox = $("#requests-menu-filter-freetext-text");
-    this.freetextFilterBox.addEventListener("input",
-      this.requestsFreetextFilterEvent, false);
-    this.freetextFilterBox.addEventListener("command",
-      this.requestsFreetextFilterEvent, false);
-
     $("#toolbar-labels").addEventListener("click",
       this.requestsMenuSortEvent, false);
     $("#toolbar-labels").addEventListener("keydown",
       this.requestsMenuSortKeyboardEvent, false);
     $("#toggle-raw-headers").addEventListener("click",
       this.toggleRawHeadersEvent, false);
     $("#requests-menu-contents").addEventListener("scroll", this._onScroll, true);
     $("#requests-menu-contents").addEventListener("contextmenu", this._onContextMenu);
 
     this.unsubscribeStore = store.subscribe(storeWatcher(
       null,
-      () => store.getState().filters.types,
-      (newTypes) => {
-        this._activeFilters = newTypes
+      () => store.getState().filters,
+      (newFilters) => {
+        this._activeFilters = newFilters.types
           .toSeq()
           .filter((checked, key) => checked)
           .keySeq()
           .toArray();
+        this._currentFreetextFilter = newFilters.url;
         this.reFilterRequests();
       }
     ));
 
-    Prefs.filters.forEach(type => store.dispatch(Actions.toggleFilter(type)));
+    Prefs.filters.forEach(type =>
+      store.dispatch(Actions.toggleFilterType(type)));
 
     window.once("connected", this._onConnect.bind(this));
   },
 
   _onConnect: function () {
     $("#requests-menu-reload-notice-button").addEventListener("command",
       this._onReloadCommand, false);
 
@@ -263,22 +254,17 @@ RequestsMenuView.prototype = Heritage.ex
     this.widget.removeEventListener("swap", this._onSwap, false);
     this._splitter.removeEventListener("mousemove", this._onResize, false);
     window.removeEventListener("resize", this._onResize, false);
 
     $("#toolbar-labels").removeEventListener("click",
       this.requestsMenuSortEvent, false);
     $("#toolbar-labels").removeEventListener("keydown",
       this.requestsMenuSortKeyboardEvent, false);
-    this.freetextFilterBox.removeEventListener("input",
-      this.requestsFreetextFilterEvent, false);
-    this.freetextFilterBox.removeEventListener("command",
-      this.requestsFreetextFilterEvent, false);
 
-    this.userInputTimer.cancel();
     this._flushRequestsTask.disarm();
 
     $("#requests-menu-reload-notice-button").removeEventListener("command",
       this._onReloadCommand, false);
     $("#requests-menu-perf-notice-button").removeEventListener("command",
       this._onContextPerfCommand, false);
     $("#requests-menu-network-summary-button").removeEventListener("command",
       this._onContextPerfCommand, false);
@@ -611,33 +597,16 @@ RequestsMenuView.prototype = Heritage.ex
     } else {
       requestTextarea.value = null;
       responseTextare.value = null;
       $("#raw-headers").hidden = true;
     }
   },
 
   /**
-   * Handles the timeout on the freetext filter textbox
-   */
-  requestsFreetextFilterEvent: function () {
-    this.userInputTimer.cancel();
-    this._currentFreetextFilter = this.freetextFilterBox.value || "";
-
-    if (this._currentFreetextFilter.length === 0) {
-      this.freetextFilterBox.removeAttribute("filled");
-    } else {
-      this.freetextFilterBox.setAttribute("filled", true);
-    }
-
-    this.userInputTimer.initWithCallback(this.reFilterRequests,
-      FREETEXT_FILTER_SEARCH_DELAY, Ci.nsITimer.TYPE_ONE_SHOT);
-  },
-
-  /**
    * Refreshes the view contents with the newly selected filters
    */
   reFilterRequests: function () {
     this.filterContents(this._filterPredicate);
     this.refreshSummary();
     this.refreshZebra();
   },
 
--- a/devtools/client/netmonitor/test/browser_net_filter-01.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-01.js
@@ -24,24 +24,22 @@ const REQUESTS_WITH_MEDIA_AND_FLASH = RE
 ]);
 
 const REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS = REQUESTS_WITH_MEDIA_AND_FLASH.concat([
   /* "Upgrade" is a reserved header and can not be set on XMLHttpRequest */
   { url: "sjs_content-type-test-server.sjs?fmt=ws" },
 ]);
 
 add_task(function* () {
+  let Actions = require("devtools/client/netmonitor/actions/index");
   let { monitor } = yield initNetMonitor(FILTERING_URL);
+  let { gStore } = monitor.panelWin;
 
   function setFreetextFilter(value) {
-    // Set the text and manually call all callbacks synchronously to avoid the timeout
-    RequestsMenu.freetextFilterBox.value = value;
-    RequestsMenu.requestsFreetextFilterEvent();
-    RequestsMenu.userInputTimer.cancel();
-    RequestsMenu.reFilterRequests();
+    gStore.dispatch(Actions.setFilterText(value));
   }
 
   info("Starting test... ");
 
   let { $, NetMonitorView } = monitor.panelWin;
   let { RequestsMenu } = NetMonitorView;
 
   RequestsMenu.lazyUpdate = false;
--- a/devtools/client/netmonitor/test/browser_net_icon-preview.js
+++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js
@@ -23,17 +23,17 @@ add_task(function* () {
 
   info("Checking the image thumbnail when all items are shown.");
   checkImageThumbnail();
 
   RequestsMenu.sortBy("size");
   info("Checking the image thumbnail when all items are sorted.");
   checkImageThumbnail();
 
-  gStore.dispatch(Actions.toggleFilter("images"));
+  gStore.dispatch(Actions.toggleFilterType("images"));
   info("Checking the image thumbnail when only images are shown.");
   checkImageThumbnail();
 
   info("Reloading the debuggee and performing all requests again...");
   wait = waitForEvents();
   yield reloadAndPerformRequests();
   yield wait;
 
--- a/devtools/client/netmonitor/test/browser_net_prefs-reload.js
+++ b/devtools/client/netmonitor/test/browser_net_prefs-reload.js
@@ -27,17 +27,17 @@ add_task(function* () {
       // A custom new value to be used for the verified preference.
       newValue: ["html", "css"],
       // Getter used to retrieve the current value from the frontend, in order
       // to verify that the pref was applied properly.
       validateValue: ($) => getView().RequestsMenu._activeFilters,
       // Predicate used to modify the frontend when setting the new pref value,
       // before trying to validate the changes.
       modifyFrontend: ($, value) => value.forEach(e =>
-        getStore().dispatch(Actions.toggleFilter(e)))
+        getStore().dispatch(Actions.toggleFilterType(e)))
     },
     networkDetailsWidth: {
       newValue: ~~(Math.random() * 200 + 100),
       validateValue: ($) => ~~$("#details-pane").getAttribute("width"),
       modifyFrontend: ($, value) => $("#details-pane").setAttribute("width", value)
     },
     networkDetailsHeight: {
       newValue: ~~(Math.random() * 300 + 100),
--- a/devtools/client/netmonitor/toolbar-view.js
+++ b/devtools/client/netmonitor/toolbar-view.js
@@ -1,16 +1,17 @@
 /* globals dumpn, $, NetMonitorView */
 "use strict";
 
 const { createFactory, DOM } = 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);
 const FilterButtons = createFactory(require("./components/filter-buttons"));
 const ToggleButton = createFactory(require("./components/toggle-button"));
+const SearchBox = createFactory(require("./components/search-box"));
 const { L10N } = require("./l10n");
 
 // Shortcuts
 const { button } = DOM;
 
 /**
  * Functions handling the toolbar view: expand/collapse button etc.
  */
@@ -23,16 +24,17 @@ ToolbarView.prototype = {
    * Initialization function, called when the debugger is started.
    */
   initialize: function (store) {
     dumpn("Initializing the ToolbarView");
 
     this._clearContainerNode = $("#react-clear-button-hook");
     this._filterContainerNode = $("#react-filter-buttons-hook");
     this._toggleContainerNode = $("#react-details-pane-toggle-hook");
+    this._searchContainerNode = $("#react-search-box-hook");
 
     // clear button
     ReactDOM.render(button({
       id: "requests-menu-clear-button",
       className: "devtools-button devtools-clear-icon",
       title: L10N.getStr("netmonitor.toolbar.clear"),
       onClick: () => {
         NetMonitorView.RequestsMenu.clear();
@@ -40,27 +42,36 @@ ToolbarView.prototype = {
     }), this._clearContainerNode);
 
     // filter button
     ReactDOM.render(Provider(
       { store },
       FilterButtons()
     ), this._filterContainerNode);
 
+    // search box
+    ReactDOM.render(Provider(
+      { store },
+      SearchBox()
+    ), this._searchContainerNode);
+
+    // details pane toggle button
     ReactDOM.render(Provider(
       { store },
       ToggleButton()
     ), this._toggleContainerNode);
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function () {
     dumpn("Destroying the ToolbarView");
 
     ReactDOM.unmountComponentAtNode(this._clearContainerNode);
     ReactDOM.unmountComponentAtNode(this._filterContainerNode);
     ReactDOM.unmountComponentAtNode(this._toggleContainerNode);
+    ReactDOM.unmountComponentAtNode(this._searchContainerNode);
   }
+
 };
 
 exports.ToolbarView = ToolbarView;
--- a/devtools/client/shared/components/search-box.js
+++ b/devtools/client/shared/components/search-box.js
@@ -39,17 +39,20 @@ module.exports = createClass({
     });
     this.shortcuts.on(this.props.keyShortcut, (name, event) => {
       event.preventDefault();
       this.refs.input.focus();
     });
   },
 
   componentWillUnmount() {
-    this.shortcuts.destroy();
+    if (this.shortcuts) {
+      this.shortcuts.destroy();
+    }
+
     // Clean up an existing timeout.
     if (this.searchTimeout) {
       clearTimeout(this.searchTimeout);
     }
   },
 
   onChange() {
     if (this.state.value !== this.refs.input.value) {
--- a/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js
+++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js
@@ -42,17 +42,17 @@ add_task(function* () {
   let panel = toolbox.getCurrentPanel();
   let selected = panel.panelWin.NetMonitorView.RequestsMenu.selectedItem;
   is(selected.attachment.method, htmlRequest.request.method,
      "The correct request is selected");
   is(selected.attachment.url, htmlRequest.request.url,
      "The correct request is definitely selected");
 
   // Filter out the HTML request.
-  panel.panelWin.gStore.dispatch(Actions.toggleFilter("js"));
+  panel.panelWin.gStore.dispatch(Actions.toggleFilterType("js"));
 
   yield toolbox.selectTool("webconsole");
   is(toolbox.currentToolId, "webconsole", "Web console was selected");
   yield hud.ui.openNetworkPanel(htmlRequest.actor);
 
   panel.panelWin.NetMonitorView.RequestsMenu.selectedItem;
   is(selected.attachment.method, htmlRequest.request.method,
      "The correct request is selected");