--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -427,75 +427,75 @@ netmonitor.tab.timings=Timings
# LOCALIZATION NOTE (netmonitor.tab.preview): This is the label displayed
# in the network details pane identifying the preview tab.
netmonitor.tab.preview=Preview
# LOCALIZATION NOTE (netmonitor.tab.security): This is the label displayed
# in the network details pane identifying the security tab.
netmonitor.tab.security=Security
-# LOCALIZATION NOTE (netmonitor.footer.filterAll): This is the label displayed
-# in the network details footer for the "All" filtering button.
-netmonitor.footer.filterAll=All
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.all): This is the label displayed
+# in the network toolbar for the "All" filtering button.
+netmonitor.toolbar.filter.all=All
-# LOCALIZATION NOTE (netmonitor.footer.filterHTML): This is the label displayed
-# in the network details footer for the "HTML" filtering button.
-netmonitor.footer.filterHTML=HTML
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.html): This is the label displayed
+# in the network toolbar for the "HTML" filtering button.
+netmonitor.toolbar.filter.html=HTML
-# LOCALIZATION NOTE (netmonitor.footer.filterCSS): This is the label displayed
-# in the network details footer for the "CSS" filtering button.
-netmonitor.footer.filterCSS=CSS
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.css): This is the label displayed
+# in the network toolbar for the "CSS" filtering button.
+netmonitor.toolbar.filter.css=CSS
-# LOCALIZATION NOTE (netmonitor.footer.filterJS): This is the label displayed
-# in the network details footer for the "JS" filtering button.
-netmonitor.footer.filterJS=JS
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.js): This is the label displayed
+# in the network toolbar for the "JS" filtering button.
+netmonitor.toolbar.filter.js=JS
-# LOCALIZATION NOTE (netmonitor.footer.filterXHR): This is the label displayed
-# in the network details footer for the "XHR" filtering button.
-netmonitor.footer.filterXHR=XHR
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.xhr): This is the label displayed
+# in the network toolbar for the "XHR" filtering button.
+netmonitor.toolbar.filter.xhr=XHR
-# LOCALIZATION NOTE (netmonitor.footer.filterFonts): This is the label displayed
-# in the network details footer for the "Fonts" filtering button.
-netmonitor.footer.filterFonts=Fonts
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.fonts): This is the label displayed
+# in the network toolbar for the "Fonts" filtering button.
+netmonitor.toolbar.filter.fonts=Fonts
-# LOCALIZATION NOTE (netmonitor.footer.filterImages): This is the label displayed
-# in the network details footer for the "Images" filtering button.
-netmonitor.footer.filterImages=Images
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.images): This is the label displayed
+# in the network toolbar for the "Images" filtering button.
+netmonitor.toolbar.filter.images=Images
-# LOCALIZATION NOTE (netmonitor.footer.filterMedia): This is the label displayed
-# in the network details footer for the "Media" filtering button.
-netmonitor.footer.filterMedia=Media
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.media): This is the label displayed
+# in the network toolbar for the "Media" filtering button.
+netmonitor.toolbar.filter.media=Media
-# LOCALIZATION NOTE (netmonitor.footer.filterFlash): This is the label displayed
-# in the network details footer for the "Flash" filtering button.
-netmonitor.footer.filterFlash=Flash
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.flash): This is the label displayed
+# in the network toolbar for the "Flash" filtering button.
+netmonitor.toolbar.filter.flash=Flash
-# LOCALIZATION NOTE (netmonitor.footer.filterWS): This is the label displayed
-# in the network details footer for the "WS" filtering button.
-netmonitor.footer.filterWS=WS
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.ws): This is the label displayed
+# in the network toolbar for the "WS" filtering button.
+netmonitor.toolbar.filter.ws=WS
-# LOCALIZATION NOTE (netmonitor.footer.filterOther): This is the label displayed
-# in the network details footer for the "Other" filtering button.
-netmonitor.footer.filterOther=Other
+# LOCALIZATION NOTE (netmonitor.toolbar.filter.other): This is the label displayed
+# in the network toolbar for the "Other" filtering button.
+netmonitor.toolbar.filter.other=Other
-# LOCALIZATION NOTE (netmonitor.footer.filterFreetext): This is the label displayed
-# in the network details footer for the url filtering textbox.
-netmonitor.footer.filterFreetext.label=Filter URLs
+# LOCALIZATION NOTE (netmonitor.toolbar.filterFreetext): This is the label displayed
+# in the network toolbar for the url filtering textbox.
+netmonitor.toolbar.filterFreetext.label=Filter URLs
# LOCALIZATION NOTE (netmonitor.toolbar.filterFreetext.key): This is the
# shortcut key to focus on the toolbar url filtering textbox
netmonitor.toolbar.filterFreetext.key=CmdOrCtrl+F
-# LOCALIZATION NOTE (netmonitor.footer.clear): This is the label displayed
-# in the network details footer for the "Clear" button.
-netmonitor.footer.clear=Clear
+# LOCALIZATION NOTE (netmonitor.toolbar.clear): This is the label displayed
+# in the network toolbar for the "Clear" button.
+netmonitor.toolbar.clear=Clear
-# LOCALIZATION NOTE (netmonitor.footer.clear): This is the label displayed
-# in the network details footer for the performance analysis button.
-netmonitor.footer.perf=Toggle performance analysis…
+# LOCALIZATION NOTE (netmonitor.toolbar.clear): This is the label displayed
+# in the network toolbar for the performance analysis button.
+netmonitor.toolbar.perf=Toggle performance analysis…
# LOCALIZATION NOTE (netmonitor.panesButton.tooltip): This is the tooltip for
# the button that toggles the panes visible or hidden in the netmonitor UI.
netmonitor.panesButton.tooltip=Toggle network info
# LOCALIZATION NOTE (netmonitor.summary.url): This is the label displayed
# in the network details headers tab identifying the URL.
netmonitor.summary.url=Request URL:
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/actions/filters.js
@@ -0,0 +1,43 @@
+/* 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,
+} = 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) {
+ return {
+ type: TOGGLE_FILTER,
+ 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) {
+ return {
+ type: ENABLE_FILTER_ONLY,
+ filter,
+ };
+}
+
+module.exports = {
+ toggleFilter,
+ enableFilterOnly,
+};
--- a/devtools/client/netmonitor/actions/index.js
+++ b/devtools/client/netmonitor/actions/index.js
@@ -1,8 +1,8 @@
/* 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";
-module.exports = {
- // actions...
-};
+const filters = require("./filters");
+
+module.exports = Object.assign({}, filters);
--- a/devtools/client/netmonitor/actions/moz.build
+++ b/devtools/client/netmonitor/actions/moz.build
@@ -1,8 +1,9 @@
# 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(
+ 'filters.js',
'index.js'
)
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/components/filter-buttons.js
@@ -0,0 +1,48 @@
+/* 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 { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { L10N } = require("../l10n");
+const Actions = require("../actions/index");
+
+const { button, div } = DOM;
+
+function FilterButtons({
+ filterTypes,
+ triggerFilterType,
+}) {
+ const buttons = filterTypes.entrySeq().map(([type, checked]) => {
+ let classList = ["menu-filter-button"];
+ checked && classList.push("checked");
+
+ return button({
+ id: `requests-menu-filter-${type}-button`,
+ className: classList.join(" "),
+ "data-key": type,
+ onClick: triggerFilterType,
+ onKeyDown: triggerFilterType,
+ }, L10N.getStr(`netmonitor.toolbar.filter.${type}`));
+ }).toArray();
+
+ return div({ id: "requests-menu-filter-buttons" }, buttons);
+}
+
+FilterButtons.PropTypes = {
+ state: PropTypes.object.isRequired,
+ triggerFilterType: PropTypes.func.iRequired,
+};
+
+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));
+ },
+ })
+)(FilterButtons);
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/components/moz.build
@@ -0,0 +1,8 @@
+# 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',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/constants.js
@@ -0,0 +1,11 @@
+/* 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 actionTypes = {
+ TOGGLE_FILTER: "TOGGLE_FILTER",
+ ENABLE_FILTER_ONLY: "ENABLE_FILTER_ONLY",
+};
+
+module.exports = actionTypes;
--- a/devtools/client/netmonitor/moz.build
+++ b/devtools/client/netmonitor/moz.build
@@ -1,25 +1,28 @@
# 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/.
DIRS += [
'actions',
+ 'components',
'har',
'reducers',
'selectors'
]
DevToolsModules(
+ 'constants.js',
'events.js',
'filter-predicates.js',
'l10n.js',
'panel.js',
'prefs.js',
'request-utils.js',
'requests-menu-view.js',
'sort-predicates.js',
- 'store.js'
+ 'store.js',
+ 'toolbar-view.js',
)
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -1,14 +1,14 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
-/* globals window, document, NetMonitorView */
+/* globals window, document, NetMonitorView, gStore, Actions */
/* exported loader */
"use strict";
var { utils: Cu } = Components;
// Descriptions for what this frontend is currently doing.
const ACTIVITY_TYPE = {
// Standing by and handling requests normally.
@@ -293,17 +293,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.
- NetMonitorView.RequestsMenu.filterOn("all");
+ gStore.dispatch(Actions.toggleFilter("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-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -23,16 +23,22 @@ const {PluralForm} = require("devtools/s
const {Filters} = require("./filter-predicates");
const {getFormDataSections,
formDataURI,
writeHeaderText,
getKeyWithEvent,
getUriHostPort} = require("./request-utils");
const {L10N} = require("./l10n");
const {RequestsMenuView} = require("./requests-menu-view");
+const {ToolbarView} = require("./toolbar-view");
+const {configureStore} = require("./store");
+const Actions = require("./actions/index");
+
+// Initialize the global redux variables
+var gStore = configureStore();
// ms
const WDA_DEFAULT_VERIFY_INTERVAL = 50;
// Use longer timeout during testing as the tests need this process to succeed
// and two seconds is quite short on slow debug builds. The timeout here should
// be at least equal to the general mochitest timeout of 45 seconds so that this
// never gets hit during testing.
@@ -83,18 +89,18 @@ const NETWORK_ANALYSIS_PIE_CHART_DIAMETE
*/
var NetMonitorView = {
/**
* Initializes the network monitor view.
*/
initialize: function () {
this._initializePanes();
- this.Toolbar.initialize();
- this.RequestsMenu.initialize();
+ this.Toolbar.initialize(gStore);
+ this.RequestsMenu.initialize(gStore);
this.NetworkDetails.initialize();
this.CustomRequest.initialize();
},
/**
* Destroys the network monitor view.
*/
destroy: function () {
@@ -290,66 +296,16 @@ var NetMonitorView = {
_detailsPane: null,
_detailsPaneToggleButton: null,
_collapsePaneString: "",
_expandPaneString: "",
_editorPromises: new Map()
};
/**
- * Functions handling the toolbar view: expand/collapse button etc.
- */
-function ToolbarView() {
- dumpn("ToolbarView was instantiated");
-
- this._onTogglePanesPressed = this._onTogglePanesPressed.bind(this);
-}
-
-ToolbarView.prototype = {
- /**
- * Initialization function, called when the debugger is started.
- */
- initialize: function () {
- dumpn("Initializing the ToolbarView");
-
- this._detailsPaneToggleButton = $("#details-pane-toggle");
- this._detailsPaneToggleButton.addEventListener("mousedown",
- this._onTogglePanesPressed, false);
- },
-
- /**
- * Destruction function, called when the debugger is closed.
- */
- destroy: function () {
- dumpn("Destroying the ToolbarView");
-
- this._detailsPaneToggleButton.removeEventListener("mousedown",
- this._onTogglePanesPressed, false);
- },
-
- /**
- * Listener handling the toggle button click event.
- */
- _onTogglePanesPressed: function () {
- let requestsMenu = NetMonitorView.RequestsMenu;
- let selectedIndex = requestsMenu.selectedIndex;
-
- // Make sure there's a selection if the button is pressed, to avoid
- // showing an empty network details pane.
- if (selectedIndex == -1 && requestsMenu.itemCount) {
- requestsMenu.selectedIndex = 0;
- } else {
- requestsMenu.selectedIndex = -1;
- }
- },
-
- _detailsPaneToggleButton: null
-};
-
-/**
* Functions handling the sidebar details view.
*/
function SidebarView() {
dumpn("SidebarView was instantiated");
}
SidebarView.prototype = {
/**
@@ -1486,17 +1442,18 @@ PerformanceStatisticsView.prototype = {
title: L10N.getStr(title),
data: data,
strings: strings,
totals: totals,
sorted: sorted
});
chart.on("click", (_, item) => {
- NetMonitorView.RequestsMenu.filterOnlyOn(item.label);
+ // Reset FilterButtons and enable one filter exclusively
+ gStore.dispatch(Actions.enableFilterOnly(item.label));
NetMonitorView.showNetworkInspectorView();
});
container.appendChild(chart.node);
},
/**
* Sanitizes the data source used for creating charts, to follow the
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -59,85 +59,29 @@
</popupset>
<deck id="body" class="theme-sidebar" flex="1">
<vbox id="network-inspector-view" flex="1">
<hbox id="netmonitor-toolbar" class="devtools-toolbar">
<toolbarbutton id="requests-menu-clear-button"
class="devtools-toolbarbutton devtools-clear-icon"
- data-localization="tooltiptext=netmonitor.footer.clear"/>
- <hbox id="requests-menu-filter-buttons">
- <button id="requests-menu-filter-all-button"
- class="requests-menu-filter-button"
- checked="true"
- data-key="all"
- data-localization="label=netmonitor.footer.filterAll">
- </button>
- <button id="requests-menu-filter-html-button"
- class="requests-menu-filter-button"
- data-key="html"
- data-localization="label=netmonitor.footer.filterHTML">
- </button>
- <button id="requests-menu-filter-css-button"
- class="requests-menu-filter-button"
- data-key="css"
- data-localization="label=netmonitor.footer.filterCSS">
- </button>
- <button id="requests-menu-filter-js-button"
- class="requests-menu-filter-button"
- data-key="js"
- data-localization="label=netmonitor.footer.filterJS">
- </button>
- <button id="requests-menu-filter-xhr-button"
- class="requests-menu-filter-button"
- data-key="xhr"
- data-localization="label=netmonitor.footer.filterXHR">
- </button>
- <button id="requests-menu-filter-fonts-button"
- class="requests-menu-filter-button"
- data-key="fonts"
- data-localization="label=netmonitor.footer.filterFonts">
- </button>
- <button id="requests-menu-filter-images-button"
- class="requests-menu-filter-button"
- data-key="images"
- data-localization="label=netmonitor.footer.filterImages">
- </button>
- <button id="requests-menu-filter-media-button"
- class="requests-menu-filter-button"
- data-key="media"
- data-localization="label=netmonitor.footer.filterMedia">
- </button>
- <button id="requests-menu-filter-flash-button"
- class="requests-menu-filter-button"
- data-key="flash"
- data-localization="label=netmonitor.footer.filterFlash">
- </button>
- <button id="requests-menu-filter-ws-button"
- class="requests-menu-filter-button"
- data-key="ws"
- data-localization="label=netmonitor.footer.filterWS">
- </button>
- <button id="requests-menu-filter-other-button"
- class="requests-menu-filter-button"
- data-key="other"
- data-localization="label=netmonitor.footer.filterOther">
- </button>
- </hbox>
+ data-localization="tooltiptext=netmonitor.toolbar.clear"/>
+ <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.footer.perf"/>
+ data-localization="tooltiptext=netmonitor.toolbar.perf"/>
<textbox id="requests-menu-filter-freetext-text"
class="devtools-filterinput"
type="search"
required="true"
- data-localization="placeholder=netmonitor.footer.filterFreetext.label"/>
+ data-localization="placeholder=netmonitor.toolbar.filterFreetext.label"/>
<toolbarbutton id="details-pane-toggle"
class="devtools-toolbarbutton"
data-localization="tooltiptext=netmonitor.panesButton.tooltip"
disabled="true"
tabindex="0"/>
</hbox>
<hbox id="network-table-and-sidebar"
class="devtools-responsive-container"
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/reducers/filters.js
@@ -0,0 +1,79 @@
+/* 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,
+} = require("../constants");
+
+const FiltersTypes = 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 }),
+});
+
+function toggleFilter(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 });
+ }
+
+ newState = state.withMutations(types => {
+ types.set("all", false);
+ types.set(filter, !state.get(filter));
+ });
+
+ if (!newState.includes(true)) {
+ newState = new FiltersTypes({ all: true });
+ }
+
+ return newState;
+}
+
+function enableFilterOnly(state, action) {
+ let { filter } = action;
+
+ // Ignore unknown filter type
+ if (!state.has(filter)) {
+ return state;
+ }
+
+ return new FiltersTypes({ [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);
+ default:
+ return state;
+ }
+}
+
+module.exports = filters;
--- a/devtools/client/netmonitor/reducers/index.js
+++ b/devtools/client/netmonitor/reducers/index.js
@@ -1,10 +1,11 @@
/* 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 { combineReducers } = require("devtools/client/shared/vendor/redux");
+const filters = require("./filters");
module.exports = combineReducers({
- // reducers...
+ filters,
});
--- a/devtools/client/netmonitor/reducers/moz.build
+++ b/devtools/client/netmonitor/reducers/moz.build
@@ -1,8 +1,9 @@
# 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(
+ 'filters.js',
'index.js'
)
--- a/devtools/client/netmonitor/requests-menu-view.js
+++ b/devtools/client/netmonitor/requests-menu-view.js
@@ -23,16 +23,17 @@ const {getFormDataSections,
formDataURI,
writeHeaderText,
getKeyWithEvent,
getAbbreviatedMimeType,
getUriNameWithQuery,
getUriHostPort,
getUriHost,
loadCauseString} = require("./request-utils");
+const Actions = require("./actions/index");
loader.lazyServiceGetter(this, "clipboardHelper",
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
loader.lazyRequireGetter(this, "HarExporter",
"devtools/client/netmonitor/har/har-exporter", true);
loader.lazyRequireGetter(this, "NetworkHelper",
@@ -82,16 +83,29 @@ const REQUEST_TIME_DECIMALS = 2;
const CONTENT_SIZE_DECIMALS = 2;
const CONTENT_MIME_TYPE_ABBREVIATIONS = {
"ecmascript": "js",
"javascript": "js",
"x-javascript": "js"
};
+// A smart store watcher to notify store changes as necessary
+function storeWatcher(initialValue, reduceValue, onChange) {
+ let currentValue = initialValue;
+
+ return () => {
+ const newValue = reduceValue(currentValue);
+ if (newValue !== currentValue) {
+ onChange(newValue, currentValue);
+ currentValue = newValue;
+ }
+ };
+}
+
/**
* Functions handling the requests menu (containing details about each request,
* like status, method, file, domain, as well as a waterfall representing
* timing imformation).
*/
function RequestsMenuView() {
dumpn("RequestsMenuView was instantiated");
@@ -103,17 +117,17 @@ function RequestsMenuView() {
this._onScroll = this._onScroll.bind(this);
this._onSecurityIconClick = this._onSecurityIconClick.bind(this);
}
RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
/**
* Initialization function, called when the network monitor is started.
*/
- initialize: function () {
+ initialize: function (store) {
dumpn("Initializing the RequestsMenuView");
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"]
@@ -122,32 +136,28 @@ RequestsMenuView.prototype = Heritage.ex
// 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
});
$("#requests-menu-contents").addEventListener("scroll", this._onScroll, true);
- Prefs.filters.forEach(type => this.filterOn(type));
this.sortContents((a, b) => Sorters.waterfall(a.attachment, b.attachment));
this.allowFocusOnRightClick = true;
this.maintainSelectionVisible = true;
this.widget.addEventListener("select", this._onSelect, false);
this.widget.addEventListener("swap", this._onSwap, false);
this._splitter.addEventListener("mousemove", this._onResize, false);
window.addEventListener("resize", this._onResize, false);
this.requestsMenuSortEvent = getKeyWithEvent(this.sortBy.bind(this));
this.requestsMenuSortKeyboardEvent = getKeyWithEvent(this.sortBy.bind(this), true);
- this.requestsMenuFilterEvent = getKeyWithEvent(this.filterOn.bind(this));
- this.requestsMenuFilterKeyboardEvent = getKeyWithEvent(
- this.filterOn.bind(this), true);
this.reqeustsMenuClearEvent = this.clear.bind(this);
this._onContextShowing = this._onContextShowing.bind(this);
this._onContextNewTabCommand = this.openRequestInTab.bind(this);
this._onContextCopyUrlCommand = this.copyUrl.bind(this);
this._onContextCopyImageAsDataUriCommand =
this.copyImageAsDataUri.bind(this);
this._onContextCopyResponseCommand = this.copyResponse.bind(this);
this._onContextResendCommand = this.cloneSelectedRequest.bind(this);
@@ -171,35 +181,46 @@ RequestsMenuView.prototype = Heritage.ex
this.requestsFreetextFilterEvent, false);
this.freetextFilterBox.addEventListener("command",
this.requestsFreetextFilterEvent, false);
$("#toolbar-labels").addEventListener("click",
this.requestsMenuSortEvent, false);
$("#toolbar-labels").addEventListener("keydown",
this.requestsMenuSortKeyboardEvent, false);
- $("#requests-menu-filter-buttons").addEventListener("click",
- this.requestsMenuFilterEvent, false);
- $("#requests-menu-filter-buttons").addEventListener("keydown",
- this.requestsMenuFilterKeyboardEvent, false);
$("#requests-menu-clear-button").addEventListener("click",
this.reqeustsMenuClearEvent, false);
$("#network-request-popup").addEventListener("popupshowing",
this._onContextShowing, false);
$("#request-menu-context-newtab").addEventListener("command",
this._onContextNewTabCommand, false);
$("#request-menu-context-copy-url").addEventListener("command",
this._onContextCopyUrlCommand, false);
$("#request-menu-context-copy-response").addEventListener("command",
this._onContextCopyResponseCommand, false);
$("#request-menu-context-copy-image-as-data-uri").addEventListener(
"command", this._onContextCopyImageAsDataUriCommand, false);
$("#toggle-raw-headers").addEventListener("click",
this.toggleRawHeadersEvent, false);
+ this.unsubscribeStore = store.subscribe(storeWatcher(
+ null,
+ () => store.getState().filters.types,
+ (newTypes) => {
+ this._activeFilters = newTypes
+ .toSeq()
+ .filter((checked, key) => checked)
+ .keySeq()
+ .toArray();
+ this.reFilterRequests();
+ }
+ ));
+
+ Prefs.filters.forEach(type => store.dispatch(Actions.toggleFilter(type)));
+
window.once("connected", this._onConnect.bind(this));
},
_onConnect: function () {
$("#requests-menu-reload-notice-button").addEventListener("command",
this._onReloadCommand, false);
if (NetMonitorController.supportsCustomRequest) {
@@ -255,20 +276,16 @@ 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);
- $("#requests-menu-filter-buttons").removeEventListener("click",
- this.requestsMenuFilterEvent, false);
- $("#requests-menu-filter-buttons").removeEventListener("keydown",
- this.requestsMenuFilterKeyboardEvent, false);
$("#requests-menu-clear-button").removeEventListener("click",
this.reqeustsMenuClearEvent, false);
this.freetextFilterBox.removeEventListener("input",
this.requestsFreetextFilterEvent, false);
this.freetextFilterBox.removeEventListener("command",
this.requestsFreetextFilterEvent, false);
this.userInputTimer.cancel();
@@ -301,16 +318,18 @@ RequestsMenuView.prototype = Heritage.ex
$("#custom-request-send-button").removeEventListener("click",
this.sendCustomRequestEvent, false);
$("#custom-request-close-button").removeEventListener("click",
this.closeCustomRequestEvent, false);
$("#headers-summary-resend").removeEventListener("click",
this.cloneSelectedRequestEvent, false);
$("#toggle-raw-headers").removeEventListener("click",
this.toggleRawHeadersEvent, false);
+
+ this.unsubscribeStore();
},
/**
* Resets this container (removes all the networking information).
*/
reset: function () {
this.empty();
this._addQueue = [];
@@ -648,107 +667,16 @@ RequestsMenuView.prototype = Heritage.ex
*/
reFilterRequests: function () {
this.filterContents(this._filterPredicate);
this.refreshSummary();
this.refreshZebra();
},
/**
- * Filters all network requests in this container by a specified type.
- *
- * @param string type
- * Either "all", "html", "css", "js", "xhr", "fonts", "images", "media"
- * "flash", "ws" or "other".
- */
- filterOn: function (type = "all") {
- if (type === "all") {
- // The filter "all" is special as it doesn't toggle.
- // - If some filters are selected and 'all' is clicked, the previously
- // selected filters will be disabled and 'all' is the only active one.
- // - If 'all' is already selected, do nothing.
- if (this._activeFilters.indexOf("all") !== -1) {
- return;
- }
-
- // Uncheck all other filters and select 'all'. Must create a copy as
- // _disableFilter removes the filters from the list while it's being
- // iterated. 'all' will be enabled automatically by _disableFilter once
- // the last filter is disabled.
- this._activeFilters.slice().forEach(this._disableFilter, this);
- } else if (this._activeFilters.indexOf(type) === -1) {
- this._enableFilter(type);
- } else {
- this._disableFilter(type);
- }
-
- this.reFilterRequests();
- },
-
- /**
- * Same as `filterOn`, except that it only allows a single type exclusively.
- *
- * @param string type
- * @see RequestsMenuView.prototype.fitlerOn
- */
- filterOnlyOn: function (type = "all") {
- this._activeFilters.slice().forEach(this._disableFilter, this);
- this.filterOn(type);
- },
-
- /**
- * Disables the given filter, its button and toggles 'all' on if the filter to
- * be disabled is the last one active.
- *
- * @param string type
- * Either "all", "html", "css", "js", "xhr", "fonts", "images", "media"
- * "flash", "ws" or "other".
- */
- _disableFilter: function (type) {
- // Remove the filter from list of active filters.
- this._activeFilters.splice(this._activeFilters.indexOf(type), 1);
-
- // Remove the checked status from the filter.
- let target = $("#requests-menu-filter-" + type + "-button");
- target.removeAttribute("checked");
-
- // Check if the filter disabled was the last one. If so, toggle all on.
- if (this._activeFilters.length === 0) {
- this._enableFilter("all");
- }
- },
-
- /**
- * Enables the given filter, its button and toggles 'all' off if the filter to
- * be enabled is the first one active.
- *
- * @param string type
- * Either "all", "html", "css", "js", "xhr", "fonts", "images", "media"
- * "flash", "ws" or "other".
- */
- _enableFilter: function (type) {
- // Make sure this is a valid filter type.
- if (!Object.keys(Filters).includes(type)) {
- return;
- }
-
- // Add the filter to the list of active filters.
- this._activeFilters.push(type);
-
- // Add the checked status to the filter button.
- let target = $("#requests-menu-filter-" + type + "-button");
- target.setAttribute("checked", true);
-
- // Check if 'all' was selected before. If so, disable it.
- if (type !== "all" && this._activeFilters.indexOf("all") !== -1) {
- this._disableFilter("all");
- }
- },
-
- /**
* Returns a predicate that can be used to test if a request matches any of
* the active filters.
*/
get _filterPredicate() {
let currentFreetextFilter = this._currentFreetextFilter;
return requestItem => {
const { attachment } = requestItem;
--- a/devtools/client/netmonitor/store.js
+++ b/devtools/client/netmonitor/store.js
@@ -1,20 +1,13 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const createStore = require("devtools/client/shared/redux/create-store");
-const reducers = require("devtools/client/netmonitor/reducers/index");
+const reducers = require("./reducers/index");
function configureStore() {
- const initialState = {
- // All initial application states will be defined as store from here
- };
-
- return createStore()(
- reducers,
- initialState
- );
+ return createStore()(reducers);
}
exports.configureStore = configureStore;
--- a/devtools/client/netmonitor/test/browser_net_icon-preview.js
+++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js
@@ -3,35 +3,37 @@
"use strict";
/**
* Tests if image responses show a thumbnail in the requests menu.
*/
add_task(function* () {
+ let Actions = require("devtools/client/netmonitor/actions/index");
+
let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
info("Starting test... ");
- let { $, $all, EVENTS, ACTIVITY_TYPE, NetMonitorView, NetMonitorController } =
- monitor.panelWin;
+ let { $, $all, EVENTS, ACTIVITY_TYPE, NetMonitorView, NetMonitorController,
+ gStore } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
let wait = waitForEvents();
yield performRequests();
yield wait;
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();
- RequestsMenu.filterOn("images");
+ gStore.dispatch(Actions.toggleFilter("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
@@ -3,38 +3,41 @@
"use strict";
/**
* Tests if the prefs that should survive across tool reloads work.
*/
add_task(function* () {
+ let Actions = require("devtools/client/netmonitor/actions/index");
let { monitor } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
// This test reopens the network monitor a bunch of times, for different
// hosts (bottom, side, window). This seems to be slow on debug builds.
requestLongerTimeout(3);
// Use these getters instead of caching instances inside the panel win,
// since the tool is reopened a bunch of times during this test
// and the instances will differ.
let getView = () => monitor.panelWin.NetMonitorView;
+ let getStore = () => monitor.panelWin.gStore;
let prefsToCheck = {
filters: {
// 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 => getView().RequestsMenu.filterOn(e))
+ modifyFrontend: ($, value) => value.forEach(e =>
+ getStore().dispatch(Actions.toggleFilter(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/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -399,25 +399,25 @@ function testFilterButtons(aMonitor, aFi
*
* @param array aIsChecked
* An array specifying if a button at given index should have a
* 'checked' attribute. For example, if the third item of the array
* evaluates to true, the third button should be checked.
*/
function testFilterButtonsCustom(aMonitor, aIsChecked) {
let doc = aMonitor.panelWin.document;
- let buttons = doc.querySelectorAll(".requests-menu-filter-button");
+ let buttons = doc.querySelectorAll(".menu-filter-button");
for (let i = 0; i < aIsChecked.length; i++) {
let button = buttons[i];
if (aIsChecked[i]) {
- is(button.hasAttribute("checked"), true,
- "The " + button.id + " button should have a 'checked' attribute.");
+ is(button.classList.contains("checked"), true,
+ "The " + button.id + " button should have a 'checked' class.");
} else {
- is(button.hasAttribute("checked"), false,
- "The " + button.id + " button should not have a 'checked' attribute.");
+ is(button.classList.contains("checked"), false,
+ "The " + button.id + " button should not have a 'checked' class.");
}
}
}
/**
* Loads shared/frame-script-utils.js in the specified tab.
*
* @param tab
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/toolbar-view.js
@@ -0,0 +1,68 @@
+/* globals dumpn, $, NetMonitorView */
+"use strict";
+
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
+const FilterButtons = createFactory(require("./components/filter-buttons"));
+
+/**
+ * Functions handling the toolbar view: expand/collapse button etc.
+ */
+function ToolbarView() {
+ dumpn("ToolbarView was instantiated");
+
+ this._onTogglePanesPressed = this._onTogglePanesPressed.bind(this);
+}
+
+ToolbarView.prototype = {
+ /**
+ * Initialization function, called when the debugger is started.
+ */
+ initialize: function (store) {
+ dumpn("Initializing the ToolbarView");
+
+ this._filterContainerNode = $("#react-filter-buttons-hook");
+
+ ReactDOM.render(Provider(
+ { store },
+ FilterButtons()
+ ), this._filterContainerNode);
+
+ this._detailsPaneToggleButton = $("#details-pane-toggle");
+ this._detailsPaneToggleButton.addEventListener("mousedown",
+ this._onTogglePanesPressed, false);
+ },
+
+ /**
+ * Destruction function, called when the debugger is closed.
+ */
+ destroy: function () {
+ dumpn("Destroying the ToolbarView");
+
+ ReactDOM.unmountComponentAtNode(this._filterContainerNode);
+
+ this._detailsPaneToggleButton.removeEventListener("mousedown",
+ this._onTogglePanesPressed, false);
+ },
+
+ /**
+ * Listener handling the toggle button click event.
+ */
+ _onTogglePanesPressed: function () {
+ let requestsMenu = NetMonitorView.RequestsMenu;
+ let selectedIndex = requestsMenu.selectedIndex;
+
+ // Make sure there's a selection if the button is pressed, to avoid
+ // showing an empty network details pane.
+ if (selectedIndex == -1 && requestsMenu.itemCount) {
+ requestsMenu.selectedIndex = 0;
+ } else {
+ requestsMenu.selectedIndex = -1;
+ }
+ },
+
+ _detailsPaneToggleButton: null
+};
+
+exports.ToolbarView = ToolbarView;
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -736,41 +736,16 @@
.custom-section {
margin-top: 0.5em;
}
#custom-method-value {
width: 4.5em;
}
-/* Main toolbar */
-.requests-menu-filter-button {
- -moz-appearance: none;
- background: rgba(128,128,128,0.1);
- border: none;
- border-radius: 2px;
- min-width: 0;
- padding: 0 5px;
- margin: 2px;
- color: var(--theme-body-color);
-}
-
-.requests-menu-filter-button:hover {
- background: rgba(128,128,128,0.2);
-}
-
-.requests-menu-filter-button:hover:active {
- background-color: var(--theme-selection-background-semitransparent);
-}
-
-.requests-menu-filter-button:not(:active)[checked] {
- background-color: var(--theme-selection-background);
- color: var(--theme-selection-color);
-}
-
/* Performance analysis buttons */
#requests-menu-network-summary-button {
background: none;
box-shadow: none;
border-color: transparent;
list-style-image: url(images/profiler-stopwatch.svg);
padding-inline-end: 0;
--- a/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js
+++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js
@@ -11,16 +11,18 @@
const TEST_FILE_URI =
"http://example.com/browser/devtools/client/webconsole/test/" +
"test-network.html";
const TEST_URI = "data:text/html;charset=utf8,<p>test file URI";
var hud;
add_task(function* () {
+ let Actions = require("devtools/client/netmonitor/actions/index");
+
let requests = [];
let { browser } = yield loadTab(TEST_URI);
yield pushPrefEnv();
hud = yield openConsole();
hud.jsterm.clearOutput();
HUDService.lastFinishedRequest.callback = request => requests.push(request);
@@ -40,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.NetMonitorView.RequestsMenu.filterOn("js");
+ panel.panelWin.gStore.dispatch(Actions.toggleFilter("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");