diff --git a/dom/chrome-webidl/ChannelWrapper.webidl b/dom/chrome-webidl/ChannelWrapper.webidl index 6185f8ee48b1..9d6b07cd5dc6 100644 --- a/dom/chrome-webidl/ChannelWrapper.webidl +++ b/dom/chrome-webidl/ChannelWrapper.webidl @@ -95,9 +95,12 @@ interface ChannelWrapper : EventTarget { /** * Cancels the request with the given nsresult status code. + * + * The optional reason parameter should be one of the BLOCKING_REASON + * constants from nsILoadInfo.idl */ [Throws] - void cancel(unsigned long result); + void cancel(unsigned long result, optional unsigned long reason = 0); /** * Redirects the wrapped HTTP channel to the given URI. For other channel diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl index 4ffab7c3aa0c..88ba788a44a2 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -1188,6 +1188,8 @@ interface nsILoadInfo : nsISupports const uint32_t BLOCKING_REASON_CONTENT_POLICY_PRELOAD = 4006; // The reason used when SEC_REQUIRE_SAME_ORIGIN_* is set and not satisifed. const uint32_t BLOCKING_REASON_NOT_SAME_ORIGIN = 5000; + // The reason used when an extension cancels the request via the WebRequest api. + const uint32_t BLOCKING_REASON_EXTENSION_WEBREQUEST = 6000; /** * If the request associated with this load info was blocked by some of diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_cancelWithReason.js b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_cancelWithReason.js new file mode 100644 index 000000000000..7de301202a7f --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_cancelWithReason.js @@ -0,0 +1,69 @@ +"use strict"; + +const server = createHttpServer(); +const gServerUrl = `http://localhost:${server.identity.primaryPort}`; + +server.registerPathHandler("/dummy", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write("ok"); +}); + +add_task(async function test_cancel_with_reason() { + let ext = ExtensionTestUtils.loadExtension({ + manifest: { + applications: { gecko: { id: "cancel@test" } }, + permissions: ["webRequest", "webRequestBlocking", ""], + }, + + background() { + browser.webRequest.onBeforeRequest.addListener( + () => { + return { cancel: true }; + }, + { urls: ["*://*/*"] }, + ["blocking"] + ); + }, + }); + await ext.startup(); + + let data = await new Promise(resolve => { + let ssm = Services.scriptSecurityManager; + + let channel = NetUtil.newChannel({ + uri: `${gServerUrl}/dummy`, + loadingPrincipal: ssm.createContentPrincipalFromOrigin( + "http://localhost" + ), + contentPolicyType: Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST, + securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, + }); + + channel.asyncOpen({ + QueryInterface: ChromeUtils.generateQI([Ci.nsIStreamListener]), + + onStartRequest(request) {}, + + onStopRequest(request, statusCode) { + let properties = request.QueryInterface(Ci.nsIPropertyBag); + let id = properties.getProperty("cancelledByExtension"); + let reason = request.loadInfo.requestBlockingReason; + resolve({ reason, id }); + }, + + onDataAvailable() {}, + }); + }); + + Assert.equal( + Ci.nsILoadInfo.BLOCKING_REASON_EXTENSION_WEBREQUEST, + data.reason, + "extension cancelled request" + ); + Assert.equal( + ext.id, + data.id, + "extension id attached to channel property bag" + ); + await ext.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini index 2cddf2910c5f..e792b48e6daa 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini @@ -165,6 +165,7 @@ skip-if = true # Too frequent intermittent failures skip-if = os == "android" && debug [test_ext_webRequest_cached.js] skip-if = os == "android" && processor != "x86_64" # Bug 1573511 +[test_ext_webRequest_cancelWithReason.js] [test_ext_webRequest_filterResponseData.js] skip-if = os == "android" && debug [test_ext_webRequest_incognito.js] diff --git a/toolkit/components/extensions/webrequest/ChannelWrapper.cpp b/toolkit/components/extensions/webrequest/ChannelWrapper.cpp index 23c5381a51f6..90a9f7b331ee 100644 --- a/toolkit/components/extensions/webrequest/ChannelWrapper.cpp +++ b/toolkit/components/extensions/webrequest/ChannelWrapper.cpp @@ -206,9 +206,14 @@ void ChannelWrapper::ClearCachedAttributes() { * ... *****************************************************************************/ -void ChannelWrapper::Cancel(uint32_t aResult, ErrorResult& aRv) { +void ChannelWrapper::Cancel(uint32_t aResult, uint32_t aReason, + ErrorResult& aRv) { nsresult rv = NS_ERROR_UNEXPECTED; if (nsCOMPtr chan = MaybeChannel()) { + nsCOMPtr loadInfo = GetLoadInfo(); + if (aReason > 0 && loadInfo) { + loadInfo->SetRequestBlockingReason(aReason); + } rv = chan->Cancel(nsresult(aResult)); ErrorCheck(); } diff --git a/toolkit/components/extensions/webrequest/ChannelWrapper.h b/toolkit/components/extensions/webrequest/ChannelWrapper.h index 83b0eed2985e..e07f1f20ebc1 100644 --- a/toolkit/components/extensions/webrequest/ChannelWrapper.h +++ b/toolkit/components/extensions/webrequest/ChannelWrapper.h @@ -135,7 +135,7 @@ class ChannelWrapper final : public DOMEventTargetHelper, void SetChannel(nsIChannel* aChannel); - void Cancel(uint32_t result, ErrorResult& aRv); + void Cancel(uint32_t result, uint32_t reason, ErrorResult& aRv); void RedirectTo(nsIURI* uri, ErrorResult& aRv); void UpgradeToSecure(ErrorResult& aRv); diff --git a/toolkit/components/extensions/webrequest/WebRequest.jsm b/toolkit/components/extensions/webrequest/WebRequest.jsm index 79cbf3430f70..bfd9d6a78999 100644 --- a/toolkit/components/extensions/webrequest/WebRequest.jsm +++ b/toolkit/components/extensions/webrequest/WebRequest.jsm @@ -972,7 +972,17 @@ HttpObserverManager = { if (result.cancel) { channel.suspended = false; - channel.cancel(Cr.NS_ERROR_ABORT); + channel.cancel( + Cr.NS_ERROR_ABORT, + Ci.nsILoadInfo.BLOCKING_REASON_EXTENSION_WEBREQUEST + ); + let { policy } = opts; + if (policy) { + let properties = channel.channel.QueryInterface( + Ci.nsIWritablePropertyBag + ); + properties.setProperty("cancelledByExtension", policy.id); + } return; }