author | Phil Ringnalda <philringnalda@gmail.com> |
Fri, 02 Dec 2016 22:17:31 -0800 | |
changeset 325159 | 557548714db55136b51e1129d649e2599797985f |
parent 325158 | a8e20bf36959426f0a6eb4b5df29ef3dd85ad4bd (current diff) |
parent 325138 | d2c5b6581d6ab82d04b6ee8d454bb7debe9f4d0e (diff) |
child 325160 | 1b2237e0b5e010ae3b182e4dbe79037635ee7997 |
child 325179 | 4f87b0fb164521efb5f21c9318ecdd26c4940093 |
child 325181 | f1bb9b5c75c039a7ac3b4d95411fd4fbca947e05 |
push id | 84610 |
push user | philringnalda@gmail.com |
push date | Sat, 03 Dec 2016 06:28:13 +0000 |
treeherder | mozilla-inbound@1b2237e0b5e0 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 53.0a1 |
first release with | nightly linux32
557548714db5
/
53.0a1
/
20161203030204
/
files
nightly linux64
557548714db5
/
53.0a1
/
20161203030204
/
files
nightly mac
557548714db5
/
53.0a1
/
20161203030204
/
files
nightly win32
557548714db5
/
53.0a1
/
20161203030204
/
files
nightly win64
557548714db5
/
53.0a1
/
20161203030204
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
53.0a1
/
20161203030204
/
pushlog to previous
nightly linux64
53.0a1
/
20161203030204
/
pushlog to previous
nightly mac
53.0a1
/
20161203030204
/
pushlog to previous
nightly win32
53.0a1
/
20161203030204
/
pushlog to previous
nightly win64
53.0a1
/
20161203030204
/
pushlog to previous
|
--- a/browser/base/content/test/general/browser_windowopen_reflows.js +++ b/browser/base/content/test/general/browser_windowopen_reflows.js @@ -5,52 +5,52 @@ const EXPECTED_REFLOWS = [ // handleEvent flushes layout to get the tabstrip width after a resize. "handleEvent@chrome://browser/content/tabbrowser.xml|", // Loading a tab causes a reflow. "loadTabs@chrome://browser/content/tabbrowser.xml|" + "loadOneOrMoreURIs@chrome://browser/content/browser.js|" + - "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|", + "_delayedStartup@chrome://browser/content/browser.js|", // Selecting the address bar causes a reflow. "select@chrome://global/content/bindings/textbox.xml|" + "focusAndSelectUrlBar@chrome://browser/content/browser.js|" + - "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|", + "_delayedStartup@chrome://browser/content/browser.js|", // Focusing the content area causes a reflow. - "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|", + "_delayedStartup@chrome://browser/content/browser.js|", // Sometimes sessionstore collects data during this test, which causes a sync reflow // (https://bugzilla.mozilla.org/show_bug.cgi?id=892154 will fix this) "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm", ]; if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") { // TabsInTitlebar._update causes a reflow on OS X and Windows trying to do calculations // since layout info is already dirty. This doesn't seem to happen before // MozAfterPaint on Linux. - EXPECTED_REFLOWS.push("TabsInTitlebar._update/rect@chrome://browser/content/browser-tabsintitlebar.js|" + - "TabsInTitlebar._update@chrome://browser/content/browser-tabsintitlebar.js|" + + EXPECTED_REFLOWS.push("rect@chrome://browser/content/browser-tabsintitlebar.js|" + + "_update@chrome://browser/content/browser-tabsintitlebar.js|" + "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js|" + "handleEvent@chrome://browser/content/tabbrowser.xml|"); } if (Services.appinfo.OS == "Darwin") { // _onOverflow causes a reflow getting widths. - EXPECTED_REFLOWS.push("OverflowableToolbar.prototype._onOverflow@resource:///modules/CustomizableUI.jsm|" + - "OverflowableToolbar.prototype.init@resource:///modules/CustomizableUI.jsm|" + - "OverflowableToolbar.prototype.observe@resource:///modules/CustomizableUI.jsm|" + - "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|"); + EXPECTED_REFLOWS.push("_onOverflow@resource:///modules/CustomizableUI.jsm|" + + "init@resource:///modules/CustomizableUI.jsm|" + + "observe@resource:///modules/CustomizableUI.jsm|" + + "_delayedStartup@chrome://browser/content/browser.js|"); // Same as above since in packaged builds there are no function names and the resource URI includes "app" EXPECTED_REFLOWS.push("@resource://app/modules/CustomizableUI.jsm|" + "@resource://app/modules/CustomizableUI.jsm|" + "@resource://app/modules/CustomizableUI.jsm|" + - "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|"); + "_delayedStartup@chrome://browser/content/browser.js|"); } /* * This test ensures that there are no unexpected * uninterruptible reflows when opening new windows. */ function test() { waitForExplicitFinish();
new file mode 100644 --- /dev/null +++ b/browser/config/mozconfigs/win32/clang @@ -0,0 +1,19 @@ +MOZ_AUTOMATION_BUILD_SYMBOLS=0 +MOZ_AUTOMATION_PACKAGE_TESTS=0 +MOZ_AUTOMATION_L10N_CHECK=0 + +. "$topsrcdir/build/mozconfig.win-common" +. "$topsrcdir/browser/config/mozconfigs/common" + +ac_add_options --enable-optimize + +#Work to make the clang-plugin work on Windows is ongoing in bug 1316545. +#ac_add_options --enable-clang-plugin + +. $topsrcdir/build/win32/mozconfig.vs-latest + +. "$topsrcdir/build/mozconfig.rust" + +. "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" +. "$topsrcdir/build/mozconfig.clang-cl"
new file mode 100644 --- /dev/null +++ b/browser/config/mozconfigs/win32/clang-debug @@ -0,0 +1,20 @@ +MOZ_AUTOMATION_BUILD_SYMBOLS=0 +MOZ_AUTOMATION_PACKAGE_TESTS=0 +MOZ_AUTOMATION_L10N_CHECK=0 + +. "$topsrcdir/build/mozconfig.win-common" +. "$topsrcdir/browser/config/mozconfigs/common" + +ac_add_options --enable-optimize +ac_add_options --enable-debug + +#Work to make the clang-plugin work on Windows is ongoing in bug 1316545. +#ac_add_options --enable-clang-plugin + +. $topsrcdir/build/win32/mozconfig.vs-latest + +. "$topsrcdir/build/mozconfig.rust" + +. "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" +. "$topsrcdir/build/mozconfig.clang-cl"
new file mode 100644 --- /dev/null +++ b/browser/config/mozconfigs/win64/clang @@ -0,0 +1,21 @@ +MOZ_AUTOMATION_L10N_CHECK=0 +MOZ_AUTOMATION_PACKAGE_TEST=0 + +. "$topsrcdir/build/mozconfig.win-common" +. "$topsrcdir/browser/config/mozconfigs/common" + +ac_add_options --target=x86_64-pc-mingw32 +ac_add_options --host=x86_64-pc-mingw32 + +ac_add_options --enable-optimize + +#Work to make the clang-plugin work on Windows is ongoing in bug 1316545. +#ac_add_options --enable-clang-plugin + +. $topsrcdir/build/win64/mozconfig.vs-latest + +. "$topsrcdir/build/mozconfig.rust" + +. "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" +. "$topsrcdir/build/mozconfig.clang-cl"
new file mode 100644 --- /dev/null +++ b/browser/config/mozconfigs/win64/clang-debug @@ -0,0 +1,22 @@ +MOZ_AUTOMATION_L10N_CHECK=0 +MOZ_AUTOMATION_PACKAGE_TEST=0 + +. "$topsrcdir/build/mozconfig.win-common" +. "$topsrcdir/browser/config/mozconfigs/common" + +ac_add_options --target=x86_64-pc-mingw32 +ac_add_options --host=x86_64-pc-mingw32 + +ac_add_options --enable-optimize +ac_add_options --enable-debug + +#Work to make the clang-plugin work on Windows is ongoing in bug 1316545. +#ac_add_options --enable-clang-plugin + +. $topsrcdir/build/win64/mozconfig.vs-latest + +. "$topsrcdir/build/mozconfig.rust" + +. "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" +. "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/config/tooltool-manifests/win32/clang.manifest +++ b/browser/config/tooltool-manifests/win32/clang.manifest @@ -18,16 +18,24 @@ "algorithm": "sha512", "visibility": "public", "filename": "sccache2.tar.bz2", "unpack": true, "digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e", "size": 1179901 }, { -"version": "clang 3.9.0/r262971", -"size": 169861843, -"digest": "8caa5a89aea981b618233d39f01bb934b79b85ee8167104bfa4f07936145df5e8ca5e8e007123d75ccc12d2baa926ffc827b40bf793fa9d4bc2670fa519b0ec0", +"version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0", +"size": 326656969, +"digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7", "algorithm": "sha512", -"filename": "clang32.tar.bz2", +"filename": "vs2015u3.zip", +"unpack": true +}, +{ +"version": "clang 4.0pre/r286542", +"size": 206088429, +"digest": "a6de709a5097d98084b6111e340f53cd8397736f783aa6f76c9d45bcb694d3983ab3b7aea7f96756fcc4ee44f7b0e57df5ebabbd59242cd51942742fe1769d9a", +"algorithm": "sha512", +"filename": "clang.tar.bz2", "unpack": true } ]
--- a/browser/config/tooltool-manifests/win64/clang.manifest +++ b/browser/config/tooltool-manifests/win64/clang.manifest @@ -19,16 +19,24 @@ "algorithm": "sha512", "visibility": "public", "filename": "sccache2.tar.bz2", "unpack": true, "digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e", "size": 1179901 }, { -"version": "clang 3.9.0/r262971", -"size": 173306045, -"digest": "806413640a964dad44c0c6055d2a89a1075730fb6f659ff37341378a14a7dc032e672941765225608b71f6b385ac721ed36bfa04eb38c211b07e2776cb72a0ee", +"version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0", +"size": 326656969, +"digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7", "algorithm": "sha512", -"filename": "clang64.tar.bz2", +"filename": "vs2015u3.zip", +"unpack": true +}, +{ +"version": "clang 4.0pre/r286542", +"size": 210049255, +"digest": "8da8c653ea1948c13aa152cf0b1b0fa94e9619c49006ec80cc625d33cbf2dafa3533764f38fc0213e469cc786a738e5099f0e39d52e30f9a711718e8a3124311", +"algorithm": "sha512", +"filename": "clang.tar.bz2", "unpack": true } ]
--- a/build/templates.mozbuild +++ b/build/templates.mozbuild @@ -56,22 +56,25 @@ def CppUnitTests(names, ext='.cpp'): @template def Library(name): '''Template for libraries.''' LIBRARY_NAME = name @template -def RustLibrary(name): +def RustLibrary(name, features=None): '''Template for Rust libraries.''' Library(name) IS_RUST_LIBRARY = True + if features: + RUST_LIBRARY_FEATURES = features + @template def SharedLibrary(name): '''Template for shared libraries.''' Library(name) FORCE_SHARED_LIB = True
--- a/build/valgrind/x86_64-redhat-linux-gnu.sup +++ b/build/valgrind/x86_64-redhat-linux-gnu.sup @@ -100,17 +100,16 @@ fun:g_cclosure_marshal_VOID__OBJECTv fun:_g_closure_invoke_va fun:g_signal_emit_valist fun:g_signal_emit fun:gtk_combo_box_constructor fun:g_object_newv fun:g_object_new_valist fun:g_object_new - fun:_ZN13nsLookAndFeel4InitEv ... } # set_color() in gtkstyle.c of GTK version 3.4.4 only can leak GdkRGBA # allocations when the theme has transparent colors: # https://git.gnome.org/browse/gtk+/tree/gtk/deprecated/gtkstyle.c?h=3.4.4#n676 { Bug 1250704 Memcheck:Leak
--- a/config/rules.mk +++ b/config/rules.mk @@ -937,41 +937,47 @@ endif # choices, and Cargo only supports two, we choose to enable various # optimization levels in our Cargo.toml files all the time, and override the # optimization level here, if necessary. (The Cargo.toml files already # specify debug-assertions appropriately for --{disable,enable}-debug.) ifndef MOZ_OPTIMIZE rustflags_override = RUSTFLAGS='-C opt-level=0' endif +CARGO_BUILD = env $(rustflags_override) CARGO_TARGET_DIR=. RUSTC=$(RUSTC) MOZ_DIST=$(DIST) $(CARGO) build $(cargo_build_flags) + ifdef RUST_LIBRARY_FILE +ifdef RUST_LIBRARY_FEATURES +rust_features_flag := --features "$(RUST_LIBRARY_FEATURES)" +endif + # Assume any system libraries rustc links against are already in the target's LIBS. # # We need to run cargo unconditionally, because cargo is the only thing that # has full visibility into how changes in Rust sources might affect the final # build. force-cargo-library-build: $(REPORT_BUILD) - env $(rustflags_override) CARGO_TARGET_DIR=. RUSTC=$(RUSTC) $(CARGO) build --lib $(cargo_build_flags) $(cargo_target_flag) -- + $(CARGO_BUILD) --lib $(cargo_target_flag) $(rust_features_flag) $(RUST_LIBRARY_FILE): force-cargo-library-build endif # RUST_LIBRARY_FILE ifdef RUST_PROGRAMS force-cargo-program-build: $(REPORT_BUILD) - env $(rustflags_override) CARGO_TARGET_DIR=. RUSTC=$(RUSTC) $(CARGO) build $(addprefix --bin ,$(RUST_CARGO_PROGRAMS)) $(cargo_build_flags) $(cargo_target_flag) -- + $(CARGO_BUILD) $(addprefix --bin ,$(RUST_CARGO_PROGRAMS)) $(cargo_target_flag) $(RUST_PROGRAMS): force-cargo-program-build endif # RUST_PROGRAMS ifdef HOST_RUST_PROGRAMS force-cargo-host-program-build: $(REPORT_BUILD) - env $(rustflags_override) CARGO_TARGET_DIR=. RUSTC=$(RUSTC) $(CARGO) build $(addprefix --bin ,$(HOST_RUST_CARGO_PROGRAMS)) $(cargo_build_flags) $(cargo_host_flag) -- + $(CARGO_BUILD) $(addprefix --bin ,$(HOST_RUST_CARGO_PROGRAMS)) $(cargo_host_flag) $(HOST_RUST_PROGRAMS): force-cargo-host-program-build endif # HOST_RUST_PROGRAMS endif # MOZ_RUST $(SOBJS): $(REPORT_BUILD) $(AS) -o $@ $(DEFINES) $(ASFLAGS) $($(notdir $<)_FLAGS) $(LOCAL_INCLUDES) -c $<
--- a/devtools/client/debugger/test/mochitest/browser_dbg_closure-inspection.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_closure-inspection.js @@ -48,23 +48,23 @@ function test() { return personFetched.then(() => { is(personNode.expanded, true, "|person| should be expanded at this point."); is(personNode.get("getName").target.querySelector(".name") .getAttribute("value"), "getName", "Should have the right property name for 'getName' in person."); is(personNode.get("getName").target.querySelector(".value") - .getAttribute("value"), "_pfactory/<.getName()", + .getAttribute("value"), "getName()", "'getName' in person should have the right value."); is(personNode.get("getFoo").target.querySelector(".name") .getAttribute("value"), "getFoo", "Should have the right property name for 'getFoo' in person."); is(personNode.get("getFoo").target.querySelector(".value") - .getAttribute("value"), "_pfactory/<.getFoo()", + .getAttribute("value"), "getFoo()", "'getFoo' in person should have the right value."); // Expand the function nodes. This causes their properties to be // retrieved and displayed. let getFooNode = personNode.get("getFoo"); let getNameNode = personNode.get("getName"); let funcsFetched = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_PROPERTIES, 2); let funcClosuresFetched = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES, 2);
--- a/devtools/client/jar.mn +++ b/devtools/client/jar.mn @@ -9,17 +9,18 @@ devtools.jar: content/shared/widgets/widgets.css (shared/widgets/widgets.css) content/shared/widgets/VariablesView.xul (shared/widgets/VariablesView.xul) content/projecteditor/chrome/content/projecteditor.xul (projecteditor/chrome/content/projecteditor.xul) content/projecteditor/lib/helpers/readdir.js (projecteditor/lib/helpers/readdir.js) content/projecteditor/chrome/content/projecteditor-loader.xul (projecteditor/chrome/content/projecteditor-loader.xul) content/projecteditor/chrome/content/projecteditor-test.xul (projecteditor/chrome/content/projecteditor-test.xul) content/projecteditor/chrome/content/projecteditor-loader.js (projecteditor/chrome/content/projecteditor-loader.js) content/netmonitor/netmonitor.xul (netmonitor/netmonitor.xul) - content/netmonitor/netmonitor.js (netmonitor/netmonitor.js) + content/netmonitor/netmonitor-controller.js (netmonitor/netmonitor-controller.js) + content/netmonitor/netmonitor-view.js (netmonitor/netmonitor-view.js) content/webconsole/webconsole.xul (webconsole/webconsole.xul) * content/scratchpad/scratchpad.xul (scratchpad/scratchpad.xul) content/scratchpad/scratchpad.js (scratchpad/scratchpad.js) content/shared/splitview.css (shared/splitview.css) content/shared/theme-switching.js (shared/theme-switching.js) content/shared/frame-script-utils.js (shared/frame-script-utils.js) content/styleeditor/styleeditor.xul (styleeditor/styleeditor.xul) content/storage/storage.xul (storage/storage.xul)
deleted file mode 100644 --- a/devtools/client/netmonitor/actions/batching.js +++ /dev/null @@ -1,42 +0,0 @@ -/* 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 { - BATCH_ACTIONS, - BATCH_ENABLE, - BATCH_RESET, -} = require("../constants"); - -/** - * Process multiple actions at once as part of one dispatch, and produce only one - * state update at the end. This action is not processed by any reducer, but by a - * special store enhancer. - */ -function batchActions(actions) { - return { - type: BATCH_ACTIONS, - actions - }; -} - -function batchEnable(enabled) { - return { - type: BATCH_ENABLE, - enabled - }; -} - -function batchReset() { - return { - type: BATCH_RESET, - }; -} - -module.exports = { - batchActions, - batchEnable, - batchReset, -};
--- a/devtools/client/netmonitor/actions/filters.js +++ b/devtools/client/netmonitor/actions/filters.js @@ -37,22 +37,22 @@ function enableFilterTypeOnly(filter) { type: ENABLE_FILTER_TYPE_ONLY, filter, }; } /** * Set filter text. * - * @param {string} text - A filter text is going to be set + * @param {string} url - A filter text is going to be set */ -function setFilterText(text) { +function setFilterText(url) { return { type: SET_FILTER_TEXT, - text, + url, }; } module.exports = { toggleFilterType, enableFilterTypeOnly, setFilterText, };
--- a/devtools/client/netmonitor/actions/index.js +++ b/devtools/client/netmonitor/actions/index.js @@ -1,23 +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 batching = require("./batching"); const filters = require("./filters"); const requests = require("./requests"); -const selection = require("./selection"); -const sort = require("./sort"); -const timingMarkers = require("./timing-markers"); const ui = require("./ui"); -Object.assign(exports, - batching, - filters, - requests, - selection, - sort, - timingMarkers, - ui -); +module.exports = Object.assign({}, filters, requests, ui);
--- a/devtools/client/netmonitor/actions/moz.build +++ b/devtools/client/netmonitor/actions/moz.build @@ -1,14 +1,10 @@ # 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( - 'batching.js', 'filters.js', 'index.js', 'requests.js', - 'selection.js', - 'sort.js', - 'timing-markers.js', 'ui.js', )
--- a/devtools/client/netmonitor/actions/requests.js +++ b/devtools/client/netmonitor/actions/requests.js @@ -1,65 +1,25 @@ /* 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 { - ADD_REQUEST, - UPDATE_REQUEST, - CLONE_SELECTED_REQUEST, - REMOVE_SELECTED_CUSTOM_REQUEST, - CLEAR_REQUESTS, + UPDATE_REQUESTS, } = require("../constants"); -function addRequest(id, data, batch) { - return { - type: ADD_REQUEST, - id, - data, - meta: { batch }, - }; -} - -function updateRequest(id, data, batch) { +/** + * Update request items + * + * @param {array} requests - visible request items + */ +function updateRequests(items) { return { - type: UPDATE_REQUEST, - id, - data, - meta: { batch }, - }; -} - -/** - * Clone the currently selected request, set the "isCustom" attribute. - * Used by the "Edit and Resend" feature. - */ -function cloneSelectedRequest() { - return { - type: CLONE_SELECTED_REQUEST - }; -} - -/** - * Remove a request from the list. Supports removing only cloned requests with a - * "isCustom" attribute. Other requests never need to be removed. - */ -function removeSelectedCustomRequest() { - return { - type: REMOVE_SELECTED_CUSTOM_REQUEST - }; -} - -function clearRequests() { - return { - type: CLEAR_REQUESTS + type: UPDATE_REQUESTS, + items, }; } module.exports = { - addRequest, - updateRequest, - cloneSelectedRequest, - removeSelectedCustomRequest, - clearRequests, + updateRequests, };
deleted file mode 100644 --- a/devtools/client/netmonitor/actions/selection.js +++ /dev/null @@ -1,67 +0,0 @@ -/* 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 { getDisplayedRequests } = require("../selectors/index"); -const { SELECT_REQUEST, PRESELECT_REQUEST } = require("../constants"); - -/** - * When a new request with a given id is added in future, select it immediately. - * Used by the "Edit and Resend" feature, where we know in advance the ID of the - * request, at a time when it wasn't sent yet. - */ -function preselectRequest(id) { - return { - type: PRESELECT_REQUEST, - id - }; -} - -/** - * Select request with a given id. - */ -function selectRequest(id) { - return { - type: SELECT_REQUEST, - id - }; -} - -const PAGE_SIZE_ITEM_COUNT_RATIO = 5; - -/** - * Move the selection up to down according to the "delta" parameter. Possible values: - * - Number: positive or negative, move up or down by specified distance - * - "PAGE_UP" | "PAGE_DOWN" (String): page up or page down - * - +Infinity | -Infinity: move to the start or end of the list - */ -function selectDelta(delta) { - return (dispatch, getState) => { - const state = getState(); - const requests = getDisplayedRequests(state); - - if (requests.isEmpty()) { - return; - } - - const selIndex = requests.findIndex(r => r.id === state.requests.selectedId); - - if (delta === "PAGE_DOWN") { - delta = Math.ceil(requests.size / PAGE_SIZE_ITEM_COUNT_RATIO); - } else if (delta === "PAGE_UP") { - delta = -Math.ceil(requests.size / PAGE_SIZE_ITEM_COUNT_RATIO); - } - - const newIndex = Math.min(Math.max(0, selIndex + delta), requests.size - 1); - const newItem = requests.get(newIndex); - dispatch(selectRequest(newItem.id)); - }; -} - -module.exports = { - preselectRequest, - selectRequest, - selectDelta, -};
deleted file mode 100644 --- a/devtools/client/netmonitor/actions/sort.js +++ /dev/null @@ -1,18 +0,0 @@ -/* 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 { SORT_BY } = require("../constants"); - -function sortBy(sortType) { - return { - type: SORT_BY, - sortType - }; -} - -module.exports = { - sortBy -};
deleted file mode 100644 --- a/devtools/client/netmonitor/actions/timing-markers.js +++ /dev/null @@ -1,19 +0,0 @@ -/* 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 { ADD_TIMING_MARKER, CLEAR_TIMING_MARKERS } = require("../constants"); - -exports.addTimingMarker = (marker) => { - return { - type: ADD_TIMING_MARKER, - marker - }; -}; - -exports.clearTimingMarkers = () => { - return { - type: CLEAR_TIMING_MARKERS - }; -};
--- a/devtools/client/netmonitor/actions/ui.js +++ b/devtools/client/netmonitor/actions/ui.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 { OPEN_SIDEBAR, - WATERFALL_RESIZE, + TOGGLE_SIDEBAR, } = require("../constants"); /** * Change sidebar open state. * * @param {boolean} open - open state */ function openSidebar(open) { @@ -20,26 +20,17 @@ function openSidebar(open) { open, }; } /** * Toggle sidebar open state. */ function toggleSidebar() { - return (dispatch, getState) => dispatch(openSidebar(!getState().ui.sidebarOpen)); -} - -/** - * Waterfall width has changed (likely on window resize). Update the UI. - */ -function resizeWaterfall(width) { return { - type: WATERFALL_RESIZE, - width + type: TOGGLE_SIDEBAR, }; } module.exports = { openSidebar, toggleSidebar, - resizeWaterfall, };
--- a/devtools/client/netmonitor/components/clear-button.js +++ b/devtools/client/netmonitor/components/clear-button.js @@ -1,32 +1,29 @@ /* 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 NetMonitorView */ + "use strict"; const { DOM } = 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 } = DOM; /* * Clear button component * A type of tool button is responsible for cleaning network requests. */ -function ClearButton({ onClick }) { +function ClearButton() { return button({ id: "requests-menu-clear-button", className: "devtools-button devtools-clear-icon", title: L10N.getStr("netmonitor.toolbar.clear"), - onClick, + onClick: () => { + NetMonitorView.RequestsMenu.clear(); + }, }); } -module.exports = connect( - undefined, - dispatch => ({ - onClick: () => dispatch(Actions.clearRequests()) - }) -)(ClearButton); +module.exports = ClearButton;
--- a/devtools/client/netmonitor/components/moz.build +++ b/devtools/client/netmonitor/components/moz.build @@ -1,18 +1,12 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( 'clear-button.js', 'filter-buttons.js', - 'request-list-content.js', - 'request-list-empty.js', - 'request-list-header.js', - 'request-list-item.js', - 'request-list-tooltip.js', - 'request-list.js', 'search-box.js', 'summary-button.js', 'toggle-button.js', 'toolbar.js', )
deleted file mode 100644 --- a/devtools/client/netmonitor/components/request-list-content.js +++ /dev/null @@ -1,255 +0,0 @@ -/* 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 NetMonitorView */ - -"use strict"; - -const { Task } = require("devtools/shared/task"); -const { createClass, createFactory, DOM } = require("devtools/client/shared/vendor/react"); -const { div } = DOM; -const Actions = require("../actions/index"); -const RequestListItem = createFactory(require("./request-list-item")); -const { connect } = require("devtools/client/shared/vendor/react-redux"); -const { setTooltipImageContent, - setTooltipStackTraceContent } = require("./request-list-tooltip"); -const { getDisplayedRequests, - getWaterfallScale } = require("../selectors/index"); -const { KeyCodes } = require("devtools/client/shared/keycodes"); - -// tooltip show/hide delay in ms -const REQUESTS_TOOLTIP_TOGGLE_DELAY = 500; - -/** - * Renders the actual contents of the request list. - */ -const RequestListContent = createClass({ - displayName: "RequestListContent", - - componentDidMount() { - // Set the CSS variables for waterfall scaling - this.setScalingStyles(); - - // Install event handler for displaying a tooltip - this.props.tooltip.startTogglingOnHover(this.refs.contentEl, this.onHover, { - toggleDelay: REQUESTS_TOOLTIP_TOGGLE_DELAY, - interactive: true - }); - - // Install event handler to hide the tooltip on scroll - this.refs.contentEl.addEventListener("scroll", this.onScroll, true); - }, - - componentWillUpdate() { - // Check if the list is scrolled to bottom, before UI update - this.shouldScrollBottom = this.isScrolledToBottom(); - }, - - componentDidUpdate(prevProps) { - // Update the CSS variables for waterfall scaling after props change - this.setScalingStyles(); - - // Keep the list scrolled to bottom if a new row was added - if (this.shouldScrollBottom) { - let node = this.refs.contentEl; - node.scrollTop = node.scrollHeight; - } - }, - - componentWillUnmount() { - this.refs.contentEl.removeEventListener("scroll", this.onScroll, true); - - // Uninstall the tooltip event handler - this.props.tooltip.stopTogglingOnHover(); - }, - - /** - * Set the CSS variables for waterfall scaling. If React supported setting CSS - * variables as part of the "style" property of a DOM element, we would use that. - * - * However, React doesn't support this, so we need to use a hack and update the - * DOM element directly: https://github.com/facebook/react/issues/6411 - */ - setScalingStyles(prevProps) { - const { scale } = this.props; - if (scale == this.currentScale) { - return; - } - - this.currentScale = scale; - - const { style } = this.refs.contentEl; - style.removeProperty("--timings-scale"); - style.removeProperty("--timings-rev-scale"); - style.setProperty("--timings-scale", scale); - style.setProperty("--timings-rev-scale", 1 / scale); - }, - - isScrolledToBottom() { - const { contentEl } = this.refs; - const lastChildEl = contentEl.lastElementChild; - - if (!lastChildEl) { - return false; - } - - let lastChildRect = lastChildEl.getBoundingClientRect(); - let contentRect = contentEl.getBoundingClientRect(); - - return (lastChildRect.height + lastChildRect.top) <= contentRect.bottom; - }, - - /** - * The predicate used when deciding whether a popup should be shown - * over a request item or not. - * - * @param nsIDOMNode target - * The element node currently being hovered. - * @param object tooltip - * The current tooltip instance. - * @return {Promise} - */ - onHover: Task.async(function* (target, tooltip) { - let itemEl = target.closest(".request-list-item"); - if (!itemEl) { - return false; - } - let itemId = itemEl.dataset.id; - if (!itemId) { - return false; - } - let requestItem = this.props.displayedRequests.find(r => r.id == itemId); - if (!requestItem) { - return false; - } - - if (requestItem.responseContent && target.closest(".requests-menu-icon-and-file")) { - return setTooltipImageContent(tooltip, itemEl, requestItem); - } else if (requestItem.cause && target.closest(".requests-menu-cause-stack")) { - return setTooltipStackTraceContent(tooltip, requestItem); - } - - return false; - }), - - /** - * Scroll listener for the requests menu view. - */ - onScroll() { - this.props.tooltip.hide(); - }, - - /** - * Handler for keyboard events. For arrow up/down, page up/down, home/end, - * move the selection up or down. - */ - onKeyDown(e) { - let delta; - - switch (e.keyCode) { - case KeyCodes.DOM_VK_UP: - case KeyCodes.DOM_VK_LEFT: - delta = -1; - break; - case KeyCodes.DOM_VK_DOWN: - case KeyCodes.DOM_VK_RIGHT: - delta = +1; - break; - case KeyCodes.DOM_VK_PAGE_UP: - delta = "PAGE_UP"; - break; - case KeyCodes.DOM_VK_PAGE_DOWN: - delta = "PAGE_DOWN"; - break; - case KeyCodes.DOM_VK_HOME: - delta = -Infinity; - break; - case KeyCodes.DOM_VK_END: - delta = +Infinity; - break; - } - - if (delta) { - // Prevent scrolling when pressing navigation keys. - e.preventDefault(); - e.stopPropagation(); - this.props.onSelectDelta(delta); - } - }, - - /** - * If selection has just changed (by keyboard navigation), don't keep the list - * scrolled to bottom, but allow scrolling up with the selection. - */ - onFocusedNodeChange() { - this.shouldScrollBottom = false; - }, - - /** - * If a focused item was unmounted, transfer the focus to the container element. - */ - onFocusedNodeUnmount() { - if (this.refs.contentEl) { - this.refs.contentEl.focus(); - } - }, - - render() { - const { selectedRequestId, - displayedRequests, - firstRequestStartedMillis, - onItemMouseDown, - onItemContextMenu, - onSecurityIconClick } = this.props; - - return div( - { - ref: "contentEl", - className: "requests-menu-contents", - tabIndex: 0, - onKeyDown: this.onKeyDown, - }, - displayedRequests.map((item, index) => RequestListItem({ - key: item.id, - item, - index, - isSelected: item.id === selectedRequestId, - firstRequestStartedMillis, - onMouseDown: e => onItemMouseDown(e, item.id), - onContextMenu: e => onItemContextMenu(e, item.id), - onSecurityIconClick: e => onSecurityIconClick(e, item), - onFocusedNodeChange: this.onFocusedNodeChange, - onFocusedNodeUnmount: this.onFocusedNodeUnmount, - })) - ); - }, -}); - -module.exports = connect( - state => ({ - displayedRequests: getDisplayedRequests(state), - selectedRequestId: state.requests.selectedId, - scale: getWaterfallScale(state), - firstRequestStartedMillis: state.requests.firstStartedMillis, - tooltip: NetMonitorView.RequestsMenu.tooltip, - }), - dispatch => ({ - onItemMouseDown: (e, item) => dispatch(Actions.selectRequest(item)), - onItemContextMenu: (e, item) => { - e.preventDefault(); - NetMonitorView.RequestsMenu.contextMenu.open(e); - }, - onSelectDelta: (delta) => dispatch(Actions.selectDelta(delta)), - /** - * A handler that opens the security tab in the details view if secure or - * broken security indicator is clicked. - */ - onSecurityIconClick: (e, item) => { - const { securityState } = item; - if (securityState && securityState !== "insecure") { - // Choose the security tab. - NetMonitorView.NetworkDetails.widget.selectedIndex = 5; - } - }, - }) -)(RequestListContent);
deleted file mode 100644 --- a/devtools/client/netmonitor/components/request-list-empty.js +++ /dev/null @@ -1,65 +0,0 @@ -/* 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 NetMonitorView */ - -"use strict"; - -const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react"); -const { L10N } = require("../l10n"); -const { div, span, button } = DOM; -const { connect } = require("devtools/client/shared/vendor/react-redux"); - -/** - * UI displayed when the request list is empty. Contains instructions on reloading - * the page and on triggering performance analysis of the page. - */ -const RequestListEmptyNotice = createClass({ - displayName: "RequestListEmptyNotice", - - propTypes: { - onReloadClick: PropTypes.func.isRequired, - onPerfClick: PropTypes.func.isRequired, - }, - - render() { - return div( - { - id: "requests-menu-empty-notice", - className: "request-list-empty-notice", - }, - div({ id: "notice-reload-message" }, - span(null, L10N.getStr("netmonitor.reloadNotice1")), - button( - { - id: "requests-menu-reload-notice-button", - className: "devtools-toolbarbutton", - "data-standalone": true, - onClick: this.props.onReloadClick, - }, - L10N.getStr("netmonitor.reloadNotice2") - ), - span(null, L10N.getStr("netmonitor.reloadNotice3")) - ), - div({ id: "notice-perf-message" }, - span(null, L10N.getStr("netmonitor.perfNotice1")), - button({ - id: "requests-menu-perf-notice-button", - title: L10N.getStr("netmonitor.perfNotice3"), - className: "devtools-button", - "data-standalone": true, - onClick: this.props.onPerfClick, - }), - span(null, L10N.getStr("netmonitor.perfNotice2")) - ) - ); - } -}); - -module.exports = connect( - undefined, - dispatch => ({ - onPerfClick: e => NetMonitorView.toggleFrontendMode(), - onReloadClick: e => NetMonitorView.reloadPage(), - }) -)(RequestListEmptyNotice);
deleted file mode 100644 --- a/devtools/client/netmonitor/components/request-list-header.js +++ /dev/null @@ -1,197 +0,0 @@ -/* 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 document */ - -"use strict"; - -const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react"); -const { div, button } = DOM; -const { connect } = require("devtools/client/shared/vendor/react-redux"); -const { L10N } = require("../l10n"); -const { getWaterfallScale } = require("../selectors/index"); -const Actions = require("../actions/index"); -const WaterfallBackground = require("../waterfall-background"); - -// ms -const REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE = 5; -// px -const REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN = 60; - -const REQUEST_TIME_DECIMALS = 2; - -const HEADERS = [ - { name: "status", label: "status3" }, - { name: "method" }, - { name: "file", boxName: "icon-and-file" }, - { name: "domain", boxName: "security-and-domain" }, - { name: "cause" }, - { name: "type" }, - { name: "transferred" }, - { name: "size" }, - { name: "waterfall" } -]; - -/** - * Render the request list header with sorting arrows for columns. - * Displays tick marks in the waterfall column header. - * Also draws the waterfall background canvas and updates it when needed. - */ -const RequestListHeader = createClass({ - displayName: "RequestListHeader", - - propTypes: { - sort: PropTypes.object, - scale: PropTypes.number, - waterfallWidth: PropTypes.number, - onHeaderClick: PropTypes.func.isRequired, - }, - - componentDidMount() { - this.background = new WaterfallBackground(document); - this.background.draw(this.props); - }, - - componentDidUpdate() { - this.background.draw(this.props); - }, - - componentWillUnmount() { - this.background.destroy(); - this.background = null; - }, - - render() { - const { sort, scale, waterfallWidth, onHeaderClick } = this.props; - - return div( - { id: "requests-menu-toolbar", className: "devtools-toolbar" }, - div({ id: "toolbar-labels" }, - HEADERS.map(header => { - const name = header.name; - const boxName = header.boxName || name; - const label = L10N.getStr(`netmonitor.toolbar.${header.label || name}`); - - let sorted, sortedTitle; - const active = sort.type == name ? true : undefined; - if (active) { - sorted = sort.ascending ? "ascending" : "descending"; - sortedTitle = L10N.getStr(sort.ascending - ? "networkMenu.sortedAsc" - : "networkMenu.sortedDesc"); - } - - return div( - { - id: `requests-menu-${boxName}-header-box`, - key: name, - className: `requests-menu-header requests-menu-${boxName}`, - // Used to style the next column. - "data-active": active, - }, - button( - { - id: `requests-menu-${name}-button`, - className: `requests-menu-header-button requests-menu-${name}`, - "data-sorted": sorted, - title: sortedTitle, - onClick: () => onHeaderClick(name), - }, - name == "waterfall" ? WaterfallLabel(waterfallWidth, scale, label) - : div({ className: "button-text" }, label), - div({ className: "button-icon" }) - ) - ); - }) - ) - ); - } -}); - -/** - * Build the waterfall header - timing tick marks with the right spacing - */ -function waterfallDivisionLabels(waterfallWidth, scale) { - let labels = []; - - // Build new millisecond tick labels... - let timingStep = REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE; - let scaledStep = scale * timingStep; - - // Ignore any divisions that would end up being too close to each other. - while (scaledStep < REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN) { - scaledStep *= 2; - } - - // Insert one label for each division on the current scale. - for (let x = 0; x < waterfallWidth; x += scaledStep) { - let millisecondTime = x / scale; - - let normalizedTime = millisecondTime; - let divisionScale = "millisecond"; - - // If the division is greater than 1 minute. - if (normalizedTime > 60000) { - normalizedTime /= 60000; - divisionScale = "minute"; - } else if (normalizedTime > 1000) { - // If the division is greater than 1 second. - normalizedTime /= 1000; - divisionScale = "second"; - } - - // Showing too many decimals is bad UX. - if (divisionScale == "millisecond") { - normalizedTime |= 0; - } else { - normalizedTime = L10N.numberWithDecimals(normalizedTime, REQUEST_TIME_DECIMALS); - } - - let width = (x + scaledStep | 0) - (x | 0); - // Adjust the first marker for the borders - if (x == 0) { - width -= 2; - } - // Last marker doesn't need a width specified at all - if (x + scaledStep >= waterfallWidth) { - width = undefined; - } - - labels.push(div( - { - key: labels.length, - className: "requests-menu-timings-division", - "data-division-scale": divisionScale, - style: { width } - }, - L10N.getFormatStr("networkMenu." + divisionScale, normalizedTime) - )); - } - - return labels; -} - -function WaterfallLabel(waterfallWidth, scale, label) { - let className = "button-text requests-menu-waterfall-label-wrapper"; - - if (scale != null) { - label = waterfallDivisionLabels(waterfallWidth, scale); - className += " requests-menu-waterfall-visible"; - } - - return div({ className }, label); -} - -module.exports = connect( - state => ({ - sort: state.sort, - scale: getWaterfallScale(state), - waterfallWidth: state.ui.waterfallWidth, - firstRequestStartedMillis: state.requests.firstStartedMillis, - timingMarkers: state.timingMarkers, - }), - dispatch => ({ - onHeaderClick: type => dispatch(Actions.sortBy(type)), - }) -)(RequestListHeader);
deleted file mode 100644 --- a/devtools/client/netmonitor/components/request-list-item.js +++ /dev/null @@ -1,346 +0,0 @@ -/* 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 { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react"); -const { div, span, img } = DOM; -const { L10N } = require("../l10n"); -const { getFormattedSize } = require("../utils/format-utils"); -const { getAbbreviatedMimeType } = require("../request-utils"); - -/** - * Render one row in the request list. - */ -const RequestListItem = createClass({ - displayName: "RequestListItem", - - propTypes: { - item: PropTypes.object.isRequired, - index: PropTypes.number.isRequired, - isSelected: PropTypes.bool.isRequired, - firstRequestStartedMillis: PropTypes.number.isRequired, - onContextMenu: PropTypes.func.isRequired, - onMouseDown: PropTypes.func.isRequired, - onSecurityIconClick: PropTypes.func.isRequired, - }, - - componentDidMount() { - if (this.props.isSelected) { - this.refs.el.focus(); - } - }, - - shouldComponentUpdate(nextProps) { - return !relevantPropsEqual(this.props.item, nextProps.item) - || this.props.index !== nextProps.index - || this.props.isSelected !== nextProps.isSelected - || this.props.firstRequestStartedMillis !== nextProps.firstRequestStartedMillis; - }, - - componentDidUpdate(prevProps) { - if (!prevProps.isSelected && this.props.isSelected) { - this.refs.el.focus(); - if (this.props.onFocusedNodeChange) { - this.props.onFocusedNodeChange(); - } - } - }, - - componentWillUnmount() { - // If this node is being destroyed and has focus, transfer the focus manually - // to the parent tree component. Otherwise, the focus will get lost and keyboard - // navigation in the tree will stop working. This is a workaround for a XUL bug. - // See bugs 1259228 and 1152441 for details. - // DE-XUL: Remove this hack once all usages are only in HTML documents. - if (this.props.isSelected) { - this.refs.el.blur(); - if (this.props.onFocusedNodeUnmount) { - this.props.onFocusedNodeUnmount(); - } - } - }, - - render() { - const { - item, - index, - isSelected, - firstRequestStartedMillis, - onContextMenu, - onMouseDown, - onSecurityIconClick - } = this.props; - - let classList = [ "request-list-item" ]; - if (isSelected) { - classList.push("selected"); - } - classList.push(index % 2 ? "odd" : "even"); - - return div( - { - ref: "el", - className: classList.join(" "), - "data-id": item.id, - tabIndex: 0, - onContextMenu, - onMouseDown, - }, - StatusColumn(item), - MethodColumn(item), - FileColumn(item), - DomainColumn(item, onSecurityIconClick), - CauseColumn(item), - TypeColumn(item), - TransferredSizeColumn(item), - ContentSizeColumn(item), - WaterfallColumn(item, firstRequestStartedMillis) - ); - } -}); - -/** - * Used by shouldComponentUpdate: compare two items, and compare only properties - * relevant for rendering the RequestListItem. Other properties (like request and - * response headers, cookies, bodies) are ignored. These are very useful for the - * sidebar details, but not here. - */ -const RELEVANT_ITEM_PROPS = [ - "status", - "statusText", - "fromCache", - "fromServiceWorker", - "method", - "url", - "responseContentDataUri", - "remoteAddress", - "securityState", - "cause", - "mimeType", - "contentSize", - "transferredSize", - "startedMillis", - "totalTime", - "eventTimings", -]; - -function relevantPropsEqual(item1, item2) { - return item1 === item2 || RELEVANT_ITEM_PROPS.every(p => item1[p] === item2[p]); -} - -function StatusColumn(item) { - const { status, statusText, fromCache, fromServiceWorker } = item; - - let code, title; - - if (status) { - if (fromCache) { - code = "cached"; - } else if (fromServiceWorker) { - code = "service worker"; - } else { - code = status; - } - - if (statusText) { - title = `${status} ${statusText}`; - if (fromCache) { - title += " (cached)"; - } - if (fromServiceWorker) { - title += " (service worker)"; - } - } - } - - return div({ className: "requests-menu-subitem requests-menu-status", title }, - div({ className: "requests-menu-status-icon", "data-code": code }), - span({ className: "subitem-label requests-menu-status-code" }, status) - ); -} - -function MethodColumn(item) { - const { method } = item; - return div({ className: "requests-menu-subitem requests-menu-method-box" }, - span({ className: "subitem-label requests-menu-method" }, method) - ); -} - -function FileColumn(item) { - const { urlDetails, responseContentDataUri } = item; - - return div({ className: "requests-menu-subitem requests-menu-icon-and-file" }, - img({ - className: "requests-menu-icon", - src: responseContentDataUri, - hidden: !responseContentDataUri, - "data-type": responseContentDataUri ? "thumbnail" : undefined - }), - div( - { - className: "subitem-label requests-menu-file", - title: urlDetails.unicodeUrl - }, - urlDetails.baseNameWithQuery - ) - ); -} - -function DomainColumn(item, onSecurityIconClick) { - const { urlDetails, remoteAddress, securityState } = item; - - let iconClassList = [ "requests-security-state-icon" ]; - let iconTitle; - if (urlDetails.isLocal) { - iconClassList.push("security-state-local"); - iconTitle = L10N.getStr("netmonitor.security.state.secure"); - } else if (securityState) { - iconClassList.push(`security-state-${securityState}`); - iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`); - } - - let title = urlDetails.host + (remoteAddress ? ` (${remoteAddress})` : ""); - - return div( - { className: "requests-menu-subitem requests-menu-security-and-domain" }, - div({ - className: iconClassList.join(" "), - title: iconTitle, - onClick: onSecurityIconClick, - }), - span({ className: "subitem-label requests-menu-domain", title }, urlDetails.host) - ); -} - -function CauseColumn(item) { - const { cause } = item; - - let causeType = ""; - let causeUri = undefined; - let causeHasStack = false; - - if (cause) { - causeType = cause.type; - causeUri = cause.loadingDocumentUri; - causeHasStack = cause.stacktrace && cause.stacktrace.length > 0; - } - - return div( - { className: "requests-menu-subitem requests-menu-cause", title: causeUri }, - span({ className: "requests-menu-cause-stack", hidden: !causeHasStack }, "JS"), - span({ className: "subitem-label" }, causeType) - ); -} - -const CONTENT_MIME_TYPE_ABBREVIATIONS = { - "ecmascript": "js", - "javascript": "js", - "x-javascript": "js" -}; - -function TypeColumn(item) { - const { mimeType } = item; - let abbrevType; - if (mimeType) { - abbrevType = getAbbreviatedMimeType(mimeType); - abbrevType = CONTENT_MIME_TYPE_ABBREVIATIONS[abbrevType] || abbrevType; - } - - return div( - { className: "requests-menu-subitem requests-menu-type", title: mimeType }, - span({ className: "subitem-label" }, abbrevType) - ); -} - -function TransferredSizeColumn(item) { - const { transferredSize, fromCache, fromServiceWorker } = item; - - let text; - let className = "subitem-label"; - if (fromCache) { - text = L10N.getStr("networkMenu.sizeCached"); - className += " theme-comment"; - } else if (fromServiceWorker) { - text = L10N.getStr("networkMenu.sizeServiceWorker"); - className += " theme-comment"; - } else if (typeof transferredSize == "number") { - text = getFormattedSize(transferredSize); - } else if (transferredSize === null) { - text = L10N.getStr("networkMenu.sizeUnavailable"); - } - - return div( - { className: "requests-menu-subitem requests-menu-transferred", title: text }, - span({ className }, text) - ); -} - -function ContentSizeColumn(item) { - const { contentSize } = item; - - let text; - if (typeof contentSize == "number") { - text = getFormattedSize(contentSize); - } - - return div( - { className: "requests-menu-subitem subitem-label requests-menu-size", title: text }, - span({ className: "subitem-label" }, text) - ); -} - -// List of properties of the timing info we want to create boxes for -const TIMING_KEYS = ["blocked", "dns", "connect", "send", "wait", "receive"]; - -function timingBoxes(item) { - const { eventTimings, totalTime, fromCache, fromServiceWorker } = item; - let boxes = []; - - if (fromCache || fromServiceWorker) { - return boxes; - } - - if (eventTimings) { - // Add a set of boxes representing timing information. - for (let key of TIMING_KEYS) { - let width = eventTimings.timings[key]; - - // Don't render anything if it surely won't be visible. - // One millisecond == one unscaled pixel. - if (width > 0) { - boxes.push(div({ - key, - className: "requests-menu-timings-box " + key, - style: { width } - })); - } - } - } - - if (typeof totalTime == "number") { - let text = L10N.getFormatStr("networkMenu.totalMS", totalTime); - boxes.push(div({ - key: "total", - className: "requests-menu-timings-total", - title: text - }, text)); - } - - return boxes; -} - -function WaterfallColumn(item, firstRequestStartedMillis) { - const startedDeltaMillis = item.startedMillis - firstRequestStartedMillis; - const paddingInlineStart = `${startedDeltaMillis}px`; - - return div({ className: "requests-menu-subitem requests-menu-waterfall" }, - div( - { className: "requests-menu-timings", style: { paddingInlineStart } }, - timingBoxes(item) - ) - ); -} - -module.exports = RequestListItem;
deleted file mode 100644 --- a/devtools/client/netmonitor/components/request-list-tooltip.js +++ /dev/null @@ -1,107 +0,0 @@ -/* 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 gNetwork, NetMonitorController */ - -"use strict"; - -const { Task } = require("devtools/shared/task"); -const { formDataURI } = require("../request-utils"); -const { WEBCONSOLE_L10N } = require("../l10n"); -const { setImageTooltip, - getImageDimensions } = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper"); - -// px -const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; -// px -const REQUESTS_TOOLTIP_STACK_TRACE_WIDTH = 600; - -const HTML_NS = "http://www.w3.org/1999/xhtml"; - -const setTooltipImageContent = Task.async(function* (tooltip, itemEl, requestItem) { - let { mimeType, text, encoding } = requestItem.responseContent.content; - - if (!mimeType || !mimeType.includes("image/")) { - return false; - } - - let string = yield gNetwork.getString(text); - let src = formDataURI(mimeType, encoding, string); - let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM; - let { naturalWidth, naturalHeight } = yield getImageDimensions(tooltip.doc, src); - let options = { maxDim, naturalWidth, naturalHeight }; - setImageTooltip(tooltip, tooltip.doc, src, options); - - return itemEl.querySelector(".requests-menu-icon"); -}); - -const setTooltipStackTraceContent = Task.async(function* (tooltip, requestItem) { - let {stacktrace} = requestItem.cause; - - if (!stacktrace || stacktrace.length == 0) { - return false; - } - - let doc = tooltip.doc; - let el = doc.createElementNS(HTML_NS, "div"); - el.className = "stack-trace-tooltip devtools-monospace"; - - for (let f of stacktrace) { - let { functionName, filename, lineNumber, columnNumber, asyncCause } = f; - - if (asyncCause) { - // if there is asyncCause, append a "divider" row into the trace - let asyncFrameEl = doc.createElementNS(HTML_NS, "div"); - asyncFrameEl.className = "stack-frame stack-frame-async"; - asyncFrameEl.textContent = - WEBCONSOLE_L10N.getFormatStr("stacktrace.asyncStack", asyncCause); - el.appendChild(asyncFrameEl); - } - - // Parse a source name in format "url -> url" - let sourceUrl = filename.split(" -> ").pop(); - - let frameEl = doc.createElementNS(HTML_NS, "div"); - frameEl.className = "stack-frame stack-frame-call"; - - let funcEl = doc.createElementNS(HTML_NS, "span"); - funcEl.className = "stack-frame-function-name"; - funcEl.textContent = - functionName || WEBCONSOLE_L10N.getStr("stacktrace.anonymousFunction"); - frameEl.appendChild(funcEl); - - let sourceEl = doc.createElementNS(HTML_NS, "span"); - sourceEl.className = "stack-frame-source-name"; - frameEl.appendChild(sourceEl); - - let sourceInnerEl = doc.createElementNS(HTML_NS, "span"); - sourceInnerEl.className = "stack-frame-source-name-inner"; - sourceEl.appendChild(sourceInnerEl); - - sourceInnerEl.textContent = sourceUrl; - sourceInnerEl.title = sourceUrl; - - let lineEl = doc.createElementNS(HTML_NS, "span"); - lineEl.className = "stack-frame-line"; - lineEl.textContent = `:${lineNumber}:${columnNumber}`; - sourceInnerEl.appendChild(lineEl); - - frameEl.addEventListener("click", () => { - // hide the tooltip immediately, not after delay - tooltip.hide(); - NetMonitorController.viewSourceInDebugger(filename, lineNumber); - }, false); - - el.appendChild(frameEl); - } - - tooltip.setContent(el, {width: REQUESTS_TOOLTIP_STACK_TRACE_WIDTH}); - - return true; -}); - -module.exports = { - setTooltipImageContent, - setTooltipStackTraceContent, -};
deleted file mode 100644 --- a/devtools/client/netmonitor/components/request-list.js +++ /dev/null @@ -1,34 +0,0 @@ -/* 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, PropTypes, DOM } = require("devtools/client/shared/vendor/react"); -const { div } = DOM; -const { connect } = require("devtools/client/shared/vendor/react-redux"); -const RequestListHeader = createFactory(require("./request-list-header")); -const RequestListEmptyNotice = createFactory(require("./request-list-empty")); -const RequestListContent = createFactory(require("./request-list-content")); - -/** - * Renders the request list - header, empty text, the actual content with rows - */ -const RequestList = function ({ isEmpty }) { - return div({ className: "request-list-container" }, - RequestListHeader(), - isEmpty ? RequestListEmptyNotice() : RequestListContent() - ); -}; - -RequestList.displayName = "RequestList"; - -RequestList.propTypes = { - isEmpty: PropTypes.bool.isRequired, -}; - -module.exports = connect( - state => ({ - isEmpty: state.requests.requests.isEmpty() - }) -)(RequestList);
--- a/devtools/client/netmonitor/components/summary-button.js +++ b/devtools/client/netmonitor/components/summary-button.js @@ -9,30 +9,32 @@ const { CONTENT_SIZE_DECIMALS, REQUEST_TIME_DECIMALS, } = require("../constants"); const { DOM, PropTypes } = require("devtools/client/shared/vendor/react"); const { connect } = require("devtools/client/shared/vendor/react-redux"); const { PluralForm } = require("devtools/shared/plural-form"); const { L10N } = require("../l10n"); -const { getDisplayedRequestsSummary } = require("../selectors/index"); +const { getSummary } = require("../selectors/index"); const { button, span } = DOM; function SummaryButton({ summary, triggerSummary, }) { - let { count, bytes, millis } = summary; + let { count, totalBytes, totalMillis } = summary; const text = (count === 0) ? L10N.getStr("networkMenu.empty") : PluralForm.get(count, L10N.getStr("networkMenu.summary")) .replace("#1", count) - .replace("#2", L10N.numberWithDecimals(bytes / 1024, CONTENT_SIZE_DECIMALS)) - .replace("#3", L10N.numberWithDecimals(millis / 1000, REQUEST_TIME_DECIMALS)); + .replace("#2", L10N.numberWithDecimals(totalBytes / 1024, + CONTENT_SIZE_DECIMALS)) + .replace("#3", L10N.numberWithDecimals(totalMillis / 1000, + REQUEST_TIME_DECIMALS)); return button({ id: "requests-menu-network-summary-button", className: "devtools-button", title: count ? text : L10N.getStr("netmonitor.toolbar.perf"), onClick: triggerSummary, }, span({ className: "summary-info-icon" }), @@ -40,16 +42,16 @@ function SummaryButton({ } SummaryButton.propTypes = { summary: PropTypes.object.isRequired, }; module.exports = connect( (state) => ({ - summary: getDisplayedRequestsSummary(state), + summary: getSummary(state), }), (dispatch) => ({ triggerSummary: () => { NetMonitorView.toggleFrontendMode(); }, }) )(SummaryButton);
--- a/devtools/client/netmonitor/components/toggle-button.js +++ b/devtools/client/netmonitor/components/toggle-button.js @@ -1,51 +1,65 @@ /* 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 NetMonitorView */ + "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 { isSidebarToggleButtonDisabled } = require("../selectors/index"); const { button } = DOM; function ToggleButton({ disabled, open, - onToggle, + triggerSidebar, }) { let className = ["devtools-button"]; if (!open) { className.push("pane-collapsed"); } const title = open ? L10N.getStr("collapseDetailsPane") : L10N.getStr("expandDetailsPane"); return button({ id: "details-pane-toggle", className: className.join(" "), title, disabled, tabIndex: "0", - onMouseDown: onToggle, + onMouseDown: triggerSidebar, }); } ToggleButton.propTypes = { disabled: PropTypes.bool.isRequired, - onToggle: PropTypes.func.isRequired, + triggerSidebar: PropTypes.func.isRequired, }; module.exports = connect( (state) => ({ - disabled: isSidebarToggleButtonDisabled(state), - open: state.ui.sidebarOpen, + disabled: state.requests.items.length === 0, + open: state.ui.sidebar.open, }), (dispatch) => ({ - onToggle: () => dispatch(Actions.toggleSidebar()) + triggerSidebar: () => { + dispatch(Actions.toggleSidebar()); + + 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; + } + }, }) )(ToggleButton);
--- a/devtools/client/netmonitor/constants.js +++ b/devtools/client/netmonitor/constants.js @@ -6,45 +6,17 @@ const general = { FREETEXT_FILTER_SEARCH_DELAY: 200, CONTENT_SIZE_DECIMALS: 2, REQUEST_TIME_DECIMALS: 2, }; const actionTypes = { - BATCH_ACTIONS: "BATCH_ACTIONS", - BATCH_ENABLE: "BATCH_ENABLE", - ADD_TIMING_MARKER: "ADD_TIMING_MARKER", - CLEAR_TIMING_MARKERS: "CLEAR_TIMING_MARKERS", - ADD_REQUEST: "ADD_REQUEST", - UPDATE_REQUEST: "UPDATE_REQUEST", - CLEAR_REQUESTS: "CLEAR_REQUESTS", - CLONE_SELECTED_REQUEST: "CLONE_SELECTED_REQUEST", - REMOVE_SELECTED_CUSTOM_REQUEST: "REMOVE_SELECTED_CUSTOM_REQUEST", - SELECT_REQUEST: "SELECT_REQUEST", - PRESELECT_REQUEST: "PRESELECT_REQUEST", - SORT_BY: "SORT_BY", TOGGLE_FILTER_TYPE: "TOGGLE_FILTER_TYPE", ENABLE_FILTER_TYPE_ONLY: "ENABLE_FILTER_TYPE_ONLY", SET_FILTER_TEXT: "SET_FILTER_TEXT", OPEN_SIDEBAR: "OPEN_SIDEBAR", - WATERFALL_RESIZE: "WATERFALL_RESIZE", + TOGGLE_SIDEBAR: "TOGGLE_SIDEBAR", + UPDATE_REQUESTS: "UPDATE_REQUESTS", }; -// Descriptions for what this frontend is currently doing. -const ACTIVITY_TYPE = { - // Standing by and handling requests normally. - NONE: 0, - - // Forcing the target to reload with cache enabled or disabled. - RELOAD: { - WITH_CACHE_ENABLED: 1, - WITH_CACHE_DISABLED: 2, - WITH_CACHE_DEFAULT: 3 - }, - - // Enabling or disabling the cache without triggering a reload. - ENABLE_CACHE: 3, - DISABLE_CACHE: 4 -}; - -module.exports = Object.assign({ ACTIVITY_TYPE }, general, actionTypes); +module.exports = Object.assign({}, general, actionTypes);
--- a/devtools/client/netmonitor/custom-request-view.js +++ b/devtools/client/netmonitor/custom-request-view.js @@ -2,21 +2,22 @@ * 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, dumpn, gNetwork, $, EVENTS, NetMonitorView */ "use strict"; const { Task } = require("devtools/shared/task"); -const { writeHeaderText, - getKeyWithEvent, - getUrlQuery, - parseQueryString } = require("./request-utils"); -const Actions = require("./actions/index"); +const { + writeHeaderText, + getKeyWithEvent, + getUrlQuery, + parseQueryString, +} = require("./request-utils"); /** * Functions handling the custom request view. */ function CustomRequestView() { dumpn("CustomRequestView was instantiated"); } @@ -70,51 +71,47 @@ CustomRequestView.prototype = { /** * Handle user input in the custom request form. * * @param object field * the field that the user updated. */ onUpdate: function (field) { let selectedItem = NetMonitorView.RequestsMenu.selectedItem; - let store = NetMonitorView.RequestsMenu.store; let value; switch (field) { case "method": value = $("#custom-method-value").value.trim(); - store.dispatch(Actions.updateRequest(selectedItem.id, { method: value })); + selectedItem.attachment.method = value; break; case "url": value = $("#custom-url-value").value; this.updateCustomQuery(value); - store.dispatch(Actions.updateRequest(selectedItem.id, { url: value })); + selectedItem.attachment.url = value; break; case "query": let query = $("#custom-query-value").value; this.updateCustomUrl(query); + field = "url"; value = $("#custom-url-value").value; - store.dispatch(Actions.updateRequest(selectedItem.id, { url: value })); + selectedItem.attachment.url = value; break; case "body": value = $("#custom-postdata-value").value; - store.dispatch(Actions.updateRequest(selectedItem.id, { - requestPostData: { - postData: { text: value } - } - })); + selectedItem.attachment.requestPostData = { postData: { text: value } }; break; case "headers": let headersText = $("#custom-headers-value").value; value = parseHeadersText(headersText); - store.dispatch(Actions.updateRequest(selectedItem.id, { - requestHeaders: { headers: value } - })); + selectedItem.attachment.requestHeaders = { headers: value }; break; } + + NetMonitorView.RequestsMenu.updateMenuView(selectedItem, field, value); }, /** * Update the query string field based on the url. * * @param object url * The URL to extract query string from. */ @@ -159,17 +156,17 @@ CustomRequestView.prototype = { function parseHeadersText(text) { return parseRequestText(text, "\\S+?", ":"); } /** * Parse readable text list of a query string. * * @param string text - * Text of query string representation + * Text of query string represetation * @return array * Array of query params {name, value} */ function parseQueryText(text) { return parseRequestText(text, ".+?", "="); } /**
--- a/devtools/client/netmonitor/details-view.js +++ b/devtools/client/netmonitor/details-view.js @@ -1,20 +1,20 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* import-globals-from ./netmonitor-controller.js */ /* eslint-disable mozilla/reject-some-requires */ -/* globals window, dumpn, $, NetMonitorView, gNetwork */ +/* globals dumpn, $, NetMonitorView, gNetwork */ "use strict"; const promise = require("promise"); const EventEmitter = require("devtools/shared/event-emitter"); -const Editor = require("devtools/client/sourceeditor/editor"); const { Heritage } = require("devtools/client/shared/widgets/view-helpers"); const { Task } = require("devtools/shared/task"); const { ToolSidebar } = require("devtools/client/framework/sidebar"); const { VariablesView } = require("resource://devtools/client/shared/widgets/VariablesView.jsm"); const { VariablesViewController } = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm"); const { EVENTS } = require("./events"); const { L10N } = require("./l10n"); const { Filters } = require("./filter-predicates"); @@ -262,16 +262,20 @@ DetailsView.prototype = { if (viewState.dirty[tab]) { // The request information was updated while the task was running. viewState.dirty[tab] = false; view.populate(viewState.latestData); } else { // Tab is selected but not dirty. We're done here. populated[tab] = true; window.emit(EVENTS.TAB_UPDATED); + + if (NetMonitorController.isConnected()) { + NetMonitorView.RequestsMenu.ensureSelectedItemIsVisible(); + } } } else if (viewState.dirty[tab]) { // Tab is dirty but no longer selected. Don't refresh it now, it'll be // done if the tab is shown again. viewState.dirty[tab] = false; } }, e => console.error(e)); }, @@ -319,17 +323,17 @@ DetailsView.prototype = { let code; if (data.fromCache) { code = "cached"; } else if (data.fromServiceWorker) { code = "service worker"; } else { code = data.status; } - $("#headers-summary-status-circle").setAttribute("data-code", code); + $("#headers-summary-status-circle").setAttribute("code", code); $("#headers-summary-status-value").setAttribute("value", data.status + " " + data.statusText); $("#headers-summary-status").removeAttribute("hidden"); } else { $("#headers-summary-status").setAttribute("hidden", "true"); } if (data.httpVersion) {
--- a/devtools/client/netmonitor/har/har-builder.js +++ b/devtools/client/netmonitor/har/har-builder.js @@ -58,17 +58,19 @@ HarBuilder.prototype = { */ build: function () { this.promises = []; // Build basic structure for data. let log = this.buildLog(); // Build entries. - for (let file of this._options.items) { + let items = this._options.items; + for (let i = 0; i < items.length; i++) { + let file = items[i].attachment; log.entries.push(this.buildEntry(log, file)); } // Some data needs to be fetched from the backend during the // build process, so wait till all is done. let { resolve, promise } = defer(); all(this.promises).then(results => resolve({ log: log }));
--- a/devtools/client/netmonitor/har/har-collector.js +++ b/devtools/client/netmonitor/har/har-collector.js @@ -196,17 +196,19 @@ HarCollector.prototype = { method: method, url: url, isXHR: isXHR }; this.files.set(actor, file); // Mimic the Net panel data structure - this.items.push(file); + this.items.push({ + attachment: file + }); }, onNetworkEventUpdate: function (type, packet) { let actor = packet.from; // Skip events from unknown actors (not in the list). // It can happen when there are zombie requests received after // the target is closed or multiple tabs are attached through
--- a/devtools/client/netmonitor/har/test/browser_net_har_throttle_upload.js +++ b/devtools/client/netmonitor/har/test/browser_net_har_throttle_upload.js @@ -11,33 +11,33 @@ add_task(function* () { }); function* throttleUploadTest(actuallyThrottle) { let { tab, monitor } = yield initNetMonitor( HAR_EXAMPLE_URL + "html_har_post-data-test-page.html"); info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")"); - let { NetMonitorController, NetMonitorView } = monitor.panelWin; + let { NetMonitorView } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; const size = 4096; const uploadSize = actuallyThrottle ? size / 3 : 0; const request = { "NetworkMonitor.throttleData": { latencyMean: 0, latencyMax: 0, downloadBPSMean: 200000, downloadBPSMax: 200000, uploadBPSMean: uploadSize, uploadBPSMax: uploadSize, }, }; - let client = NetMonitorController.webConsoleClient; + let client = monitor._controller.webConsoleClient; info("sending throttle request"); let deferred = promise.defer(); client.setPreferences(request, response => { deferred.resolve(response); }); yield deferred.promise;
deleted file mode 100644 --- a/devtools/client/netmonitor/middleware/batching.js +++ /dev/null @@ -1,132 +0,0 @@ -/* 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 { BATCH_ACTIONS, BATCH_ENABLE, BATCH_RESET } = require("../constants"); - -// ms -const REQUESTS_REFRESH_RATE = 50; - -/** - * Middleware that watches for actions with a "batch = true" value in their meta field. - * These actions are queued and dispatched as one batch after a timeout. - * Special actions that are handled by this middleware: - * - BATCH_ENABLE can be used to enable and disable the batching. - * - BATCH_RESET discards the actions that are currently in the queue. - */ -function batchingMiddleware(store) { - return next => { - let queuedActions = []; - let enabled = true; - let flushTask = null; - - return action => { - if (action.type === BATCH_ENABLE) { - return setEnabled(action.enabled); - } - - if (action.type === BATCH_RESET) { - return resetQueue(); - } - - if (action.meta && action.meta.batch) { - if (!enabled) { - next(action); - return Promise.resolve(); - } - - queuedActions.push(action); - - if (!flushTask) { - flushTask = new DelayedTask(flushActions, REQUESTS_REFRESH_RATE); - } - - return flushTask.promise; - } - - return next(action); - }; - - function setEnabled(value) { - enabled = value; - - // If disabling the batching, flush the actions that have been queued so far - if (!enabled && flushTask) { - flushTask.runNow(); - } - } - - function resetQueue() { - queuedActions = []; - - if (flushTask) { - flushTask.cancel(); - flushTask = null; - } - } - - function flushActions() { - const actions = queuedActions; - queuedActions = []; - - next({ - type: BATCH_ACTIONS, - actions, - }); - - flushTask = null; - } - }; -} - -/** - * Create a delayed task that calls the specified task function after a delay. - */ -function DelayedTask(taskFn, delay) { - this._promise = new Promise((resolve, reject) => { - this.runTask = (cancel) => { - if (cancel) { - reject("Task cancelled"); - } else { - taskFn(); - resolve(); - } - this.runTask = null; - }; - this.timeout = setTimeout(this.runTask, delay); - }).catch(console.error); -} - -DelayedTask.prototype = { - /** - * Return a promise that is resolved after the task is performed or canceled. - */ - get promise() { - return this._promise; - }, - - /** - * Cancel the execution of the task. - */ - cancel() { - clearTimeout(this.timeout); - if (this.runTask) { - this.runTask(true); - } - }, - - /** - * Execute the scheduled task immediately, without waiting for the timeout. - * Resolves the promise correctly. - */ - runNow() { - clearTimeout(this.timeout); - if (this.runTask) { - this.runTask(); - } - } -}; - -module.exports = batchingMiddleware;
deleted file mode 100644 --- a/devtools/client/netmonitor/middleware/moz.build +++ /dev/null @@ -1,7 +0,0 @@ -# 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( - 'batching.js', -)
--- a/devtools/client/netmonitor/moz.build +++ b/devtools/client/netmonitor/moz.build @@ -1,37 +1,32 @@ # 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', - 'middleware', 'reducers', - 'selectors', - 'utils', + 'selectors' ] DevToolsModules( 'constants.js', 'custom-request-view.js', 'details-view.js', 'events.js', 'filter-predicates.js', 'l10n.js', - 'netmonitor-controller.js', - 'netmonitor-view.js', 'panel.js', 'performance-statistics-view.js', 'prefs.js', 'request-list-context-menu.js', 'request-utils.js', 'requests-menu-view.js', 'sidebar-view.js', 'sort-predicates.js', 'store.js', 'toolbar-view.js', - 'waterfall-background.js', ) BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/devtools/client/netmonitor/netmonitor-controller.js +++ b/devtools/client/netmonitor/netmonitor-controller.js @@ -1,40 +1,65 @@ /* 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/. */ /* eslint-disable mozilla/reject-some-requires */ -/* globals window, NetMonitorView, gStore, dumpn */ +/* 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. + NONE: 0, + + // Forcing the target to reload with cache enabled or disabled. + RELOAD: { + WITH_CACHE_ENABLED: 1, + WITH_CACHE_DISABLED: 2, + WITH_CACHE_DEFAULT: 3 + }, + + // Enabling or disabling the cache without triggering a reload. + ENABLE_CACHE: 3, + DISABLE_CACHE: 4 +}; + +var BrowserLoaderModule = {}; +Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule); +var { loader, require } = BrowserLoaderModule.BrowserLoader({ + baseURI: "resource://devtools/client/netmonitor/", + window +}); + const promise = require("promise"); const Services = require("Services"); const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm"); const EventEmitter = require("devtools/shared/event-emitter"); const Editor = require("devtools/client/sourceeditor/editor"); const {TimelineFront} = require("devtools/shared/fronts/timeline"); const {Task} = require("devtools/shared/task"); -const { ACTIVITY_TYPE } = require("./constants"); -const { EVENTS } = require("./events"); -const { configureStore } = require("./store"); +const {Prefs} = require("./prefs"); +const {EVENTS} = require("./events"); const Actions = require("./actions/index"); -const { getDisplayedRequestById } = require("./selectors/index"); -const { Prefs } = require("./prefs"); -XPCOMUtils.defineConstant(window, "EVENTS", EVENTS); -XPCOMUtils.defineConstant(window, "ACTIVITY_TYPE", ACTIVITY_TYPE); -XPCOMUtils.defineConstant(window, "Editor", Editor); -XPCOMUtils.defineConstant(window, "Prefs", Prefs); -XPCOMUtils.defineLazyModuleGetter(window, "Chart", +XPCOMUtils.defineConstant(this, "EVENTS", EVENTS); +XPCOMUtils.defineConstant(this, "ACTIVITY_TYPE", ACTIVITY_TYPE); +XPCOMUtils.defineConstant(this, "Editor", Editor); +XPCOMUtils.defineConstant(this, "Prefs", Prefs); + +XPCOMUtils.defineLazyModuleGetter(this, "Chart", "resource://devtools/client/shared/widgets/Chart.jsm"); -// Initialize the global Redux store -window.gStore = configureStore(); +XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper", + "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); /** * Object defining the network monitor controller components. */ var NetMonitorController = { /** * Initializes the view and connects the monitor client. * @@ -61,17 +86,16 @@ var NetMonitorController = { * A promise that is resolved when the monitor finishes shutdown. */ shutdownNetMonitor: Task.async(function* () { if (this._shutdown) { return this._shutdown.promise; } this._shutdown = promise.defer(); { - gStore.dispatch(Actions.batchReset()); NetMonitorView.destroy(); this.TargetEventsHandler.disconnect(); this.NetworkEventsHandler.disconnect(); yield this.disconnect(); } this._shutdown.resolve(); return undefined; }), @@ -258,28 +282,29 @@ var NetMonitorController = { * A promise resolved once the task finishes. */ inspectRequest: function (requestId) { // Look for the request in the existing ones or wait for it to appear, if // the network monitor is still loading. let deferred = promise.defer(); let request = null; let inspector = function () { - request = getDisplayedRequestById(gStore.getState(), requestId); + let predicate = i => i.value === requestId; + request = NetMonitorView.RequestsMenu.getItemForPredicate(predicate); if (!request) { // Reset filters so that the request is visible. gStore.dispatch(Actions.toggleFilterType("all")); - request = getDisplayedRequestById(gStore.getState(), requestId); + 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); - gStore.dispatch(Actions.selectRequest(request.id)); + NetMonitorView.RequestsMenu.selectedItem = request; deferred.resolve(); } }; inspector(); if (!request) { window.on(EVENTS.REQUEST_ADDED, inspector); } @@ -368,24 +393,24 @@ TargetEventsHandler.prototype = { * Packet received from the server. */ _onTabNavigated: function (type, packet) { switch (type) { case "will-navigate": { // Reset UI. if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) { NetMonitorView.RequestsMenu.reset(); - } else { - // If the log is persistent, just clear all accumulated timing markers. - gStore.dispatch(Actions.clearTimingMarkers()); + NetMonitorView.Sidebar.toggle(false); } // Switch to the default network traffic inspector view. if (NetMonitorController.getCurrentActivity() == ACTIVITY_TYPE.NONE) { NetMonitorView.showNetworkInspectorView(); } + // Clear any accumulated markers. + NetMonitorController.NetworkEventsHandler.clearMarkers(); window.emit(EVENTS.TARGET_WILL_NAVIGATE); break; } case "navigate": { window.emit(EVENTS.TARGET_DID_NAVIGATE); break; } @@ -399,16 +424,18 @@ TargetEventsHandler.prototype = { NetMonitorController.shutdownNetMonitor(); } }; /** * Functions handling target network events. */ function NetworkEventsHandler() { + this._markers = []; + this._onNetworkEvent = this._onNetworkEvent.bind(this); this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this); this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this); this._onRequestHeaders = this._onRequestHeaders.bind(this); this._onRequestCookies = this._onRequestCookies.bind(this); this._onRequestPostData = this._onRequestPostData.bind(this); this._onResponseHeaders = this._onResponseHeaders.bind(this); this._onResponseCookies = this._onResponseCookies.bind(this); @@ -424,16 +451,29 @@ NetworkEventsHandler.prototype = { get webConsoleClient() { return NetMonitorController.webConsoleClient; }, get timelineFront() { return NetMonitorController.timelineFront; }, + get firstDocumentDOMContentLoadedTimestamp() { + let marker = this._markers.filter(e => { + return e.name == "document::DOMContentLoaded"; + })[0]; + + return marker ? marker.unixTime / 1000 : -1; + }, + + get firstDocumentLoadTimestamp() { + let marker = this._markers.filter(e => e.name == "document::Load")[0]; + return marker ? marker.unixTime / 1000 : -1; + }, + /** * Connect to the current target client. */ connect: function () { dumpn("NetworkEventsHandler is connecting..."); this.webConsoleClient.on("networkEvent", this._onNetworkEvent); this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate); @@ -480,17 +520,17 @@ NetworkEventsHandler.prototype = { }, /** * The "DOMContentLoaded" and "Load" events sent by the timeline actor. * @param object marker */ _onDocLoadingMarker: function (marker) { window.emit(EVENTS.TIMELINE_EVENT, marker); - gStore.dispatch(Actions.addTimingMarker(marker)); + this._markers.push(marker); }, /** * The "networkEvent" message type handler. * * @param string type * Message type. * @param object networkInfo @@ -502,17 +542,18 @@ NetworkEventsHandler.prototype = { request: { method, url }, isXHR, cause, fromCache, fromServiceWorker } = networkInfo; NetMonitorView.RequestsMenu.addRequest( - actor, {startedDateTime, method, url, isXHR, cause, fromCache, fromServiceWorker} + actor, startedDateTime, method, url, isXHR, cause, fromCache, + fromServiceWorker ); window.emit(EVENTS.NETWORK_EVENT, actor); }, /** * The "networkEventUpdate" message type handler. * * @param string type @@ -591,139 +632,155 @@ NetworkEventsHandler.prototype = { * Handles additional information received for a "requestHeaders" packet. * * @param object response * The message received from the server. */ _onRequestHeaders: function (response) { NetMonitorView.RequestsMenu.updateRequest(response.from, { requestHeaders: response - }).then(() => { + }, () => { window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from); }); }, /** * Handles additional information received for a "requestCookies" packet. * * @param object response * The message received from the server. */ _onRequestCookies: function (response) { NetMonitorView.RequestsMenu.updateRequest(response.from, { requestCookies: response - }).then(() => { + }, () => { window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from); }); }, /** * Handles additional information received for a "requestPostData" packet. * * @param object response * The message received from the server. */ _onRequestPostData: function (response) { NetMonitorView.RequestsMenu.updateRequest(response.from, { requestPostData: response - }).then(() => { + }, () => { window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from); }); }, /** * Handles additional information received for a "securityInfo" packet. * * @param object response * The message received from the server. */ _onSecurityInfo: function (response) { NetMonitorView.RequestsMenu.updateRequest(response.from, { securityInfo: response.securityInfo - }).then(() => { + }, () => { window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from); }); }, /** * Handles additional information received for a "responseHeaders" packet. * * @param object response * The message received from the server. */ _onResponseHeaders: function (response) { NetMonitorView.RequestsMenu.updateRequest(response.from, { responseHeaders: response - }).then(() => { + }, () => { window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from); }); }, /** * Handles additional information received for a "responseCookies" packet. * * @param object response * The message received from the server. */ _onResponseCookies: function (response) { NetMonitorView.RequestsMenu.updateRequest(response.from, { responseCookies: response - }).then(() => { + }, () => { window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from); }); }, /** * Handles additional information received for a "responseContent" packet. * * @param object response * The message received from the server. */ _onResponseContent: function (response) { NetMonitorView.RequestsMenu.updateRequest(response.from, { responseContent: response - }).then(() => { + }, () => { window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from); }); }, /** * Handles additional information received for a "eventTimings" packet. * * @param object response * The message received from the server. */ _onEventTimings: function (response) { NetMonitorView.RequestsMenu.updateRequest(response.from, { eventTimings: response - }).then(() => { + }, () => { window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from); }); }, /** + * Clears all accumulated markers. + */ + clearMarkers: function () { + this._markers.length = 0; + }, + + /** * Fetches the full text of a LongString. * * @param object | string stringGrip * The long string grip containing the corresponding actor. * If you pass in a plain string (by accident or because you're lazy), * then a promise of the same string is simply returned. * @return object Promise * A promise that is resolved when the full string contents * are available, or rejected if something goes wrong. */ getString: function (stringGrip) { return this.webConsoleClient.getString(stringGrip); } }; /** + * Returns true if this is document is in RTL mode. + * @return boolean + */ +XPCOMUtils.defineLazyGetter(window, "isRTL", function () { + return window.getComputedStyle(document.documentElement, null) + .direction == "rtl"; +}); + +/** * Convenient way of emitting events from the panel window. */ -EventEmitter.decorate(window); +EventEmitter.decorate(this); /** * Preliminary setup for the NetMonitorController object. */ NetMonitorController.TargetEventsHandler = new TargetEventsHandler(); NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler(); /** @@ -733,9 +790,19 @@ Object.defineProperties(window, { "gNetwork": { get: function () { return NetMonitorController.NetworkEventsHandler; }, configurable: true } }); -exports.NetMonitorController = NetMonitorController; +/** + * Helper method for debugging. + * @param string + */ +function dumpn(str) { + if (wantLogging) { + dump("NET-FRONTEND: " + str + "\n"); + } +} + +var wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
--- a/devtools/client/netmonitor/netmonitor-view.js +++ b/devtools/client/netmonitor/netmonitor-view.js @@ -1,31 +1,31 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* import-globals-from ./netmonitor-controller.js */ /* eslint-disable mozilla/reject-some-requires */ -/* globals $, gStore, NetMonitorController, dumpn */ +/* globals Prefs, setInterval, setTimeout, clearInterval, clearTimeout, btoa */ +/* exported $, $all */ "use strict"; const { testing: isTesting } = require("devtools/shared/flags"); -const promise = require("promise"); -const Editor = require("devtools/client/sourceeditor/editor"); -const { Task } = require("devtools/shared/task"); const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers"); +const { configureStore } = require("./store"); const { RequestsMenuView } = require("./requests-menu-view"); const { CustomRequestView } = require("./custom-request-view"); const { ToolbarView } = require("./toolbar-view"); const { SidebarView } = require("./sidebar-view"); const { DetailsView } = require("./details-view"); const { PerformanceStatisticsView } = require("./performance-statistics-view"); -const { ACTIVITY_TYPE } = require("./constants"); -const Actions = require("./actions/index"); -const { Prefs } = require("./prefs"); + +// 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. @@ -75,16 +75,22 @@ var NetMonitorView = { dumpn("Initializing the NetMonitorView panes"); this._body = $("#body"); this._detailsPane = $("#details-pane"); this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth); this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight); this.toggleDetailsPane({ visible: false }); + + // Disable the performance statistics mode. + if (!Prefs.statistics) { + $("#request-menu-context-perf").hidden = true; + $("#notice-perf-message").hidden = true; + } }, /** * Destroys the UI for all the displayed panes. */ _destroyPanes: Task.async(function* () { dumpn("Destroying the NetMonitorView panes"); @@ -158,16 +164,17 @@ var NetMonitorView = { } }, /** * Switches to the "Inspector" frontend view mode. */ showNetworkInspectorView: function () { this._body.selectedPanel = $("#network-inspector-view"); + this.RequestsMenu._flushWaterfallViews(true); }, /** * Switches to the "Statistics" frontend view mode. */ showNetworkStatisticsView: function () { this._body.selectedPanel = $("#network-statistics-view"); @@ -180,27 +187,26 @@ var NetMonitorView = { yield controller.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED); try { // • The response headers and status code are required for determining // whether a response is "fresh" (cacheable). // • The response content size and request total time are necessary for // populating the statistics view. // • The response mime type is used for categorization. - yield whenDataAvailable(requestsView.store, [ + yield whenDataAvailable(requestsView, [ "responseHeaders", "status", "contentSize", "mimeType", "totalTime" ]); } catch (ex) { // Timed out while waiting for data. Continue with what we have. console.error(ex); } - const requests = requestsView.store.getState().requests.requests; - statisticsView.createPrimedCacheChart(requests); - statisticsView.createEmptyCacheChart(requests); + statisticsView.createPrimedCacheChart(requestsView.items); + statisticsView.createEmptyCacheChart(requestsView.items); }); }, reloadPage: function () { NetMonitorController.triggerActivity( ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT); }, @@ -231,53 +237,56 @@ var NetMonitorView = { }, _body: null, _detailsPane: null, _editorPromises: new Map() }; /** + * DOM query helper. + * TODO: Move it into "dom-utils.js" module and "require" it when needed. + */ +var $ = (selector, target = document) => target.querySelector(selector); +var $all = (selector, target = document) => target.querySelectorAll(selector); + +/** * Makes sure certain properties are available on all objects in a data store. * - * @param Store dataStore - * A Redux store for which to check the availability of properties. + * @param array dataStore + * The request view object from which to fetch the item list. * @param array mandatoryFields * A list of strings representing properties of objects in dataStore. * @return object * A promise resolved when all objects in dataStore contain the * properties defined in mandatoryFields. */ -function whenDataAvailable(dataStore, mandatoryFields) { - return new Promise((resolve, reject) => { - let interval = setInterval(() => { - const { requests } = dataStore.getState().requests; - const allFieldsPresent = !requests.isEmpty() && requests.every( - item => mandatoryFields.every( - field => item.get(field) !== undefined - ) - ); +function whenDataAvailable(requestsView, mandatoryFields) { + let deferred = promise.defer(); - if (allFieldsPresent) { - clearInterval(interval); - clearTimeout(timer); - resolve(); - } - }, WDA_DEFAULT_VERIFY_INTERVAL); + let interval = setInterval(() => { + const { attachments } = requestsView; + if (attachments.length > 0 && attachments.every(item => { + return mandatoryFields.every(field => field in item); + })) { + clearInterval(interval); + clearTimeout(timer); + deferred.resolve(); + } + }, WDA_DEFAULT_VERIFY_INTERVAL); - let timer = setTimeout(() => { - clearInterval(interval); - reject(new Error("Timed out while waiting for data")); - }, WDA_DEFAULT_GIVE_UP_TIMEOUT); - }); + let timer = setTimeout(() => { + clearInterval(interval); + deferred.reject(new Error("Timed out while waiting for data")); + }, WDA_DEFAULT_GIVE_UP_TIMEOUT); + + return deferred.promise; } /** * Preliminary setup for the NetMonitorView object. */ NetMonitorView.Toolbar = new ToolbarView(); NetMonitorView.Sidebar = new SidebarView(); NetMonitorView.NetworkDetails = new DetailsView(); NetMonitorView.RequestsMenu = new RequestsMenuView(); NetMonitorView.CustomRequest = new CustomRequestView(); NetMonitorView.PerformanceStatistics = new PerformanceStatisticsView(); - -exports.NetMonitorView = NetMonitorView;
deleted file mode 100644 --- a/devtools/client/netmonitor/netmonitor.js +++ /dev/null @@ -1,60 +0,0 @@ -/* 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, NetMonitorController, NetMonitorView */ -/* exported Netmonitor, NetMonitorController, NetMonitorView, $, $all, dumpn */ - -"use strict"; - -const Cu = Components.utils; -const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); -const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {}); - -function Netmonitor(toolbox) { - const { require } = BrowserLoader({ - baseURI: "resource://devtools/client/netmonitor/", - window, - commonLibRequire: toolbox.browserRequire, - }); - - window.windowRequire = require; - - const { NetMonitorController } = require("./netmonitor-controller.js"); - const { NetMonitorView } = require("./netmonitor-view.js"); - - window.NetMonitorController = NetMonitorController; - window.NetMonitorView = NetMonitorView; - - NetMonitorController._toolbox = toolbox; - NetMonitorController._target = toolbox.target; -} - -Netmonitor.prototype = { - init() { - return window.NetMonitorController.startupNetMonitor(); - }, - - destroy() { - return window.NetMonitorController.shutdownNetMonitor(); - } -}; - -/** - * DOM query helper. - * TODO: Move it into "dom-utils.js" module and "require" it when needed. - */ -var $ = (selector, target = document) => target.querySelector(selector); -var $all = (selector, target = document) => target.querySelectorAll(selector); - -/** - * Helper method for debugging. - * @param string - */ -function dumpn(str) { - if (wantLogging) { - dump("NET-FRONTEND: " + str + "\n"); - } -} - -var wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
--- a/devtools/client/netmonitor/netmonitor.xul +++ b/devtools/client/netmonitor/netmonitor.xul @@ -7,33 +7,212 @@ <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?> <?xml-stylesheet href="chrome://devtools/skin/netmonitor.css" type="text/css"?> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"> <script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/> - <script type="text/javascript" src="netmonitor.js"/> + <script type="text/javascript" src="netmonitor-controller.js"/> + <script type="text/javascript" src="netmonitor-view.js"/> <deck id="body" class="theme-sidebar" flex="1" data-localization-bundle="devtools/client/locales/netmonitor.properties"> <vbox id="network-inspector-view" flex="1"> <html:div xmlns="http://www.w3.org/1999/xhtml" id="react-toolbar-hook"/> <hbox id="network-table-and-sidebar" class="devtools-responsive-container" flex="1"> - <html:div xmlns="http://www.w3.org/1999/xhtml" - id="network-table" - class="devtools-main-content"> - </html:div> + <vbox id="network-table" flex="1" class="devtools-main-content"> + <toolbar id="requests-menu-toolbar" + class="devtools-toolbar" + align="center"> + <hbox id="toolbar-labels" flex="1"> + <hbox id="requests-menu-status-header-box" + class="requests-menu-header requests-menu-status" + align="center"> + <button id="requests-menu-status-button" + class="requests-menu-header-button requests-menu-status" + data-key="status" + data-localization="label=netmonitor.toolbar.status3" + flex="1"> + </button> + </hbox> + <hbox id="requests-menu-method-header-box" + class="requests-menu-header requests-menu-method" + align="center"> + <button id="requests-menu-method-button" + class="requests-menu-header-button requests-menu-method" + data-key="method" + data-localization="label=netmonitor.toolbar.method" + crop="end" + flex="1"> + </button> + </hbox> + <hbox id="requests-menu-icon-and-file-header-box" + class="requests-menu-header requests-menu-icon-and-file" + align="center"> + <button id="requests-menu-file-button" + class="requests-menu-header-button requests-menu-file" + data-key="file" + data-localization="label=netmonitor.toolbar.file" + crop="end" + flex="1"> + </button> + </hbox> + <hbox id="requests-menu-domain-header-box" + class="requests-menu-header requests-menu-security-and-domain" + align="center"> + <button id="requests-menu-domain-button" + class="requests-menu-header-button requests-menu-security-and-domain" + data-key="domain" + data-localization="label=netmonitor.toolbar.domain" + crop="end" + flex="1"> + </button> + </hbox> + <hbox id="requests-menu-cause-header-box" + class="requests-menu-header requests-menu-cause" + align="center"> + <button id="requests-menu-cause-button" + class="requests-menu-header-button requests-menu-cause" + data-key="cause" + data-localization="label=netmonitor.toolbar.cause" + crop="end" + flex="1"> + </button> + </hbox> + <hbox id="requests-menu-type-header-box" + class="requests-menu-header requests-menu-type" + align="center"> + <button id="requests-menu-type-button" + class="requests-menu-header-button requests-menu-type" + data-key="type" + data-localization="label=netmonitor.toolbar.type" + crop="end" + flex="1"> + </button> + </hbox> + <hbox id="requests-menu-transferred-header-box" + class="requests-menu-header requests-menu-transferred" + align="center"> + <button id="requests-menu-transferred-button" + class="requests-menu-header-button requests-menu-transferred" + data-key="transferred" + data-localization="label=netmonitor.toolbar.transferred" + crop="end" + flex="1"> + </button> + </hbox> + <hbox id="requests-menu-size-header-box" + class="requests-menu-header requests-menu-size" + align="center"> + <button id="requests-menu-size-button" + class="requests-menu-header-button requests-menu-size" + data-key="size" + data-localization="label=netmonitor.toolbar.size" + crop="end" + flex="1"> + </button> + </hbox> + <hbox id="requests-menu-waterfall-header-box" + class="requests-menu-header requests-menu-waterfall" + align="center" + flex="1"> + <button id="requests-menu-waterfall-button" + class="requests-menu-header-button requests-menu-waterfall" + data-key="waterfall" + pack="start" + data-localization="label=netmonitor.toolbar.waterfall" + flex="1"> + <image id="requests-menu-waterfall-image"/> + <box id="requests-menu-waterfall-label-wrapper"> + <label id="requests-menu-waterfall-label" + class="plain requests-menu-waterfall" + data-localization="value=netmonitor.toolbar.waterfall"/> + </box> + </button> + </hbox> + </hbox> + </toolbar> + + <vbox id="requests-menu-empty-notice" + class="side-menu-widget-empty-text"> + <hbox id="notice-reload-message" align="center"> + <label data-localization="content=netmonitor.reloadNotice1"/> + <button id="requests-menu-reload-notice-button" + class="devtools-toolbarbutton" + standalone="true" + data-localization="label=netmonitor.reloadNotice2"/> + <label data-localization="content=netmonitor.reloadNotice3"/> + </hbox> + <hbox id="notice-perf-message" align="center"> + <label data-localization="content=netmonitor.perfNotice1"/> + <button id="requests-menu-perf-notice-button" + class="devtools-toolbarbutton" + standalone="true" + data-localization="tooltiptext=netmonitor.perfNotice3"/> + <label data-localization="content=netmonitor.perfNotice2"/> + </hbox> + </vbox> + + <vbox id="requests-menu-contents" flex="1"> + <hbox id="requests-menu-item-template" hidden="true"> + <hbox class="requests-menu-subitem requests-menu-status" + align="center"> + <box class="requests-menu-status-icon"/> + <label class="plain requests-menu-status-code" + crop="end"/> + </hbox> + <hbox class="requests-menu-subitem requests-menu-method-box" + align="center"> + <label class="plain requests-menu-method" + crop="end" + flex="1"/> + </hbox> + <hbox class="requests-menu-subitem requests-menu-icon-and-file" + align="center"> + <image class="requests-menu-icon" hidden="true"/> + <label class="plain requests-menu-file" + crop="end" + flex="1"/> + </hbox> + <hbox class="requests-menu-subitem requests-menu-security-and-domain" + align="center"> + <image class="requests-security-state-icon" /> + <label class="plain requests-menu-domain" + crop="end" + flex="1"/> + </hbox> + <hbox class="requests-menu-subitem requests-menu-cause" align="center"> + <label class="requests-menu-cause-stack" value="JS" hidden="true"/> + <label class="plain requests-menu-cause-label" flex="1" crop="end"/> + </hbox> + <label class="plain requests-menu-subitem requests-menu-type" + crop="end"/> + <label class="plain requests-menu-subitem requests-menu-transferred" + crop="end"/> + <label class="plain requests-menu-subitem requests-menu-size" + crop="end"/> + <hbox class="requests-menu-subitem requests-menu-waterfall" + align="center" + flex="1"> + <hbox class="requests-menu-timings" + align="center"> + <label class="plain requests-menu-timings-total"/> + </hbox> + </hbox> + </hbox> + </vbox> + </vbox> <splitter id="network-inspector-view-splitter" class="devtools-side-splitter"/> <deck id="details-pane" hidden="true"> <vbox id="custom-pane" class="tabpanel-content">
--- a/devtools/client/netmonitor/panel.js +++ b/devtools/client/netmonitor/panel.js @@ -9,17 +9,20 @@ const EventEmitter = require("devtools/s const { Task } = require("devtools/shared/task"); const { localizeMarkup } = require("devtools/shared/l10n"); function NetMonitorPanel(iframeWindow, toolbox) { this.panelWin = iframeWindow; this.panelDoc = iframeWindow.document; this._toolbox = toolbox; - this._netmonitor = new iframeWindow.Netmonitor(toolbox); + this._view = this.panelWin.NetMonitorView; + this._controller = this.panelWin.NetMonitorController; + this._controller._target = this.target; + this._controller._toolbox = this._toolbox; EventEmitter.decorate(this); } exports.NetMonitorPanel = NetMonitorPanel; NetMonitorPanel.prototype = { /** @@ -38,18 +41,17 @@ NetMonitorPanel.prototype = { let deferred = promise.defer(); this._opening = deferred.promise; // Local monitoring needs to make the target remote. if (!this.target.isRemote) { yield this.target.makeRemote(); } - yield this._netmonitor.init(); - + yield this._controller.startupNetMonitor(); this.isReady = true; this.emit("ready"); deferred.resolve(this); return this._opening; }), // DevToolPanel API @@ -60,15 +62,15 @@ NetMonitorPanel.prototype = { destroy: Task.async(function* () { if (this._destroying) { return this._destroying; } let deferred = promise.defer(); this._destroying = deferred.promise; - yield this._netmonitor.destroy(); + yield this._controller.shutdownNetMonitor(); this.emit("destroyed"); deferred.resolve(); return this._destroying; }) };
--- a/devtools/client/netmonitor/performance-statistics-view.js +++ b/devtools/client/netmonitor/performance-statistics-view.js @@ -1,27 +1,22 @@ /* 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/. */ -/* eslint-disable mozilla/reject-some-requires */ -/* globals $, window, document, NetMonitorView */ +/* import-globals-from ./netmonitor-controller.js */ +/* globals $ */ "use strict"; -const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm"); const {PluralForm} = require("devtools/shared/plural-form"); const {Filters} = require("./filter-predicates"); const {L10N} = require("./l10n"); -const {EVENTS} = require("./events"); const Actions = require("./actions/index"); -XPCOMUtils.defineLazyModuleGetter(this, "Chart", - "resource://devtools/client/shared/widgets/Chart.jsm"); - const REQUEST_TIME_DECIMALS = 2; const CONTENT_SIZE_DECIMALS = 2; // px const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200; /** * Functions handling the performance statistics view. @@ -172,17 +167,17 @@ PerformanceStatisticsView.prototype = { cached: 0, count: 0, label: e, size: 0, time: 0 })); for (let requestItem of items) { - let details = requestItem; + let details = requestItem.attachment; let type; if (Filters.html(details)) { // "html" type = 0; } else if (Filters.css(details)) { // "css" type = 1; @@ -237,18 +232,21 @@ PerformanceStatisticsView.prototype = { */ function responseIsFresh({ responseHeaders, status }) { // Check for a "304 Not Modified" status and response headers availability. if (status != 304 || !responseHeaders) { return false; } let list = responseHeaders.headers; - let cacheControl = list.find(e => e.name.toLowerCase() == "cache-control"); - let expires = list.find(e => e.name.toLowerCase() == "expires"); + let cacheControl = list.filter(e => { + return e.name.toLowerCase() == "cache-control"; + })[0]; + + let expires = list.filter(e => e.name.toLowerCase() == "expires")[0]; // Check the "Cache-Control" header for a maximum age value. if (cacheControl) { let maxAgeMatch = cacheControl.value.match(/s-maxage\s*=\s*(\d+)/) || cacheControl.value.match(/max-age\s*=\s*(\d+)/); if (maxAgeMatch && maxAgeMatch.pop() > 0) {
--- a/devtools/client/netmonitor/prefs.js +++ b/devtools/client/netmonitor/prefs.js @@ -8,10 +8,11 @@ const {PrefsHelper} = require("devtools/ /** * Shortcuts for accessing various network monitor preferences. */ exports.Prefs = new PrefsHelper("devtools.netmonitor", { networkDetailsWidth: ["Int", "panes-network-details-width"], networkDetailsHeight: ["Int", "panes-network-details-height"], + statistics: ["Bool", "statistics"], filters: ["Json", "filters"] });
deleted file mode 100644 --- a/devtools/client/netmonitor/reducers/batching.js +++ /dev/null @@ -1,25 +0,0 @@ -/* 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 { BATCH_ACTIONS } = require("../constants"); - -/** - * A reducer to handle batched actions. For each action in the BATCH_ACTIONS array, - * the reducer is called successively on the array of batched actions, resulting in - * only one state update. - */ -function batchingReducer(nextReducer) { - return function reducer(state, action) { - switch (action.type) { - case BATCH_ACTIONS: - return action.actions.reduce(reducer, state); - default: - return nextReducer(state, action); - } - }; -} - -module.exports = batchingReducer;
--- a/devtools/client/netmonitor/reducers/filters.js +++ b/devtools/client/netmonitor/reducers/filters.js @@ -22,17 +22,17 @@ const FilterTypes = I.Record({ media: false, flash: false, ws: false, other: false, }); const Filters = I.Record({ types: new FilterTypes({ all: true }), - text: "", + url: "", }); function toggleFilterType(state, action) { let { filter } = action; let newState; // Ignore unknown filter type if (!state.has(filter)) { @@ -67,15 +67,15 @@ function enableFilterTypeOnly(state, act function filters(state = new Filters(), action) { switch (action.type) { 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("text", action.text); + return state.set("url", action.url); default: return state; } } module.exports = filters;
--- a/devtools/client/netmonitor/reducers/index.js +++ b/devtools/client/netmonitor/reducers/index.js @@ -1,23 +1,16 @@ /* 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 batchingReducer = require("./batching"); +const filters = require("./filters"); const requests = require("./requests"); -const sort = require("./sort"); -const filters = require("./filters"); -const timingMarkers = require("./timing-markers"); const ui = require("./ui"); -module.exports = batchingReducer( - combineReducers({ - requests, - sort, - filters, - timingMarkers, - ui, - }) -); +module.exports = combineReducers({ + filters, + requests, + ui, +});
--- a/devtools/client/netmonitor/reducers/moz.build +++ b/devtools/client/netmonitor/reducers/moz.build @@ -1,13 +1,10 @@ # 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( - 'batching.js', 'filters.js', 'index.js', 'requests.js', - 'sort.js', - 'timing-markers.js', 'ui.js', )
--- a/devtools/client/netmonitor/reducers/requests.js +++ b/devtools/client/netmonitor/reducers/requests.js @@ -1,246 +1,29 @@ /* 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 { getUrlDetails } = require("../request-utils"); const { - ADD_REQUEST, - UPDATE_REQUEST, - CLEAR_REQUESTS, - SELECT_REQUEST, - PRESELECT_REQUEST, - CLONE_SELECTED_REQUEST, - REMOVE_SELECTED_CUSTOM_REQUEST, - OPEN_SIDEBAR + UPDATE_REQUESTS, } = require("../constants"); -const Request = I.Record({ - id: null, - // Set to true in case of a request that's being edited as part of "edit and resend" - isCustom: false, - // Request properties - at the beginning, they are unknown and are gradually filled in - startedMillis: undefined, - method: undefined, - url: undefined, - urlDetails: undefined, - remotePort: undefined, - remoteAddress: undefined, - isXHR: undefined, - cause: undefined, - fromCache: undefined, - fromServiceWorker: undefined, - status: undefined, - statusText: undefined, - httpVersion: undefined, - securityState: undefined, - securityInfo: undefined, - mimeType: undefined, - contentSize: undefined, - transferredSize: undefined, - totalTime: undefined, - eventTimings: undefined, - headersSize: undefined, - requestHeaders: undefined, - requestHeadersFromUploadStream: undefined, - requestCookies: undefined, - requestPostData: undefined, - responseHeaders: undefined, - responseCookies: undefined, - responseContent: undefined, - responseContentDataUri: undefined, -}); - const Requests = I.Record({ - // The request list - requests: I.List(), - // Selection state - selectedId: null, - preselectedId: null, - // Auxiliary fields to hold requests stats - firstStartedMillis: +Infinity, - lastEndedMillis: -Infinity, + items: [], }); -const UPDATE_PROPS = [ - "method", - "url", - "remotePort", - "remoteAddress", - "status", - "statusText", - "httpVersion", - "securityState", - "securityInfo", - "mimeType", - "contentSize", - "transferredSize", - "totalTime", - "eventTimings", - "headersSize", - "requestHeaders", - "requestHeadersFromUploadStream", - "requestCookies", - "requestPostData", - "responseHeaders", - "responseCookies", - "responseContent", - "responseContentDataUri" -]; - -function requestsReducer(state = new Requests(), action) { - switch (action.type) { - case ADD_REQUEST: { - return state.withMutations(st => { - let newRequest = new Request(Object.assign( - { id: action.id }, - action.data, - { urlDetails: getUrlDetails(action.data.url) } - )); - st.requests = st.requests.push(newRequest); - - // Update the started/ended timestamps - let { startedMillis } = action.data; - if (startedMillis < st.firstStartedMillis) { - st.firstStartedMillis = startedMillis; - } - if (startedMillis > st.lastEndedMillis) { - st.lastEndedMillis = startedMillis; - } - - // Select the request if it was preselected and there is no other selection - if (st.preselectedId && st.preselectedId === action.id) { - st.selectedId = st.selectedId || st.preselectedId; - st.preselectedId = null; - } - }); - } - - case UPDATE_REQUEST: { - let { requests, lastEndedMillis } = state; - - let updateIdx = requests.findIndex(r => r.id === action.id); - if (updateIdx === -1) { - return state; - } - - requests = requests.update(updateIdx, r => r.withMutations(request => { - for (let [key, value] of Object.entries(action.data)) { - if (!UPDATE_PROPS.includes(key)) { - continue; - } - - request[key] = value; +function updateRequests(state, action) { + return state.set("items", action.items || state.items); +} - switch (key) { - case "url": - // Compute the additional URL details - request.urlDetails = getUrlDetails(value); - break; - case "responseContent": - // If there's no mime type available when the response content - // is received, assume text/plain as a fallback. - if (!request.mimeType) { - request.mimeType = "text/plain"; - } - break; - case "totalTime": - const endedMillis = request.startedMillis + value; - lastEndedMillis = Math.max(lastEndedMillis, endedMillis); - break; - case "requestPostData": - request.requestHeadersFromUploadStream = { - headers: [], - headersSize: 0, - }; - break; - } - } - })); - - return state.withMutations(st => { - st.requests = requests; - st.lastEndedMillis = lastEndedMillis; - }); - } - case CLEAR_REQUESTS: { - return new Requests(); - } - case SELECT_REQUEST: { - return state.set("selectedId", action.id); - } - case PRESELECT_REQUEST: { - return state.set("preselectedId", action.id); - } - case CLONE_SELECTED_REQUEST: { - let { requests, selectedId } = state; - - if (!selectedId) { - return state; - } - - let clonedIdx = requests.findIndex(r => r.id === selectedId); - if (clonedIdx === -1) { - return state; - } - - let clonedRequest = requests.get(clonedIdx); - let newRequest = new Request({ - id: clonedRequest.id + "-clone", - method: clonedRequest.method, - url: clonedRequest.url, - urlDetails: clonedRequest.urlDetails, - requestHeaders: clonedRequest.requestHeaders, - requestPostData: clonedRequest.requestPostData, - isCustom: true - }); - - // Insert the clone right after the original. This ensures that the requests - // are always sorted next to each other, even when multiple requests are - // equal according to the sorting criteria. - requests = requests.insert(clonedIdx + 1, newRequest); - - return state.withMutations(st => { - st.requests = requests; - st.selectedId = newRequest.id; - }); - } - case REMOVE_SELECTED_CUSTOM_REQUEST: { - let { requests, selectedId } = state; - - if (!selectedId) { - return state; - } - - let removedRequest = requests.find(r => r.id === selectedId); - - // Only custom requests can be removed - if (!removedRequest || !removedRequest.isCustom) { - return state; - } - - return state.withMutations(st => { - st.requests = requests.filter(r => r !== removedRequest); - st.selectedId = null; - }); - } - case OPEN_SIDEBAR: { - if (!action.open) { - return state.set("selectedId", null); - } - - if (!state.selectedId && !state.requests.isEmpty()) { - return state.set("selectedId", state.requests.get(0).id); - } - - return state; - } - +function requests(state = new Requests(), action) { + switch (action.type) { + case UPDATE_REQUESTS: + return updateRequests(state, action); default: return state; } } -module.exports = requestsReducer; +module.exports = requests;
deleted file mode 100644 --- a/devtools/client/netmonitor/reducers/sort.js +++ /dev/null @@ -1,33 +0,0 @@ -/* 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 { SORT_BY } = require("../constants"); - -const Sort = I.Record({ - // null means: sort by "waterfall", but don't highlight the table header - type: null, - ascending: true, -}); - -function sortReducer(state = new Sort(), action) { - switch (action.type) { - case SORT_BY: { - return state.withMutations(st => { - if (action.sortType == st.type) { - st.ascending = !st.ascending; - } else { - st.type = action.sortType; - st.ascending = true; - } - }); - } - default: - return state; - } -} - -module.exports = sortReducer;
deleted file mode 100644 --- a/devtools/client/netmonitor/reducers/timing-markers.js +++ /dev/null @@ -1,54 +0,0 @@ -/* 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 { ADD_TIMING_MARKER, - CLEAR_TIMING_MARKERS, - CLEAR_REQUESTS } = require("../constants"); - -const TimingMarkers = I.Record({ - firstDocumentDOMContentLoadedTimestamp: -1, - firstDocumentLoadTimestamp: -1, -}); - -function addTimingMarker(state, action) { - if (action.marker.name == "document::DOMContentLoaded" && - state.firstDocumentDOMContentLoadedTimestamp == -1) { - return state.set("firstDocumentDOMContentLoadedTimestamp", - action.marker.unixTime / 1000); - } - - if (action.marker.name == "document::Load" && - state.firstDocumentLoadTimestamp == -1) { - return state.set("firstDocumentLoadTimestamp", - action.marker.unixTime / 1000); - } - - return state; -} - -function clearTimingMarkers(state) { - return state.withMutations(st => { - st.remove("firstDocumentDOMContentLoadedTimestamp"); - st.remove("firstDocumentLoadTimestamp"); - }); -} - -function timingMarkers(state = new TimingMarkers(), action) { - switch (action.type) { - case ADD_TIMING_MARKER: - return addTimingMarker(state, action); - - case CLEAR_REQUESTS: - case CLEAR_TIMING_MARKERS: - return clearTimingMarkers(state); - - default: - return state; - } -} - -module.exports = timingMarkers;
--- a/devtools/client/netmonitor/reducers/ui.js +++ b/devtools/client/netmonitor/reducers/ui.js @@ -2,39 +2,39 @@ * 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 { OPEN_SIDEBAR, - WATERFALL_RESIZE, + TOGGLE_SIDEBAR, } = require("../constants"); +const Sidebar = I.Record({ + open: false, +}); + const UI = I.Record({ - sidebarOpen: false, - waterfallWidth: 300, + sidebar: new Sidebar(), }); function openSidebar(state, action) { - return state.set("sidebarOpen", action.open); + return state.setIn(["sidebar", "open"], action.open); } -// Safe bounds for waterfall width (px) -const REQUESTS_WATERFALL_SAFE_BOUNDS = 90; - -function resizeWaterfall(state, action) { - return state.set("waterfallWidth", action.width - REQUESTS_WATERFALL_SAFE_BOUNDS); +function toggleSidebar(state, action) { + return state.setIn(["sidebar", "open"], !state.sidebar.open); } function ui(state = new UI(), action) { switch (action.type) { case OPEN_SIDEBAR: return openSidebar(state, action); - case WATERFALL_RESIZE: - return resizeWaterfall(state, action); + case TOGGLE_SIDEBAR: + return toggleSidebar(state, action); default: return state; } } module.exports = ui;
--- a/devtools/client/netmonitor/request-list-context-menu.js +++ b/devtools/client/netmonitor/request-list-context-menu.js @@ -53,110 +53,112 @@ RequestListContextMenu.prototype = { visible: !!selectedItem, click: () => this.copyUrl(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-url-params", label: L10N.getStr("netmonitor.context.copyUrlParams"), accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"), - visible: !!(selectedItem && getUrlQuery(selectedItem.url)), + visible: !!(selectedItem && getUrlQuery(selectedItem.attachment.url)), click: () => this.copyUrlParams(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-post-data", label: L10N.getStr("netmonitor.context.copyPostData"), accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"), - visible: !!(selectedItem && selectedItem.requestPostData), + visible: !!(selectedItem && selectedItem.attachment.requestPostData), click: () => this.copyPostData(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-as-curl", label: L10N.getStr("netmonitor.context.copyAsCurl"), accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"), - visible: !!selectedItem, + visible: !!(selectedItem && selectedItem.attachment), click: () => this.copyAsCurl(), })); menu.append(new MenuItem({ type: "separator", visible: !!selectedItem, })); menu.append(new MenuItem({ id: "request-menu-context-copy-request-headers", label: L10N.getStr("netmonitor.context.copyRequestHeaders"), accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"), - visible: !!(selectedItem && selectedItem.requestHeaders), + visible: !!(selectedItem && selectedItem.attachment.requestHeaders), click: () => this.copyRequestHeaders(), })); menu.append(new MenuItem({ id: "response-menu-context-copy-response-headers", label: L10N.getStr("netmonitor.context.copyResponseHeaders"), accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"), - visible: !!(selectedItem && selectedItem.responseHeaders), + visible: !!(selectedItem && selectedItem.attachment.responseHeaders), click: () => this.copyResponseHeaders(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-response", label: L10N.getStr("netmonitor.context.copyResponse"), accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"), visible: !!(selectedItem && - selectedItem.responseContent && - selectedItem.responseContent.content.text && - selectedItem.responseContent.content.text.length !== 0), + selectedItem.attachment.responseContent && + selectedItem.attachment.responseContent.content.text && + selectedItem.attachment.responseContent.content.text.length !== 0), click: () => this.copyResponse(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-image-as-data-uri", label: L10N.getStr("netmonitor.context.copyImageAsDataUri"), accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"), visible: !!(selectedItem && - selectedItem.responseContent && - selectedItem.responseContent.content.mimeType.includes("image/")), + selectedItem.attachment.responseContent && + selectedItem.attachment.responseContent.content + .mimeType.includes("image/")), click: () => this.copyImageAsDataUri(), })); menu.append(new MenuItem({ type: "separator", visible: !!selectedItem, })); menu.append(new MenuItem({ id: "request-menu-context-copy-all-as-har", label: L10N.getStr("netmonitor.context.copyAllAsHar"), accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"), - visible: this.items.size > 0, + visible: !!this.items.length, click: () => this.copyAllAsHar(), })); menu.append(new MenuItem({ id: "request-menu-context-save-all-as-har", label: L10N.getStr("netmonitor.context.saveAllAsHar"), accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"), - visible: this.items.size > 0, + visible: !!this.items.length, click: () => this.saveAllAsHar(), })); menu.append(new MenuItem({ type: "separator", visible: !!selectedItem, })); menu.append(new MenuItem({ id: "request-menu-context-resend", label: L10N.getStr("netmonitor.context.editAndResend"), accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"), visible: !!(NetMonitorController.supportsCustomRequest && - selectedItem && !selectedItem.isCustom), + selectedItem && + !selectedItem.attachment.isCustom), click: () => NetMonitorView.RequestsMenu.cloneSelectedRequest(), })); menu.append(new MenuItem({ type: "separator", visible: !!selectedItem, })); @@ -180,43 +182,44 @@ RequestListContextMenu.prototype = { return menu; }, /** * Opens selected item in a new tab. */ openRequestInTab() { let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); - win.openUILinkIn(this.selectedItem.url, "tab", { relatedToCurrent: true }); + let { url } = this.selectedItem.attachment; + win.openUILinkIn(url, "tab", { relatedToCurrent: true }); }, /** * Copy the request url from the currently selected item. */ copyUrl() { - clipboardHelper.copyString(this.selectedItem.url); + clipboardHelper.copyString(this.selectedItem.attachment.url); }, /** * Copy the request url query string parameters from the currently * selected item. */ copyUrlParams() { - let { url } = this.selectedItem; + let { url } = this.selectedItem.attachment; let params = getUrlQuery(url).split("&"); let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n"); clipboardHelper.copyString(string); }, /** * Copy the request form data parameters (or raw payload) from * the currently selected item. */ copyPostData: Task.async(function* () { - let selected = this.selectedItem; + let selected = this.selectedItem.attachment; // Try to extract any form data parameters. let formDataSections = yield getFormDataSections( selected.requestHeaders, selected.requestHeadersFromUploadStream, selected.requestPostData, gNetwork.getString.bind(gNetwork)); @@ -243,17 +246,17 @@ RequestListContextMenu.prototype = { clipboardHelper.copyString(string); }), /** * Copy a cURL command from the currently selected item. */ copyAsCurl: Task.async(function* () { - let selected = this.selectedItem; + let selected = this.selectedItem.attachment; // Create a sanitized object for the Curl command generator. let data = { url: selected.url, method: selected.method, headers: [], httpVersion: selected.httpVersion, postDataText: null @@ -273,51 +276,55 @@ RequestListContextMenu.prototype = { clipboardHelper.copyString(Curl.generateCommand(data)); }), /** * Copy the raw request headers from the currently selected item. */ copyRequestHeaders() { - let rawHeaders = this.selectedItem.requestHeaders.rawHeaders.trim(); + let selected = this.selectedItem.attachment; + let rawHeaders = selected.requestHeaders.rawHeaders.trim(); if (Services.appinfo.OS !== "WINNT") { rawHeaders = rawHeaders.replace(/\r/g, ""); } clipboardHelper.copyString(rawHeaders); }, /** * Copy the raw response headers from the currently selected item. */ copyResponseHeaders() { - let rawHeaders = this.selectedItem.responseHeaders.rawHeaders.trim(); + let selected = this.selectedItem.attachment; + let rawHeaders = selected.responseHeaders.rawHeaders.trim(); if (Services.appinfo.OS !== "WINNT") { rawHeaders = rawHeaders.replace(/\r/g, ""); } clipboardHelper.copyString(rawHeaders); }, /** * Copy image as data uri. */ copyImageAsDataUri() { - const { mimeType, text, encoding } = this.selectedItem.responseContent.content; + let selected = this.selectedItem.attachment; + let { mimeType, text, encoding } = selected.responseContent.content; gNetwork.getString(text).then(string => { let data = formDataURI(mimeType, encoding, string); clipboardHelper.copyString(data); }); }, /** * Copy response data as a string. */ copyResponse() { - const { text } = this.selectedItem.responseContent.content; + let selected = this.selectedItem.attachment; + let text = selected.responseContent.content.text; gNetwork.getString(text).then(string => { clipboardHelper.copyString(string); }); }, /** * Copy HAR from the network panel content to the clipboard. @@ -336,15 +343,16 @@ RequestListContextMenu.prototype = { }, getDefaultHarOptions() { let form = NetMonitorController._target.form; let title = form.title || form.url; return { getString: gNetwork.getString.bind(gNetwork), - items: this.items, + view: NetMonitorView.RequestsMenu, + items: NetMonitorView.RequestsMenu.items, title: title }; } }; module.exports = RequestListContextMenu;
--- a/devtools/client/netmonitor/request-utils.js +++ b/devtools/client/netmonitor/request-utils.js @@ -45,18 +45,18 @@ function getKeyWithEvent(callback, onlyS * @param {object} postData - the "requestPostData". * @param {function} getString - callback to retrieve a string from a LongStringGrip. * @return {array} a promise list that is resolved with the extracted form data. */ const getFormDataSections = Task.async(function* (headers, uploadHeaders, postData, getString) { let formDataSections = []; - let requestHeaders = headers.headers; - let payloadHeaders = uploadHeaders ? uploadHeaders.headers : []; + let { headers: requestHeaders } = headers; + let { headers: payloadHeaders } = uploadHeaders; let allHeaders = [...payloadHeaders, ...requestHeaders]; let contentTypeHeader = allHeaders.find(e => { return e.name.toLowerCase() == "content-type"; }); let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : ""; @@ -184,47 +184,16 @@ function getUrlHostName(url) { * @param {string} url - url string * @return {string} unicode host of a url */ function getUrlHost(url) { return decodeUnicodeUrl((new URL(url)).host); } /** - * Extract several details fields from a URL at once. - */ -function getUrlDetails(url) { - let baseNameWithQuery = getUrlBaseNameWithQuery(url); - let host = getUrlHost(url); - let hostname = getUrlHostName(url); - let unicodeUrl = decodeUnicodeUrl(url); - - // Mark local hosts specially, where "local" is as defined in the W3C - // spec for secure contexts. - // http://www.w3.org/TR/powerful-features/ - // - // * If the name falls under 'localhost' - // * If the name is an IPv4 address within 127.0.0.0/8 - // * If the name is an IPv6 address within ::1/128 - // - // IPv6 parsing is a little sloppy; it assumes that the address has - // been validated before it gets here. - let isLocal = hostname.match(/(.+\.)?localhost$/) || - hostname.match(/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}/) || - hostname.match(/\[[0:]+1\]/); - - return { - baseNameWithQuery, - host, - unicodeUrl, - isLocal - }; -} - -/** * Parse a url's query string into its components * * @param {string} query - query string of a url portion * @return {array} array of query params { name, value } */ function parseQueryString(query) { if (!query) { return null; @@ -279,12 +248,11 @@ module.exports = { writeHeaderText, decodeUnicodeUrl, getAbbreviatedMimeType, getUrlBaseName, getUrlQuery, getUrlBaseNameWithQuery, getUrlHostName, getUrlHost, - getUrlDetails, parseQueryString, loadCauseString, };
--- a/devtools/client/netmonitor/requests-menu-view.js +++ b/devtools/client/netmonitor/requests-menu-view.js @@ -1,46 +1,90 @@ /* 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, dumpn, $, gNetwork, NetMonitorController, NetMonitorView */ +/* eslint-disable mozilla/reject-some-requires */ +/* globals document, window, dumpn, $, gNetwork, EVENTS, Prefs, + NetMonitorController, NetMonitorView */ "use strict"; -const { Task } = require("devtools/shared/task"); -const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip"); -const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers"); -const { CurlUtils } = require("devtools/client/shared/curl"); -const { L10N } = require("./l10n"); -const { EVENTS } = require("./events"); -const { createElement, createFactory } = require("devtools/client/shared/vendor/react"); -const ReactDOM = require("devtools/client/shared/vendor/react-dom"); -const { Provider } = require("devtools/client/shared/vendor/react-redux"); -const RequestList = createFactory(require("./components/request-list")); +const { Cu } = require("chrome"); +const {Task} = require("devtools/shared/task"); +const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {}); +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"); +const {Heritage, WidgetMethods, setNamedTimeout} = + require("devtools/client/shared/widgets/view-helpers"); +const {CurlUtils} = require("devtools/client/shared/curl"); +const {Filters, isFreetextMatch} = require("./filter-predicates"); +const {Sorters} = require("./sort-predicates"); +const {L10N, WEBCONSOLE_L10N} = require("./l10n"); +const {formDataURI, + writeHeaderText, + decodeUnicodeUrl, + getKeyWithEvent, + getAbbreviatedMimeType, + getUrlBaseNameWithQuery, + getUrlHost, + getUrlHostName, + loadCauseString} = require("./request-utils"); +const Actions = require("./actions/index"); const RequestListContextMenu = require("./request-list-context-menu"); -const Actions = require("./actions/index"); -const { Prefs } = require("./prefs"); -const { - formDataURI, - writeHeaderText, - loadCauseString -} = require("./request-utils"); - -const { - getActiveFilters, - getSortedRequests, - getDisplayedRequests, - getRequestById, - getSelectedRequest -} = require("./selectors/index"); - +const HTML_NS = "http://www.w3.org/1999/xhtml"; +const EPSILON = 0.001; // ms const RESIZE_REFRESH_RATE = 50; +// ms +const REQUESTS_REFRESH_RATE = 50; +// tooltip show/hide delay in ms +const REQUESTS_TOOLTIP_TOGGLE_DELAY = 500; +// px +const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; +// px +const REQUESTS_TOOLTIP_STACK_TRACE_WIDTH = 600; +// px +const REQUESTS_WATERFALL_SAFE_BOUNDS = 90; +// ms +const REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE = 5; +// px +const REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN = 60; +// ms +const REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE = 5; +const REQUESTS_WATERFALL_BACKGROUND_TICKS_SCALES = 3; +// 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]; + +// 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; + +// TODO: duplicated from netmonitor-view.js. Move to a format-utils.js module. +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 oldValue = currentValue; const newValue = reduceValue(currentValue); @@ -49,337 +93,1462 @@ function storeWatcher(initialValue, redu onChange(newValue, oldValue); } }; } /** * Functions handling the requests menu (containing details about each request, * like status, method, file, domain, as well as a waterfall representing - * timing information). + * timing imformation). */ function RequestsMenuView() { dumpn("RequestsMenuView was instantiated"); + + this._flushRequests = this._flushRequests.bind(this); + this._onHover = this._onHover.bind(this); + this._onSelect = this._onSelect.bind(this); + this._onSwap = this._onSwap.bind(this); + this._onResize = this._onResize.bind(this); + this._onScroll = this._onScroll.bind(this); + this._onSecurityIconClick = this._onSecurityIconClick.bind(this); } -RequestsMenuView.prototype = { +RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { /** * Initialization function, called when the network monitor is started. */ initialize: function (store) { dumpn("Initializing the RequestsMenuView"); this.store = store; this.contextMenu = new RequestListContextMenu(); - Prefs.filters.forEach(type => store.dispatch(Actions.toggleFilterType(type))); + let widgetParentEl = $("#requests-menu-contents"); + this.widget = new SideMenuWidget(widgetParentEl); + this._splitter = $("#network-inspector-view-splitter"); + + // 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 + }); + + this.sortContents((a, b) => Sorters.waterfall(a.attachment, b.attachment)); - // Watch selection changes - this.store.subscribe(storeWatcher( - null, - () => getSelectedRequest(this.store.getState()), - (newSelected, oldSelected) => this.onSelectionUpdate(newSelected, oldSelected) - )); + 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._onContextMenu = this._onContextMenu.bind(this); this._onContextPerfCommand = () => NetMonitorView.toggleFrontendMode(); + this._onReloadCommand = () => NetMonitorView.reloadPage(); + 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); - $("#toggle-raw-headers") - .addEventListener("click", this.toggleRawHeadersEvent, false); + this.reFilterRequests = this.reFilterRequests.bind(this); - this._summary = $("#requests-menu-network-summary-button"); - this._summary.setAttribute("label", L10N.getStr("networkMenu.empty")); + $("#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.onResize = this.onResize.bind(this); - this._splitter = $("#network-inspector-view-splitter"); - this._splitter.addEventListener("mousemove", this.onResize, false); - window.addEventListener("resize", this.onResize, false); + this.unsubscribeStore = store.subscribe(storeWatcher( + null, + () => store.getState().filters, + (newFilters) => { + this._activeFilters = newFilters.types + .toSeq() + .filter((checked, key) => checked) + .keySeq() + .toArray(); + this._currentFreetextFilter = newFilters.url; + this.reFilterRequests(); + } + )); - this.tooltip = new HTMLTooltip(NetMonitorController._toolbox.doc, { type: "arrow" }); - - this.mountPoint = $("#network-table"); - ReactDOM.render(createElement(Provider, - { store: this.store }, - RequestList() - ), this.mountPoint); + Prefs.filters.forEach(type => + store.dispatch(Actions.toggleFilterType(type))); window.once("connected", this._onConnect.bind(this)); }, - _onConnect() { + _onConnect: function () { + $("#requests-menu-reload-notice-button").addEventListener("command", + this._onReloadCommand, false); + if (NetMonitorController.supportsCustomRequest) { - $("#custom-request-send-button") - .addEventListener("click", this.sendCustomRequestEvent, false); - $("#custom-request-close-button") - .addEventListener("click", this.closeCustomRequestEvent, false); - $("#headers-summary-resend") - .addEventListener("click", this.cloneSelectedRequestEvent, false); + $("#custom-request-send-button").addEventListener("click", + this.sendCustomRequestEvent, false); + $("#custom-request-close-button").addEventListener("click", + this.closeCustomRequestEvent, false); + $("#headers-summary-resend").addEventListener("click", + this.cloneSelectedRequestEvent, false); } else { $("#headers-summary-resend").hidden = true; } - $("#network-statistics-back-button") - .addEventListener("command", this._onContextPerfCommand, false); + if (NetMonitorController.supportsPerfStats) { + $("#requests-menu-perf-notice-button").addEventListener("command", + this._onContextPerfCommand, false); + $("#network-statistics-back-button").addEventListener("command", + this._onContextPerfCommand, false); + } else { + $("#notice-perf-message").hidden = true; + } + + if (!NetMonitorController.supportsTransferredResponseSize) { + $("#requests-menu-transferred-header-box").hidden = true; + $("#requests-menu-item-template .requests-menu-transferred") + .hidden = true; + } }, /** * Destruction function, called when the network monitor is closed. */ - destroy() { + destroy: function () { dumpn("Destroying the RequestsMenuView"); - Prefs.filters = getActiveFilters(this.store.getState()); + Prefs.filters = this._activeFilters; + + /* Destroy the tooltip */ + this.tooltip.stopTogglingOnHover(); + this.tooltip.destroy(); + $("#requests-menu-contents").removeEventListener("scroll", this._onScroll, true); + $("#requests-menu-contents").removeEventListener("contextmenu", this._onContextMenu); - // this.flushRequestsTask.disarm(); + this.widget.removeEventListener("select", this._onSelect, false); + 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); - $("#network-statistics-back-button") - .removeEventListener("command", this._onContextPerfCommand, false); - $("#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._flushRequestsTask.disarm(); + + $("#requests-menu-reload-notice-button").removeEventListener("command", + this._onReloadCommand, false); + $("#requests-menu-perf-notice-button").removeEventListener("command", + this._onContextPerfCommand, false); + $("#network-statistics-back-button").removeEventListener("command", + this._onContextPerfCommand, false); - this._splitter.removeEventListener("mousemove", this.onResize, false); - window.removeEventListener("resize", this.onResize, false); + $("#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.tooltip.destroy(); - - ReactDOM.unmountComponentAtNode(this.mountPoint); + this.unsubscribeStore(); }, /** * Resets this container (removes all the networking information). */ - reset() { - this.store.dispatch(Actions.batchReset()); - this.store.dispatch(Actions.clearRequests()); + reset: function () { + this.empty(); + this._addQueue = []; + this._updateQueue = []; + this._firstRequestStartedMillis = -1; + this._lastRequestEndedMillis = -1; }, /** - * Removes all network requests and closes the sidebar if open. + * Specifies if this view may be updated lazily. */ - clear() { - this.store.dispatch(Actions.clearRequests()); - }, - - addRequest(id, data) { - let { method, url, isXHR, cause, startedDateTime, fromCache, - fromServiceWorker } = data; - - // Convert the received date/time string to a unix timestamp. - let startedMillis = Date.parse(startedDateTime); + _lazyUpdate: true, - // Convert the cause from a Ci.nsIContentPolicy constant to a string - if (cause) { - let type = loadCauseString(cause.type); - cause = Object.assign({}, cause, { type }); - } - - const action = Actions.addRequest( - id, - { - startedMillis, - method, - url, - isXHR, - cause, - fromCache, - fromServiceWorker - }, - true - ); - - this.store.dispatch(action).then(() => window.emit(EVENTS.REQUEST_ADDED, action.id)); + get lazyUpdate() { + return this._lazyUpdate; }, - updateRequest: Task.async(function* (id, data) { - const action = Actions.updateRequest(id, data, true); - yield this.store.dispatch(action); - - const { responseContent, requestPostData } = action.data; - - // Fetch response data if the response is an image (to display thumbnail) - if (responseContent && responseContent.content) { - let request = getRequestById(this.store.getState(), action.id); - if (request) { - let { mimeType } = request; - if (mimeType.includes("image/")) { - let { text, encoding } = responseContent.content; - let responseBody = yield gNetwork.getString(text); - const dataUri = formDataURI(mimeType, encoding, responseBody); - yield this.store.dispatch(Actions.updateRequest( - action.id, - { responseContentDataUri: dataUri }, - true - )); - window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); - } - } - } - - // Search the POST data upload stream for request headers and add - // them as a separate property, different from the classic headers. - if (requestPostData && requestPostData.postData) { - let { text } = requestPostData.postData; - let postData = yield gNetwork.getString(text); - const headers = CurlUtils.getHeadersFromMultipartText(postData); - const headersSize = headers.reduce((acc, { name, value }) => { - return acc + name.length + value.length + 2; - }, 0); - yield this.store.dispatch(Actions.updateRequest(action.id, { - requestHeadersFromUploadStream: { headers, headersSize } - }, true)); - } - }), - - /** - * Disable batched updates. Used by tests. - */ set lazyUpdate(value) { - this.store.dispatch(Actions.batchEnable(value)); - }, - - get items() { - return getSortedRequests(this.store.getState()); - }, - - get visibleItems() { - return getDisplayedRequests(this.store.getState()); - }, - - get itemCount() { - return this.store.getState().requests.requests.size; - }, - - getItemAtIndex(index) { - return getSortedRequests(this.store.getState()).get(index); - }, - - get selectedIndex() { - const state = this.store.getState(); - if (!state.requests.selectedId) { - return -1; - } - return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId); - }, - - set selectedIndex(index) { - const requests = getSortedRequests(this.store.getState()); - let itemId = null; - if (index >= 0 && index < requests.size) { - itemId = requests.get(index).id; - } - this.store.dispatch(Actions.selectRequest(itemId)); - }, - - get selectedItem() { - return getSelectedRequest(this.store.getState()); - }, - - set selectedItem(item) { - this.store.dispatch(Actions.selectRequest(item ? item.id : null)); - }, - - /** - * Updates the sidebar status when something about the selection changes - */ - onSelectionUpdate(newSelected, oldSelected) { - if (newSelected && oldSelected && newSelected.id === oldSelected.id) { - // The same item is still selected, its data only got updated - NetMonitorView.NetworkDetails.populate(newSelected); - } else if (newSelected) { - // Another item just got selected - NetMonitorView.Sidebar.populate(newSelected); - NetMonitorView.Sidebar.toggle(true); - } else { - // Selection just got empty - NetMonitorView.Sidebar.toggle(false); + this._lazyUpdate = value; + if (!value) { + this._flushRequests(); } }, /** - * The resize listener for this container's window. + * Adds a network request to this container. + * + * @param string id + * An identifier coming from the network monitor controller. + * @param string startedDateTime + * A string representation of when the request was started, which + * can be parsed by Date (for example "2012-09-17T19:50:03.699Z"). + * @param string method + * Specifies the request method (e.g. "GET", "POST", etc.) + * @param string url + * Specifies the request's url. + * @param boolean isXHR + * True if this request was initiated via XHR. + * @param object cause + * Specifies the request's cause. Has the following properties: + * - type: nsContentPolicyType constant + * - loadingDocumentUri: URI of the request origin + * - stacktrace: JS stacktrace of the request + * @param boolean fromCache + * Indicates if the result came from the browser cache + * @param boolean fromServiceWorker + * Indicates if the request has been intercepted by a Service Worker */ - onResize() { - // Allow requests to settle down first. - setNamedTimeout("resize-events", RESIZE_REFRESH_RATE, () => { - const waterfallHeaderEl = $("#requests-menu-waterfall-header-box"); - if (waterfallHeaderEl) { - const { width } = waterfallHeaderEl.getBoundingClientRect(); - this.store.dispatch(Actions.resizeWaterfall(width)); - } - }); + addRequest: function (id, startedDateTime, method, url, isXHR, cause, + fromCache, fromServiceWorker) { + this._addQueue.push([id, startedDateTime, method, url, isXHR, cause, + fromCache, fromServiceWorker]); + + // Lazy updating is disabled in some tests. + if (!this.lazyUpdate) { + return void this._flushRequests(); + } + + this._flushRequestsTask.arm(); + return undefined; }, /** * Create a new custom request form populated with the data from * the currently selected request. */ - cloneSelectedRequest() { - this.store.dispatch(Actions.cloneSelectedRequest()); - }, + cloneSelectedRequest: function () { + let selected = this.selectedItem.attachment; - /** - * Shows raw request/response headers in textboxes. - */ - toggleRawHeaders: function () { - let requestTextarea = $("#raw-request-headers-textarea"); - let responseTextarea = $("#raw-response-headers-textarea"); - let rawHeadersHidden = $("#raw-headers").getAttribute("hidden"); + // Create the element node for the network request item. + let menuView = this._createMenuView(selected.method, selected.url, + selected.cause); - if (rawHeadersHidden) { - let selected = getSelectedRequest(this.store.getState()); - let selectedRequestHeaders = selected.requestHeaders.headers; - let selectedResponseHeaders = selected.responseHeaders.headers; - requestTextarea.value = writeHeaderText(selectedRequestHeaders); - responseTextarea.value = writeHeaderText(selectedResponseHeaders); - $("#raw-headers").hidden = false; - } else { - requestTextarea.value = null; - responseTextarea.value = null; - $("#raw-headers").hidden = true; - } + // Append a network request item to this container. + let newItem = this.push([menuView], { + attachment: Object.create(selected, { + isCustom: { value: true } + }) + }); + + // Immediately switch to new request pane. + this.selectedItem = newItem; }, /** * Send a new HTTP request using the data in the custom request form. */ sendCustomRequest: function () { - let selected = getSelectedRequest(this.store.getState()); + let selected = this.selectedItem.attachment; let data = { url: selected.url, method: selected.method, httpVersion: selected.httpVersion, }; if (selected.requestHeaders) { data.headers = selected.requestHeaders.headers; } if (selected.requestPostData) { data.body = selected.requestPostData.postData.text; } NetMonitorController.webConsoleClient.sendHTTPRequest(data, response => { let id = response.eventActor.actor; - this.store.dispatch(Actions.preselectRequest(id)); + this._preferredItemId = id; }); this.closeCustomRequest(); }, /** * Remove the currently selected custom request. */ - closeCustomRequest() { - this.store.dispatch(Actions.removeSelectedCustomRequest()); + closeCustomRequest: function () { + this.remove(this.selectedItem); + NetMonitorView.Sidebar.toggle(false); + }, + + /** + * Shows raw request/response headers in textboxes. + */ + toggleRawHeaders: function () { + let requestTextarea = $("#raw-request-headers-textarea"); + let responseTextare = $("#raw-response-headers-textarea"); + let rawHeadersHidden = $("#raw-headers").getAttribute("hidden"); + + if (rawHeadersHidden) { + let selected = this.selectedItem.attachment; + let selectedRequestHeaders = selected.requestHeaders.headers; + let selectedResponseHeaders = selected.responseHeaders.headers; + requestTextarea.value = writeHeaderText(selectedRequestHeaders); + responseTextare.value = writeHeaderText(selectedResponseHeaders); + $("#raw-headers").hidden = false; + } else { + requestTextarea.value = null; + responseTextare.value = null; + $("#raw-headers").hidden = true; + } + }, + + /** + * Refreshes the view contents with the newly selected filters + */ + reFilterRequests: function () { + this.filterContents(this._filterPredicate); + this.updateRequests(); + this.refreshZebra(); + }, + + /** + * 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; + return this._activeFilters.some(filterName => Filters[filterName](attachment)) && + isFreetextMatch(attachment, currentFreetextFilter); + }; + }, + + /** + * Sorts all network requests in this container by a specified detail. + * + * @param string type + * Either "status", "method", "file", "domain", "type", "transferred", + * "size" or "waterfall". + */ + sortBy: function (type = "waterfall") { + let target = $("#requests-menu-" + type + "-button"); + let headers = document.querySelectorAll(".requests-menu-header-button"); + + for (let header of headers) { + if (header != target) { + header.removeAttribute("sorted"); + header.removeAttribute("tooltiptext"); + header.parentNode.removeAttribute("active"); + } + } + + let direction = ""; + if (target) { + if (target.getAttribute("sorted") == "ascending") { + target.setAttribute("sorted", direction = "descending"); + target.setAttribute("tooltiptext", + L10N.getStr("networkMenu.sortedDesc")); + } else { + target.setAttribute("sorted", direction = "ascending"); + target.setAttribute("tooltiptext", + L10N.getStr("networkMenu.sortedAsc")); + } + // Used to style the next column. + target.parentNode.setAttribute("active", "true"); + } + + // Sort by whatever was requested. + switch (type) { + case "status": + if (direction == "ascending") { + this.sortContents((a, b) => Sorters.status(a.attachment, b.attachment)); + } else { + this.sortContents((a, b) => -Sorters.status(a.attachment, b.attachment)); + } + break; + case "method": + if (direction == "ascending") { + this.sortContents((a, b) => Sorters.method(a.attachment, b.attachment)); + } else { + this.sortContents((a, b) => -Sorters.method(a.attachment, b.attachment)); + } + break; + case "file": + if (direction == "ascending") { + this.sortContents((a, b) => Sorters.file(a.attachment, b.attachment)); + } else { + this.sortContents((a, b) => -Sorters.file(a.attachment, b.attachment)); + } + break; + case "domain": + if (direction == "ascending") { + this.sortContents((a, b) => Sorters.domain(a.attachment, b.attachment)); + } else { + this.sortContents((a, b) => -Sorters.domain(a.attachment, b.attachment)); + } + break; + case "cause": + if (direction == "ascending") { + this.sortContents((a, b) => Sorters.cause(a.attachment, b.attachment)); + } else { + this.sortContents((a, b) => -Sorters.cause(a.attachment, b.attachment)); + } + break; + case "type": + if (direction == "ascending") { + this.sortContents((a, b) => Sorters.type(a.attachment, b.attachment)); + } else { + this.sortContents((a, b) => -Sorters.type(a.attachment, b.attachment)); + } + break; + case "transferred": + if (direction == "ascending") { + this.sortContents((a, b) => Sorters.transferred(a.attachment, b.attachment)); + } else { + this.sortContents((a, b) => -Sorters.transferred(a.attachment, b.attachment)); + } + break; + case "size": + if (direction == "ascending") { + this.sortContents((a, b) => Sorters.size(a.attachment, b.attachment)); + } else { + this.sortContents((a, b) => -Sorters.size(a.attachment, b.attachment)); + } + break; + case "waterfall": + if (direction == "ascending") { + this.sortContents((a, b) => Sorters.waterfall(a.attachment, b.attachment)); + } else { + this.sortContents((a, b) => -Sorters.waterfall(a.attachment, b.attachment)); + } + break; + } + + this.updateRequests(); + this.refreshZebra(); + }, + + /** + * Removes all network requests and closes the sidebar if open. + */ + clear: function () { + NetMonitorController.NetworkEventsHandler.clearMarkers(); + NetMonitorView.Sidebar.toggle(false); + + $("#requests-menu-empty-notice").hidden = false; + + this.empty(); + this.updateRequests(); + }, + + /** + * Update store request itmes and trigger related UI update + */ + updateRequests: function () { + this.store.dispatch(Actions.updateRequests(this.visibleItems)); + }, + + /** + * Adds odd/even attributes to all the visible items in this container. + */ + refreshZebra: function () { + let visibleItems = this.visibleItems; + + for (let i = 0, len = visibleItems.length; i < len; i++) { + let requestItem = visibleItems[i]; + let requestTarget = requestItem.target; + + if (i % 2 == 0) { + requestTarget.setAttribute("even", ""); + requestTarget.removeAttribute("odd"); + } else { + requestTarget.setAttribute("odd", ""); + requestTarget.removeAttribute("even"); + } + } + }, + + /** + * Attaches security icon click listener for the given request menu item. + * + * @param object item + * The network request item to attach the listener to. + */ + attachSecurityIconClickListener: function ({ target }) { + let icon = $(".requests-security-state-icon", target); + icon.addEventListener("click", this._onSecurityIconClick); + }, + + /** + * Schedules adding additional information to a network request. + * + * @param string id + * An identifier coming from the network monitor controller. + * @param object data + * An object containing several { key: value } tuples of network info. + * Supported keys are "httpVersion", "status", "statusText" etc. + * @param function callback + * A function to call once the request has been updated in the view. + */ + updateRequest: function (id, data, callback) { + this._updateQueue.push([id, data, callback]); + + // Lazy updating is disabled in some tests. + if (!this.lazyUpdate) { + return void this._flushRequests(); + } + + this._flushRequestsTask.arm(); + return undefined; + }, + + /** + * Starts adding all queued additional information about network requests. + */ + _flushRequests: function () { + // Prevent displaying any updates received after the target closed. + if (NetMonitorView._isDestroyed) { + return; + } + + let widget = NetMonitorView.RequestsMenu.widget; + let isScrolledToBottom = widget.isScrolledToBottom(); + + for (let [id, startedDateTime, method, url, isXHR, cause, fromCache, + fromServiceWorker] of this._addQueue) { + // Convert the received date/time string to a unix timestamp. + let unixTime = Date.parse(startedDateTime); + + // Create the element node for the network request item. + let menuView = this._createMenuView(method, url, cause); + + // Remember the first and last event boundaries. + this._registerFirstRequestStart(unixTime); + this._registerLastRequestEnd(unixTime); + + // Append a network request item to this container. + let requestItem = this.push([menuView, id], { + attachment: { + startedDeltaMillis: unixTime - this._firstRequestStartedMillis, + startedMillis: unixTime, + method: method, + url: url, + isXHR: isXHR, + cause: cause, + fromCache: fromCache, + fromServiceWorker: fromServiceWorker + } + }); + + if (id == this._preferredItemId) { + this.selectedItem = requestItem; + } + + window.emit(EVENTS.REQUEST_ADDED, id); + } + + if (isScrolledToBottom && this._addQueue.length) { + widget.scrollToBottom(); + } + + // For each queued additional information packet, get the corresponding + // request item in the view and update it based on the specified data. + for (let [id, data, callback] of this._updateQueue) { + let requestItem = this.getItemByValue(id); + if (!requestItem) { + // Packet corresponds to a dead request item, target navigated. + continue; + } + + // Each information packet may contain several { key: value } tuples of + // network info, so update the view based on each one. + for (let key in data) { + let val = data[key]; + if (val === undefined) { + // The information in the packet is empty, it can be safely ignored. + continue; + } + + switch (key) { + case "requestHeaders": + requestItem.attachment.requestHeaders = val; + break; + case "requestCookies": + requestItem.attachment.requestCookies = val; + break; + case "requestPostData": + // Search the POST data upload stream for request headers and add + // them to a separate store, different from the classic headers. + // XXX: Be really careful here! We're creating a function inside + // a loop, so remember the actual request item we want to modify. + let currentItem = requestItem; + let currentStore = { headers: [], headersSize: 0 }; + + Task.spawn(function* () { + let postData = yield gNetwork.getString(val.postData.text); + let payloadHeaders = CurlUtils.getHeadersFromMultipartText( + postData); + + currentStore.headers = payloadHeaders; + currentStore.headersSize = payloadHeaders.reduce( + (acc, { name, value }) => + acc + name.length + value.length + 2, 0); + + // The `getString` promise is async, so we need to refresh the + // information displayed in the network details pane again here. + refreshNetworkDetailsPaneIfNecessary(currentItem); + }); + + requestItem.attachment.requestPostData = val; + requestItem.attachment.requestHeadersFromUploadStream = + currentStore; + break; + case "securityState": + requestItem.attachment.securityState = val; + this.updateMenuView(requestItem, key, val); + break; + case "securityInfo": + requestItem.attachment.securityInfo = val; + break; + case "responseHeaders": + requestItem.attachment.responseHeaders = val; + break; + case "responseCookies": + requestItem.attachment.responseCookies = val; + break; + case "httpVersion": + requestItem.attachment.httpVersion = val; + break; + case "remoteAddress": + requestItem.attachment.remoteAddress = val; + this.updateMenuView(requestItem, key, val); + break; + case "remotePort": + requestItem.attachment.remotePort = val; + break; + case "status": + requestItem.attachment.status = val; + this.updateMenuView(requestItem, key, { + status: val, + cached: requestItem.attachment.fromCache, + serviceWorker: requestItem.attachment.fromServiceWorker + }); + break; + case "statusText": + requestItem.attachment.statusText = val; + let text = (requestItem.attachment.status + " " + + requestItem.attachment.statusText); + if (requestItem.attachment.fromCache) { + text += " (cached)"; + } else if (requestItem.attachment.fromServiceWorker) { + text += " (service worker)"; + } + + this.updateMenuView(requestItem, key, text); + break; + case "headersSize": + requestItem.attachment.headersSize = val; + break; + case "contentSize": + requestItem.attachment.contentSize = val; + this.updateMenuView(requestItem, key, val); + break; + case "transferredSize": + if (requestItem.attachment.fromCache) { + requestItem.attachment.transferredSize = 0; + this.updateMenuView(requestItem, key, "cached"); + } else if (requestItem.attachment.fromServiceWorker) { + requestItem.attachment.transferredSize = 0; + this.updateMenuView(requestItem, key, "service worker"); + } else { + requestItem.attachment.transferredSize = val; + this.updateMenuView(requestItem, key, val); + } + break; + case "mimeType": + requestItem.attachment.mimeType = val; + this.updateMenuView(requestItem, key, val); + break; + case "responseContent": + // If there's no mime type available when the response content + // is received, assume text/plain as a fallback. + if (!requestItem.attachment.mimeType) { + requestItem.attachment.mimeType = "text/plain"; + this.updateMenuView(requestItem, "mimeType", "text/plain"); + } + requestItem.attachment.responseContent = val; + this.updateMenuView(requestItem, key, val); + break; + case "totalTime": + requestItem.attachment.totalTime = val; + requestItem.attachment.endedMillis = + requestItem.attachment.startedMillis + val; + + this.updateMenuView(requestItem, key, val); + this._registerLastRequestEnd(requestItem.attachment.endedMillis); + break; + case "eventTimings": + requestItem.attachment.eventTimings = val; + this._createWaterfallView( + requestItem, val.timings, + requestItem.attachment.fromCache || + requestItem.attachment.fromServiceWorker + ); + break; + } + } + refreshNetworkDetailsPaneIfNecessary(requestItem); + + if (callback) { + callback(); + } + } + + /** + * Refreshes the information displayed in the sidebar, in case this update + * may have additional information about a request which isn't shown yet + * in the network details pane. + * + * @param object requestItem + * The item to repopulate the sidebar with in case it's selected in + * this requests menu. + */ + function refreshNetworkDetailsPaneIfNecessary(requestItem) { + let selectedItem = NetMonitorView.RequestsMenu.selectedItem; + if (selectedItem == requestItem) { + NetMonitorView.NetworkDetails.populate(selectedItem.attachment); + } + } + + // We're done flushing all the requests, clear the update queue. + this._updateQueue = []; + this._addQueue = []; + + $("#requests-menu-empty-notice").hidden = !!this.itemCount; + + // Make sure all the requests are sorted and filtered. + // Freshly added requests may not yet contain all the information required + // for sorting and filtering predicates, so this is done each time the + // network requests table is flushed (don't worry, events are drained first + // so this doesn't happen once per network event update). + this.sortContents(); + this.filterContents(); + this.updateRequests(); + this.refreshZebra(); + + // Rescale all the waterfalls so that everything is visible at once. + this._flushWaterfallViews(); + }, + + /** + * Customization function for creating an item's UI. + * + * @param string method + * Specifies the request method (e.g. "GET", "POST", etc.) + * @param string url + * Specifies the request's url. + * @param object cause + * Specifies the request's cause. Has two properties: + * - type: nsContentPolicyType constant + * - uri: URI of the request origin + * @return nsIDOMNode + * The network request view. + */ + _createMenuView: function (method, url, cause) { + let template = $("#requests-menu-item-template"); + let fragment = document.createDocumentFragment(); + + // Flatten the DOM by removing one redundant box (the template container). + for (let node of template.childNodes) { + fragment.appendChild(node.cloneNode(true)); + } + + this.updateMenuView(fragment, "method", method); + this.updateMenuView(fragment, "url", url); + this.updateMenuView(fragment, "cause", cause); + + return fragment; + }, + + /** + * Get a human-readable string from a number of bytes, with the B, KB, MB, or + * GB value. Note that the transition between abbreviations is by 1000 rather + * than 1024 in order to keep the displayed digits smaller as "1016 KB" is + * more awkward than 0.99 MB" + */ + getFormattedSize(bytes) { + if (bytes < MAX_BYTES_SIZE) { + return L10N.getFormatStr("networkMenu.sizeB", bytes); + } else if (bytes < MAX_KB_SIZE) { + let kb = bytes / BYTES_IN_KB; + let size = L10N.numberWithDecimals(kb, CONTENT_SIZE_DECIMALS); + return L10N.getFormatStr("networkMenu.sizeKB", size); + } else if (bytes < MAX_MB_SIZE) { + let mb = bytes / BYTES_IN_MB; + let size = L10N.numberWithDecimals(mb, CONTENT_SIZE_DECIMALS); + return L10N.getFormatStr("networkMenu.sizeMB", size); + } + let gb = bytes / BYTES_IN_GB; + let size = L10N.numberWithDecimals(gb, CONTENT_SIZE_DECIMALS); + return L10N.getFormatStr("networkMenu.sizeGB", size); }, -}; + + /** + * Updates the information displayed in a network request item view. + * + * @param object item + * The network request item in this container. + * @param string key + * The type of information that is to be updated. + * @param any value + * The new value to be shown. + * @return object + * A promise that is resolved once the information is displayed. + */ + updateMenuView: Task.async(function* (item, key, value) { + let target = item.target || item; + + switch (key) { + case "method": { + let node = $(".requests-menu-method", target); + node.setAttribute("value", value); + break; + } + case "url": { + let nameWithQuery = getUrlBaseNameWithQuery(value); + let hostPort = getUrlHost(value); + let host = getUrlHostName(value); + let unicodeUrl = decodeUnicodeUrl(value); + + let file = $(".requests-menu-file", target); + file.setAttribute("value", nameWithQuery); + file.setAttribute("tooltiptext", unicodeUrl); + + let domain = $(".requests-menu-domain", target); + domain.setAttribute("value", hostPort); + domain.setAttribute("tooltiptext", hostPort); + + // Mark local hosts specially, where "local" is as defined in the W3C + // spec for secure contexts. + // http://www.w3.org/TR/powerful-features/ + // + // * If the name falls under 'localhost' + // * If the name is an IPv4 address within 127.0.0.0/8 + // * If the name is an IPv6 address within ::1/128 + // + // IPv6 parsing is a little sloppy; it assumes that the address has + // been validated before it gets here. + let icon = $(".requests-security-state-icon", target); + icon.classList.remove("security-state-local"); + if (host.match(/(.+\.)?localhost$/) || + host.match(/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}/) || + host.match(/\[[0:]+1\]/)) { + let tooltip = L10N.getStr("netmonitor.security.state.secure"); + icon.classList.add("security-state-local"); + icon.setAttribute("tooltiptext", tooltip); + } + + break; + } + case "remoteAddress": + let domain = $(".requests-menu-domain", target); + let tooltip = (domain.getAttribute("value") + + (value ? " (" + value + ")" : "")); + domain.setAttribute("tooltiptext", tooltip); + break; + case "securityState": { + let icon = $(".requests-security-state-icon", target); + this.attachSecurityIconClickListener(item); + + // Security icon for local hosts is set in the "url" branch + if (icon.classList.contains("security-state-local")) { + break; + } + + let tooltip2 = L10N.getStr("netmonitor.security.state." + value); + icon.classList.add("security-state-" + value); + icon.setAttribute("tooltiptext", tooltip2); + break; + } + case "status": { + let node = $(".requests-menu-status-icon", target); + // "code" attribute is only used by css to determine the icon color + let code; + if (value.cached) { + code = "cached"; + } else if (value.serviceWorker) { + code = "service worker"; + } else { + code = value.status; + } + node.setAttribute("code", code); + let codeNode = $(".requests-menu-status-code", target); + codeNode.setAttribute("value", value.status); + break; + } + case "statusText": { + let node = $(".requests-menu-status", target); + node.setAttribute("tooltiptext", value); + break; + } + case "cause": { + let labelNode = $(".requests-menu-cause-label", target); + labelNode.setAttribute("value", loadCauseString(value.type)); + if (value.loadingDocumentUri) { + labelNode.setAttribute("tooltiptext", value.loadingDocumentUri); + } + + let stackNode = $(".requests-menu-cause-stack", target); + if (value.stacktrace && value.stacktrace.length > 0) { + stackNode.removeAttribute("hidden"); + } + break; + } + case "contentSize": { + let node = $(".requests-menu-size", target); + + let text = this.getFormattedSize(value); + + node.setAttribute("value", text); + node.setAttribute("tooltiptext", text); + break; + } + case "transferredSize": { + let node = $(".requests-menu-transferred", target); + + let text; + if (value === null) { + text = L10N.getStr("networkMenu.sizeUnavailable"); + } else if (value === "cached") { + text = L10N.getStr("networkMenu.sizeCached"); + node.classList.add("theme-comment"); + } else if (value === "service worker") { + text = L10N.getStr("networkMenu.sizeServiceWorker"); + node.classList.add("theme-comment"); + } else { + text = this.getFormattedSize(value); + } + + node.setAttribute("value", text); + node.setAttribute("tooltiptext", text); + break; + } + case "mimeType": { + let type = getAbbreviatedMimeType(value); + let node = $(".requests-menu-type", target); + let text = CONTENT_MIME_TYPE_ABBREVIATIONS[type] || type; + node.setAttribute("value", text); + node.setAttribute("tooltiptext", value); + break; + } + case "responseContent": { + let { mimeType } = item.attachment; + + if (mimeType.includes("image/")) { + let { text, encoding } = value.content; + let responseBody = yield gNetwork.getString(text); + let node = $(".requests-menu-icon", item.target); + node.src = formDataURI(mimeType, encoding, responseBody); + node.setAttribute("type", "thumbnail"); + node.removeAttribute("hidden"); + + window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); + } + break; + } + case "totalTime": { + let node = $(".requests-menu-timings-total", target); + + // integer + let text = L10N.getFormatStr("networkMenu.totalMS", value); + node.setAttribute("value", text); + node.setAttribute("tooltiptext", text); + break; + } + } + }), + + /** + * Creates a waterfall representing timing information in a network + * request item view. + * + * @param object item + * The network request item in this container. + * @param object timings + * An object containing timing information. + * @param boolean fromCache + * Indicates if the result came from the browser cache or + * a service worker + */ + _createWaterfallView: function (item, timings, fromCache) { + let { target } = item; + let sections = ["blocked", "dns", "connect", "send", "wait", "receive"]; + // Skipping "blocked" because it doesn't work yet. + + let timingsNode = $(".requests-menu-timings", target); + let timingsTotal = $(".requests-menu-timings-total", timingsNode); + + if (fromCache) { + timingsTotal.style.display = "none"; + return; + } + + // Add a set of boxes representing timing information. + for (let key of sections) { + let width = timings[key]; + + // Don't render anything if it surely won't be visible. + // One millisecond == one unscaled pixel. + if (width > 0) { + let timingBox = document.createElement("hbox"); + timingBox.className = "requests-menu-timings-box " + key; + timingBox.setAttribute("width", width); + timingsNode.insertBefore(timingBox, timingsTotal); + } + } + }, + + /** + * Rescales and redraws all the waterfall views in this container. + * + * @param boolean reset + * True if this container's width was changed. + */ + _flushWaterfallViews: function (reset) { + // Don't paint things while the waterfall view isn't even visible, + // or there are no items added to this container. + if (NetMonitorView.currentFrontendMode != + "network-inspector-view" || !this.itemCount) { + return; + } + + // To avoid expensive operations like getBoundingClientRect() and + // rebuilding the waterfall background each time a new request comes in, + // stuff is cached. However, in certain scenarios like when the window + // is resized, this needs to be invalidated. + if (reset) { + this._cachedWaterfallWidth = 0; + } + + // Determine the scaling to be applied to all the waterfalls so that + // everything is visible at once. One millisecond == one unscaled pixel. + let availableWidth = this._waterfallWidth - REQUESTS_WATERFALL_SAFE_BOUNDS; + let longestWidth = this._lastRequestEndedMillis - + this._firstRequestStartedMillis; + let scale = Math.min(Math.max(availableWidth / longestWidth, EPSILON), 1); + + // Redraw and set the canvas background for each waterfall view. + this._showWaterfallDivisionLabels(scale); + this._drawWaterfallBackground(scale); + + // Apply CSS transforms to each waterfall in this container totalTime + // accurately translate and resize as needed. + for (let { target, attachment } of this) { + let timingsNode = $(".requests-menu-timings", target); + let totalNode = $(".requests-menu-timings-total", target); + let direction = window.isRTL ? -1 : 1; + + // Render the timing information at a specific horizontal translation + // based on the delta to the first monitored event network. + let translateX = "translateX(" + (direction * + attachment.startedDeltaMillis) + "px)"; + + // Based on the total time passed until the last request, rescale + // all the waterfalls to a reasonable size. + let scaleX = "scaleX(" + scale + ")"; + + // Certain nodes should not be scaled, even if they're children of + // another scaled node. In this case, apply a reversed transformation. + let revScaleX = "scaleX(" + (1 / scale) + ")"; + + timingsNode.style.transform = scaleX + " " + translateX; + totalNode.style.transform = revScaleX; + } + }, + + /** + * Creates the labels displayed on the waterfall header in this container. + * + * @param number scale + * The current waterfall scale. + */ + _showWaterfallDivisionLabels: function (scale) { + let container = $("#requests-menu-waterfall-label-wrapper"); + let availableWidth = this._waterfallWidth - REQUESTS_WATERFALL_SAFE_BOUNDS; + + // Nuke all existing labels. + while (container.hasChildNodes()) { + container.firstChild.remove(); + } + + // Build new millisecond tick labels... + let timingStep = REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE; + let optimalTickIntervalFound = false; + + while (!optimalTickIntervalFound) { + // Ignore any divisions that would end up being too close to each other. + let scaledStep = scale * timingStep; + if (scaledStep < REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN) { + timingStep <<= 1; + continue; + } + optimalTickIntervalFound = true; + + // Insert one label for each division on the current scale. + let fragment = document.createDocumentFragment(); + let direction = window.isRTL ? -1 : 1; + + for (let x = 0; x < availableWidth; x += scaledStep) { + let translateX = "translateX(" + ((direction * x) | 0) + "px)"; + let millisecondTime = x / scale; + + let normalizedTime = millisecondTime; + let divisionScale = "millisecond"; + + // If the division is greater than 1 minute. + if (normalizedTime > 60000) { + normalizedTime /= 60000; + divisionScale = "minute"; + } else if (normalizedTime > 1000) { + // If the division is greater than 1 second. + normalizedTime /= 1000; + divisionScale = "second"; + } + + // Showing too many decimals is bad UX. + if (divisionScale == "millisecond") { + normalizedTime |= 0; + } else { + normalizedTime = L10N.numberWithDecimals(normalizedTime, + REQUEST_TIME_DECIMALS); + } + + let node = document.createElement("label"); + let text = L10N.getFormatStr("networkMenu." + + divisionScale, normalizedTime); + node.className = "plain requests-menu-timings-division"; + node.setAttribute("division-scale", divisionScale); + node.style.transform = translateX; + + node.setAttribute("value", text); + fragment.appendChild(node); + } + container.appendChild(fragment); + + container.className = "requests-menu-waterfall-visible"; + } + }, + + /** + * Creates the background displayed on each waterfall view in this container. + * + * @param number scale + * The current waterfall scale. + */ + _drawWaterfallBackground: function (scale) { + if (!this._canvas || !this._ctx) { + this._canvas = document.createElementNS(HTML_NS, "canvas"); + this._ctx = this._canvas.getContext("2d"); + } + let canvas = this._canvas; + let ctx = this._ctx; + + // Nuke the context. + let canvasWidth = canvas.width = this._waterfallWidth; + // Awww yeah, 1px, repeats on Y axis. + let canvasHeight = canvas.height = 1; + + // Start over. + let imageData = ctx.createImageData(canvasWidth, canvasHeight); + let pixelArray = imageData.data; + + let buf = new ArrayBuffer(pixelArray.length); + let view8bit = new Uint8ClampedArray(buf); + let view32bit = new Uint32Array(buf); + + // Build new millisecond tick lines... + let timingStep = REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE; + let [r, g, b] = REQUESTS_WATERFALL_BACKGROUND_TICKS_COLOR_RGB; + let alphaComponent = REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN; + let optimalTickIntervalFound = false; + + while (!optimalTickIntervalFound) { + // Ignore any divisions that would end up being too close to each other. + let scaledStep = scale * timingStep; + if (scaledStep < REQUESTS_WATERFALL_BACKGROUND_TICKS_SPACING_MIN) { + timingStep <<= 1; + continue; + } + optimalTickIntervalFound = true; + + // Insert one pixel for each division on each scale. + for (let i = 1; i <= REQUESTS_WATERFALL_BACKGROUND_TICKS_SCALES; i++) { + let increment = scaledStep * Math.pow(2, i); + for (let x = 0; x < canvasWidth; x += increment) { + let position = (window.isRTL ? canvasWidth - x : x) | 0; + view32bit[position] = + (alphaComponent << 24) | (b << 16) | (g << 8) | r; + } + alphaComponent += REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD; + } + } + + { + let t = NetMonitorController.NetworkEventsHandler + .firstDocumentDOMContentLoadedTimestamp; + + let delta = Math.floor((t - this._firstRequestStartedMillis) * scale); + let [r1, g1, b1, a1] = + REQUESTS_WATERFALL_DOMCONTENTLOADED_TICKS_COLOR_RGBA; + view32bit[delta] = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; + } + { + let t = NetMonitorController.NetworkEventsHandler + .firstDocumentLoadTimestamp; + + let delta = Math.floor((t - this._firstRequestStartedMillis) * scale); + let [r2, g2, b2, a2] = REQUESTS_WATERFALL_LOAD_TICKS_COLOR_RGBA; + view32bit[delta] = (a2 << 24) | (r2 << 16) | (g2 << 8) | b2; + } + + // Flush the image data and cache the waterfall background. + pixelArray.set(view8bit); + ctx.putImageData(imageData, 0, 0); + document.mozSetImageElement("waterfall-background", canvas); + }, + + /** + * The selection listener for this container. + */ + _onSelect: function ({ detail: item }) { + if (item) { + NetMonitorView.Sidebar.populate(item.attachment); + NetMonitorView.Sidebar.toggle(true); + } else { + NetMonitorView.Sidebar.toggle(false); + } + }, + + /** + * The swap listener for this container. + * Called when two items switch places, when the contents are sorted. + */ + _onSwap: function ({ detail: [firstItem, secondItem] }) { + // Reattach click listener to the security icons + this.attachSecurityIconClickListener(firstItem); + this.attachSecurityIconClickListener(secondItem); + }, + + /** + * The predicate used when deciding whether a popup should be shown + * over a request item or not. + * + * @param nsIDOMNode target + * The element node currently being hovered. + * @param object tooltip + * The current tooltip instance. + * @return {Promise} + */ + _onHover: Task.async(function* (target, tooltip) { + let requestItem = this.getItemForElement(target); + if (!requestItem) { + return false; + } + + let hovered = requestItem.attachment; + if (hovered.responseContent && target.closest(".requests-menu-icon-and-file")) { + return this._setTooltipImageContent(tooltip, requestItem); + } else if (hovered.cause && target.closest(".requests-menu-cause-stack")) { + return this._setTooltipStackTraceContent(tooltip, requestItem); + } + + return false; + }), + + _setTooltipImageContent: Task.async(function* (tooltip, requestItem) { + let { mimeType, text, encoding } = requestItem.attachment.responseContent.content; + + if (!mimeType || !mimeType.includes("image/")) { + return false; + } + + let string = yield gNetwork.getString(text); + let src = formDataURI(mimeType, encoding, string); + let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM; + let { naturalWidth, naturalHeight } = yield getImageDimensions(tooltip.doc, src); + let options = { maxDim, naturalWidth, naturalHeight }; + setImageTooltip(tooltip, tooltip.doc, src, options); + + return $(".requests-menu-icon", requestItem.target); + }), + + _setTooltipStackTraceContent: Task.async(function* (tooltip, requestItem) { + let {stacktrace} = requestItem.attachment.cause; + + if (!stacktrace || stacktrace.length == 0) { + return false; + } + + let doc = tooltip.doc; + let el = doc.createElementNS(HTML_NS, "div"); + el.className = "stack-trace-tooltip devtools-monospace"; + + for (let f of stacktrace) { + let { functionName, filename, lineNumber, columnNumber, asyncCause } = f; + + if (asyncCause) { + // if there is asyncCause, append a "divider" row into the trace + let asyncFrameEl = doc.createElementNS(HTML_NS, "div"); + asyncFrameEl.className = "stack-frame stack-frame-async"; + asyncFrameEl.textContent = + WEBCONSOLE_L10N.getFormatStr("stacktrace.asyncStack", asyncCause); + el.appendChild(asyncFrameEl); + } + + // Parse a source name in format "url -> url" + let sourceUrl = filename.split(" -> ").pop(); + + let frameEl = doc.createElementNS(HTML_NS, "div"); + frameEl.className = "stack-frame stack-frame-call"; + + let funcEl = doc.createElementNS(HTML_NS, "span"); + funcEl.className = "stack-frame-function-name"; + funcEl.textContent = + functionName || WEBCONSOLE_L10N.getStr("stacktrace.anonymousFunction"); + frameEl.appendChild(funcEl); + + let sourceEl = doc.createElementNS(HTML_NS, "span"); + sourceEl.className = "stack-frame-source-name"; + frameEl.appendChild(sourceEl); + + let sourceInnerEl = doc.createElementNS(HTML_NS, "span"); + sourceInnerEl.className = "stack-frame-source-name-inner"; + sourceEl.appendChild(sourceInnerEl); + + sourceInnerEl.textContent = sourceUrl; + sourceInnerEl.title = sourceUrl; + + let lineEl = doc.createElementNS(HTML_NS, "span"); + lineEl.className = "stack-frame-line"; + lineEl.textContent = `:${lineNumber}:${columnNumber}`; + sourceInnerEl.appendChild(lineEl); + + frameEl.addEventListener("click", () => { + // hide the tooltip immediately, not after delay + tooltip.hide(); + NetMonitorController.viewSourceInDebugger(filename, lineNumber); + }, false); + + el.appendChild(frameEl); + } + + tooltip.setContent(el, {width: REQUESTS_TOOLTIP_STACK_TRACE_WIDTH}); + + return true; + }), + + /** + * A handler that opens the security tab in the details view if secure or + * broken security indicator is clicked. + */ + _onSecurityIconClick: function (e) { + let state = this.selectedItem.attachment.securityState; + if (state !== "insecure") { + // Choose the security tab. + NetMonitorView.NetworkDetails.widget.selectedIndex = 5; + } + }, + + /** + * The resize listener for this container's window. + */ + _onResize: function (e) { + // Allow requests to settle down first. + setNamedTimeout("resize-events", + RESIZE_REFRESH_RATE, () => this._flushWaterfallViews(true)); + }, + + /** + * Scroll listener for the requests menu view. + */ + _onScroll: function () { + this.tooltip.hide(); + }, + + /** + * Open context menu + */ + _onContextMenu: function (e) { + e.preventDefault(); + this.contextMenu.open(e); + }, + + /** + * Checks if the specified unix time is the first one to be known of, + * and saves it if so. + * + * @param number unixTime + * The milliseconds to check and save. + */ + _registerFirstRequestStart: function (unixTime) { + if (this._firstRequestStartedMillis == -1) { + this._firstRequestStartedMillis = unixTime; + } + }, + + /** + * Checks if the specified unix time is the last one to be known of, + * and saves it if so. + * + * @param number unixTime + * The milliseconds to check and save. + */ + _registerLastRequestEnd: function (unixTime) { + if (this._lastRequestEndedMillis < unixTime) { + this._lastRequestEndedMillis = unixTime; + } + }, + + /** + * Gets the available waterfall width in this container. + * @return number + */ + get _waterfallWidth() { + if (this._cachedWaterfallWidth == 0) { + let container = $("#requests-menu-toolbar"); + let waterfall = $("#requests-menu-waterfall-header-box"); + let containerBounds = container.getBoundingClientRect(); + let waterfallBounds = waterfall.getBoundingClientRect(); + if (!window.isRTL) { + this._cachedWaterfallWidth = containerBounds.width - + waterfallBounds.left; + } else { + this._cachedWaterfallWidth = waterfallBounds.right; + } + } + return this._cachedWaterfallWidth; + }, + + _splitter: null, + _summary: null, + _canvas: null, + _ctx: null, + _cachedWaterfallWidth: 0, + _firstRequestStartedMillis: -1, + _lastRequestEndedMillis: -1, + _updateQueue: [], + _addQueue: [], + _updateTimeout: null, + _resizeTimeout: null, + _activeFilters: ["all"], + _currentFreetextFilter: "" +}); exports.RequestsMenuView = RequestsMenuView;
deleted file mode 100644 --- a/devtools/client/netmonitor/selectors/filters.js +++ /dev/null @@ -1,13 +0,0 @@ -/* 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"; - -function getActiveFilters(state) { - return state.filters.types.toSeq().filter(checked => checked).keySeq().toArray(); -} - -module.exports = { - getActiveFilters -};
--- a/devtools/client/netmonitor/selectors/index.js +++ b/devtools/client/netmonitor/selectors/index.js @@ -1,15 +1,63 @@ /* 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 filters = require("./filters"); -const requests = require("./requests"); -const ui = require("./ui"); +const { createSelector } = require("devtools/client/shared/vendor/reselect"); + +/** + * Gets the total number of bytes representing the cumulated content size of + * a set of requests. Returns 0 for an empty set. + * + * @param {array} items - an array of request items + * @return {number} total bytes of requests + */ +function getTotalBytesOfRequests(items) { + if (!items.length) { + return 0; + } + + let result = 0; + items.forEach((item) => { + let size = item.attachment.contentSize; + result += (typeof size == "number") ? size : 0; + }); + + return result; +} -Object.assign(exports, - filters, - requests, - ui +/** + * Gets the total milliseconds for all requests. Returns null for an + * empty set. + * + * @param {array} items - an array of request items + * @return {object} total milliseconds for all requests + */ +function getTotalMillisOfRequests(items) { + if (!items.length) { + return null; + } + + const oldest = items.reduce((prev, curr) => + prev.attachment.startedMillis < curr.attachment.startedMillis ? + prev : curr); + const newest = items.reduce((prev, curr) => + prev.attachment.startedMillis > curr.attachment.startedMillis ? + prev : curr); + + return newest.attachment.endedMillis - oldest.attachment.startedMillis; +} + +const getSummary = createSelector( + (state) => state.requests.items, + (requests) => ({ + count: requests.length, + totalBytes: getTotalBytesOfRequests(requests), + totalMillis: getTotalMillisOfRequests(requests), + }) ); + +module.exports = { + getSummary, +};
--- a/devtools/client/netmonitor/selectors/moz.build +++ b/devtools/client/netmonitor/selectors/moz.build @@ -1,10 +1,7 @@ # 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', - 'requests.js', - 'ui.js', + 'index.js' )
deleted file mode 100644 --- a/devtools/client/netmonitor/selectors/requests.js +++ /dev/null @@ -1,119 +0,0 @@ -/* 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 { createSelector } = require("devtools/client/shared/vendor/reselect"); -const { Filters, isFreetextMatch } = require("../filter-predicates"); -const { Sorters } = require("../sort-predicates"); - -/** - * Check if the given requests is a clone, find and return the original request if it is. - * Cloned requests are sorted by comparing the original ones. - */ -function getOrigRequest(requests, req) { - if (!req.id.endsWith("-clone")) { - return req; - } - - const origId = req.id.replace(/-clone$/, ""); - return requests.find(r => r.id === origId); -} - -const getFilterFn = createSelector( - state => state.filters, - filters => r => { - const matchesType = filters.types.some((enabled, filter) => { - return enabled && Filters[filter] && Filters[filter](r); - }); - return matchesType && isFreetextMatch(r, filters.text); - } -); - -const getSortFn = createSelector( - state => state.requests.requests, - state => state.sort, - (requests, sort) => { - let dataSorter = Sorters[sort.type || "waterfall"]; - - function sortWithClones(a, b) { - // If one request is a clone of the other, sort them next to each other - if (a.id == b.id + "-clone") { - return +1; - } else if (a.id + "-clone" == b.id) { - return -1; - } - - // Otherwise, get the original requests and compare them - return dataSorter( - getOrigRequest(requests, a), - getOrigRequest(requests, b) - ); - } - - const ascending = sort.ascending ? +1 : -1; - return (a, b) => ascending * sortWithClones(a, b, dataSorter); - } -); - -const getSortedRequests = createSelector( - state => state.requests.requests, - getSortFn, - (requests, sortFn) => requests.sort(sortFn) -); - -const getDisplayedRequests = createSelector( - state => state.requests.requests, - getFilterFn, - getSortFn, - (requests, filterFn, sortFn) => requests.filter(filterFn).sort(sortFn) -); - -const getDisplayedRequestsSummary = createSelector( - getDisplayedRequests, - state => state.requests.lastEndedMillis - state.requests.firstStartedMillis, - (requests, totalMillis) => { - if (requests.size == 0) { - return { count: 0, bytes: 0, millis: 0 }; - } - - const totalBytes = requests.reduce((total, item) => { - if (typeof item.contentSize == "number") { - total += item.contentSize; - } - return total; - }, 0); - - return { - count: requests.size, - bytes: totalBytes, - millis: totalMillis, - }; - } -); - -function getRequestById(state, id) { - return state.requests.requests.find(r => r.id === id); -} - -function getDisplayedRequestById(state, id) { - return getDisplayedRequests(state).find(r => r.id === id); -} - -function getSelectedRequest(state) { - if (!state.requests.selectedId) { - return null; - } - - return getRequestById(state, state.requests.selectedId); -} - -module.exports = { - getSortedRequests, - getDisplayedRequests, - getDisplayedRequestsSummary, - getRequestById, - getDisplayedRequestById, - getSelectedRequest, -};
deleted file mode 100644 --- a/devtools/client/netmonitor/selectors/ui.js +++ /dev/null @@ -1,32 +0,0 @@ -/* 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 { getDisplayedRequests } = require("./requests"); - -function isSidebarToggleButtonDisabled(state) { - return getDisplayedRequests(state).isEmpty(); -} - -const EPSILON = 0.001; - -function getWaterfallScale(state) { - const { requests, timingMarkers, ui } = state; - - if (requests.firstStartedMillis == +Infinity) { - return null; - } - - const lastEventMillis = Math.max(requests.lastEndedMillis, - timingMarkers.firstDocumentDOMContentLoadedTimestamp, - timingMarkers.firstDocumentLoadTimestamp); - const longestWidth = lastEventMillis - requests.firstStartedMillis; - return Math.min(Math.max(ui.waterfallWidth / longestWidth, EPSILON), 1); -} - -module.exports = { - isSidebarToggleButtonDisabled, - getWaterfallScale, -};
--- a/devtools/client/netmonitor/sidebar-view.js +++ b/devtools/client/netmonitor/sidebar-view.js @@ -1,13 +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/. */ -/* globals window, dumpn, $, NetMonitorView */ +/* import-globals-from ./netmonitor-controller.js */ +/* globals dumpn, $, NetMonitorView */ "use strict"; const { Task } = require("devtools/shared/task"); const { EVENTS } = require("./events"); /** * Functions handling the sidebar details view. @@ -20,16 +21,17 @@ SidebarView.prototype = { /** * Sets this view hidden or visible. It's visible by default. * * @param boolean visibleFlag * Specifies the intended visibility. */ toggle: function (visibleFlag) { NetMonitorView.toggleDetailsPane({ visible: visibleFlag }); + NetMonitorView.RequestsMenu._flushWaterfallViews(true); }, /** * Populates this view with the specified data. * * @param object data * The data source (this should be the attachment of a request item). * @return object
--- a/devtools/client/netmonitor/sort-predicates.js +++ b/devtools/client/netmonitor/sort-predicates.js @@ -3,16 +3,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const { getAbbreviatedMimeType, getUrlBaseNameWithQuery, getUrlHost, + loadCauseString, } = require("./request-utils"); /** * Predicates used when sorting items. * * @param object first * The first item used in the comparison. * @param object second @@ -54,18 +55,18 @@ function domain(first, second) { let secondDomain = getUrlHost(second.url).toLowerCase(); if (firstDomain == secondDomain) { return first.startedMillis - second.startedMillis; } return firstDomain > secondDomain ? 1 : -1; } function cause(first, second) { - let firstCause = first.cause.type; - let secondCause = second.cause.type; + let firstCause = loadCauseString(first.cause.type); + let secondCause = loadCauseString(second.cause.type); if (firstCause == secondCause) { return first.startedMillis - second.startedMillis; } return firstCause > secondCause ? 1 : -1; } function type(first, second) { let firstType = getAbbreviatedMimeType(first.mimeType).toLowerCase();
--- a/devtools/client/netmonitor/store.js +++ b/devtools/client/netmonitor/store.js @@ -1,22 +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 { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux"); -const { thunk } = require("devtools/client/shared/redux/middleware/thunk"); -const batching = require("./middleware/batching"); -const rootReducer = require("./reducers/index"); +const createStore = require("devtools/client/shared/redux/create-store"); +const reducers = require("./reducers/index"); function configureStore() { - return createStore( - rootReducer, - applyMiddleware( - thunk, - batching - ) - ); + return createStore()(reducers); } exports.configureStore = configureStore;
--- a/devtools/client/netmonitor/test/browser.ini +++ b/devtools/client/netmonitor/test/browser.ini @@ -49,16 +49,17 @@ support-files = !/devtools/client/framework/test/shared-head.js [browser_net_aaa_leaktest.js] [browser_net_accessibility-01.js] [browser_net_accessibility-02.js] skip-if = (toolkit == "cocoa" && e10s) # bug 1252254 [browser_net_api-calls.js] [browser_net_autoscroll.js] +skip-if = true # Bug 1309191 - replace with rewritten version in React [browser_net_cached-status.js] [browser_net_cause.js] [browser_net_cause_redirect.js] [browser_net_service-worker-status.js] [browser_net_charts-01.js] [browser_net_charts-02.js] [browser_net_charts-03.js] [browser_net_charts-04.js] @@ -83,17 +84,17 @@ subsuite = clipboard [browser_net_copy_headers.js] subsuite = clipboard [browser_net_copy_as_curl.js] subsuite = clipboard [browser_net_cors_requests.js] [browser_net_cyrillic-01.js] [browser_net_cyrillic-02.js] [browser_net_details-no-duplicated-content.js] -skip-if = true # Test broken in React version, is too low-level +skip-if = (os == 'linux' && e10s && debug) # Bug 1242204 [browser_net_frame.js] [browser_net_filter-01.js] [browser_net_filter-02.js] [browser_net_filter-03.js] [browser_net_filter-04.js] [browser_net_footer-summary.js] [browser_net_html-preview.js] [browser_net_icon-preview.js] @@ -134,21 +135,19 @@ skip-if = true # Test broken in React ve [browser_net_send-beacon-other-tab.js] [browser_net_simple-init.js] [browser_net_simple-request-data.js] skip-if = true # Bug 1258809 [browser_net_simple-request-details.js] skip-if = true # Bug 1258809 [browser_net_simple-request.js] [browser_net_sort-01.js] -skip-if = true # Redundant for React/Redux version [browser_net_sort-02.js] [browser_net_sort-03.js] [browser_net_statistics-01.js] [browser_net_statistics-02.js] [browser_net_statistics-03.js] [browser_net_status-codes.js] [browser_net_streaming-response.js] [browser_net_throttle.js] [browser_net_timeline_ticks.js] -skip-if = true # TODO: fix the test [browser_net_timing-division.js] [browser_net_persistent_logs.js]
--- a/devtools/client/netmonitor/test/browser_net_accessibility-01.js +++ b/devtools/client/netmonitor/test/browser_net_accessibility-01.js @@ -3,25 +3,23 @@ "use strict"; /** * Tests if focus modifiers work for the SideMenuWidget. */ add_task(function* () { - let Actions = require("devtools/client/netmonitor/actions/index"); - let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); info("Starting test... "); // It seems that this test may be slow on Ubuntu builds running on ec2. requestLongerTimeout(2); - let { NetMonitorView, gStore } = monitor.panelWin; + let { NetMonitorView } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; RequestsMenu.lazyUpdate = false; let count = 0; function check(selectedIndex, paneVisibility) { info("Performing check " + (count++) + "."); @@ -34,51 +32,56 @@ add_task(function* () { let wait = waitForNetworkEvents(monitor, 2); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(2); }); yield wait; check(-1, false); - gStore.dispatch(Actions.selectDelta(+Infinity)); + RequestsMenu.focusLastVisibleItem(); check(1, true); - gStore.dispatch(Actions.selectDelta(-Infinity)); + RequestsMenu.focusFirstVisibleItem(); check(0, true); - gStore.dispatch(Actions.selectDelta(+1)); + RequestsMenu.focusNextItem(); check(1, true); - gStore.dispatch(Actions.selectDelta(-1)); + RequestsMenu.focusPrevItem(); check(0, true); - gStore.dispatch(Actions.selectDelta(+10)); + RequestsMenu.focusItemAtDelta(+1); check(1, true); - gStore.dispatch(Actions.selectDelta(-10)); + RequestsMenu.focusItemAtDelta(-1); + check(0, true); + + RequestsMenu.focusItemAtDelta(+10); + check(1, true); + RequestsMenu.focusItemAtDelta(-10); check(0, true); wait = waitForNetworkEvents(monitor, 18); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(18); }); yield wait; - gStore.dispatch(Actions.selectDelta(+Infinity)); + RequestsMenu.focusLastVisibleItem(); check(19, true); - gStore.dispatch(Actions.selectDelta(-Infinity)); + RequestsMenu.focusFirstVisibleItem(); check(0, true); - gStore.dispatch(Actions.selectDelta(+1)); + RequestsMenu.focusNextItem(); check(1, true); - gStore.dispatch(Actions.selectDelta(-1)); + RequestsMenu.focusPrevItem(); check(0, true); - gStore.dispatch(Actions.selectDelta(+10)); + RequestsMenu.focusItemAtDelta(+10); check(10, true); - gStore.dispatch(Actions.selectDelta(-10)); + RequestsMenu.focusItemAtDelta(-10); check(0, true); - gStore.dispatch(Actions.selectDelta(+100)); + RequestsMenu.focusItemAtDelta(+100); check(19, true); - gStore.dispatch(Actions.selectDelta(-100)); + RequestsMenu.focusItemAtDelta(-100); check(0, true); - return teardown(monitor); + yield teardown(monitor); });
--- a/devtools/client/netmonitor/test/browser_net_accessibility-02.js +++ b/devtools/client/netmonitor/test/browser_net_accessibility-02.js @@ -30,18 +30,16 @@ add_task(function* () { } let wait = waitForNetworkEvents(monitor, 2); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(2); }); yield wait; - $(".requests-menu-contents").focus(); - check(-1, false); EventUtils.sendKey("DOWN", window); check(0, true); EventUtils.sendKey("UP", window); check(0, true); EventUtils.sendKey("PAGE_DOWN", window); @@ -120,13 +118,13 @@ add_task(function* () { EventUtils.sendKey("END", window); check(19, true); EventUtils.sendKey("DOWN", window); check(19, true); EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle")); check(-1, false); - EventUtils.sendMouseEvent({ type: "mousedown" }, $(".request-list-item")); + EventUtils.sendMouseEvent({ type: "mousedown" }, $(".side-menu-widget-item")); check(0, true); yield teardown(monitor); });
--- a/devtools/client/netmonitor/test/browser_net_api-calls.js +++ b/devtools/client/netmonitor/test/browser_net_api-calls.js @@ -27,13 +27,13 @@ add_task(function* () { let wait = waitForNetworkEvents(monitor, 5); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; REQUEST_URIS.forEach(function (uri, index) { - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(index), "GET", uri); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(index), "GET", uri); }); yield teardown(monitor); });
--- a/devtools/client/netmonitor/test/browser_net_autoscroll.js +++ b/devtools/client/netmonitor/test/browser_net_autoscroll.js @@ -5,83 +5,71 @@ /** * Bug 863102 - Automatically scroll down upon new network requests. */ add_task(function* () { requestLongerTimeout(2); let { monitor } = yield initNetMonitor(INFINITE_GET_URL); - let { $ } = monitor.panelWin; - - // Wait until the first request makes the empty notice disappear - yield waitForRequestListToAppear(); - - let requestsContainer = $(".requests-menu-contents"); - ok(requestsContainer, "Container element exists as expected."); + let win = monitor.panelWin; + let topNode = win.document.getElementById("requests-menu-contents"); + let requestsContainer = topNode.getElementsByTagName("scrollbox")[0]; + ok(!!requestsContainer, "Container element exists as expected."); // (1) Check that the scroll position is maintained at the bottom // when the requests overflow the vertical size of the container. yield waitForRequestsToOverflowContainer(); yield waitForScroll(); - ok(true, "Scrolled to bottom on overflow."); + ok(scrolledToBottom(requestsContainer), "Scrolled to bottom on overflow."); - // (2) Now set the scroll position to the first item and check + // (2) Now set the scroll position somewhere in the middle and check // that additional requests do not change the scroll position. - let firstNode = requestsContainer.firstChild; - firstNode.scrollIntoView(); - yield waitSomeTime(); + let children = requestsContainer.childNodes; + let middleNode = children.item(children.length / 2); + middleNode.scrollIntoView(); ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom."); // save for comparison later let scrollTop = requestsContainer.scrollTop; yield waitForNetworkEvents(monitor, 8); yield waitSomeTime(); is(requestsContainer.scrollTop, scrollTop, "Did not scroll."); // (3) Now set the scroll position back at the bottom and check that // additional requests *do* cause the container to scroll down. requestsContainer.scrollTop = requestsContainer.scrollHeight; ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom."); yield waitForNetworkEvents(monitor, 8); yield waitForScroll(); - ok(true, "Still scrolled to bottom."); + ok(scrolledToBottom(requestsContainer), "Still scrolled to bottom."); // (4) Now select an item in the list and check that additional requests // do not change the scroll position. monitor.panelWin.NetMonitorView.RequestsMenu.selectedIndex = 0; yield waitForNetworkEvents(monitor, 8); yield waitSomeTime(); is(requestsContainer.scrollTop, 0, "Did not scroll."); // Done: clean up. - return teardown(monitor); - - function waitForRequestListToAppear() { - info("Waiting until the empty notice disappears and is replaced with the list"); - return waitUntil(() => !!$(".requests-menu-contents")); - } + yield teardown(monitor); function* waitForRequestsToOverflowContainer() { - info("Waiting for enough requests to overflow the container"); while (true) { - info("Waiting for one network request"); yield waitForNetworkEvents(monitor, 1); if (requestsContainer.scrollHeight > requestsContainer.clientHeight) { - info("The list is long enough, returning"); return; } } } function scrolledToBottom(element) { return element.scrollTop + element.clientHeight >= element.scrollHeight; } function waitSomeTime() { // Wait to make sure no scrolls happen return wait(50); } function waitForScroll() { - info("Waiting for the list to scroll to bottom"); - return waitUntil(() => scrolledToBottom(requestsContainer)); + return monitor._view.RequestsMenu.widget.once("scroll-to-bottom"); } });
--- a/devtools/client/netmonitor/test/browser_net_brotli.js +++ b/devtools/client/netmonitor/test/browser_net_brotli.js @@ -22,17 +22,17 @@ add_task(function* () { RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, BROTLI_REQUESTS); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", HTTPS_CONTENT_TYPE_SJS + "?fmt=br", { status: 200, statusText: "Connected", type: "plain", fullMimeType: "text/plain", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 10), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 64), time: true
--- a/devtools/client/netmonitor/test/browser_net_cached-status.js +++ b/devtools/client/netmonitor/test/browser_net_cached-status.js @@ -88,18 +88,17 @@ add_task(function* () { info("Performing requests #2..."); yield performRequestsAndWait(); let index = 0; for (let request of REQUEST_DATA) { let item = RequestsMenu.getItemAtIndex(index); info("Verifying request #" + index); - yield verifyRequestItemTarget(RequestsMenu, item, - request.method, request.uri, request.details); + yield verifyRequestItemTarget(item, request.method, request.uri, request.details); index++; } yield teardown(monitor); function* performRequestsAndWait() { let wait = waitForNetworkEvents(monitor, 3);
--- a/devtools/client/netmonitor/test/browser_net_cause.js +++ b/devtools/client/netmonitor/test/browser_net_cause.js @@ -10,17 +10,17 @@ const CAUSE_FILE_NAME = "html_cause-test-page.html"; const CAUSE_URL = EXAMPLE_URL + CAUSE_FILE_NAME; const EXPECTED_REQUESTS = [ { method: "GET", url: CAUSE_URL, causeType: "document", - causeUri: null, + causeUri: "", // The document load has internal privileged JS code on the stack stack: true }, { method: "GET", url: EXAMPLE_URL + "stylesheet_request", causeType: "stylesheet", causeUri: CAUSE_URL, @@ -98,21 +98,21 @@ add_task(function* () { is(RequestsMenu.itemCount, EXPECTED_REQUESTS.length, "All the page events should be recorded."); EXPECTED_REQUESTS.forEach((spec, i) => { let { method, url, causeType, causeUri, stack } = spec; let requestItem = RequestsMenu.getItemAtIndex(i); - verifyRequestItemTarget(RequestsMenu, requestItem, + verifyRequestItemTarget(requestItem, method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } } ); - let { stacktrace } = requestItem.cause; + let { stacktrace } = requestItem.attachment.cause; let stackLen = stacktrace ? stacktrace.length : 0; if (stack) { ok(stacktrace, `Request #${i} has a stacktrace`); ok(stackLen > 0, `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`); // if "stack" is array, check the details about the top stack frames @@ -132,14 +132,16 @@ add_task(function* () { is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`); } }); // Sort the requests by cause and check the order EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-cause-button")); let expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort(); expectedOrder.forEach((expectedCause, i) => { - const cause = RequestsMenu.getItemAtIndex(i).cause.type; + let { target } = RequestsMenu.getItemAtIndex(i); + let causeLabel = target.querySelector(".requests-menu-cause-label"); + let cause = causeLabel.getAttribute("value"); is(cause, expectedCause, `The request #${i} has the expected cause after sorting`); }); yield teardown(monitor); });
--- a/devtools/client/netmonitor/test/browser_net_cause_redirect.js +++ b/devtools/client/netmonitor/test/browser_net_cause_redirect.js @@ -22,21 +22,21 @@ add_task(function* () { let { RequestsMenu } = monitor.panelWin.NetMonitorView; RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length); yield performRequests(2, HSTS_SJS); yield wait; EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => { - let item = RequestsMenu.getItemAtIndex(i); + let { attachment } = RequestsMenu.getItemAtIndex(i); - is(item.status, status, `Request #${i} has the expected status`); + is(attachment.status, status, `Request #${i} has the expected status`); - let { stacktrace } = item.cause; + let { stacktrace } = attachment.cause; let stackLen = stacktrace ? stacktrace.length : 0; if (hasStack) { ok(stacktrace, `Request #${i} has a stacktrace`); ok(stackLen > 0, `Request #${i} has a stacktrace with ${stackLen} items`); } else { is(stackLen, 0, `Request #${i} has an empty stacktrace`); }
--- a/devtools/client/netmonitor/test/browser_net_content-type.js +++ b/devtools/client/netmonitor/test/browser_net_content-type.js @@ -19,72 +19,72 @@ add_task(function* () { RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", CONTENT_TYPE_SJS + "?fmt=xml", { status: 200, statusText: "OK", type: "xml", fullMimeType: "text/xml; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 42), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(1), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), "GET", CONTENT_TYPE_SJS + "?fmt=css", { status: 200, statusText: "OK", type: "css", fullMimeType: "text/css; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(2), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2), "GET", CONTENT_TYPE_SJS + "?fmt=js", { status: 200, statusText: "OK", type: "js", fullMimeType: "application/javascript; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(3), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3), "GET", CONTENT_TYPE_SJS + "?fmt=json", { status: 200, statusText: "OK", type: "json", fullMimeType: "application/json; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(4), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4), "GET", CONTENT_TYPE_SJS + "?fmt=bogus", { status: 404, statusText: "Not Found", type: "html", fullMimeType: "text/html; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 24), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(5), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5), "GET", TEST_IMAGE, { fuzzyUrl: true, status: 200, statusText: "OK", type: "png", fullMimeType: "image/png", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 580), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(6), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6), "GET", CONTENT_TYPE_SJS + "?fmt=gzip", { status: 200, statusText: "OK", type: "plain", fullMimeType: "text/plain", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 73), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 10.73), time: true
--- a/devtools/client/netmonitor/test/browser_net_copy_headers.js +++ b/devtools/client/netmonitor/test/browser_net_copy_headers.js @@ -18,17 +18,17 @@ add_task(function* () { let wait = waitForNetworkEvents(monitor, 1); tab.linkedBrowser.reload(); yield wait; let requestItem = RequestsMenu.getItemAtIndex(0); RequestsMenu.selectedItem = requestItem; - let { method, httpVersion, status, statusText } = requestItem; + let { method, httpVersion, status, statusText } = requestItem.attachment; const EXPECTED_REQUEST_HEADERS = [ `${method} ${SIMPLE_URL} ${httpVersion}`, "Host: example.com", "User-Agent: " + navigator.userAgent + "", "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language: " + navigator.languages.join(",") + ";q=0.5", "Accept-Encoding: gzip, deflate",
--- a/devtools/client/netmonitor/test/browser_net_copy_url.js +++ b/devtools/client/netmonitor/test/browser_net_copy_url.js @@ -20,12 +20,12 @@ add_task(function* () { }); yield wait; let requestItem = RequestsMenu.getItemAtIndex(0); RequestsMenu.selectedItem = requestItem; yield waitForClipboardPromise(function setup() { RequestsMenu.contextMenu.copyUrl(); - }, requestItem.url); + }, requestItem.attachment.url); yield teardown(monitor); });
--- a/devtools/client/netmonitor/test/browser_net_cors_requests.js +++ b/devtools/client/netmonitor/test/browser_net_cors_requests.js @@ -20,14 +20,13 @@ add_task(function* () { content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data"); }); info("Waiting until the requests appear in netmonitor"); yield wait; info("Checking the preflight and flight methods"); ["OPTIONS", "POST"].forEach((method, i) => { - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(i), - method, requestUrl); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), method, requestUrl); }); yield teardown(monitor); });
--- a/devtools/client/netmonitor/test/browser_net_curl-utils.js +++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js @@ -26,29 +26,29 @@ add_task(function* () { let requests = { get: RequestsMenu.getItemAtIndex(0), post: RequestsMenu.getItemAtIndex(1), multipart: RequestsMenu.getItemAtIndex(2), multipartForm: RequestsMenu.getItemAtIndex(3) }; - let data = yield createCurlData(requests.get, gNetwork); + let data = yield createCurlData(requests.get.attachment, gNetwork); testFindHeader(data); - data = yield createCurlData(requests.post, gNetwork); + data = yield createCurlData(requests.post.attachment, gNetwork); testIsUrlEncodedRequest(data); testWritePostDataTextParams(data); - data = yield createCurlData(requests.multipart, gNetwork); + data = yield createCurlData(requests.multipart.attachment, gNetwork); testIsMultipartRequest(data); testGetMultipartBoundary(data); testRemoveBinaryDataFromMultipartText(data); - data = yield createCurlData(requests.multipartForm, gNetwork); + data = yield createCurlData(requests.multipartForm.attachment, gNetwork); testGetHeadersFromMultipartText(data); if (Services.appinfo.OS != "WINNT") { testEscapeStringPosix(); } else { testEscapeStringWin(); }
--- a/devtools/client/netmonitor/test/browser_net_cyrillic-01.js +++ b/devtools/client/netmonitor/test/browser_net_cyrillic-01.js @@ -17,17 +17,17 @@ add_task(function* () { RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, 1); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", CONTENT_TYPE_SJS + "?fmt=txt", { status: 200, statusText: "DA DA DA" }); EventUtils.sendMouseEvent({ type: "mousedown" }, document.getElementById("details-pane-toggle")); EventUtils.sendMouseEvent({ type: "mousedown" },
--- a/devtools/client/netmonitor/test/browser_net_cyrillic-02.js +++ b/devtools/client/netmonitor/test/browser_net_cyrillic-02.js @@ -16,17 +16,17 @@ add_task(function* () { let { RequestsMenu } = NetMonitorView; RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, 1); tab.linkedBrowser.reload(); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", CYRILLIC_URL, { status: 200, statusText: "OK" }); EventUtils.sendMouseEvent({ type: "mousedown" }, document.getElementById("details-pane-toggle")); EventUtils.sendMouseEvent({ type: "mousedown" },
--- a/devtools/client/netmonitor/test/browser_net_filter-01.js +++ b/devtools/client/netmonitor/test/browser_net_filter-01.js @@ -23,119 +23,18 @@ const REQUESTS_WITH_MEDIA_AND_FLASH = RE { url: "sjs_content-type-test-server.sjs?fmt=flash" }, ]); 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" }, ]); -const EXPECTED_REQUESTS = [ - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=html", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "html", - fullMimeType: "text/html; charset=utf-8" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=css", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "css", - fullMimeType: "text/css; charset=utf-8" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=js", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "js", - fullMimeType: "application/javascript; charset=utf-8" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=font", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "woff", - fullMimeType: "font/woff" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=image", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "png", - fullMimeType: "image/png" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=audio", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "ogg", - fullMimeType: "audio/ogg" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=video", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "webm", - fullMimeType: "video/webm" - }, - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=flash", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "x-shockwave-flash", - fullMimeType: "application/x-shockwave-flash" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=ws", - data: { - fuzzyUrl: true, - status: 101, - statusText: "Switching Protocols", - } - } -]; - add_task(function* () { let Actions = require("devtools/client/netmonitor/actions/index"); - let { monitor } = yield initNetMonitor(FILTERING_URL); let { gStore } = monitor.panelWin; function setFreetextFilter(value) { gStore.dispatch(Actions.setFilterText(value)); } info("Starting test... "); @@ -276,30 +175,90 @@ add_task(function* () { function testContents(visibility) { isnot(RequestsMenu.selectedItem, null, "There should still be a selected item after filtering."); is(RequestsMenu.selectedIndex, 0, "The first item should be still selected after filtering."); is(NetMonitorView.detailsPaneHidden, false, "The details pane should still be visible after filtering."); - const items = RequestsMenu.items; - const visibleItems = RequestsMenu.visibleItems; - - is(items.size, visibility.length, + is(RequestsMenu.items.length, visibility.length, "There should be a specific amount of items in the requests menu."); - is(visibleItems.size, visibility.filter(e => e).length, - "There should be a specific amount of visible items in the requests menu."); + is(RequestsMenu.visibleItems.length, visibility.filter(e => e).length, + "There should be a specific amount of visbile items in the requests menu."); for (let i = 0; i < visibility.length; i++) { - let itemId = items.get(i).id; - let shouldBeVisible = !!visibility[i]; - let isThere = visibleItems.some(r => r.id == itemId); - is(isThere, shouldBeVisible, - `The item at index ${i} has visibility=${shouldBeVisible}`); + is(RequestsMenu.getItemAtIndex(i).target.hidden, !visibility[i], + "The item at index " + i + " doesn't have the correct hidden state."); + } - if (shouldBeVisible) { - let { method, url, data } = EXPECTED_REQUESTS[i]; - verifyRequestItemTarget(RequestsMenu, items.get(i), method, url, data); - } - } + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=html", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "html", + fullMimeType: "text/html; charset=utf-8" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), + "GET", CONTENT_TYPE_SJS + "?fmt=css", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "css", + fullMimeType: "text/css; charset=utf-8" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2), + "GET", CONTENT_TYPE_SJS + "?fmt=js", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "js", + fullMimeType: "application/javascript; charset=utf-8" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3), + "GET", CONTENT_TYPE_SJS + "?fmt=font", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "woff", + fullMimeType: "font/woff" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4), + "GET", CONTENT_TYPE_SJS + "?fmt=image", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "png", + fullMimeType: "image/png" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5), + "GET", CONTENT_TYPE_SJS + "?fmt=audio", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "ogg", + fullMimeType: "audio/ogg" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6), + "GET", CONTENT_TYPE_SJS + "?fmt=video", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "webm", + fullMimeType: "video/webm" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(7), + "GET", CONTENT_TYPE_SJS + "?fmt=flash", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "x-shockwave-flash", + fullMimeType: "application/x-shockwave-flash" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(8), + "GET", CONTENT_TYPE_SJS + "?fmt=ws", { + fuzzyUrl: true, + status: 101, + statusText: "Switching Protocols", + }); } });
--- a/devtools/client/netmonitor/test/browser_net_filter-02.js +++ b/devtools/client/netmonitor/test/browser_net_filter-02.js @@ -24,116 +24,16 @@ const REQUESTS_WITH_MEDIA_AND_FLASH = RE { url: "sjs_content-type-test-server.sjs?fmt=flash" }, ]); 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" }, ]); -const EXPECTED_REQUESTS = [ - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=html", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "html", - fullMimeType: "text/html; charset=utf-8" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=css", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "css", - fullMimeType: "text/css; charset=utf-8" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=js", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "js", - fullMimeType: "application/javascript; charset=utf-8" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=font", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "woff", - fullMimeType: "font/woff" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=image", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "png", - fullMimeType: "image/png" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=audio", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "ogg", - fullMimeType: "audio/ogg" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=video", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "webm", - fullMimeType: "video/webm" - }, - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=flash", - data: { - fuzzyUrl: true, - status: 200, - statusText: "OK", - type: "x-shockwave-flash", - fullMimeType: "application/x-shockwave-flash" - } - }, - { - method: "GET", - url: CONTENT_TYPE_SJS + "?fmt=ws", - data: { - fuzzyUrl: true, - status: 101, - statusText: "Switching Protocols", - } - } -]; - add_task(function* () { let { monitor } = yield initNetMonitor(FILTERING_URL); info("Starting test... "); // It seems that this test may be slow on Ubuntu builds running on ec2. requestLongerTimeout(2); let { $, NetMonitorView } = monitor.panelWin; @@ -193,34 +93,108 @@ add_task(function* () { function testContents(visibility) { isnot(RequestsMenu.selectedItem, null, "There should still be a selected item after filtering."); is(RequestsMenu.selectedIndex, 0, "The first item should be still selected after filtering."); is(NetMonitorView.detailsPaneHidden, false, "The details pane should still be visible after filtering."); - const items = RequestsMenu.items; - const visibleItems = RequestsMenu.visibleItems; - - is(items.size, visibility.length, + is(RequestsMenu.items.length, visibility.length, "There should be a specific amount of items in the requests menu."); - is(visibleItems.size, visibility.filter(e => e).length, - "There should be a specific amount of visible items in the requests menu."); + is(RequestsMenu.visibleItems.length, visibility.filter(e => e).length, + "There should be a specific amount of visbile items in the requests menu."); for (let i = 0; i < visibility.length; i++) { - let itemId = items.get(i).id; - let shouldBeVisible = !!visibility[i]; - let isThere = visibleItems.some(r => r.id == itemId); - is(isThere, shouldBeVisible, - `The item at index ${i} has visibility=${shouldBeVisible}`); + is(RequestsMenu.getItemAtIndex(i).target.hidden, !visibility[i], + "The item at index " + i + " doesn't have the correct hidden state."); } - for (let i = 0; i < EXPECTED_REQUESTS.length; i++) { - let { method, url, data } = EXPECTED_REQUESTS[i]; - for (let j = i; j < visibility.length; j += EXPECTED_REQUESTS.length) { - if (visibility[j]) { - verifyRequestItemTarget(RequestsMenu, items.get(j), method, url, data); - } - } + for (let i = 0; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=html", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "html", + fullMimeType: "text/html; charset=utf-8" + }); + } + for (let i = 1; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=css", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "css", + fullMimeType: "text/css; charset=utf-8" + }); + } + for (let i = 2; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=js", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "js", + fullMimeType: "application/javascript; charset=utf-8" + }); + } + for (let i = 3; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=font", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "woff", + fullMimeType: "font/woff" + }); + } + for (let i = 4; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=image", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "png", + fullMimeType: "image/png" + }); + } + for (let i = 5; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=audio", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "ogg", + fullMimeType: "audio/ogg" + }); + } + for (let i = 6; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=video", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "webm", + fullMimeType: "video/webm" + }); + } + for (let i = 7; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=flash", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "x-shockwave-flash", + fullMimeType: "application/x-shockwave-flash" + }); + } + for (let i = 8; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=ws", { + fuzzyUrl: true, + status: 101, + statusText: "Switching Protocols" + }); } } });
--- a/devtools/client/netmonitor/test/browser_net_filter-03.js +++ b/devtools/client/netmonitor/test/browser_net_filter-03.js @@ -99,11 +99,87 @@ add_task(function* () { is(RequestsMenu.selectedIndex, selection, "The first item should be still selected after filtering."); is(NetMonitorView.detailsPaneHidden, false, "The details pane should still be visible after filtering."); is(RequestsMenu.items.length, order.length, "There should be a specific amount of items in the requests menu."); is(RequestsMenu.visibleItems.length, visible, - "There should be a specific amount of visible items in the requests menu."); + "There should be a specific amount of visbile items in the requests menu."); + + for (let i = 0; i < order.length; i++) { + is(RequestsMenu.getItemAtIndex(i), RequestsMenu.items[i], + "The requests menu items aren't ordered correctly. Misplaced item " + i + "."); + } + + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i]), + "GET", CONTENT_TYPE_SJS + "?fmt=html", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "html", + fullMimeType: "text/html; charset=utf-8" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len]), + "GET", CONTENT_TYPE_SJS + "?fmt=css", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "css", + fullMimeType: "text/css; charset=utf-8" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 2]), + "GET", CONTENT_TYPE_SJS + "?fmt=js", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "js", + fullMimeType: "application/javascript; charset=utf-8" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 3]), + "GET", CONTENT_TYPE_SJS + "?fmt=font", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "woff", + fullMimeType: "font/woff" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 4]), + "GET", CONTENT_TYPE_SJS + "?fmt=image", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "png", + fullMimeType: "image/png" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 5]), + "GET", CONTENT_TYPE_SJS + "?fmt=audio", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "ogg", + fullMimeType: "audio/ogg" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 6]), + "GET", CONTENT_TYPE_SJS + "?fmt=video", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "webm", + fullMimeType: "video/webm" + }); + } } });
--- a/devtools/client/netmonitor/test/browser_net_footer-summary.js +++ b/devtools/client/netmonitor/test/browser_net_footer-summary.js @@ -1,30 +1,31 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /** - * Test if the summary text displayed in the network requests menu footer is correct. + * Test if the summary text displayed in the network requests menu footer + * is correct. */ add_task(function* () { requestLongerTimeout(2); let { tab, monitor } = yield initNetMonitor(FILTERING_URL); info("Starting test... "); - let { $, NetMonitorView, gStore, windowRequire } = monitor.panelWin; + let { $, NetMonitorView, gStore } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; - let { getDisplayedRequestsSummary } = - windowRequire("devtools/client/netmonitor/selectors/index"); - let { L10N } = windowRequire("devtools/client/netmonitor/l10n"); - let { PluralForm } = windowRequire("devtools/shared/plural-form"); + let winRequire = monitor.panelWin.require; + let { getSummary } = winRequire("devtools/client/netmonitor/selectors/index"); + let { L10N } = winRequire("devtools/client/netmonitor/l10n"); + let { PluralForm } = winRequire("devtools/shared/plural-form"); RequestsMenu.lazyUpdate = false; testStatus(); for (let i = 0; i < 2; i++) { info(`Performing requests in batch #${i}`); let wait = waitForNetworkEvents(monitor, 8); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { @@ -40,32 +41,31 @@ add_task(function* () { EventUtils.sendMouseEvent({ type: "click" }, buttonEl); testStatus(); } } yield teardown(monitor); function testStatus() { + const { count, totalBytes, totalMillis } = getSummary(gStore.getState()); let value = $("#requests-menu-network-summary-button").textContent; info("Current summary: " + value); - let state = gStore.getState(); - let totalRequestsCount = state.requests.requests.size; - let requestsSummary = getDisplayedRequestsSummary(state); - info(`Current requests: ${requestsSummary.count} of ${totalRequestsCount}.`); + let totalRequestsCount = RequestsMenu.itemCount; + info("Current requests: " + count + " of " + totalRequestsCount + "."); - if (!totalRequestsCount || !requestsSummary.count) { + if (!totalRequestsCount || !count) { is(value, L10N.getStr("networkMenu.empty"), "The current summary text is incorrect, expected an 'empty' label."); return; } - info(`Computed total bytes: ${requestsSummary.bytes}`); - info(`Computed total millis: ${requestsSummary.millis}`); + info("Computed total bytes: " + totalBytes); + info("Computed total millis: " + totalMillis); - is(value, PluralForm.get(requestsSummary.count, L10N.getStr("networkMenu.summary")) - .replace("#1", requestsSummary.count) - .replace("#2", L10N.numberWithDecimals(requestsSummary.bytes / 1024, 2)) - .replace("#3", L10N.numberWithDecimals(requestsSummary.millis / 1000, 2)) - , "The current summary text is correct."); + is(value, PluralForm.get(count, L10N.getStr("networkMenu.summary")) + .replace("#1", count) + .replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024, 2)) + .replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000, 2)) + , "The current summary text is incorrect."); } });
--- a/devtools/client/netmonitor/test/browser_net_frame.js +++ b/devtools/client/netmonitor/test/browser_net_frame.js @@ -12,17 +12,17 @@ const SUB_FILE_NAME = "html_frame-subdoc const TOP_URL = EXAMPLE_URL + TOP_FILE_NAME; const SUB_URL = EXAMPLE_URL + SUB_FILE_NAME; const EXPECTED_REQUESTS_TOP = [ { method: "GET", url: TOP_URL, causeType: "document", - causeUri: null, + causeUri: "", stack: true }, { method: "GET", url: EXAMPLE_URL + "stylesheet_request", causeType: "stylesheet", causeUri: TOP_URL, stack: false @@ -171,31 +171,32 @@ add_task(function* () { // While there is a defined order for requests in each document separately, the requests // from different documents may interleave in various ways that change per test run, so // there is not a single order when considering all the requests together. let currentTop = 0; let currentSub = 0; for (let i = 0; i < REQUEST_COUNT; i++) { let requestItem = RequestsMenu.getItemAtIndex(i); - let itemUrl = requestItem.url; - let itemCauseUri = requestItem.cause.loadingDocumentUri; + let itemUrl = requestItem.attachment.url; + let itemCauseUri = requestItem.target.querySelector(".requests-menu-cause-label") + .getAttribute("tooltiptext"); let spec; if (itemUrl == SUB_URL || itemCauseUri == SUB_URL) { spec = EXPECTED_REQUESTS_SUB[currentSub++]; } else { spec = EXPECTED_REQUESTS_TOP[currentTop++]; } let { method, url, causeType, causeUri, stack } = spec; - verifyRequestItemTarget(RequestsMenu, requestItem, + verifyRequestItemTarget(requestItem, method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } } ); - let { stacktrace } = requestItem.cause; + let { stacktrace } = requestItem.attachment.cause; let stackLen = stacktrace ? stacktrace.length : 0; if (stack) { ok(stacktrace, `Request #${i} has a stacktrace`); ok(stackLen > 0, `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`); // if "stack" is array, check the details about the top stack frames
--- a/devtools/client/netmonitor/test/browser_net_icon-preview.js +++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js @@ -19,17 +19,17 @@ add_task(function* () { let wait = waitForEvents(); yield performRequests(); yield wait; info("Checking the image thumbnail when all items are shown."); checkImageThumbnail(); - gStore.dispatch(Actions.sortBy("size")); + RequestsMenu.sortBy("size"); info("Checking the image thumbnail when all items are sorted."); checkImageThumbnail(); 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..."); @@ -56,16 +56,16 @@ add_task(function* () { } function* reloadAndPerformRequests() { yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED); yield performRequests(); } function checkImageThumbnail() { - is($all(".requests-menu-icon[data-type=thumbnail]").length, 1, + is($all(".requests-menu-icon[type=thumbnail]").length, 1, "There should be only one image request with a thumbnail displayed."); - is($(".requests-menu-icon[data-type=thumbnail]").src, TEST_IMAGE_DATA_URI, + is($(".requests-menu-icon[type=thumbnail]").src, TEST_IMAGE_DATA_URI, "The image requests-menu-icon thumbnail is displayed correctly."); - is($(".requests-menu-icon[data-type=thumbnail]").hidden, false, + is($(".requests-menu-icon[type=thumbnail]").hidden, false, "The image requests-menu-icon thumbnail should not be hidden."); } });
--- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js +++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js @@ -21,38 +21,38 @@ add_task(function* test() { let onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS); let onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); yield performRequests(); yield onEvents; yield onThumbnail; info("Checking the image thumbnail after a few requests were made..."); - yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.getItemAtIndex(0)); + yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[0]); // Hide tooltip before next test, to avoid the situation that tooltip covers // the icon for the request of the next test. info("Checking the image thumbnail gets hidden..."); - yield hideTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.getItemAtIndex(0)); + yield hideTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[0]); // +1 extra document reload onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS + 1); onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); info("Reloading the debuggee and performing all requests again..."); yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED); yield performRequests(); yield onEvents; yield onThumbnail; info("Checking the image thumbnail after a reload."); - yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.getItemAtIndex(1)); + yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[1]); info("Checking if the image thumbnail is hidden when mouse leaves the menu widget"); - let requestsMenuEl = $(".requests-menu-contents"); + let requestsMenuEl = $("#requests-menu-contents"); let onHidden = RequestsMenu.tooltip.once("hidden"); EventUtils.synthesizeMouse(requestsMenuEl, 0, 0, {type: "mouseout"}, monitor.panelWin); yield onHidden; yield teardown(monitor); function performRequests() { return ContentTask.spawn(tab.linkedBrowser, {}, function* () { @@ -60,17 +60,17 @@ add_task(function* test() { }); } /** * Show a tooltip on the {requestItem} and verify that it was displayed * with the expected content. */ function* showTooltipAndVerify(tooltip, requestItem) { - let anchor = $(".requests-menu-file", getItemTarget(RequestsMenu, requestItem)); + let anchor = $(".requests-menu-file", requestItem.target); yield showTooltipOn(tooltip, anchor); info("Tooltip was successfully opened for the image request."); is(tooltip.panel.querySelector("img").src, TEST_IMAGE_DATA_URI, "The tooltip's image content is displayed correctly."); } /** @@ -83,19 +83,19 @@ add_task(function* test() { EventUtils.synthesizeMouseAtCenter(element, {type: "mousemove"}, win); return onShown; } /** * Hide a tooltip on the {requestItem} and verify that it was closed. */ function* hideTooltipAndVerify(tooltip, requestItem) { - // Hovering over the "method" column hides the tooltip. - let anchor = $(".requests-menu-method", getItemTarget(RequestsMenu, requestItem)); + // Hovering method hides tooltip. + let anchor = $(".requests-menu-method", requestItem.target); - let onTooltipHidden = tooltip.once("hidden"); + let onHidden = tooltip.once("hidden"); let win = anchor.ownerDocument.defaultView; EventUtils.synthesizeMouseAtCenter(anchor, {type: "mousemove"}, win); - yield onTooltipHidden; + yield onHidden; info("Tooltip was successfully closed."); } });
--- a/devtools/client/netmonitor/test/browser_net_json-long.js +++ b/devtools/client/netmonitor/test/browser_net_json-long.js @@ -23,17 +23,17 @@ add_task(function* () { RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, 1); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", CONTENT_TYPE_SJS + "?fmt=json-long", { status: 200, statusText: "OK", type: "json", fullMimeType: "text/json; charset=utf-8", size: L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(85975 / 1024, 2)), time: true
--- a/devtools/client/netmonitor/test/browser_net_json-malformed.js +++ b/devtools/client/netmonitor/test/browser_net_json-malformed.js @@ -17,17 +17,17 @@ add_task(function* () { RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, 1); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", CONTENT_TYPE_SJS + "?fmt=json-malformed", { status: 200, statusText: "OK", type: "json", fullMimeType: "text/json; charset=utf-8" }); let onEvent = monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED);
--- a/devtools/client/netmonitor/test/browser_net_json_custom_mime.js +++ b/devtools/client/netmonitor/test/browser_net_json_custom_mime.js @@ -19,17 +19,17 @@ add_task(function* () { RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, 1); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", CONTENT_TYPE_SJS + "?fmt=json-custom-mime", { status: 200, statusText: "OK", type: "x-bigcorp-json", fullMimeType: "text/x-bigcorp-json; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41), time: true });
--- a/devtools/client/netmonitor/test/browser_net_json_text_mime.js +++ b/devtools/client/netmonitor/test/browser_net_json_text_mime.js @@ -19,17 +19,17 @@ add_task(function* () { RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, 1); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", CONTENT_TYPE_SJS + "?fmt=json-text-mime", { status: 200, statusText: "OK", type: "plain", fullMimeType: "text/plain; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41), time: true });
--- a/devtools/client/netmonitor/test/browser_net_jsonp.js +++ b/devtools/client/netmonitor/test/browser_net_jsonp.js @@ -20,26 +20,26 @@ add_task(function* () { NetworkDetails._json.lazyEmpty = false; let wait = waitForNetworkEvents(monitor, 2); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", CONTENT_TYPE_SJS + "?fmt=jsonp&jsonp=$_0123Fun", { status: 200, statusText: "OK", type: "json", fullMimeType: "text/json; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(1), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), "GET", CONTENT_TYPE_SJS + "?fmt=jsonp2&jsonp=$_4567Sad", { status: 200, statusText: "OK", type: "json", fullMimeType: "text/json; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 54), time: true });
--- a/devtools/client/netmonitor/test/browser_net_large-response.js +++ b/devtools/client/netmonitor/test/browser_net_large-response.js @@ -23,17 +23,17 @@ add_task(function* () { RequestsMenu.lazyUpdate = false; let wait = waitForNetworkEvents(monitor, 1); yield ContentTask.spawn(tab.linkedBrowser, HTML_LONG_URL, function* (url) { content.wrappedJSObject.performRequests(1, url); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "GET", CONTENT_TYPE_SJS + "?fmt=html-long", { status: 200, statusText: "OK" }); let onEvent = monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); EventUtils.sendMouseEvent({ type: "mousedown" }, document.getElementById("details-pane-toggle"));
--- a/devtools/client/netmonitor/test/browser_net_page-nav.js +++ b/devtools/client/netmonitor/test/browser_net_page-nav.js @@ -54,16 +54,16 @@ add_task(function* () { function* testClose() { info("Closing..."); let onDestroyed = monitor.once("destroyed"); removeTab(tab); yield onDestroyed; - ok(!monitor.panelWin.NetMonitorController.client, + ok(!monitor._controller.client, "There shouldn't be a client available after destruction."); - ok(!monitor.panelWin.NetMonitorController.tabClient, + ok(!monitor._controller.tabClient, "There shouldn't be a tabClient available after destruction."); - ok(!monitor.panelWin.NetMonitorController.webConsoleClient, + ok(!monitor._controller.webConsoleClient, "There shouldn't be a webConsoleClient available after destruction."); } });
--- a/devtools/client/netmonitor/test/browser_net_post-data-01.js +++ b/devtools/client/netmonitor/test/browser_net_post-data-01.js @@ -20,26 +20,26 @@ add_task(function* () { NetworkDetails._params.lazyEmpty = false; let wait = waitForNetworkEvents(monitor, 0, 2); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), "POST", SIMPLE_SJS + "?foo=bar&baz=42&type=urlencoded", { status: 200, statusText: "Och Aye", type: "plain", fullMimeType: "text/plain; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(1), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), "POST", SIMPLE_SJS + "?foo=bar&baz=42&type=multipart", { status: 200, statusText: "Och Aye", type: "plain", fullMimeType: "text/plain; charset=utf-8", size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12), time: true });
--- a/devtools/client/netmonitor/test/browser_net_prefs-reload.js +++ b/devtools/client/netmonitor/test/browser_net_prefs-reload.js @@ -4,38 +4,36 @@ "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 { getActiveFilters } = require("devtools/client/netmonitor/selectors/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 getState = () => getStore().getState(); 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: ($) => getActiveFilters(getState()), + 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.toggleFilterType(e))) }, networkDetailsWidth: { newValue: ~~(Math.random() * 200 + 100), validateValue: ($) => ~~$("#details-pane").getAttribute("width"),
--- a/devtools/client/netmonitor/test/browser_net_raw_headers.js +++ b/devtools/client/netmonitor/test/browser_net_raw_headers.js @@ -26,37 +26,37 @@ add_task(function* () { let onTabEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); RequestsMenu.selectedItem = origItem; yield onTabEvent; EventUtils.sendMouseEvent({ type: "click" }, document.getElementById("toggle-raw-headers")); - testShowRawHeaders(origItem); + testShowRawHeaders(origItem.attachment); EventUtils.sendMouseEvent({ type: "click" }, document.getElementById("toggle-raw-headers")); testHideRawHeaders(document); return teardown(monitor); /* * Tests that raw headers were displayed correctly */ function testShowRawHeaders(data) { let requestHeaders = document.getElementById("raw-request-headers-textarea").value; for (let header of data.requestHeaders.headers) { - ok(requestHeaders.includes(header.name + ": " + header.value), + ok(requestHeaders.indexOf(header.name + ": " + header.value) >= 0, "textarea contains request headers"); } let responseHeaders = document.getElementById("raw-response-headers-textarea").value; for (let header of data.responseHeaders.headers) { - ok(responseHeaders.includes(header.name + ": " + header.value), + ok(responseHeaders.indexOf(header.name + ": " + header.value) >= 0, "textarea contains response headers"); } } /* * Tests that raw headers textareas are hidden and empty */ function testHideRawHeaders() {
--- a/devtools/client/netmonitor/test/browser_net_reload-button.js +++ b/devtools/client/netmonitor/test/browser_net_reload-button.js @@ -3,23 +3,23 @@ "use strict"; /** * Tests if the empty-requests reload button works. */ add_task(function* () { - let { monitor } = yield initNetMonitor(SIMPLE_URL); + let { monitor } = yield initNetMonitor(SINGLE_GET_URL); info("Starting test... "); let { document, NetMonitorView } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; - let wait = waitForNetworkEvents(monitor, 1); + let wait = waitForNetworkEvents(monitor, 2); let button = document.querySelector("#requests-menu-reload-notice-button"); button.click(); yield wait; - is(RequestsMenu.itemCount, 1, "The request menu should have one item after reloading"); + is(RequestsMenu.itemCount, 2, "The request menu should have two items after reloading"); return teardown(monitor); });
--- a/devtools/client/netmonitor/test/browser_net_reload-markers.js +++ b/devtools/client/netmonitor/test/browser_net_reload-markers.js @@ -3,30 +3,30 @@ "use strict"; /** * Tests if the empty-requests reload button works. */ add_task(function* () { - let { monitor } = yield initNetMonitor(SIMPLE_URL); + let { monitor } = yield initNetMonitor(SINGLE_GET_URL); info("Starting test... "); let { document, EVENTS } = monitor.panelWin; let button = document.querySelector("#requests-menu-reload-notice-button"); button.click(); let markers = []; monitor.panelWin.on(EVENTS.TIMELINE_EVENT, (_, marker) => { markers.push(marker); }); - yield waitForNetworkEvents(monitor, 1); + yield waitForNetworkEvents(monitor, 2); yield waitUntil(() => markers.length == 2); ok(true, "Reloading finished"); is(markers[0].name, "document::DOMContentLoaded", "The first received marker is correct."); is(markers[1].name, "document::Load", "The second received marker is correct.");
--- a/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js +++ b/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js @@ -49,17 +49,17 @@ add_task(function* () { }); yield wait; verifyRequest(1); return teardown(monitor); function verifyRequest(offset) { - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(offset), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(offset), "GET", CONTENT_TYPE_SJS + "?fmt=json-long", { status: 200, statusText: "OK", type: "json", fullMimeType: "text/json; charset=utf-8", size: L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(85975 / 1024, 2)), time: true
--- a/devtools/client/netmonitor/test/browser_net_resend.js +++ b/devtools/client/netmonitor/test/browser_net_resend.js @@ -34,47 +34,55 @@ add_task(function* () { RequestsMenu.selectedItem = origItem; yield onTabUpdated; // add a new custom request cloned from selected request let onPopulated = panelWin.once(EVENTS.CUSTOMREQUESTVIEW_POPULATED); RequestsMenu.cloneSelectedRequest(); yield onPopulated; - testCustomForm(origItem); + testCustomForm(origItem.attachment); let customItem = RequestsMenu.selectedItem; testCustomItem(customItem, origItem); // edit the custom request yield editCustomForm(); - // FIXME: reread the customItem, it's been replaced by a new object (immutable!) - customItem = RequestsMenu.selectedItem; testCustomItemChanged(customItem, origItem); // send the new request wait = waitForNetworkEvents(monitor, 0, 1); RequestsMenu.sendCustomRequest(); yield wait; let sentItem = RequestsMenu.selectedItem; - testSentRequest(sentItem, origItem); + testSentRequest(sentItem.attachment, origItem.attachment); return teardown(monitor); function testCustomItem(item, orig) { - is(item.method, orig.method, "item is showing the same method as original request"); - is(item.url, orig.url, "item is showing the same URL as original request"); + let method = item.target.querySelector(".requests-menu-method").value; + let origMethod = orig.target.querySelector(".requests-menu-method").value; + is(method, origMethod, "menu item is showing the same method as original request"); + + let file = item.target.querySelector(".requests-menu-file").value; + let origFile = orig.target.querySelector(".requests-menu-file").value; + is(file, origFile, "menu item is showing the same file name as original request"); + + let domain = item.target.querySelector(".requests-menu-domain").value; + let origDomain = orig.target.querySelector(".requests-menu-domain").value; + is(domain, origDomain, "menu item is showing the same domain as original request"); } function testCustomItemChanged(item, orig) { - let url = item.url; - let expectedUrl = orig.url + "&" + ADD_QUERY; + let file = item.target.querySelector(".requests-menu-file").value; + let expectedFile = orig.target.querySelector(".requests-menu-file").value + + "&" + ADD_QUERY; - is(url, expectedUrl, "menu item is updated to reflect url entered in form"); + is(file, expectedFile, "menu item is updated to reflect url entered in form"); } /* * Test that the New Request form was populated correctly */ function testCustomForm(data) { is(document.getElementById("custom-method-value").value, data.method, "new request form showing correct method");
--- a/devtools/client/netmonitor/test/browser_net_resend_cors.js +++ b/devtools/client/netmonitor/test/browser_net_resend_cors.js @@ -25,19 +25,19 @@ add_task(function* () { content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data"); }); yield wait; const METHODS = ["OPTIONS", "POST"]; // Check the requests that were sent for (let [i, method] of METHODS.entries()) { - let item = RequestsMenu.getItemAtIndex(i); - is(item.method, method, `The ${method} request has the right method`); - is(item.url, requestUrl, `The ${method} request has the right URL`); + let { attachment } = RequestsMenu.getItemAtIndex(i); + is(attachment.method, method, `The ${method} request has the right method`); + is(attachment.url, requestUrl, `The ${method} request has the right URL`); } // Resend both requests without modification. Wait for resent OPTIONS, then POST. // POST is supposed to have no preflight OPTIONS request this time (CORS is disabled) let onRequests = waitForNetworkEvents(monitor, 1, 1); for (let [i, method] of METHODS.entries()) { let item = RequestsMenu.getItemAtIndex(i); @@ -56,17 +56,17 @@ add_task(function* () { } info("Waiting for both resent requests"); yield onRequests; // Check the resent requests for (let [i, method] of METHODS.entries()) { let index = i + 2; - let item = RequestsMenu.getItemAtIndex(index); + let item = RequestsMenu.getItemAtIndex(index).attachment; is(item.method, method, `The ${method} request has the right method`); is(item.url, requestUrl, `The ${method} request has the right URL`); is(item.status, 200, `The ${method} response has the right status`); if (method === "POST") { is(item.requestPostData.postData.text, "post-data", "The POST request has the right POST data"); // eslint-disable-next-line mozilla/no-cpows-in-tests
--- a/devtools/client/netmonitor/test/browser_net_resend_headers.js +++ b/devtools/client/netmonitor/test/browser_net_resend_headers.js @@ -30,31 +30,31 @@ add_task(function* () { NetMonitorController.webConsoleClient.sendHTTPRequest({ url: requestUrl, method: "POST", headers: requestHeaders, body: "Hello" }); yield wait; - let item = RequestsMenu.getItemAtIndex(0); - is(item.method, "POST", "The request has the right method"); - is(item.url, requestUrl, "The request has the right URL"); + let { attachment } = RequestsMenu.getItemAtIndex(0); + is(attachment.method, "POST", "The request has the right method"); + is(attachment.url, requestUrl, "The request has the right URL"); - for (let { name, value } of item.requestHeaders.headers) { + for (let { name, value } of attachment.requestHeaders.headers) { info(`Request header: ${name}: ${value}`); } function hasRequestHeader(name, value) { - let { headers } = item.requestHeaders; + let { headers } = attachment.requestHeaders; return headers.some(h => h.name === name && h.value === value); } function hasNotRequestHeader(name) { - let { headers } = item.requestHeaders; + let { headers } = attachment.requestHeaders; return headers.every(h => h.name !== name); } for (let { name, value } of requestHeaders) { ok(hasRequestHeader(name, value), `The ${name} header has the right value`); } // Check that the Cookie header was not added silently (i.e., that the request is
--- a/devtools/client/netmonitor/test/browser_net_security-icon-click.js +++ b/devtools/client/netmonitor/test/browser_net_security-icon-click.js @@ -37,21 +37,21 @@ add_task(function* () { let wait = waitForNetworkEvents(monitor, 1); yield ContentTask.spawn(tab.linkedBrowser, { url }, function* (args) { content.wrappedJSObject.performRequests(1, args.url); }); return wait; } function* clickAndTestSecurityIcon() { - let item = RequestsMenu.getItemAtIndex(0); - let target = getItemTarget(RequestsMenu, item); - let icon = $(".requests-security-state-icon", target); + let item = RequestsMenu.items[0]; + let icon = $(".requests-security-state-icon", item.target); - info("Clicking security icon of the first request and waiting for panel update."); - EventUtils.synthesizeMouseAtCenter(icon, {}, monitor.panelWin); + info("Clicking security icon of the first request and waiting for the " + + "panel to update."); + icon.click(); yield monitor.panelWin.once(EVENTS.TAB_UPDATED); is(NetworkDetails.widget.selectedPanel, $("#security-tabpanel"), "Security tab is selected."); } });
--- a/devtools/client/netmonitor/test/browser_net_security-redirect.js +++ b/devtools/client/netmonitor/test/browser_net_security-redirect.js @@ -17,23 +17,21 @@ add_task(function* () { let wait = waitForNetworkEvents(monitor, 2); yield ContentTask.spawn(tab.linkedBrowser, HTTPS_REDIRECT_SJS, function* (url) { content.wrappedJSObject.performRequests(1, url); }); yield wait; is(RequestsMenu.itemCount, 2, "There were two requests due to redirect."); - let initial = RequestsMenu.getItemAtIndex(0); - let redirect = RequestsMenu.getItemAtIndex(1); + let initial = RequestsMenu.items[0]; + let redirect = RequestsMenu.items[1]; - let initialSecurityIcon = - $(".requests-security-state-icon", getItemTarget(RequestsMenu, initial)); - let redirectSecurityIcon = - $(".requests-security-state-icon", getItemTarget(RequestsMenu, redirect)); + let initialSecurityIcon = $(".requests-security-state-icon", initial.target); + let redirectSecurityIcon = $(".requests-security-state-icon", redirect.target); ok(initialSecurityIcon.classList.contains("security-state-insecure"), "Initial request was marked insecure."); ok(redirectSecurityIcon.classList.contains("security-state-secure"), "Redirected request was marked secure."); yield teardown(monitor);
--- a/devtools/client/netmonitor/test/browser_net_security-state.js +++ b/devtools/client/netmonitor/test/browser_net_security-state.js @@ -19,23 +19,22 @@ add_task(function* () { let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); let { $, EVENTS, NetMonitorView } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; RequestsMenu.lazyUpdate = false; yield performRequests(); for (let item of RequestsMenu.items) { - let target = getItemTarget(RequestsMenu, item); - let domain = $(".requests-menu-domain", target).textContent; + let domain = $(".requests-menu-domain", item.target).value; info("Found a request to " + domain); ok(domain in EXPECTED_SECURITY_STATES, "Domain " + domain + " was expected."); - let classes = $(".requests-security-state-icon", target).classList; + let classes = $(".requests-security-state-icon", item.target).classList; let expectedClass = EXPECTED_SECURITY_STATES[domain]; info("Classes of security state icon are: " + classes); info("Security state icon is expected to contain class: " + expectedClass); ok(classes.contains(expectedClass), "Icon contained the correct class name."); } return teardown(monitor);
--- a/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js +++ b/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js @@ -53,29 +53,29 @@ add_task(function* () { }); info("Waiting for new network event."); yield onNewItem; info("Selecting the request."); RequestsMenu.selectedIndex = 0; - is(RequestsMenu.selectedItem.securityState, undefined, + is(RequestsMenu.selectedItem.attachment.securityState, undefined, "Security state has not yet arrived."); is(tabEl.hidden, !testcase.visibleOnNewEvent, "Security tab is " + (testcase.visibleOnNewEvent ? "visible" : "hidden") + " after new request was added to the menu."); is(tabpanel.hidden, false, "#security-tabpanel is visible after new request was added to the menu."); info("Waiting for security information to arrive."); yield onSecurityInfo; - ok(RequestsMenu.selectedItem.securityState, + ok(RequestsMenu.selectedItem.attachment.securityState, "Security state arrived."); is(tabEl.hidden, !testcase.visibleOnSecurityInfo, "Security tab is " + (testcase.visibleOnSecurityInfo ? "visible" : "hidden") + " after security information arrived."); is(tabpanel.hidden, false, "#security-tabpanel is visible after security information arrived.");
--- a/devtools/client/netmonitor/test/browser_net_send-beacon-other-tab.js +++ b/devtools/client/netmonitor/test/browser_net_send-beacon-other-tab.js @@ -21,14 +21,14 @@ add_task(function* () { yield ContentTask.spawn(beaconTab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequest(); }); tab.linkedBrowser.reload(); yield wait; is(RequestsMenu.itemCount, 1, "Only the reload should be recorded."); let request = RequestsMenu.getItemAtIndex(0); - is(request.method, "GET", "The method is correct."); - is(request.status, "200", "The status is correct."); + is(request.attachment.method, "GET", "The method is correct."); + is(request.attachment.status, "200", "The status is correct."); yield removeTab(beaconTab); return teardown(monitor); });
--- a/devtools/client/netmonitor/test/browser_net_send-beacon.js +++ b/devtools/client/netmonitor/test/browser_net_send-beacon.js @@ -18,14 +18,14 @@ add_task(function* () { let wait = waitForNetworkEvents(monitor, 1); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequest(); }); yield wait; is(RequestsMenu.itemCount, 1, "The beacon should be recorded."); let request = RequestsMenu.getItemAtIndex(0); - is(request.method, "POST", "The method is correct."); - ok(request.url.endsWith("beacon_request"), "The URL is correct."); - is(request.status, "404", "The status is correct."); + is(request.attachment.method, "POST", "The method is correct."); + ok(request.attachment.url.endsWith("beacon_request"), "The URL is correct."); + is(request.attachment.status, "404", "The status is correct."); return teardown(monitor); });
--- a/devtools/client/netmonitor/test/browser_net_service-worker-status.js +++ b/devtools/client/netmonitor/test/browser_net_service-worker-status.js @@ -46,20 +46,19 @@ add_task(function* () { }); yield wait; let index = 0; for (let request of REQUEST_DATA) { let item = RequestsMenu.getItemAtIndex(index); info(`Verifying request #${index}`); - yield verifyRequestItemTarget(RequestsMenu, item, - request.method, request.uri, request.details); + yield verifyRequestItemTarget(item, request.method, request.uri, request.details); - let { stacktrace } = item.cause; + let { stacktrace } = item.attachment.cause; let stackLen = stacktrace ? stacktrace.length : 0; ok(stacktrace, `Request #${index} has a stacktrace`); ok(stackLen >= request.stackFunctions.length, `Request #${index} has a stacktrace with enough (${stackLen}) items`); request.stackFunctions.forEach((functionName, j) => { is(stacktrace[j].functionName, functionName,
--- a/devtools/client/netmonitor/test/browser_net_simple-init.js +++ b/devtools/client/netmonitor/test/browser_net_simple-init.js @@ -17,77 +17,77 @@ function test() { info("Starting test... "); is(tab.linkedBrowser.currentURI.spec, SIMPLE_URL, "The current tab's location is the correct one."); function checkIfInitialized(tag) { info(`Checking if initialization is ok (${tag}).`); - ok(monitor.panelWin.NetMonitorView, + ok(monitor._view, `The network monitor view object exists (${tag}).`); - ok(monitor.panelWin.NetMonitorController, + ok(monitor._controller, `The network monitor controller object exists (${tag}).`); - ok(monitor.panelWin.NetMonitorController._startup, + ok(monitor._controller._startup, `The network monitor controller object exists and is initialized (${tag}).`); ok(monitor.isReady, `The network monitor panel appears to be ready (${tag}).`); - ok(monitor.panelWin.NetMonitorController.tabClient, + ok(monitor._controller.tabClient, `There should be a tabClient available at this point (${tag}).`); - ok(monitor.panelWin.NetMonitorController.webConsoleClient, + ok(monitor._controller.webConsoleClient, `There should be a webConsoleClient available at this point (${tag}).`); - ok(monitor.panelWin.NetMonitorController.timelineFront, + ok(monitor._controller.timelineFront, `There should be a timelineFront available at this point (${tag}).`); } function checkIfDestroyed(tag) { gInfo("Checking if destruction is ok."); - gOk(monitor.panelWin.NetMonitorView, + gOk(monitor._view, `The network monitor view object still exists (${tag}).`); - gOk(monitor.panelWin.NetMonitorController, + gOk(monitor._controller, `The network monitor controller object still exists (${tag}).`); - gOk(monitor.panelWin.NetMonitorController._shutdown, + gOk(monitor._controller._shutdown, `The network monitor controller object still exists and is destroyed (${tag}).`); - gOk(!monitor.panelWin.NetMonitorController.tabClient, + gOk(!monitor._controller.tabClient, `There shouldn't be a tabClient available after destruction (${tag}).`); - gOk(!monitor.panelWin.NetMonitorController.webConsoleClient, + gOk(!monitor._controller.webConsoleClient, `There shouldn't be a webConsoleClient available after destruction (${tag}).`); - gOk(!monitor.panelWin.NetMonitorController.timelineFront, + gOk(!monitor._controller.timelineFront, `There shouldn't be a timelineFront available after destruction (${tag}).`); } executeSoon(() => { checkIfInitialized(1); - monitor.panelWin.NetMonitorController.startupNetMonitor() + monitor._controller.startupNetMonitor() .then(() => { info("Starting up again shouldn't do anything special."); checkIfInitialized(2); - return monitor.panelWin.NetMonitorController.connect(); + return monitor._controller.connect(); }) .then(() => { info("Connecting again shouldn't do anything special."); checkIfInitialized(3); return teardown(monitor); }) .then(finish); }); registerCleanupFunction(() => { checkIfDestroyed(1); - monitor.panelWin.NetMonitorController.shutdownNetMonitor() + monitor._controller.shutdownNetMonitor() .then(() => { gInfo("Shutting down again shouldn't do anything special."); checkIfDestroyed(2); - return monitor.panelWin.NetMonitorController.disconnect(); + return monitor._controller.disconnect(); }) .then(() => { gInfo("Disconnecting again shouldn't do anything special."); checkIfDestroyed(3); }); }); }); }
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js +++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js @@ -27,210 +27,221 @@ function test() { "There shouldn't be any selected item in the requests menu."); is(RequestsMenu.itemCount, 1, "The requests menu should not be empty after the first request."); is(NetMonitorView.detailsPaneHidden, true, "The details pane should still be hidden after the first request."); let requestItem = RequestsMenu.getItemAtIndex(0); - is(typeof requestItem.id, "string", + is(typeof requestItem.value, "string", "The attached request id is incorrect."); - isnot(requestItem.id, "", + isnot(requestItem.value, "", "The attached request id should not be empty."); - is(typeof requestItem.startedMillis, "number", + is(typeof requestItem.attachment.startedDeltaMillis, "number", + "The attached startedDeltaMillis is incorrect."); + is(requestItem.attachment.startedDeltaMillis, 0, + "The attached startedDeltaMillis should be zero."); + + is(typeof requestItem.attachment.startedMillis, "number", "The attached startedMillis is incorrect."); - isnot(requestItem.startedMillis, 0, + isnot(requestItem.attachment.startedMillis, 0, "The attached startedMillis should not be zero."); - is(requestItem.requestHeaders, undefined, + is(requestItem.attachment.requestHeaders, undefined, "The requestHeaders should not yet be set."); - is(requestItem.requestCookies, undefined, + is(requestItem.attachment.requestCookies, undefined, "The requestCookies should not yet be set."); - is(requestItem.requestPostData, undefined, + is(requestItem.attachment.requestPostData, undefined, "The requestPostData should not yet be set."); - is(requestItem.responseHeaders, undefined, + is(requestItem.attachment.responseHeaders, undefined, "The responseHeaders should not yet be set."); - is(requestItem.responseCookies, undefined, + is(requestItem.attachment.responseCookies, undefined, "The responseCookies should not yet be set."); - is(requestItem.httpVersion, undefined, + is(requestItem.attachment.httpVersion, undefined, "The httpVersion should not yet be set."); - is(requestItem.status, undefined, + is(requestItem.attachment.status, undefined, "The status should not yet be set."); - is(requestItem.statusText, undefined, + is(requestItem.attachment.statusText, undefined, "The statusText should not yet be set."); - is(requestItem.headersSize, undefined, + is(requestItem.attachment.headersSize, undefined, "The headersSize should not yet be set."); - is(requestItem.transferredSize, undefined, + is(requestItem.attachment.transferredSize, undefined, "The transferredSize should not yet be set."); - is(requestItem.contentSize, undefined, + is(requestItem.attachment.contentSize, undefined, "The contentSize should not yet be set."); - is(requestItem.mimeType, undefined, + is(requestItem.attachment.mimeType, undefined, "The mimeType should not yet be set."); - is(requestItem.responseContent, undefined, + is(requestItem.attachment.responseContent, undefined, "The responseContent should not yet be set."); - is(requestItem.totalTime, undefined, + is(requestItem.attachment.totalTime, undefined, "The totalTime should not yet be set."); - is(requestItem.eventTimings, undefined, + is(requestItem.attachment.eventTimings, undefined, "The eventTimings should not yet be set."); - verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS); + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); }); monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_HEADERS, () => { let requestItem = RequestsMenu.getItemAtIndex(0); - ok(requestItem.requestHeaders, - "There should be a requestHeaders data available."); - is(requestItem.requestHeaders.headers.length, 10, - "The requestHeaders data has an incorrect |headers| property."); - isnot(requestItem.requestHeaders.headersSize, 0, - "The requestHeaders data has an incorrect |headersSize| property."); + + ok(requestItem.attachment.requestHeaders, + "There should be a requestHeaders attachment available."); + is(requestItem.attachment.requestHeaders.headers.length, 9, + "The requestHeaders attachment has an incorrect |headers| property."); + isnot(requestItem.attachment.requestHeaders.headersSize, 0, + "The requestHeaders attachment has an incorrect |headersSize| property."); // Can't test for the exact request headers size because the value may // vary across platforms ("User-Agent" header differs). verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); }); monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_COOKIES, () => { let requestItem = RequestsMenu.getItemAtIndex(0); - ok(requestItem.requestCookies, - "There should be a requestCookies data available."); - is(requestItem.requestCookies.cookies.length, 2, - "The requestCookies data has an incorrect |cookies| property."); + ok(requestItem.attachment.requestCookies, + "There should be a requestCookies attachment available."); + is(requestItem.attachment.requestCookies.cookies.length, 2, + "The requestCookies attachment has an incorrect |cookies| property."); - verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS); + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); }); monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_POST_DATA, () => { ok(false, "Trap listener: this request doesn't have any post data."); }); monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_HEADERS, () => { let requestItem = RequestsMenu.getItemAtIndex(0); - ok(requestItem.responseHeaders, - "There should be a responseHeaders data available."); - is(requestItem.responseHeaders.headers.length, 10, - "The responseHeaders data has an incorrect |headers| property."); - is(requestItem.responseHeaders.headersSize, 330, - "The responseHeaders data has an incorrect |headersSize| property."); + ok(requestItem.attachment.responseHeaders, + "There should be a responseHeaders attachment available."); + is(requestItem.attachment.responseHeaders.headers.length, 10, + "The responseHeaders attachment has an incorrect |headers| property."); + is(requestItem.attachment.responseHeaders.headersSize, 330, + "The responseHeaders attachment has an incorrect |headersSize| property."); - verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS); + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); }); monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_COOKIES, () => { let requestItem = RequestsMenu.getItemAtIndex(0); - ok(requestItem.responseCookies, - "There should be a responseCookies data available."); - is(requestItem.responseCookies.cookies.length, 2, - "The responseCookies data has an incorrect |cookies| property."); + ok(requestItem.attachment.responseCookies, + "There should be a responseCookies attachment available."); + is(requestItem.attachment.responseCookies.cookies.length, 2, + "The responseCookies attachment has an incorrect |cookies| property."); - verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS); + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); }); monitor.panelWin.once(monitor.panelWin.EVENTS.STARTED_RECEIVING_RESPONSE, () => { let requestItem = RequestsMenu.getItemAtIndex(0); - is(requestItem.httpVersion, "HTTP/1.1", - "The httpVersion data has an incorrect value."); - is(requestItem.status, "200", - "The status data has an incorrect value."); - is(requestItem.statusText, "Och Aye", - "The statusText data has an incorrect value."); - is(requestItem.headersSize, 330, - "The headersSize data has an incorrect value."); + is(requestItem.attachment.httpVersion, "HTTP/1.1", + "The httpVersion attachment has an incorrect value."); + is(requestItem.attachment.status, "200", + "The status attachment has an incorrect value."); + is(requestItem.attachment.statusText, "Och Aye", + "The statusText attachment has an incorrect value."); + is(requestItem.attachment.headersSize, 330, + "The headersSize attachment has an incorrect value."); - verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, { + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { status: "200", statusText: "Och Aye" }); }); monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_RESPONSE_CONTENT, () => { let requestItem = RequestsMenu.getItemAtIndex(0); - is(requestItem.transferredSize, "12", - "The transferredSize data has an incorrect value."); - is(requestItem.contentSize, "12", - "The contentSize data has an incorrect value."); - is(requestItem.mimeType, "text/plain; charset=utf-8", - "The mimeType data has an incorrect value."); + is(requestItem.attachment.transferredSize, "12", + "The transferredSize attachment has an incorrect value."); + is(requestItem.attachment.contentSize, "12", + "The contentSize attachment has an incorrect value."); + is(requestItem.attachment.mimeType, "text/plain; charset=utf-8", + "The mimeType attachment has an incorrect value."); - verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, { + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { type: "plain", fullMimeType: "text/plain; charset=utf-8", - transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12), - size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12), + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), }); }); monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_CONTENT, () => { let requestItem = RequestsMenu.getItemAtIndex(0); - ok(requestItem.responseContent, - "There should be a responseContent data available."); - is(requestItem.responseContent.content.mimeType, + ok(requestItem.attachment.responseContent, + "There should be a responseContent attachment available."); + is(requestItem.attachment.responseContent.content.mimeType, "text/plain; charset=utf-8", - "The responseContent data has an incorrect |content.mimeType| property."); - is(requestItem.responseContent.content.text, + "The responseContent attachment has an incorrect |content.mimeType| property."); + is(requestItem.attachment.responseContent.content.text, "Hello world!", - "The responseContent data has an incorrect |content.text| property."); - is(requestItem.responseContent.content.size, + "The responseContent attachment has an incorrect |content.text| property."); + is(requestItem.attachment.responseContent.content.size, 12, - "The responseContent data has an incorrect |content.size| property."); + "The responseContent attachment has an incorrect |content.size| property."); - verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, { + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { type: "plain", fullMimeType: "text/plain; charset=utf-8", - transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12), - size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12), + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), }); }); monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_EVENT_TIMINGS, () => { let requestItem = RequestsMenu.getItemAtIndex(0); - is(typeof requestItem.totalTime, "number", + is(typeof requestItem.attachment.totalTime, "number", "The attached totalTime is incorrect."); - ok(requestItem.totalTime >= 0, + ok(requestItem.attachment.totalTime >= 0, "The attached totalTime should be positive."); - verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, { + is(typeof requestItem.attachment.endedMillis, "number", + "The attached endedMillis is incorrect."); + ok(requestItem.attachment.endedMillis >= 0, + "The attached endedMillis should be positive."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { time: true }); }); monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_EVENT_TIMINGS, () => { let requestItem = RequestsMenu.getItemAtIndex(0); - ok(requestItem.eventTimings, - "There should be a eventTimings data available."); - is(typeof requestItem.eventTimings.timings.blocked, "number", - "The eventTimings data has an incorrect |timings.blocked| property."); - is(typeof requestItem.eventTimings.timings.dns, "number", - "The eventTimings data has an incorrect |timings.dns| property."); - is(typeof requestItem.eventTimings.timings.connect, "number", - "The eventTimings data has an incorrect |timings.connect| property."); - is(typeof requestItem.eventTimings.timings.send, "number", - "The eventTimings data has an incorrect |timings.send| property."); - is(typeof requestItem.eventTimings.timings.wait, "number", - "The eventTimings data has an incorrect |timings.wait| property."); - is(typeof requestItem.eventTimings.timings.receive, "number", - "The eventTimings data has an incorrect |timings.receive| property."); - is(typeof requestItem.eventTimings.totalTime, "number", - "The eventTimings data has an incorrect |totalTime| property."); + ok(requestItem.attachment.eventTimings, + "There should be a eventTimings attachment available."); + is(typeof requestItem.attachment.eventTimings.timings.blocked, "number", + "The eventTimings attachment has an incorrect |timings.blocked| property."); + is(typeof requestItem.attachment.eventTimings.timings.dns, "number", + "The eventTimings attachment has an incorrect |timings.dns| property."); + is(typeof requestItem.attachment.eventTimings.timings.connect, "number", + "The eventTimings attachment has an incorrect |timings.connect| property."); + is(typeof requestItem.attachment.eventTimings.timings.send, "number", + "The eventTimings attachment has an incorrect |timings.send| property."); + is(typeof requestItem.attachment.eventTimings.timings.wait, "number", + "The eventTimings attachment has an incorrect |timings.wait| property."); + is(typeof requestItem.attachment.eventTimings.timings.receive, "number", + "The eventTimings attachment has an incorrect |timings.receive| property."); + is(typeof requestItem.attachment.eventTimings.totalTime, "number", + "The eventTimings attachment has an incorrect |totalTime| property."); - verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, { + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { time: true }); }); tab.linkedBrowser.reload(); }); }
--- a/devtools/client/netmonitor/test/browser_net_simple-request-details.js +++ b/devtools/client/netmonitor/test/browser_net_simple-request-details.js @@ -57,17 +57,17 @@ add_task(function* () { is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("value"), SIMPLE_SJS, "The url summary value is incorrect."); is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("tooltiptext"), SIMPLE_SJS, "The url summary tooltiptext is incorrect."); is(tabpanel.querySelector("#headers-summary-method-value").getAttribute("value"), "GET", "The method summary value is incorrect."); is(tabpanel.querySelector("#headers-summary-address-value").getAttribute("value"), "127.0.0.1:8888", "The remote address summary value is incorrect."); - is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("data-code"), + is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("code"), "200", "The status summary code is incorrect."); is(tabpanel.querySelector("#headers-summary-status-value").getAttribute("value"), "200 Och Aye", "The status summary value is incorrect."); is(tabpanel.querySelectorAll(".variables-view-scope").length, 2, "There should be 2 header scopes displayed in this tabpanel."); is(tabpanel.querySelectorAll(".variable-or-property").length, 19, "There should be 19 header values displayed in this tabpanel.");
--- a/devtools/client/netmonitor/test/browser_net_simple-request.js +++ b/devtools/client/netmonitor/test/browser_net_simple-request.js @@ -18,50 +18,50 @@ add_task(function* () { let { document, NetMonitorView } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; RequestsMenu.lazyUpdate = false; is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), true, "The pane toggle button should be disabled when the frontend is opened."); - ok(document.querySelector("#requests-menu-empty-notice"), + is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), false, "An empty notice should be displayed when the frontend is opened."); is(RequestsMenu.itemCount, 0, "The requests menu should be empty when the frontend is opened."); is(NetMonitorView.detailsPaneHidden, true, "The details pane should be hidden when the frontend is opened."); yield reloadAndWait(); is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), false, "The pane toggle button should be enabled after the first request."); - ok(!document.querySelector("#requests-menu-empty-notice"), + is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), true, "The empty notice should be hidden after the first request."); is(RequestsMenu.itemCount, 1, "The requests menu should not be empty after the first request."); is(NetMonitorView.detailsPaneHidden, true, "The details pane should still be hidden after the first request."); yield reloadAndWait(); is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), false, "The pane toggle button should be still be enabled after a reload."); - ok(!document.querySelector("#requests-menu-empty-notice"), + is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), true, "The empty notice should be still hidden after a reload."); is(RequestsMenu.itemCount, 1, "The requests menu should not be empty after a reload."); is(NetMonitorView.detailsPaneHidden, true, "The details pane should still be hidden after a reload."); RequestsMenu.clear(); is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), true, "The pane toggle button should be disabled when after clear."); - ok(document.querySelector("#requests-menu-empty-notice"), + is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), false, "An empty notice should be displayed again after clear."); is(RequestsMenu.itemCount, 0, "The requests menu should be empty after clear."); is(NetMonitorView.detailsPaneHidden, true, "The details pane should be hidden after clear."); return teardown(monitor);
--- a/devtools/client/netmonitor/test/browser_net_sort-01.js +++ b/devtools/client/netmonitor/test/browser_net_sort-01.js @@ -157,60 +157,71 @@ add_task(function* () { return teardown(monitor); function testContents([a, b, c, d, e]) { is(RequestsMenu.items.length, 5, "There should be a total of 5 items in the requests menu."); is(RequestsMenu.visibleItems.length, 5, "There should be a total of 5 visbile items in the requests menu."); - is($all(".request-list-item").length, 5, + is($all(".side-menu-widget-item").length, 5, "The visible items in the requests menu are, in fact, visible!"); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(a), + is(RequestsMenu.getItemAtIndex(0), RequestsMenu.items[0], + "The requests menu items aren't ordered correctly. First item is misplaced."); + is(RequestsMenu.getItemAtIndex(1), RequestsMenu.items[1], + "The requests menu items aren't ordered correctly. Second item is misplaced."); + is(RequestsMenu.getItemAtIndex(2), RequestsMenu.items[2], + "The requests menu items aren't ordered correctly. Third item is misplaced."); + is(RequestsMenu.getItemAtIndex(3), RequestsMenu.items[3], + "The requests menu items aren't ordered correctly. Fourth item is misplaced."); + is(RequestsMenu.getItemAtIndex(4), RequestsMenu.items[4], + "The requests menu items aren't ordered correctly. Fifth item is misplaced."); + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(a), "GET", STATUS_CODES_SJS + "?sts=100", { status: 101, statusText: "Switching Protocols", type: "plain", fullMimeType: "text/plain; charset=utf-8", transferred: L10N.getStr("networkMenu.sizeUnavailable"), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(b), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(b), "GET", STATUS_CODES_SJS + "?sts=200", { status: 202, statusText: "Created", type: "plain", fullMimeType: "text/plain; charset=utf-8", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(c), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c), "GET", STATUS_CODES_SJS + "?sts=300", { status: 303, statusText: "See Other", type: "plain", fullMimeType: "text/plain; charset=utf-8", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(d), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d), "GET", STATUS_CODES_SJS + "?sts=400", { status: 404, statusText: "Not Found", type: "plain", fullMimeType: "text/plain; charset=utf-8", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(e), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e), "GET", STATUS_CODES_SJS + "?sts=500", { status: 501, statusText: "Not Implemented", type: "plain", fullMimeType: "text/plain; charset=utf-8", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), time: true
--- a/devtools/client/netmonitor/test/browser_net_sort-02.js +++ b/devtools/client/netmonitor/test/browser_net_sort-02.js @@ -167,91 +167,102 @@ add_task(function* () { function testHeaders(sortType, direction) { let doc = monitor.panelWin.document; let target = doc.querySelector("#requests-menu-" + sortType + "-button"); let headers = doc.querySelectorAll(".requests-menu-header-button"); for (let header of headers) { if (header != target) { - ok(!header.hasAttribute("data-sorted"), - "The " + header.id + " header does not have a 'data-sorted' attribute."); - ok(!header.getAttribute("title"), - "The " + header.id + " header does not have a 'title' attribute."); + is(header.hasAttribute("sorted"), false, + "The " + header.id + " header should not have a 'sorted' attribute."); + is(header.hasAttribute("tooltiptext"), false, + "The " + header.id + " header should not have a 'tooltiptext' attribute."); } else { - is(header.getAttribute("data-sorted"), direction, - "The " + header.id + " header has a correct 'data-sorted' attribute."); - is(header.getAttribute("title"), direction == "ascending" + is(header.getAttribute("sorted"), direction, + "The " + header.id + " header has an incorrect 'sorted' attribute."); + is(header.getAttribute("tooltiptext"), direction == "ascending" ? L10N.getStr("networkMenu.sortedAsc") : L10N.getStr("networkMenu.sortedDesc"), - "The " + header.id + " header has a correct 'title' attribute."); + "The " + header.id + " has an incorrect 'tooltiptext' attribute."); } } } function testContents([a, b, c, d, e]) { isnot(RequestsMenu.selectedItem, null, "There should still be a selected item after sorting."); is(RequestsMenu.selectedIndex, a, "The first item should be still selected after sorting."); is(NetMonitorView.detailsPaneHidden, false, "The details pane should still be visible after sorting."); is(RequestsMenu.items.length, 5, "There should be a total of 5 items in the requests menu."); is(RequestsMenu.visibleItems.length, 5, - "There should be a total of 5 visible items in the requests menu."); - is($all(".request-list-item").length, 5, + "There should be a total of 5 visbile items in the requests menu."); + is($all(".side-menu-widget-item").length, 5, "The visible items in the requests menu are, in fact, visible!"); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(a), + is(RequestsMenu.getItemAtIndex(0), RequestsMenu.items[0], + "The requests menu items aren't ordered correctly. First item is misplaced."); + is(RequestsMenu.getItemAtIndex(1), RequestsMenu.items[1], + "The requests menu items aren't ordered correctly. Second item is misplaced."); + is(RequestsMenu.getItemAtIndex(2), RequestsMenu.items[2], + "The requests menu items aren't ordered correctly. Third item is misplaced."); + is(RequestsMenu.getItemAtIndex(3), RequestsMenu.items[3], + "The requests menu items aren't ordered correctly. Fourth item is misplaced."); + is(RequestsMenu.getItemAtIndex(4), RequestsMenu.items[4], + "The requests menu items aren't ordered correctly. Fifth item is misplaced."); + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(a), "GET1", SORTING_SJS + "?index=1", { fuzzyUrl: true, status: 101, statusText: "Meh", type: "1", fullMimeType: "text/1", transferred: L10N.getStr("networkMenu.sizeUnavailable"), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(b), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(b), "GET2", SORTING_SJS + "?index=2", { fuzzyUrl: true, status: 200, statusText: "Meh", type: "2", fullMimeType: "text/2", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(c), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c), "GET3", SORTING_SJS + "?index=3", { fuzzyUrl: true, status: 300, statusText: "Meh", type: "3", fullMimeType: "text/3", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(d), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d), "GET4", SORTING_SJS + "?index=4", { fuzzyUrl: true, status: 400, statusText: "Meh", type: "4", fullMimeType: "text/4", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39), time: true }); - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(e), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e), "GET5", SORTING_SJS + "?index=5", { fuzzyUrl: true, status: 500, statusText: "Meh", type: "5", fullMimeType: "text/5", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
--- a/devtools/client/netmonitor/test/browser_net_sort-03.js +++ b/devtools/client/netmonitor/test/browser_net_sort-03.js @@ -100,105 +100,105 @@ add_task(function* () { function testHeaders(sortType, direction) { let doc = monitor.panelWin.document; let target = doc.querySelector("#requests-menu-" + sortType + "-button"); let headers = doc.querySelectorAll(".requests-menu-header-button"); for (let header of headers) { if (header != target) { - ok(!header.hasAttribute("data-sorted"), - "The " + header.id + " header does not have a 'data-sorted' attribute."); - ok(!header.getAttribute("title"), - "The " + header.id + " header does not have a 'title' attribute."); + is(header.hasAttribute("sorted"), false, + "The " + header.id + " header should not have a 'sorted' attribute."); + is(header.hasAttribute("tooltiptext"), false, + "The " + header.id + " header should not have a 'tooltiptext' attribute."); } else { - is(header.getAttribute("data-sorted"), direction, - "The " + header.id + " header has a correct 'data-sorted' attribute."); - is(header.getAttribute("title"), direction == "ascending" + is(header.getAttribute("sorted"), direction, + "The " + header.id + " header has an incorrect 'sorted' attribute."); + is(header.getAttribute("tooltiptext"), direction == "ascending" ? L10N.getStr("networkMenu.sortedAsc") : L10N.getStr("networkMenu.sortedDesc"), - "The " + header.id + " header has a correct 'title' attribute."); + "The " + header.id + " has an incorrect 'tooltiptext' attribute."); } } } function testContents(order, selection) { isnot(RequestsMenu.selectedItem, null, "There should still be a selected item after sorting."); is(RequestsMenu.selectedIndex, selection, "The first item should be still selected after sorting."); is(NetMonitorView.detailsPaneHidden, false, "The details pane should still be visible after sorting."); is(RequestsMenu.items.length, order.length, "There should be a specific number of items in the requests menu."); is(RequestsMenu.visibleItems.length, order.length, "There should be a specific number of visbile items in the requests menu."); - is($all(".request-list-item").length, order.length, + is($all(".side-menu-widget-item").length, order.length, "The visible items in the requests menu are, in fact, visible!"); + for (let i = 0; i < order.length; i++) { + is(RequestsMenu.getItemAtIndex(i), RequestsMenu.items[i], + "The requests menu items aren't ordered correctly. Misplaced item " + i + "."); + } + for (let i = 0, len = order.length / 5; i < len; i++) { - verifyRequestItemTarget(RequestsMenu, - RequestsMenu.getItemAtIndex(order[i]), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i]), "GET1", SORTING_SJS + "?index=1", { fuzzyUrl: true, status: 101, statusText: "Meh", type: "1", fullMimeType: "text/1", transferred: L10N.getStr("networkMenu.sizeUnavailable"), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0), time: true }); } for (let i = 0, len = order.length / 5; i < len; i++) { - verifyRequestItemTarget(RequestsMenu, - RequestsMenu.getItemAtIndex(order[i + len]), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len]), "GET2", SORTING_SJS + "?index=2", { fuzzyUrl: true, status: 200, statusText: "Meh", type: "2", fullMimeType: "text/2", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19), time: true }); } for (let i = 0, len = order.length / 5; i < len; i++) { - verifyRequestItemTarget(RequestsMenu, - RequestsMenu.getItemAtIndex(order[i + len * 2]), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 2]), "GET3", SORTING_SJS + "?index=3", { fuzzyUrl: true, status: 300, statusText: "Meh", type: "3", fullMimeType: "text/3", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), time: true }); } for (let i = 0, len = order.length / 5; i < len; i++) { - verifyRequestItemTarget(RequestsMenu, - RequestsMenu.getItemAtIndex(order[i + len * 3]), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 3]), "GET4", SORTING_SJS + "?index=4", { fuzzyUrl: true, status: 400, statusText: "Meh", type: "4", fullMimeType: "text/4", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39), time: true }); } for (let i = 0, len = order.length / 5; i < len; i++) { - verifyRequestItemTarget(RequestsMenu, - RequestsMenu.getItemAtIndex(order[i + len * 4]), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 4]), "GET5", SORTING_SJS + "?index=5", { fuzzyUrl: true, status: 500, statusText: "Meh", type: "5", fullMimeType: "text/5", transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49), size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
--- a/devtools/client/netmonitor/test/browser_net_statistics-03.js +++ b/devtools/client/netmonitor/test/browser_net_statistics-03.js @@ -15,17 +15,17 @@ add_task(function* () { let panel = monitor.panelWin; let { $, EVENTS, NetMonitorView } = panel; EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button")); EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button")); EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-js-button")); EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-ws-button")); EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-other-button")); - testFilterButtonsCustom(monitor, [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1]); + testFilterButtonsCustom(monitor, [0, 1, 1, 1, 0, 0, 0, 0, 0, 1]); info("The correct filtering predicates are used before entering perf. analysis mode."); let onEvents = promise.all([ panel.once(EVENTS.PRIMED_CACHE_CHART_DISPLAYED), panel.once(EVENTS.EMPTY_CACHE_CHART_DISPLAYED) ]); NetMonitorView.toggleFrontendMode(); yield onEvents;
--- a/devtools/client/netmonitor/test/browser_net_status-codes.js +++ b/devtools/client/netmonitor/test/browser_net_status-codes.js @@ -109,18 +109,17 @@ add_task(function* () { function* verifyRequests() { info("Verifying requests contain correct information."); let index = 0; for (let request of REQUEST_DATA) { let item = RequestsMenu.getItemAtIndex(index); requestItems[index] = item; info("Verifying request #" + index); - yield verifyRequestItemTarget(RequestsMenu, item, - request.method, request.uri, request.details); + yield verifyRequestItemTarget(item, request.method, request.uri, request.details); index++; } } /** * A helper that opens a given tab of request details pane, selects and passes * all requests to the given test function. @@ -155,17 +154,17 @@ add_task(function* () { function* testSummary(data) { let tabpanel = document.querySelectorAll("#details-pane tabpanel")[0]; let { method, uri, details: { status, statusText } } = data; is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("value"), uri, "The url summary value is incorrect."); is(tabpanel.querySelector("#headers-summary-method-value").getAttribute("value"), method, "The method summary value is incorrect."); - is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("data-code"), + is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("code"), status, "The status summary code is incorrect."); is(tabpanel.querySelector("#headers-summary-status-value").getAttribute("value"), status + " " + statusText, "The status summary value is incorrect."); } /** * A function that tests "Params" tab contains correct information. */ @@ -203,13 +202,12 @@ add_task(function* () { } /** * A helper that clicks on a specified request and returns a promise resolved * when NetworkDetails has been populated with the data of the given request. */ function chooseRequest(index) { let onTabUpdated = monitor.panelWin.once(EVENTS.TAB_UPDATED); - let target = getItemTarget(RequestsMenu, requestItems[index]); - EventUtils.sendMouseEvent({ type: "mousedown" }, target); + EventUtils.sendMouseEvent({ type: "mousedown" }, requestItems[index].target); return onTabUpdated; } });
--- a/devtools/client/netmonitor/test/browser_net_streaming-response.js +++ b/devtools/client/netmonitor/test/browser_net_streaming-response.js @@ -28,17 +28,17 @@ add_task(function* () { let url = CONTENT_TYPE_SJS + "?fmt=" + fmt; yield ContentTask.spawn(tab.linkedBrowser, { url }, function* (args) { content.wrappedJSObject.performRequests(1, args.url); }); } yield wait; REQUESTS.forEach(([ fmt ], i) => { - verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(i), + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), "GET", CONTENT_TYPE_SJS + "?fmt=" + fmt, { status: 200, statusText: "OK" }); }); EventUtils.sendMouseEvent({ type: "mousedown" }, document.getElementById("details-pane-toggle"));
--- a/devtools/client/netmonitor/test/browser_net_throttle.js +++ b/devtools/client/netmonitor/test/browser_net_throttle.js @@ -27,31 +27,31 @@ function* throttleTest(actuallyThrottle) latencyMean: 0, latencyMax: 0, downloadBPSMean: size, downloadBPSMax: size, uploadBPSMean: 10000, uploadBPSMax: 10000, }, }; - let client = NetMonitorController.webConsoleClient; + let client = monitor._controller.webConsoleClient; info("sending throttle request"); let deferred = promise.defer(); client.setPreferences(request, response => { deferred.resolve(response); }); yield deferred.promise; let eventPromise = monitor.panelWin.once(EVENTS.RECEIVED_EVENT_TIMINGS); yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED); yield eventPromise; let requestItem = NetMonitorView.RequestsMenu.getItemAtIndex(0); - const reportedOneSecond = requestItem.eventTimings.timings.receive > 1000; + const reportedOneSecond = requestItem.attachment.eventTimings.timings.receive > 1000; if (actuallyThrottle) { ok(reportedOneSecond, "download reported as taking more than one second"); } else { ok(!reportedOneSecond, "download reported as taking less than one second"); } yield teardown(monitor); }
--- a/devtools/client/netmonitor/test/browser_net_timeline_ticks.js +++ b/devtools/client/netmonitor/test/browser_net_timeline_ticks.js @@ -14,18 +14,18 @@ add_task(function* () { info("Starting test... "); let { $, $all, NetMonitorView, NetMonitorController } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; // Disable transferred size column support for this test. // Without this, the waterfall only has enough room for one division, which // would remove most of the value of this test. - // $("#requests-menu-transferred-header-box").hidden = true; - // $("#requests-menu-item-template .requests-menu-transferred").hidden = true; + $("#requests-menu-transferred-header-box").hidden = true; + $("#requests-menu-item-template .requests-menu-transferred").hidden = true; RequestsMenu.lazyUpdate = false; ok($("#requests-menu-waterfall-label"), "An timeline label should be displayed when the frontend is opened."); ok($all(".requests-menu-timings-division").length == 0, "No tick labels should be displayed when the frontend is opened."); @@ -41,27 +41,32 @@ add_task(function* () { NetMonitorController.NetworkEventsHandler.clearMarkers(); RequestsMenu._flushWaterfallViews(true); ok(!$("#requests-menu-waterfall-label"), "The timeline label should be hidden after the first request."); ok($all(".requests-menu-timings-division").length >= 3, "There should be at least 3 tick labels in the network requests header."); - let timingDivisionEls = $all(".requests-menu-timings-division"); - is(timingDivisionEls[0].textContent, L10N.getFormatStr("networkMenu.millisecond", 0), - "The first tick label has correct value"); - is(timingDivisionEls[1].textContent, L10N.getFormatStr("networkMenu.millisecond", 80), - "The second tick label has correct value"); - is(timingDivisionEls[2].textContent, L10N.getFormatStr("networkMenu.millisecond", 160), - "The third tick label has correct value"); + is($all(".requests-menu-timings-division")[0].getAttribute("value"), + L10N.getFormatStr("networkMenu.millisecond", 0), + "The first tick label has an incorrect value"); + is($all(".requests-menu-timings-division")[1].getAttribute("value"), + L10N.getFormatStr("networkMenu.millisecond", 80), + "The second tick label has an incorrect value"); + is($all(".requests-menu-timings-division")[2].getAttribute("value"), + L10N.getFormatStr("networkMenu.millisecond", 160), + "The third tick label has an incorrect value"); - is(timingDivisionEls[0].style.width, "78px", "The first tick label has correct width"); - is(timingDivisionEls[1].style.width, "80px", "The second tick label has correct width"); - is(timingDivisionEls[2].style.width, "80px", "The third tick label has correct width"); + is($all(".requests-menu-timings-division")[0].style.transform, "translateX(0px)", + "The first tick label has an incorrect translation"); + is($all(".requests-menu-timings-division")[1].style.transform, "translateX(80px)", + "The second tick label has an incorrect translation"); + is($all(".requests-menu-timings-division")[2].style.transform, "translateX(160px)", + "The third tick label has an incorrect translation"); ok(RequestsMenu._canvas, "A canvas should be created after the first request."); ok(RequestsMenu._ctx, "A 2d context should be created after the first request."); let imageData = RequestsMenu._ctx.getImageData(0, 0, 161, 1); ok(imageData, "The image data should have been created."); let data = imageData.data;
--- a/devtools/client/netmonitor/test/browser_net_timing-division.js +++ b/devtools/client/netmonitor/test/browser_net_timing-division.js @@ -18,37 +18,44 @@ add_task(function* () { let wait = waitForNetworkEvents(monitor, 2); // Timeout needed for having enough divisions on the time scale. yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(2, null, 3000); }); yield wait; - let milDivs = $all(".requests-menu-timings-division[data-division-scale=millisecond]"); - let secDivs = $all(".requests-menu-timings-division[data-division-scale=second]"); - let minDivs = $all(".requests-menu-timings-division[data-division-scale=minute]"); + let milDivs = $all(".requests-menu-timings-division[division-scale=millisecond]"); + let secDivs = $all(".requests-menu-timings-division[division-scale=second]"); + let minDivs = $all(".requests-menu-timings-division[division-scale=minute]"); info("Number of millisecond divisions: " + milDivs.length); info("Number of second divisions: " + secDivs.length); info("Number of minute divisions: " + minDivs.length); - milDivs.forEach(div => info(`Millisecond division: ${div.textContent}`)); - secDivs.forEach(div => info(`Second division: ${div.textContent}`)); - minDivs.forEach(div => info(`Minute division: ${div.textContent}`)); + for (let div of milDivs) { + info("Millisecond division: " + div.getAttribute("value")); + } + for (let div of secDivs) { + info("Second division: " + div.getAttribute("value")); + } + for (let div of minDivs) { + info("Minute division: " + div.getAttribute("value")); + } - is(RequestsMenu.itemCount, 2, "There should be only two requests made."); + is(RequestsMenu.itemCount, 2, + "There should be only two requests made."); let firstRequest = RequestsMenu.getItemAtIndex(0); let lastRequest = RequestsMenu.getItemAtIndex(1); info("First request happened at: " + - firstRequest.responseHeaders.headers.find(e => e.name == "Date").value); + firstRequest.attachment.responseHeaders.headers.find(e => e.name == "Date").value); info("Last request happened at: " + - lastRequest.responseHeaders.headers.find(e => e.name == "Date").value); + lastRequest.attachment.responseHeaders.headers.find(e => e.name == "Date").value); ok(secDivs.length, "There should be at least one division on the seconds time scale."); - ok(secDivs[0].textContent.match(/\d+\.\d{2}\s\w+/), + ok(secDivs[0].getAttribute("value").match(/\d+\.\d{2}\s\w+/), "The division on the seconds time scale looks legit."); return teardown(monitor); });
--- a/devtools/client/netmonitor/test/head.js +++ b/devtools/client/netmonitor/test/head.js @@ -245,135 +245,132 @@ function waitForNetworkEvents(aMonitor, executeSoon(deferred.resolve); } } awaitedEventsToListeners.forEach(([e, l]) => panel.on(events[e], l)); return deferred.promise; } -/** - * Convert a store record (model) to the rendered element. Tests that need to use - * this should be rewritten - test the rendered markup at unit level, integration - * mochitest should check only the store state. - */ -function getItemTarget(requestList, requestItem) { - const items = requestList.mountPoint.querySelectorAll(".request-list-item"); - return [...items].find(el => el.dataset.id == requestItem.id); -} - -function verifyRequestItemTarget(requestList, requestItem, aMethod, aUrl, aData = {}) { +function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) { info("> Verifying: " + aMethod + " " + aUrl + " " + aData.toSource()); // This bloats log sizes significantly in automation (bug 992485) - // info("> Request: " + requestItem.toSource()); + // info("> Request: " + aRequestItem.attachment.toSource()); - let visibleIndex = requestList.visibleItems.indexOf(requestItem); + let requestsMenu = aRequestItem.ownerView; + let widgetIndex = requestsMenu.indexOfItem(aRequestItem); + let visibleIndex = requestsMenu.visibleItems.indexOf(aRequestItem); + info("Widget index of item: " + widgetIndex); info("Visible index of item: " + visibleIndex); let { fuzzyUrl, status, statusText, cause, type, fullMimeType, transferred, size, time, displayedStatus } = aData; - - let target = getItemTarget(requestList, requestItem); + let { attachment, target } = aRequestItem; let unicodeUrl = decodeUnicodeUrl(aUrl); let name = getUrlBaseName(aUrl); let query = getUrlQuery(aUrl); let hostPort = getUrlHost(aUrl); - let remoteAddress = requestItem.remoteAddress; + let remoteAddress = attachment.remoteAddress; if (fuzzyUrl) { - ok(requestItem.method.startsWith(aMethod), "The attached method is correct."); - ok(requestItem.url.startsWith(aUrl), "The attached url is correct."); + ok(attachment.method.startsWith(aMethod), "The attached method is correct."); + ok(attachment.url.startsWith(aUrl), "The attached url is correct."); } else { - is(requestItem.method, aMethod, "The attached method is correct."); - is(requestItem.url, aUrl, "The attached url is correct."); + is(attachment.method, aMethod, "The attached method is correct."); + is(attachment.url, aUrl, "The attached url is correct."); } - is(target.querySelector(".requests-menu-method").textContent, + is(target.querySelector(".requests-menu-method").getAttribute("value"), aMethod, "The displayed method is correct."); if (fuzzyUrl) { - ok(target.querySelector(".requests-menu-file").textContent.startsWith( + ok(target.querySelector(".requests-menu-file").getAttribute("value").startsWith( name + (query ? "?" + query : "")), "The displayed file is correct."); - ok(target.querySelector(".requests-menu-file").getAttribute("title").startsWith(unicodeUrl), + ok(target.querySelector(".requests-menu-file").getAttribute("tooltiptext").startsWith(unicodeUrl), "The tooltip file is correct."); } else { - is(target.querySelector(".requests-menu-file").textContent, + is(target.querySelector(".requests-menu-file").getAttribute("value"), name + (query ? "?" + query : ""), "The displayed file is correct."); - is(target.querySelector(".requests-menu-file").getAttribute("title"), + is(target.querySelector(".requests-menu-file").getAttribute("tooltiptext"), unicodeUrl, "The tooltip file is correct."); } - is(target.querySelector(".requests-menu-domain").textContent, + is(target.querySelector(".requests-menu-domain").getAttribute("value"), hostPort, "The displayed domain is correct."); let domainTooltip = hostPort + (remoteAddress ? " (" + remoteAddress + ")" : ""); - is(target.querySelector(".requests-menu-domain").getAttribute("title"), + is(target.querySelector(".requests-menu-domain").getAttribute("tooltiptext"), domainTooltip, "The tooltip domain is correct."); if (status !== undefined) { - let value = target.querySelector(".requests-menu-status-icon").getAttribute("data-code"); - let codeValue = target.querySelector(".requests-menu-status-code").textContent; - let tooltip = target.querySelector(".requests-menu-status").getAttribute("title"); + let value = target.querySelector(".requests-menu-status-icon").getAttribute("code"); + let codeValue = target.querySelector(".requests-menu-status-code").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-status").getAttribute("tooltiptext"); info("Displayed status: " + value); info("Displayed code: " + codeValue); info("Tooltip status: " + tooltip); is(value, displayedStatus ? displayedStatus : status, "The displayed status is correct."); is(codeValue, status, "The displayed status code is correct."); is(tooltip, status + " " + statusText, "The tooltip status is correct."); } if (cause !== undefined) { - let value = target.querySelector(".requests-menu-cause > .subitem-label").textContent; - let tooltip = target.querySelector(".requests-menu-cause").getAttribute("title"); + let causeLabel = target.querySelector(".requests-menu-cause-label"); + let value = causeLabel.getAttribute("value"); + let tooltip = causeLabel.getAttribute("tooltiptext"); info("Displayed cause: " + value); info("Tooltip cause: " + tooltip); is(value, cause.type, "The displayed cause is correct."); is(tooltip, cause.loadingDocumentUri, "The tooltip cause is correct.") } if (type !== undefined) { - let value = target.querySelector(".requests-menu-type").textContent; - let tooltip = target.querySelector(".requests-menu-type").getAttribute("title"); + let value = target.querySelector(".requests-menu-type").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-type").getAttribute("tooltiptext"); info("Displayed type: " + value); info("Tooltip type: " + tooltip); is(value, type, "The displayed type is correct."); is(tooltip, fullMimeType, "The tooltip type is correct."); } if (transferred !== undefined) { - let value = target.querySelector(".requests-menu-transferred").textContent; - let tooltip = target.querySelector(".requests-menu-transferred").getAttribute("title"); + let value = target.querySelector(".requests-menu-transferred").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-transferred").getAttribute("tooltiptext"); info("Displayed transferred size: " + value); info("Tooltip transferred size: " + tooltip); is(value, transferred, "The displayed transferred size is correct."); is(tooltip, transferred, "The tooltip transferred size is correct."); } if (size !== undefined) { - let value = target.querySelector(".requests-menu-size").textContent; - let tooltip = target.querySelector(".requests-menu-size").getAttribute("title"); + let value = target.querySelector(".requests-menu-size").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-size").getAttribute("tooltiptext"); info("Displayed size: " + value); info("Tooltip size: " + tooltip); is(value, size, "The displayed size is correct."); is(tooltip, size, "The tooltip size is correct."); } if (time !== undefined) { - let value = target.querySelector(".requests-menu-timings-total").textContent; - let tooltip = target.querySelector(".requests-menu-timings-total").getAttribute("title"); + let value = target.querySelector(".requests-menu-timings-total").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-timings-total").getAttribute("tooltiptext"); info("Displayed time: " + value); info("Tooltip time: " + tooltip); ok(~~(value.match(/[0-9]+/)) >= 0, "The displayed time is correct."); ok(~~(tooltip.match(/[0-9]+/)) >= 0, "The tooltip time is correct."); } if (visibleIndex != -1) { if (visibleIndex % 2 == 0) { - ok(target.classList.contains("even"), "Item should have 'even' class."); - ok(!target.classList.contains("odd"), "Item shouldn't have 'odd' class."); + ok(aRequestItem.target.hasAttribute("even"), + aRequestItem.value + " should have 'even' attribute."); + ok(!aRequestItem.target.hasAttribute("odd"), + aRequestItem.value + " shouldn't have 'odd' attribute."); } else { - ok(!target.classList.contains("even"), "Item shouldn't have 'even' class."); - ok(target.classList.contains("odd"), "Item should have 'odd' class."); + ok(!aRequestItem.target.hasAttribute("even"), + aRequestItem.value + " shouldn't have 'even' attribute."); + ok(aRequestItem.target.hasAttribute("odd"), + aRequestItem.value + " should have 'odd' attribute."); } } } /** * Helper function for waiting for an event to fire before resolving a promise. * Example: waitFor(aMonitor.panelWin, aMonitor.panelWin.EVENTS.TAB_UPDATED); *
deleted file mode 100644 --- a/devtools/client/netmonitor/utils/format-utils.js +++ /dev/null @@ -1,44 +0,0 @@ -/* 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 { L10N } = require("../l10n"); - -// Constants for formatting bytes. -const BYTES_IN_KB = 1024; -const BYTES_IN_MB = Math.pow(BYTES_IN_KB, 2);