summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js')
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js350
1 files changed, 350 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js
new file mode 100644
index 0000000000..23c29aa155
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js
@@ -0,0 +1,350 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+AddonTestUtils.init(this);
+AddonTestUtils.createAppInfo(
+ "xpcshell@tests.mozilla.org",
+ "XPCShell",
+ "42",
+ "42"
+);
+
+const server = createHttpServer({ hosts: ["example.com"] });
+
+let clearLastPendingRequest;
+
+server.registerPathHandler("/pending_request", (request, response) => {
+ response.processAsync();
+ response.setHeader("Content-Length", "10000", false);
+ response.write("somedata\n");
+ let intervalID = setInterval(() => response.write("continue\n"), 50);
+
+ const clearPendingRequest = () => {
+ try {
+ clearInterval(intervalID);
+ response.finish();
+ } catch (e) {
+ // This will throw, but we don't care at this point.
+ }
+ };
+
+ clearLastPendingRequest = clearPendingRequest;
+ registerCleanupFunction(clearPendingRequest);
+});
+
+server.registerPathHandler("/completed_request", (request, response) => {
+ response.write("somedata\n");
+});
+
+add_setup(async () => {
+ await AddonTestUtils.promiseStartupManager();
+});
+
+async function test_idletimeout_on_streamfilter({
+ manifest_version,
+ expectResetIdle,
+ expectStreamFilterStop,
+ requestUrlPath,
+}) {
+ const extension = ExtensionTestUtils.loadExtension({
+ background: `(${async function (urlPath) {
+ browser.webRequest.onBeforeRequest.addListener(
+ request => {
+ browser.test.log(`webRequest request intercepted: ${request.url}`);
+ const filter = browser.webRequest.filterResponseData(
+ request.requestId
+ );
+ const decoder = new TextDecoder("utf-8");
+ const encoder = new TextEncoder();
+ filter.onstart = () => {
+ browser.test.sendMessage("streamfilter:started");
+ };
+ filter.ondata = event => {
+ let str = decoder.decode(event.data, { stream: true });
+ filter.write(encoder.encode(str));
+ };
+ filter.onstop = () => {
+ filter.close();
+ browser.test.sendMessage("streamfilter:stopped");
+ };
+ },
+ {
+ urls: [`http://example.com/${urlPath}`],
+ },
+ ["blocking"]
+ );
+ browser.test.sendMessage("bg:ready");
+ }})("${requestUrlPath}")`,
+
+ useAddonManager: "temporary",
+ manifest: {
+ manifest_version,
+ background: manifest_version >= 3 ? {} : { persistent: false },
+ granted_host_permissions: manifest_version >= 3,
+ permissions:
+ manifest_version >= 3
+ ? ["webRequest", "webRequestBlocking", "webRequestFilterResponse"]
+ : ["webRequest", "webRequestBlocking"],
+ // host_permissions are merged with permissions on a MV2 test extension.
+ host_permissions: ["http://example.com/*"],
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("bg:ready");
+ const { contextId } = extension.extension.backgroundContext;
+ notEqual(contextId, undefined, "Got a contextId for the background context");
+
+ info("Trigger a webRequest");
+ const testURL = `http://example.com/${requestUrlPath}`;
+ const promiseRequestCompleted = ExtensionTestUtils.fetch(
+ "http://example.com/",
+ testURL
+ ).catch(err => {
+ // This request is expected to be aborted when cleared after the test is exiting,
+ // otherwise rethrow the error to trigger an explicit failure.
+ if (/The operation was aborted/.test(err.message)) {
+ info(`Test webRequest fetching "${testURL}" aborted`);
+ } else {
+ ok(
+ false,
+ `Unexpected rejection triggered by the test webRequest fetching "${testURL}": ${err.message}`
+ );
+ throw err;
+ }
+ });
+
+ info("Wait for the stream filter to be started");
+ await extension.awaitMessage("streamfilter:started");
+
+ if (expectStreamFilterStop) {
+ await extension.awaitMessage("streamfilter:stopped");
+ }
+
+ info("Terminate the background script (simulated idle timeout)");
+
+ if (expectResetIdle) {
+ const promiseResetIdle = promiseExtensionEvent(
+ extension,
+ "background-script-reset-idle"
+ );
+
+ clearHistograms();
+ assertHistogramEmpty(WEBEXT_EVENTPAGE_IDLE_RESULT_COUNT);
+ assertKeyedHistogramEmpty(WEBEXT_EVENTPAGE_IDLE_RESULT_COUNT_BY_ADDONID);
+
+ await extension.terminateBackground();
+ info("Wait for 'background-script-reset-idle' event to be emitted");
+ await promiseResetIdle;
+ equal(
+ extension.extension.backgroundContext.contextId,
+ contextId,
+ "Initial background context is still available as expected"
+ );
+
+ assertHistogramCategoryNotEmpty(WEBEXT_EVENTPAGE_IDLE_RESULT_COUNT, {
+ category: "reset_streamfilter",
+ categories: HISTOGRAM_EVENTPAGE_IDLE_RESULT_CATEGORIES,
+ });
+
+ assertHistogramCategoryNotEmpty(
+ WEBEXT_EVENTPAGE_IDLE_RESULT_COUNT_BY_ADDONID,
+ {
+ keyed: true,
+ key: extension.id,
+ category: "reset_streamfilter",
+ categories: HISTOGRAM_EVENTPAGE_IDLE_RESULT_CATEGORIES,
+ }
+ );
+ } else {
+ const { Management } = ChromeUtils.importESModule(
+ "resource://gre/modules/Extension.sys.mjs"
+ );
+ const promiseProxyContextUnloaded = new Promise(resolve => {
+ function listener(evt, context) {
+ if (context.extension.id === extension.id) {
+ Management.off("proxy-context-unload", listener);
+ resolve();
+ }
+ }
+ Management.on("proxy-context-unload", listener);
+ });
+ await extension.terminateBackground();
+ await promiseProxyContextUnloaded;
+ equal(
+ extension.extension.backgroundContext,
+ undefined,
+ "Initial background context should have been terminated as expected"
+ );
+ }
+
+ await extension.unload();
+ clearLastPendingRequest();
+ await promiseRequestCompleted;
+}
+
+add_task(
+ {
+ pref_set: [["extensions.eventPages.enabled", true]],
+ },
+ async function test_idletimeout_on_active_streamfilter_mv2_eventpage() {
+ await test_idletimeout_on_streamfilter({
+ manifest_version: 2,
+ requestUrlPath: "pending_request",
+ expectStreamFilterStop: false,
+ expectResetIdle: true,
+ });
+ }
+);
+
+add_task(
+ {
+ pref_set: [["extensions.manifestV3.enabled", true]],
+ },
+ async function test_idletimeout_on_active_streamfilter_mv3() {
+ await test_idletimeout_on_streamfilter({
+ manifest_version: 3,
+ requestUrlPath: "pending_request",
+ expectStreamFilterStop: false,
+ expectResetIdle: true,
+ });
+ }
+);
+
+add_task(
+ {
+ pref_set: [["extensions.eventPages.enabled", true]],
+ },
+ async function test_idletimeout_on_inactive_streamfilter_mv2_eventpage() {
+ await test_idletimeout_on_streamfilter({
+ manifest_version: 2,
+ requestUrlPath: "completed_request",
+ expectStreamFilterStop: true,
+ expectResetIdle: false,
+ });
+ }
+);
+
+add_task(
+ {
+ pref_set: [["extensions.manifestV3.enabled", true]],
+ },
+ async function test_idletimeout_on_inactive_streamfilter_mv3() {
+ await test_idletimeout_on_streamfilter({
+ manifest_version: 3,
+ requestUrlPath: "completed_request",
+ expectStreamFilterStop: true,
+ expectResetIdle: false,
+ });
+ }
+);
+
+async function test_create_new_streamfilter_while_suspending({
+ manifest_version,
+}) {
+ const extension = ExtensionTestUtils.loadExtension({
+ async background() {
+ let interceptedRequestId;
+ let resolvePendingWebRequest;
+
+ browser.runtime.onSuspend.addListener(async () => {
+ await browser.test.assertThrows(
+ () => browser.webRequest.filterResponseData(interceptedRequestId),
+ /forbidden while background extension global is suspending/,
+ "Got the expected exception raised from filterResponseData calls while suspending"
+ );
+ browser.test.sendMessage("suspend-listener");
+ });
+
+ browser.runtime.onSuspendCanceled.addListener(async () => {
+ // Once onSuspendCanceled is emitted, filterResponseData
+ // is expected to don't throw.
+ const filter =
+ browser.webRequest.filterResponseData(interceptedRequestId);
+ resolvePendingWebRequest();
+ filter.onstop = () => {
+ filter.disconnect();
+ browser.test.sendMessage("suspend-canceled-listener");
+ };
+ });
+
+ browser.webRequest.onBeforeRequest.addListener(
+ request => {
+ browser.test.log(`webRequest request intercepted: ${request.url}`);
+ interceptedRequestId = request.requestId;
+ return new Promise(resolve => {
+ resolvePendingWebRequest = resolve;
+ browser.test.sendMessage("webrequest-listener:done");
+ });
+ },
+ {
+ urls: [`http://example.com/completed_request`],
+ },
+ ["blocking"]
+ );
+ browser.test.sendMessage("bg:ready");
+ },
+
+ useAddonManager: "temporary",
+ manifest: {
+ manifest_version,
+ background: manifest_version >= 3 ? {} : { persistent: false },
+ granted_host_permissions: manifest_version >= 3,
+ permissions:
+ manifest_version >= 3
+ ? ["webRequest", "webRequestBlocking", "webRequestFilterResponse"]
+ : ["webRequest", "webRequestBlocking"],
+ // host_permissions are merged with permissions on a MV2 test extension.
+ host_permissions: ["http://example.com/*"],
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("bg:ready");
+ const { contextId } = extension.extension.backgroundContext;
+ notEqual(contextId, undefined, "Got a contextId for the background context");
+
+ info("Trigger a webRequest");
+ ExtensionTestUtils.fetch(
+ "http://example.com/",
+ `http://example.com/completed_request`
+ );
+
+ info("Wait for the web request to be intercepted and suspended");
+ await extension.awaitMessage("webrequest-listener:done");
+
+ info("Terminate the background script (simulated idle timeout)");
+
+ extension.terminateBackground({ disableResetIdleForTest: true });
+ await extension.awaitMessage("suspend-listener");
+
+ info("Simulated idle timeout canceled");
+ extension.extension.emit("background-script-reset-idle");
+ await extension.awaitMessage("suspend-canceled-listener");
+
+ await extension.unload();
+}
+
+add_task(
+ {
+ pref_set: [["extensions.eventPages.enabled", true]],
+ },
+ async function test_error_creating_new_streamfilter_while_suspending_mv2_eventpage() {
+ await test_create_new_streamfilter_while_suspending({
+ manifest_version: 2,
+ });
+ }
+);
+
+add_task(
+ {
+ pref_set: [["extensions.manifestV3.enabled", true]],
+ },
+ async function test_error_creating_new_streamfilter_while_suspending_mv3() {
+ await test_create_new_streamfilter_while_suspending({
+ manifest_version: 3,
+ });
+ }
+);