diff options
Diffstat (limited to 'devtools/shared/commands/network')
6 files changed, 268 insertions, 0 deletions
diff --git a/devtools/shared/commands/network/moz.build b/devtools/shared/commands/network/moz.build new file mode 100644 index 0000000000..e765e5ac76 --- /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 0000000000..44cdf4e759 --- /dev/null +++ b/devtools/shared/commands/network/network-command.js @@ -0,0 +1,96 @@ +/* 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 }; + } + + /* + * Get the list of blocked URL filters. + * + * A URL filter is a RegExp string so that one filter can match many URLs. + * It can be an absolute URL to match only one precise request: + * http://mozilla.org/index.html + * Or just a string which would match all URL containing this string: + * mozilla + * Or a RegExp to match various types of URLs: + * http://*mozilla.org/*.css + * + * @return {Array} + * List of all currently blocked URL filters. + */ + async getBlockedUrls() { + const networkParentFront = await this.watcherFront.getNetworkParentActor(); + return networkParentFront.getBlockedUrls(); + } + + /** + * Updates the list of blocked URL filters. + * + * @param {Array} urls + * An array of URL filter strings. + * See getBlockedUrls for definition of URL filters. + */ + async setBlockedUrls(urls) { + const networkParentFront = await this.watcherFront.getNetworkParentActor(); + return networkParentFront.setBlockedUrls(urls); + } + + /** + * Block only one additional URL filter + * + * @param {String} url + * URL filter to block. + * See getBlockedUrls for definition of URL filters. + */ + async blockRequestForUrl(url) { + const networkParentFront = await this.watcherFront.getNetworkParentActor(); + return networkParentFront.blockRequest({ url }); + } + + /** + * Stop blocking only one specific URL filter + * + * @param {String} url + * URL filter to unblock. + * See getBlockedUrls for definition of URL filters. + */ + async unblockRequestForUrl(url) { + const networkParentFront = await this.watcherFront.getNetworkParentActor(); + return networkParentFront.unblockRequest({ url }); + } + + 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 0000000000..ce1860f0f0 --- /dev/null +++ b/devtools/shared/commands/network/tests/browser.ini @@ -0,0 +1,11 @@ +[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_request_blocking.js] +[browser_network_command_sendHTTPRequest.js] diff --git a/devtools/shared/commands/network/tests/browser_network_command_request_blocking.js b/devtools/shared/commands/network/tests/browser_network_command_request_blocking.js new file mode 100644 index 0000000000..dd1167fa9b --- /dev/null +++ b/devtools/shared/commands/network/tests/browser_network_command_request_blocking.js @@ -0,0 +1,61 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the NetworkCommand API around request blocking + +add_task(async function () { + info("Test NetworkCommand request blocking"); + const tab = await addTab("data:text/html,foo"); + const commands = await CommandsFactory.forTab(tab); + const networkCommand = commands.networkCommand; + const resourceCommand = commands.resourceCommand; + + // Usage of request blocking APIs requires to listen to NETWORK_EVENT. + await resourceCommand.watchResources([resourceCommand.TYPES.NETWORK_EVENT], { + onAvailable: () => {}, + }); + + let blockedUrls = await networkCommand.getBlockedUrls(); + Assert.deepEqual( + blockedUrls, + [], + "The list of blocked URLs is originaly empty" + ); + + await networkCommand.blockRequestForUrl("https://foo.com"); + blockedUrls = await networkCommand.getBlockedUrls(); + Assert.deepEqual( + blockedUrls, + ["https://foo.com"], + "The freshly added blocked URL is reported as blocked" + ); + + // We pass "url filters" which can be only part of a URL string + await networkCommand.blockRequestForUrl("bar"); + blockedUrls = await networkCommand.getBlockedUrls(); + Assert.deepEqual( + blockedUrls, + ["https://foo.com", "bar"], + "The second blocked URL is also reported as blocked" + ); + + await networkCommand.setBlockedUrls(["https://mozilla.org"]); + blockedUrls = await networkCommand.getBlockedUrls(); + Assert.deepEqual( + blockedUrls, + ["https://mozilla.org"], + "setBlockedUrls replace the whole list of blocked URLs" + ); + + await networkCommand.unblockRequestForUrl("https://mozilla.org"); + blockedUrls = await networkCommand.getBlockedUrls(); + Assert.deepEqual( + blockedUrls, + [], + "The unblocked URL disappear from the list of blocked URLs" + ); + + await commands.destroy(); +}); 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 0000000000..1d84a8a668 --- /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 0000000000..ce65b3d827 --- /dev/null +++ b/devtools/shared/commands/network/tests/head.js @@ -0,0 +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/. */ + +"use strict"; + +/* eslint no-unused-vars: [2, {"vars": "local"}] */ + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", + this +); |