From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../xpcshell/test_ext_extension_page_navigated.js | 341 +++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 toolkit/components/extensions/test/xpcshell/test_ext_extension_page_navigated.js (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_extension_page_navigated.js') diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_extension_page_navigated.js b/toolkit/components/extensions/test/xpcshell/test_ext_extension_page_navigated.js new file mode 100644 index 0000000000..e573595afb --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_extension_page_navigated.js @@ -0,0 +1,341 @@ +/* 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 = AddonTestUtils.createHttpServer({ hosts: ["example.com"] }); + +server.registerPathHandler("/", (request, response) => { + response.write(` + + + + test webpage + + + `); +}); + +function createTestExtPage({ script }) { + return ` + + + + + + + `; +} + +function createTestExtPageScript(name) { + return `(${async function (pageName) { + browser.webRequest.onBeforeRequest.addListener( + details => { + browser.test.log( + `${pageName} got a webRequest.onBeforeRequest event: ${details.url}` + ); + browser.test.sendMessage(`event-received:${pageName}`); + }, + { urls: ["http://example.com/request*"] } + ); + + // Calling an API implemented in the parent process to make sure + // the webRequest.onBeforeRequest listener is got registered in + // the parent process by the time the test is going to expect that + // listener to intercept a test web request. + await browser.runtime.getBrowserInfo(); + browser.test.sendMessage(`page-loaded:${pageName}`); + }})("${name}");`; +} + +const getExtensionContextIdAndURL = extensionId => { + const { ExtensionProcessScript } = ChromeUtils.importESModule( + "resource://gre/modules/ExtensionProcessScript.sys.mjs" + ); + let extWindow = this.content.window; + let extChild = ExtensionProcessScript.getExtensionChild(extensionId); + + let contextIds = []; + let contextURLs = []; + for (let ctx of extChild.views) { + if (ctx.contentWindow === extWindow) { + // Only one is expected, but we collect details from all + // the ones that match to make sure the test will fails + // in case there are unexpected multiple extension contexts + // associated to the same contentWindow. + contextIds.push(ctx.contextId); + contextURLs.push(ctx.contentWindow.location.href); + } + } + return { contextIds, contextURLs }; +}; + +const getExtensionContextStatusByContextId = ( + extensionId, + extPageContextId +) => { + const { ExtensionProcessScript } = ChromeUtils.importESModule( + "resource://gre/modules/ExtensionProcessScript.sys.mjs" + ); + let extChild = ExtensionProcessScript.getExtensionChild(extensionId); + + let context; + for (let ctx of extChild.views) { + if (ctx.contextId === extPageContextId) { + context = ctx; + } + } + return context?.active; +}; + +add_task(async function test_extension_page_sameprocess_navigation() { + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["webRequest", "http://example.com/*"], + }, + files: { + "extpage1.html": createTestExtPage({ script: "extpage1.js" }), + "extpage1.js": createTestExtPageScript("extpage1"), + "extpage2.html": createTestExtPage({ script: "extpage2.js" }), + "extpage2.js": createTestExtPageScript("extpage2"), + }, + }); + + await extension.startup(); + + const policy = WebExtensionPolicy.getByID(extension.id); + + const extPageURL1 = policy.extension.baseURI.resolve("extpage1.html"); + const extPageURL2 = policy.extension.baseURI.resolve("extpage2.html"); + + info("Opening extension page in a first browser element"); + const extPage = await ExtensionTestUtils.loadContentPage(extPageURL1); + await extension.awaitMessage("page-loaded:extpage1"); + + const { contextIds, contextURLs } = await extPage.spawn( + [extension.id], + getExtensionContextIdAndURL + ); + + Assert.deepEqual( + contextURLs, + [extPageURL1], + `Found an extension context with the expected page url` + ); + + ok( + contextIds[0], + `Found an extension context with contextId ${contextIds[0]}` + ); + ok( + contextIds.length, + `There should be only one extension context for a given content window, found ${contextIds.length}` + ); + + const [contextId] = contextIds; + + await ExtensionTestUtils.fetch( + "http://example.com", + "http://example.com/request1" + ); + await extension.awaitMessage("event-received:extpage1"); + + info("Load a second extension page in the same browser element"); + await extPage.loadURL(extPageURL2); + await extension.awaitMessage("page-loaded:extpage2"); + + let active; + + let { messages } = await AddonTestUtils.promiseConsoleOutput(async () => { + // We only expect extpage2 to be able to receive API events. + await ExtensionTestUtils.fetch( + "http://example.com", + "http://example.com/request2" + ); + await extension.awaitMessage("event-received:extpage2"); + + active = await extPage.spawn( + [extension.id, contextId], + getExtensionContextStatusByContextId + ); + }); + + if ( + Services.appinfo.sessionHistoryInParent && + WebExtensionPolicy.isExtensionProcess + ) { + // When the extension are running in the main process while the webpages run + // in a separate child process, the extension page doesn't enter the BFCache + // because nsFrameLoader::changeRemotenessCommon bails out due to retainPaint + // being computed as true (see + // https://searchfox.org/mozilla-central/rev/24c1cdc33ccce692612276cd0d3e9a44f6c22fd3/dom/base/nsFrameLoaderOwner.cpp#185-196 + // ). + equal(active, undefined, "extension page context should not exist anymore"); + } else { + equal( + active, + false, + "extension page context is expected to be inactive while moved into the BFCache" + ); + } + + if (typeof active === "boolean") { + AddonTestUtils.checkMessages( + messages, + { + forbidden: [ + // We should not have tried to deserialize the event data for the extension page + // that got moved into the BFCache (See Bug 1499129). + { + message: + /StructureCloneHolder.deserialize: Argument 1 is not an object/, + }, + ], + expected: [ + // If the extension page is expected to be in the BFCache, then we expect to see + // a warning message logged for the ignored listener. + { + message: + /Ignored listener for inactive context .* path=webRequest.onBeforeRequest/, + }, + ], + }, + "Expect no StructureCloneHolder error due to trying to send the event to inactive context" + ); + } + + await extPage.close(); + await extension.unload(); +}); + +add_task(async function test_extension_page_context_navigated_to_web_page() { + const extension = ExtensionTestUtils.loadExtension({ + files: { + "extpage.html": createTestExtPage({ script: "extpage.js" }), + "extpage.js": function () { + dump("loaded extension page\n"); + window.addEventListener( + "pageshow", + () => { + browser.test.log("Extension page got a pageshow event"); + browser.test.sendMessage("extpage:pageshow"); + }, + { once: true } + ); + window.addEventListener( + "pagehide", + () => { + browser.test.log("Extension page got a pagehide event"); + browser.test.sendMessage("extpage:pagehide"); + }, + { once: true } + ); + }, + }, + }); + + await extension.startup(); + + const policy = WebExtensionPolicy.getByID(extension.id); + + const extPageURL = policy.extension.baseURI.resolve("extpage.html"); + const webPageURL = "http://example.com/"; + + info("Opening extension page in a browser element"); + const extPage = await ExtensionTestUtils.loadContentPage(extPageURL); + await extension.awaitMessage("extpage:pageshow"); + + const { contextIds, contextURLs } = await extPage.spawn( + [extension.id], + getExtensionContextIdAndURL + ); + + Assert.deepEqual( + contextURLs, + [extPageURL], + `Found an extension context with the expected page url` + ); + + ok( + contextIds[0], + `Found an extension context with contextId ${contextIds[0]}` + ); + ok( + contextIds.length, + `There should be only one extension context for a given content window, found ${contextIds.length}` + ); + + const [contextId] = contextIds; + + info("Load a webpage in the same browser element"); + await extPage.loadURL(webPageURL); + await extension.awaitMessage("extpage:pagehide"); + + info("Open extension page in a second browser element"); + const extPage2 = await ExtensionTestUtils.loadContentPage(extPageURL); + await extension.awaitMessage("extpage:pageshow"); + + let active = await extPage2.spawn( + [extension.id, contextId], + getExtensionContextStatusByContextId + ); + + if (WebExtensionPolicy.isExtensionProcess) { + // When the extension are running in the main process while the webpages run + // in a separate child process, the extension page doesn't enter the BFCache + // because nsFrameLoader::changeRemotenessCommon bails out due to retainPaint + // being computed as true (see + // https://searchfox.org/mozilla-central/rev/24c1cdc33ccce692612276cd0d3e9a44f6c22fd3/dom/base/nsFrameLoaderOwner.cpp#185-196 + // ). + equal(active, undefined, "extension page context should not exist anymore"); + } else if (Services.appinfo.sessionHistoryInParent) { + // When SHIP is enabled and the extensions runs in their own child extension + // process, the BFCache is managed entirely from the parent process and the + // extension page is expected to be able to enter the BFCache. + equal( + active, + false, + "extension page context is expected to be inactive while moved into the BFCache" + ); + } else { + // With the extension running in a separate child process but fission disabled, + // we expect the extension page to don't enter the BFCache. + equal(active, undefined, "extension page context should not exist anymore"); + } + + if (active === false) { + info( + "Navigating to more web pages to confirm the extension page have been evicted from the BFCache" + ); + for (let i = 2; i < 5; i++) { + const url = `${webPageURL}/page${i}`; + info(`Navigating to ${url}`); + await extPage.loadURL(url); + } + equal( + await extPage2.spawn( + [extension.id, contextId], + getExtensionContextStatusByContextId + ), + undefined, + "extension page context should have been evicted" + ); + } + + info("Cleanup and exit test"); + + await Promise.all([ + extPage.close(), + extPage2.close(), + extension.awaitMessage("extpage:pagehide"), + ]); + await extension.unload(); +}); -- cgit v1.2.3