diff --git a/.eslintrc-test-paths.js b/.eslintrc-test-paths.js index 9b7ce23e769de47f021789464ebf521a8b8027aa..9806aa4420ad09a6db0ff67314664d064152726c 100644 --- a/.eslintrc-test-paths.js +++ b/.eslintrc-test-paths.js @@ -165,6 +165,7 @@ const extraBrowserTestPaths = [ "devtools/client/styleeditor/test/", "devtools/shared/commands/inspected-window/tests/", "devtools/shared/commands/inspector/tests/", + "devtools/shared/commands/network/tests/", "devtools/shared/commands/resource/tests/", "devtools/shared/commands/script/tests/", "devtools/shared/commands/target-configuration/tests/", diff --git a/devtools/client/netmonitor/src/actions/http-custom-request.js b/devtools/client/netmonitor/src/actions/http-custom-request.js index 163aab6e1cb663f574859537ae6b2f893ae53515..e045107410733a1f156ce7222673bfae1367eb37 100644 --- a/devtools/client/netmonitor/src/actions/http-custom-request.js +++ b/devtools/client/netmonitor/src/actions/http-custom-request.js @@ -69,8 +69,8 @@ function toggleHTTPCustomRequestPanel() { /** * Send a new HTTP request using the data in the custom request form. */ -function sendHTTPCustomRequest(connector, request) { - return async ({ dispatch, getState }) => { +function sendHTTPCustomRequest(request) { + return async ({ dispatch, getState, connector, commands }) => { if (!request) { return; } @@ -104,7 +104,7 @@ function sendHTTPCustomRequest(connector, request) { data.body = request.requestPostData.postData?.text; } - const { channelId } = await connector.sendHTTPRequest(data); + const { channelId } = await commands.networkCommand.sendHTTPRequest(data); const newRequest = getRequestByChannelId(getState(), channelId); // If the new custom request is available already select the request, else diff --git a/devtools/client/netmonitor/src/actions/requests.js b/devtools/client/netmonitor/src/actions/requests.js index f229b7e2025d28ef980246ab5edb56ceac36d017..838f2509a99bd24d63952baa36445c08314f5f63 100644 --- a/devtools/client/netmonitor/src/actions/requests.js +++ b/devtools/client/netmonitor/src/actions/requests.js @@ -85,8 +85,8 @@ function cloneSelectedRequest() { /** * Send a new HTTP request using the data in the custom request form. */ -function sendCustomRequest(connector, requestId = null) { - return async ({ dispatch, getState }) => { +function sendCustomRequest(requestId = null) { + return async ({ dispatch, getState, connector, commands }) => { let request; if (requestId) { request = getRequestById(getState(), requestId); @@ -123,13 +123,11 @@ function sendCustomRequest(connector, requestId = null) { data.body = request.requestPostData.postData.text; } - // @backward-compat { version 85 } Introduced `channelId` to eventually - // replace `actor`. - const { channelId, actor } = await connector.sendHTTPRequest(data); + const { channelId } = await commands.networkCommand.sendHTTPRequest(data); dispatch({ type: SEND_CUSTOM_REQUEST, - id: channelId || actor, + id: channelId, }); }; } diff --git a/devtools/client/netmonitor/src/api.js b/devtools/client/netmonitor/src/api.js index ed2a8e3914a2401c82fd8efa9660a555247e5594..8d760de788aa811c4d54b95fead8582bc24fb780 100644 --- a/devtools/client/netmonitor/src/api.js +++ b/devtools/client/netmonitor/src/api.js @@ -209,7 +209,7 @@ NetMonitorAPI.prototype = { this.store.dispatch(Actions.batchFlush()); // Send custom request with same url, headers and body as the request // with the given requestId. - this.store.dispatch(Actions.sendCustomRequest(this.connector, requestId)); + this.store.dispatch(Actions.sendCustomRequest(requestId)); }, }; diff --git a/devtools/client/netmonitor/src/components/CustomRequestPanel.js b/devtools/client/netmonitor/src/components/CustomRequestPanel.js index e024bdf2d4c0a93cdebde09b31bd155cd78a19ff..922665fad2d48a0986c1073b7a968d3fb62a825b 100644 --- a/devtools/client/netmonitor/src/components/CustomRequestPanel.js +++ b/devtools/client/netmonitor/src/components/CustomRequestPanel.js @@ -369,8 +369,7 @@ module.exports = connect( (dispatch, props) => ({ removeSelectedCustomRequest: () => dispatch(Actions.removeSelectedCustomRequest()), - sendCustomRequest: () => - dispatch(Actions.sendCustomRequest(props.connector)), + sendCustomRequest: () => dispatch(Actions.sendCustomRequest()), updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)), }) diff --git a/devtools/client/netmonitor/src/components/new-request/HTTPCustomRequestPanel.js b/devtools/client/netmonitor/src/components/new-request/HTTPCustomRequestPanel.js index 8a6854a18d10f4ca31b6fbc2ea28b2f602dcd579..a898fe38ebcca962f782fd7782ffac5868b471e0 100644 --- a/devtools/client/netmonitor/src/components/new-request/HTTPCustomRequestPanel.js +++ b/devtools/client/netmonitor/src/components/new-request/HTTPCustomRequestPanel.js @@ -507,6 +507,6 @@ module.exports = connect( state => ({ request: getClickedRequest(state) }), (dispatch, props) => ({ sendCustomRequest: request => - dispatch(Actions.sendHTTPCustomRequest(props.connector, request)), + dispatch(Actions.sendHTTPCustomRequest(request)), }) )(HTTPCustomRequestPanel); diff --git a/devtools/client/netmonitor/src/components/request-details/HeadersPanel.js b/devtools/client/netmonitor/src/components/request-details/HeadersPanel.js index a4fdbed0b3bb3e1c56a3a73ba13e01e8ca6f1bf0..b8e28766848ce27e858b345848ccefa21a2278b5 100644 --- a/devtools/client/netmonitor/src/components/request-details/HeadersPanel.js +++ b/devtools/client/netmonitor/src/components/request-details/HeadersPanel.js @@ -845,7 +845,6 @@ module.exports = connect( openHTTPCustomRequestTab: () => dispatch(Actions.openHTTPCustomRequest(true)), cloneRequest: id => dispatch(Actions.cloneRequest(id)), - sendCustomRequest: () => - dispatch(Actions.sendCustomRequest(props.connector)), + sendCustomRequest: () => dispatch(Actions.sendCustomRequest()), }) )(HeadersPanel); diff --git a/devtools/client/netmonitor/src/components/request-list/RequestListContent.js b/devtools/client/netmonitor/src/components/request-list/RequestListContent.js index 362f77295a4f8c3c6bb2528aea3d76e6720d98ca..7610886b69316fc2d0325a83831bc839916f8040 100644 --- a/devtools/client/netmonitor/src/components/request-list/RequestListContent.js +++ b/devtools/client/netmonitor/src/components/request-list/RequestListContent.js @@ -482,10 +482,9 @@ module.exports = connect( dispatch(Actions.openHTTPCustomRequest(true)), closeHTTPCustomRequestTab: () => dispatch(Actions.openHTTPCustomRequest(false)), - sendCustomRequest: () => - dispatch(Actions.sendCustomRequest(props.connector)), + sendCustomRequest: () => dispatch(Actions.sendCustomRequest()), sendHTTPCustomRequest: request => - dispatch(Actions.sendHTTPCustomRequest(props.connector, request)), + dispatch(Actions.sendHTTPCustomRequest(request)), openStatistics: open => dispatch(Actions.openStatistics(props.connector, open)), openRequestBlockingAndAddUrl: url => diff --git a/devtools/client/netmonitor/src/connector/index.js b/devtools/client/netmonitor/src/connector/index.js index 17218e5ab1a01cb833aa504dd00cbe8d6f67bc21..22d17628af77ee3efc12ba79fed13229a851c6b3 100644 --- a/devtools/client/netmonitor/src/connector/index.js +++ b/devtools/client/netmonitor/src/connector/index.js @@ -37,7 +37,6 @@ class Connector { this.disconnect = this.disconnect.bind(this); this.willNavigate = this.willNavigate.bind(this); this.navigate = this.navigate.bind(this); - this.sendHTTPRequest = this.sendHTTPRequest.bind(this); this.triggerActivity = this.triggerActivity.bind(this); this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this); this.requestData = this.requestData.bind(this); @@ -76,6 +75,7 @@ class Connector { this.getState = getState; this.toolbox = connection.toolbox; this.commands = this.toolbox.commands; + this.networkCommand = this.commands.networkCommand; // The owner object (NetMonitorAPI) received all events. this.owner = connection.owner; @@ -344,19 +344,6 @@ class Connector { this.emitForTests(TEST_EVENTS.TIMELINE_EVENT, resource); } - /** - * Send a HTTP request data payload - * - * @param {object} data data payload would like to sent to backend - */ - async sendHTTPRequest(data) { - const networkContentFront = await this.currentTarget.getFront( - "networkContent" - ); - const { channelId } = await networkContentFront.sendHTTPRequest(data); - return { channelId }; - } - /* * Get the list of blocked URLs */ diff --git a/devtools/client/netmonitor/test/browser_net_new_request_panel_send_request.js b/devtools/client/netmonitor/test/browser_net_new_request_panel_send_request.js index 677d02d45e3088e31894fdbe148af05da2048c25..9337c7cb94634a217b8ae6264a680d9a2bcc4bfb 100644 --- a/devtools/client/netmonitor/test/browser_net_new_request_panel_send_request.js +++ b/devtools/client/netmonitor/test/browser_net_new_request_panel_send_request.js @@ -20,8 +20,6 @@ add_task(async function() { info("Starting test... "); const { document, store, windowRequire, connector } = monitor.panelWin; - const { sendHTTPRequest } = connector; - // Action should be processed synchronously in tests. const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); store.dispatch(Actions.batchEnable(false)); @@ -50,7 +48,7 @@ add_task(async function() { }, }; const waitUntilRequestDisplayed = waitForNetworkEvents(monitor, 1); - sendHTTPRequest(request); + connector.networkCommand.sendHTTPRequest(request); await waitUntilRequestDisplayed; info("selecting first request"); diff --git a/devtools/client/netmonitor/test/browser_net_resend.js b/devtools/client/netmonitor/test/browser_net_resend.js index 20d09e526f76ad9849eae53877657ece81574504..43c7c15ee8dba345a86dde5252dca325c6425147 100644 --- a/devtools/client/netmonitor/test/browser_net_resend.js +++ b/devtools/client/netmonitor/test/browser_net_resend.js @@ -175,7 +175,7 @@ async function testOldEditAndResendPanel() { }); info("Starting test... "); - const { document, store, windowRequire, connector } = monitor.panelWin; + const { document, store, windowRequire } = monitor.panelWin; const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); const { getSelectedRequest, getSortedRequests } = windowRequire( "devtools/client/netmonitor/src/selectors/index" @@ -214,7 +214,7 @@ async function testOldEditAndResendPanel() { // send the new request const wait = waitForNetworkEvents(monitor, 1); - store.dispatch(Actions.sendCustomRequest(connector)); + store.dispatch(Actions.sendCustomRequest()); await wait; let sentItem; diff --git a/devtools/client/netmonitor/test/browser_net_resend_cors.js b/devtools/client/netmonitor/test/browser_net_resend_cors.js index ff1d6fc2818bd252113989bc25ba2c5d9973a3f7..031d55f8e4c2182397f8c1d9d04828da1f59c4c1 100644 --- a/devtools/client/netmonitor/test/browser_net_resend_cors.js +++ b/devtools/client/netmonitor/test/browser_net_resend_cors.js @@ -70,7 +70,7 @@ add_task(async function() { store.dispatch(Actions.cloneRequest(item.id)); info("Sending the cloned request (without change)"); - store.dispatch(Actions.sendCustomRequest(connector, item.id)); + store.dispatch(Actions.sendCustomRequest(item.id)); await waitUntil( () => getSortedRequests(store.getState()).length === length + 1 diff --git a/devtools/client/netmonitor/test/browser_net_resend_headers.js b/devtools/client/netmonitor/test/browser_net_resend_headers.js index 34b35f5c2540c24a2e6277f2543861f8c384e861..d3b65bb7e3430ccaf72c154b209b77e8e2b0b6fe 100644 --- a/devtools/client/netmonitor/test/browser_net_resend_headers.js +++ b/devtools/client/netmonitor/test/browser_net_resend_headers.js @@ -15,7 +15,7 @@ add_task(async function() { const { store, windowRequire, connector } = monitor.panelWin; const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); - const { requestData, sendHTTPRequest } = connector; + const { requestData } = connector; const { getSortedRequests } = windowRequire( "devtools/client/netmonitor/src/selectors/index" ); @@ -33,7 +33,7 @@ add_task(async function() { ]; const wait = waitForNetworkEvents(monitor, 1); - sendHTTPRequest({ + connector.networkCommand.sendHTTPRequest({ url: requestUrl, method: "POST", headers: requestHeaders, diff --git a/devtools/client/netmonitor/test/browser_net_resend_hidden_headers.js b/devtools/client/netmonitor/test/browser_net_resend_hidden_headers.js index 138a97b51d88c85939c3ae13058e47ce9f6ef1fb..cbf7c390e3a54ae742313af68e2e8c49dc487cd9 100644 --- a/devtools/client/netmonitor/test/browser_net_resend_hidden_headers.js +++ b/devtools/client/netmonitor/test/browser_net_resend_hidden_headers.js @@ -15,7 +15,6 @@ add_task(async function() { const { store, windowRequire, connector } = monitor.panelWin; const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); - const { sendHTTPRequest } = connector; const { getSortedRequests } = windowRequire( "devtools/client/netmonitor/src/selectors/index" @@ -29,7 +28,7 @@ add_task(async function() { ]; const originalRequest = waitForNetworkEvents(monitor, 1); - sendHTTPRequest({ + connector.networkCommand.sendHTTPRequest({ url: requestUrl, method: "GET", headers: requestHeaders, @@ -49,7 +48,7 @@ add_task(async function() { const clonedRequest = waitForNetworkEvents(monitor, 1); - store.dispatch(Actions.sendCustomRequest(connector, originalItem.id)); + store.dispatch(Actions.sendCustomRequest(originalItem.id)); await clonedRequest; diff --git a/devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js b/devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js index 24232610c6a1989281bff760cd543b2253e32d84..6ca977ab897adff0dc313334c36a721d4c46fa8c 100644 --- a/devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js +++ b/devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js @@ -179,7 +179,6 @@ function NetworkEventMessage({ getLongString: grip => { return serviceContainer.getLongString(grip); }, - sendHTTPRequest: () => {}, triggerActivity: () => {}, requestData: (requestId, dataType) => { return serviceContainer.requestData(requestId, dataType); diff --git a/devtools/shared/commands/index.js b/devtools/shared/commands/index.js index 01bd52895a7b02ac162d005dd0b8f08b5aaa2524..94cf7717cb83144f4dd4c3d27a8e853c1aa94a17 100644 --- a/devtools/shared/commands/index.js +++ b/devtools/shared/commands/index.js @@ -12,6 +12,7 @@ const Commands = { inspectedWindowCommand: "devtools/shared/commands/inspected-window/inspected-window-command", inspectorCommand: "devtools/shared/commands/inspector/inspector-command", + networkCommand: "devtools/shared/commands/network/network-command", resourceCommand: "devtools/shared/commands/resource/resource-command", rootResourceCommand: "devtools/shared/commands/root-resource/root-resource-command", diff --git a/devtools/shared/commands/moz.build b/devtools/shared/commands/moz.build index 36bacf281d58375e6cfbfd32b11466b6d7ed6ba3..bcd8a148108168eb59f95c9859b226146455ca3a 100644 --- a/devtools/shared/commands/moz.build +++ b/devtools/shared/commands/moz.build @@ -5,6 +5,7 @@ DIRS += [ "inspected-window", "inspector", + "network", "resource", "root-resource", "script", diff --git a/devtools/shared/commands/network/moz.build b/devtools/shared/commands/network/moz.build new file mode 100644 index 0000000000000000000000000000000000000000..e765e5ac76d56f7a1a71e1fa74055fbed54ee63e --- /dev/null +++ b/devtools/shared/commands/network/moz.build @@ -0,0 +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( + "network-command.js", +) + +if CONFIG["MOZ_BUILD_APP"] != "mobile/android": + BROWSER_CHROME_MANIFESTS += ["tests/browser.ini"] diff --git a/devtools/shared/commands/network/network-command.js b/devtools/shared/commands/network/network-command.js new file mode 100644 index 0000000000000000000000000000000000000000..0bc04aee29a9aa2a94ecce0fc226674558cd3d1c --- /dev/null +++ b/devtools/shared/commands/network/network-command.js @@ -0,0 +1,42 @@ +/* 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"; + +class NetworkCommand { + /** + * This class helps listen, inspect and control network requests. + * + * @param {DescriptorFront} descriptorFront + * The context to inspect identified by this descriptor. + * @param {WatcherFront} watcherFront + * If available, a reference to the related Watcher Front. + * @param {Object} commands + * The commands object with all interfaces defined from devtools/shared/commands/ + */ + constructor({ descriptorFront, watcherFront, commands }) { + this.commands = commands; + this.descriptorFront = descriptorFront; + this.watcherFront = watcherFront; + } + + /** + * Send a HTTP request data payload + * + * @param {object} data data payload would like to sent to backend + */ + async sendHTTPRequest(data) { + // By default use the top-level target, but we might at some point + // allow using another target. + const networkContentFront = await this.commands.targetCommand.targetFront.getFront( + "networkContent" + ); + const { channelId } = await networkContentFront.sendHTTPRequest(data); + return { channelId }; + } + + destroy() {} +} + +module.exports = NetworkCommand; diff --git a/devtools/shared/commands/network/tests/browser.ini b/devtools/shared/commands/network/tests/browser.ini new file mode 100644 index 0000000000000000000000000000000000000000..32ebfef8b3ba38c63c23011e60cfdca05b854007 --- /dev/null +++ b/devtools/shared/commands/network/tests/browser.ini @@ -0,0 +1,10 @@ +[DEFAULT] +tags = devtools +subsuite = devtools +support-files = + !/devtools/client/shared/test/shared-head.js + !/devtools/client/shared/test/telemetry-test-helpers.js + !/devtools/client/shared/test/highlighter-test-actor.js + head.js + +[browser_network_command_sendHTTPRequest.js] diff --git a/devtools/shared/commands/network/tests/browser_network_command_sendHTTPRequest.js b/devtools/shared/commands/network/tests/browser_network_command_sendHTTPRequest.js new file mode 100644 index 0000000000000000000000000000000000000000..9062b0de7870039845304056d1a92d26541c7683 --- /dev/null +++ b/devtools/shared/commands/network/tests/browser_network_command_sendHTTPRequest.js @@ -0,0 +1,78 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the NetworkCommand's sendHTTPRequest + +add_task(async function() { + info("Test NetworkCommand.sendHTTPRequest"); + const tab = await addTab("data:text/html,foo"); + const commands = await CommandsFactory.forTab(tab); + + // We have to ensure TargetCommand is initialized to have access to the top level target + // from NetworkCommand.sendHTTPRequest + await commands.targetCommand.startListening(); + + const { networkCommand } = commands; + + const httpServer = createTestHTTPServer(); + const onRequest = new Promise(resolve => { + httpServer.registerPathHandler( + "/http-request.html", + (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write("Response body"); + resolve(request); + } + ); + }); + const url = `http://localhost:${httpServer.identity.primaryPort}/http-request.html`; + + info("Call NetworkCommand.sendHTTPRequest"); + const { resourceCommand } = commands; + const { onResource } = await resourceCommand.waitForNextResource( + resourceCommand.TYPES.NETWORK_EVENT + ); + const { channelId } = await networkCommand.sendHTTPRequest({ + url, + method: "POST", + headers: [{ name: "Request", value: "Header" }], + body: "Hello", + cause: { + loadingDocumentUri: "https://example.com", + stacktraceAvailable: true, + type: "xhr", + }, + }); + ok(channelId, "Received a channel id in response"); + const resource = await onResource; + is( + resource.resourceId, + channelId, + "NETWORK_EVENT resource channelId is the same as the one returned by sendHTTPRequest" + ); + + const request = await onRequest; + is(request.method, "POST", "Request method is correct"); + is(request.getHeader("Request"), "Header", "The custom header was passed"); + is(fetchRequestBody(request), "Hello", "The request POST's body is correct"); + + await commands.destroy(); +}); + +const BinaryInputStream = Components.Constructor( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +function fetchRequestBody(request) { + let body = ""; + const bodyStream = new BinaryInputStream(request.bodyInputStream); + let avail = 0; + while ((avail = bodyStream.available()) > 0) { + body += String.fromCharCode.apply(String, bodyStream.readByteArray(avail)); + } + return body; +} diff --git a/devtools/shared/commands/network/tests/head.js b/devtools/shared/commands/network/tests/head.js new file mode 100644 index 0000000000000000000000000000000000000000..227e8ae9d967ded2216a857d86673a6ba5c9afaf --- /dev/null +++ b/devtools/shared/commands/network/tests/head.js @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/* eslint no-unused-vars: [2, {"vars": "local"}] */ +/* import-globals-from ../../../../client/shared/test/shared-head.js */ + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", + this +);