From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../test/browser/browser_ext_getViews.js | 439 +++++++++++++++++++++ 1 file changed, 439 insertions(+) create mode 100644 browser/components/extensions/test/browser/browser_ext_getViews.js (limited to 'browser/components/extensions/test/browser/browser_ext_getViews.js') diff --git a/browser/components/extensions/test/browser/browser_ext_getViews.js b/browser/components/extensions/test/browser/browser_ext_getViews.js new file mode 100644 index 0000000000..1af190e753 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_getViews.js @@ -0,0 +1,439 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +function genericChecker() { + let kind = window.location.search.slice(1) || "background"; + window.kind = kind; + + let bcGroupId = SpecialPowers.wrap(window).browsingContext.group.id; + + browser.test.onMessage.addListener((msg, ...args) => { + if (msg == kind + "-check-views") { + let counts = { + background: 0, + tab: 0, + popup: 0, + kind: 0, + sidebar: 0, + }; + if (kind !== "background") { + counts.kind = browser.extension.getViews({ type: kind }).length; + } + let views = browser.extension.getViews(); + let background; + for (let i = 0; i < views.length; i++) { + let view = views[i]; + browser.test.assertTrue(view.kind in counts, "view type is valid"); + counts[view.kind]++; + if (view.kind == "background") { + browser.test.assertTrue( + view === browser.extension.getBackgroundPage(), + "background page is correct" + ); + background = view; + } + + browser.test.assertEq( + bcGroupId, + SpecialPowers.wrap(view).browsingContext.group.id, + "browsing context group is correct" + ); + } + if (background) { + browser.runtime.getBackgroundPage().then(view => { + browser.test.assertEq( + background, + view, + "runtime.getBackgroundPage() is correct" + ); + browser.test.sendMessage("counts", counts); + }); + } else { + browser.test.sendMessage("counts", counts); + } + } else if (msg == kind + "-getViews-with-filter") { + let filter = args[0]; + let count = browser.extension.getViews(filter).length; + browser.test.sendMessage("getViews-count", count); + } else if (msg == kind + "-open-tab") { + let url = browser.runtime.getURL("page.html?tab"); + browser.tabs + .create({ windowId: args[0], url }) + .then(tab => browser.test.sendMessage("opened-tab", tab.id)); + } else if (msg == kind + "-close-tab") { + browser.tabs.query( + { + windowId: args[0], + }, + tabs => { + let tab = tabs.find(tab => tab.url.includes("page.html?tab")); + browser.tabs.remove(tab.id, () => { + browser.test.sendMessage("closed"); + }); + } + ); + } + }); + browser.test.sendMessage(kind + "-ready"); +} + +async function promiseBrowserContentUnloaded(browser) { + // Wait until the content has unloaded before resuming the test, to avoid + // calling extension.getViews too early (and having intermittent failures). + const MSG_WINDOW_DESTROYED = "Test:BrowserContentDestroyed"; + let unloadPromise = new Promise(resolve => { + Services.ppmm.addMessageListener(MSG_WINDOW_DESTROYED, function listener() { + Services.ppmm.removeMessageListener(MSG_WINDOW_DESTROYED, listener); + resolve(); + }); + }); + + await ContentTask.spawn( + browser, + MSG_WINDOW_DESTROYED, + MSG_WINDOW_DESTROYED => { + let innerWindowId = this.content.windowGlobalChild.innerWindowId; + let observer = subject => { + if ( + innerWindowId === subject.QueryInterface(Ci.nsISupportsPRUint64).data + ) { + Services.obs.removeObserver(observer, "inner-window-destroyed"); + + // Use process message manager to ensure that the message is delivered + // even after the 's message manager is disconnected. + Services.cpmm.sendAsyncMessage(MSG_WINDOW_DESTROYED); + } + }; + // Observe inner-window-destroyed, like ExtensionPageChild, to ensure that + // the ExtensionPageContextChild instance has been unloaded when we resolve + // the unloadPromise. + Services.obs.addObserver(observer, "inner-window-destroyed"); + } + ); + + // Return an object so that callers can use "await". + return { unloadPromise }; +} + +add_task(async function () { + let win1 = await BrowserTestUtils.openNewBrowserWindow(); + let win2 = await BrowserTestUtils.openNewBrowserWindow(); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", // To automatically show sidebar on load. + manifest: { + permissions: ["tabs"], + + browser_action: { + default_popup: "page.html?popup", + default_area: "navbar", + }, + + sidebar_action: { + default_panel: "page.html?sidebar", + }, + }, + + files: { + "page.html": ` + + + + + + + `, + + "page.js": genericChecker, + }, + + background: genericChecker, + }); + + await Promise.all([ + extension.startup(), + extension.awaitMessage("background-ready"), + ]); + + await extension.awaitMessage("sidebar-ready"); + await extension.awaitMessage("sidebar-ready"); + await extension.awaitMessage("sidebar-ready"); + + info("started"); + + const { + Management: { + global: { windowTracker }, + }, + } = ChromeUtils.importESModule("resource://gre/modules/Extension.sys.mjs"); + + let winId1 = windowTracker.getId(win1); + let winId2 = windowTracker.getId(win2); + + async function openTab(winId) { + extension.sendMessage("background-open-tab", winId); + await extension.awaitMessage("tab-ready"); + return extension.awaitMessage("opened-tab"); + } + + async function checkViews(kind, tabCount, popupCount, kindCount) { + extension.sendMessage(kind + "-check-views"); + let counts = await extension.awaitMessage("counts"); + if (kind === "sidebar") { + // We have 3 sidebars thaat will answer. + await extension.awaitMessage("counts"); + await extension.awaitMessage("counts"); + } + is(counts.background, 1, "background count correct"); + is(counts.tab, tabCount, "tab count correct"); + is(counts.popup, popupCount, "popup count correct"); + is(counts.kind, kindCount, "count for type correct"); + is(counts.sidebar, 3, "sidebar count is constant"); + } + + async function checkViewsWithFilter(filter, expectedCount) { + extension.sendMessage("background-getViews-with-filter", filter); + let count = await extension.awaitMessage("getViews-count"); + is(count, expectedCount, `count for ${JSON.stringify(filter)} correct`); + } + + await checkViews("background", 0, 0, 0); + await checkViews("sidebar", 0, 0, 3); + await checkViewsWithFilter({ windowId: -1 }, 1); + await checkViewsWithFilter({ windowId: 0 }, 0); + await checkViewsWithFilter({ tabId: -1 }, 4); + await checkViewsWithFilter({ tabId: 0 }, 0); + + let tabId1 = await openTab(winId1); + + await checkViews("background", 1, 0, 0); + await checkViews("sidebar", 1, 0, 3); + await checkViews("tab", 1, 0, 1); + await checkViewsWithFilter({ windowId: winId1 }, 2); + await checkViewsWithFilter({ tabId: tabId1 }, 1); + + let tabId2 = await openTab(winId2); + + await checkViews("background", 2, 0, 0); + await checkViews("sidebar", 2, 0, 3); + await checkViewsWithFilter({ windowId: winId2 }, 2); + await checkViewsWithFilter({ tabId: tabId2 }, 1); + + async function triggerPopup(win, callback) { + // Window needs focus to open popups. + await focusWindow(win); + await clickBrowserAction(extension, win); + let browser = await awaitExtensionPanel(extension, win); + + await extension.awaitMessage("popup-ready"); + + await callback(); + + let { unloadPromise } = await promiseBrowserContentUnloaded(browser); + closeBrowserAction(extension, win); + await unloadPromise; + } + + await triggerPopup(win1, async function () { + await checkViews("background", 2, 1, 0); + await checkViews("sidebar", 2, 1, 3); + await checkViews("popup", 2, 1, 1); + await checkViewsWithFilter({ windowId: winId1 }, 3); + await checkViewsWithFilter({ type: "popup", tabId: -1 }, 1); + }); + + await triggerPopup(win2, async function () { + await checkViews("background", 2, 1, 0); + await checkViews("sidebar", 2, 1, 3); + await checkViews("popup", 2, 1, 1); + await checkViewsWithFilter({ windowId: winId2 }, 3); + await checkViewsWithFilter({ type: "popup", tabId: -1 }, 1); + }); + + info("checking counts after popups"); + + await checkViews("background", 2, 0, 0); + await checkViews("sidebar", 2, 0, 3); + await checkViewsWithFilter({ windowId: winId1 }, 2); + await checkViewsWithFilter({ tabId: -1 }, 4); + + info("closing one tab"); + + let { unloadPromise } = await promiseBrowserContentUnloaded( + win1.gBrowser.selectedBrowser + ); + extension.sendMessage("background-close-tab", winId1); + await extension.awaitMessage("closed"); + await unloadPromise; + + info("one tab closed, one remains"); + + await checkViews("background", 1, 0, 0); + await checkViews("sidebar", 1, 0, 3); + + info("opening win1 popup"); + + await triggerPopup(win1, async function () { + await checkViews("background", 1, 1, 0); + await checkViews("sidebar", 1, 1, 3); + await checkViews("tab", 1, 1, 1); + await checkViews("popup", 1, 1, 1); + }); + + info("opening win2 popup"); + + await triggerPopup(win2, async function () { + await checkViews("background", 1, 1, 0); + await checkViews("sidebar", 1, 1, 3); + await checkViews("tab", 1, 1, 1); + await checkViews("popup", 1, 1, 1); + }); + + await checkViews("sidebar", 1, 0, 3); + + await extension.unload(); + + await BrowserTestUtils.closeWindow(win1); + await BrowserTestUtils.closeWindow(win2); +}); + +add_task(async function test_getViews_excludes_blocked_parsing_documents() { + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + browser_action: { + default_popup: "popup.html", + default_area: "navbar", + }, + }, + files: { + "popup.html": ` + +

ExtensionPopup

+ `, + "popup.js": function () { + browser.test.sendMessage( + "browserActionPopup:loaded", + window.location.href + ); + }, + }, + background() { + browser.test.onMessage.addListener(msg => { + browser.test.assertEq("getViews", msg, "Got the expected test message"); + const views = browser.extension + .getViews() + .map(win => win.location?.href); + + browser.test.sendMessage("getViews:done", views); + }); + browser.test.sendMessage("bgpage:loaded", window.location.href); + }, + }); + + await extension.startup(); + const bgpageURL = await extension.awaitMessage("bgpage:loaded"); + extension.sendMessage("getViews"); + Assert.deepEqual( + await extension.awaitMessage("getViews:done"), + [bgpageURL], + "Expect only the background page to be initially listed in getViews" + ); + + const { + Management: { + global: { browserActionFor }, + }, + } = ChromeUtils.importESModule("resource://gre/modules/Extension.sys.mjs"); + + let ext = WebExtensionPolicy.getByID(extension.id)?.extension; + let browserAction = browserActionFor(ext); + + // Ensure the mouse is not initially hovering the browserAction widget. + EventUtils.synthesizeMouseAtCenter( + window.gURLBar.textbox, + { type: "mouseover" }, + window + ); + + let widget = await TestUtils.waitForCondition( + () => getBrowserActionWidget(extension).forWindow(window), + "Wait for browserAction widget" + ); + + await TestUtils.waitForCondition( + () => !browserAction.pendingPopup, + "Wait for no pending preloaded popup" + ); + + await TestUtils.waitForCondition(async () => { + // Trigger preload browserAction popup (by directly dispatching a MouseEvent + // to prevent intermittent failures that where often triggered in macos + // PGO builds when this was using EventUtils.synthesizeMouseAtCenter). + let mouseOverEvent = new MouseEvent("mouseover"); + widget.node.firstElementChild.dispatchEvent(mouseOverEvent); + + await TestUtils.waitForCondition( + () => browserAction.pendingPopup?.browser, + "Wait for pending preloaded popup browser" + ); + + return SpecialPowers.spawn( + browserAction.pendingPopup.browser, + [], + async () => { + const policy = this.content.WebExtensionPolicy.getByHostname( + this.content.location.hostname + ); + return policy?.weakExtension + ?.get() + ?.blockedParsingDocuments.has(this.content.document); + } + ).catch(err => { + // Tolerate errors triggered by SpecialPowers.spawn + // being aborted before we got a result back. + if (err.name === "AbortError") { + return false; + } + throw err; + }); + }, "Wait for preload browserAction document to be blocked on parsing"); + + extension.sendMessage("getViews"); + Assert.deepEqual( + await extension.awaitMessage("getViews:done"), + [bgpageURL], + "Expect preloaded browserAction popup to not be listed in getViews" + ); + + // Test browserAction popup is listed in getViews once document parser is unblocked. + EventUtils.synthesizeMouseAtCenter( + widget.node, + { type: "mousedown", button: 0 }, + window + ); + EventUtils.synthesizeMouseAtCenter( + widget.node, + { type: "mouseup", button: 0 }, + window + ); + + const popupURL = await extension.awaitMessage("browserActionPopup:loaded"); + + extension.sendMessage("getViews"); + Assert.deepEqual( + (await extension.awaitMessage("getViews:done")).sort(), + [bgpageURL, popupURL].sort(), + "Expect loaded browserAction popup to be listed in getViews" + ); + + // Ensure the mouse is not hovering the browserAction widget anymore when exiting the test case. + EventUtils.synthesizeMouseAtCenter( + window.gURLBar.textbox, + { type: "mouseover", button: 0 }, + window + ); + + await extension.unload(); +}); -- cgit v1.2.3