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
This commit is contained in:
Alexandre Poirot 2023-01-09 13:09:43 +00:00
parent 00c3224b88
commit b642d61aff
22 changed files with 178 additions and 44 deletions

View file

@ -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/",

View file

@ -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

View file

@ -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,
});
};
}

View file

@ -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));
},
};

View file

@ -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)),
})

View file

@ -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);

View file

@ -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);

View file

@ -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 =>

View file

@ -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
*/

View file

@ -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");

View file

@ -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;

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -179,7 +179,6 @@ function NetworkEventMessage({
getLongString: grip => {
return serviceContainer.getLongString(grip);
},
sendHTTPRequest: () => {},
triggerActivity: () => {},
requestData: (requestId, dataType) => {
return serviceContainer.requestData(requestId, dataType);

View file

@ -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",

View file

@ -5,6 +5,7 @@
DIRS += [
"inspected-window",
"inspector",
"network",
"resource",
"root-resource",
"script",

View file

@ -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"]

View file

@ -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;

View file

@ -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]

View file

@ -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;
}

View file

@ -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
);