From b642d61aff059dad1db472754a436a5f5ce84808 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot <poirot.alex@gmail.com> Date: Mon, 9 Jan 2023 13:09:43 +0000 Subject: [PATCH] Bug 1693495 - [devtools] Implement a beginning of "Network" commands, starting with sendHTTPRequest. r=devtools-reviewers,nchevobbe Use new "commands" thunk argument. We were passing connector as argument whereas we could have fetched it from thunk arguments. This will help slowly convert Connector/NetMonitorAPI to a command. Differential Revision: https://phabricator.services.mozilla.com/D166055 --- .eslintrc-test-paths.js | 1 + .../src/actions/http-custom-request.js | 6 +- .../client/netmonitor/src/actions/requests.js | 10 +-- devtools/client/netmonitor/src/api.js | 2 +- .../src/components/CustomRequestPanel.js | 3 +- .../new-request/HTTPCustomRequestPanel.js | 2 +- .../request-details/HeadersPanel.js | 3 +- .../request-list/RequestListContent.js | 5 +- .../client/netmonitor/src/connector/index.js | 15 +--- ...wser_net_new_request_panel_send_request.js | 4 +- .../netmonitor/test/browser_net_resend.js | 4 +- .../test/browser_net_resend_cors.js | 2 +- .../test/browser_net_resend_headers.js | 4 +- .../test/browser_net_resend_hidden_headers.js | 5 +- .../message-types/NetworkEventMessage.js | 1 - devtools/shared/commands/index.js | 1 + devtools/shared/commands/moz.build | 1 + devtools/shared/commands/network/moz.build | 10 +++ .../commands/network/network-command.js | 42 ++++++++++ .../shared/commands/network/tests/browser.ini | 10 +++ ...browser_network_command_sendHTTPRequest.js | 78 +++++++++++++++++++ .../shared/commands/network/tests/head.js | 13 ++++ 22 files changed, 178 insertions(+), 44 deletions(-) create mode 100644 devtools/shared/commands/network/moz.build create mode 100644 devtools/shared/commands/network/network-command.js create mode 100644 devtools/shared/commands/network/tests/browser.ini create mode 100644 devtools/shared/commands/network/tests/browser_network_command_sendHTTPRequest.js create mode 100644 devtools/shared/commands/network/tests/head.js diff --git a/.eslintrc-test-paths.js b/.eslintrc-test-paths.js index 9b7ce23e769de..9806aa4420ad0 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 163aab6e1cb66..e045107410733 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 f229b7e2025d2..838f2509a99bd 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 ed2a8e3914a24..8d760de788aa8 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 e024bdf2d4c0a..922665fad2d48 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 8a6854a18d10f..a898fe38ebcca 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 a4fdbed0b3bb3..b8e28766848ce 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 362f77295a4f8..7610886b69316 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 17218e5ab1a01..22d17628af77e 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 677d02d45e308..9337c7cb94634 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 20d09e526f76a..43c7c15ee8dba 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 ff1d6fc2818bd..031d55f8e4c21 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 34b35f5c2540c..d3b65bb7e3430 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 138a97b51d88c..cbf7c390e3a54 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 24232610c6a19..6ca977ab897ad 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 01bd52895a7b0..94cf7717cb831 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 36bacf281d583..bcd8a14810816 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 0000000000000..e765e5ac76d56 --- /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 0000000000000..0bc04aee29a9a --- /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 0000000000000..32ebfef8b3ba3 --- /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 0000000000000..9062b0de78700 --- /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 0000000000000..227e8ae9d967d --- /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 +); -- GitLab