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 --- .../browser/browser_ext_windows_create_tabId.js | 387 +++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js (limited to 'browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js') diff --git a/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js b/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js new file mode 100644 index 0000000000..83fda199b7 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js @@ -0,0 +1,387 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +function assertNoLeaksInTabTracker() { + // Check that no tabs have been leaked by the internal tabTracker helper class. + const { ExtensionParent } = ChromeUtils.importESModule( + "resource://gre/modules/ExtensionParent.sys.mjs" + ); + const { tabTracker } = ExtensionParent.apiManager.global; + + for (const [tabId, nativeTab] of tabTracker._tabIds) { + if (!nativeTab.ownerGlobal) { + ok( + false, + `A tab with tabId ${tabId} has been leaked in the tabTracker ("${nativeTab.title}")` + ); + } + } +} + +add_task(async function testWindowCreate() { + async function background() { + let promiseTabAttached = () => { + return new Promise(resolve => { + browser.tabs.onAttached.addListener(function listener() { + browser.tabs.onAttached.removeListener(listener); + resolve(); + }); + }); + }; + + let promiseTabUpdated = expected => { + return new Promise(resolve => { + browser.tabs.onUpdated.addListener(function listener( + tabId, + changeInfo, + tab + ) { + if (changeInfo.url === expected) { + browser.tabs.onUpdated.removeListener(listener); + resolve(); + } + }); + }); + }; + + try { + let window = await browser.windows.getCurrent(); + let windowId = window.id; + + browser.test.log("Create additional tab in window 1"); + let tab = await browser.tabs.create({ windowId, url: "about:blank" }); + let tabId = tab.id; + + browser.test.log("Create a new window, adopting the new tab"); + + // Note that we want to check against actual boolean values for + // all of the `incognito` property tests. + browser.test.assertEq(false, tab.incognito, "Tab is not private"); + + { + let [, window] = await Promise.all([ + promiseTabAttached(), + browser.windows.create({ tabId: tabId }), + ]); + browser.test.assertEq( + false, + window.incognito, + "New window is not private" + ); + browser.test.assertEq( + tabId, + window.tabs[0].id, + "tabs property populated correctly" + ); + + browser.test.log("Close the new window"); + await browser.windows.remove(window.id); + } + + { + browser.test.log("Create a new private window"); + let privateWindow = await browser.windows.create({ incognito: true }); + browser.test.assertEq( + true, + privateWindow.incognito, + "Private window is private" + ); + + browser.test.log("Create additional tab in private window"); + let privateTab = await browser.tabs.create({ + windowId: privateWindow.id, + }); + browser.test.assertEq( + true, + privateTab.incognito, + "Private tab is private" + ); + + browser.test.log("Create a new window, adopting the new private tab"); + let [, newWindow] = await Promise.all([ + promiseTabAttached(), + browser.windows.create({ tabId: privateTab.id }), + ]); + browser.test.assertEq( + true, + newWindow.incognito, + "New private window is private" + ); + + browser.test.log("Close the new private window"); + await browser.windows.remove(newWindow.id); + + browser.test.log("Close the private window"); + await browser.windows.remove(privateWindow.id); + } + + browser.test.log("Try to create a window with both a tab and a URL"); + [tab] = await browser.tabs.query({ windowId, active: true }); + await browser.test.assertRejects( + browser.windows.create({ tabId: tab.id, url: "http://example.com/" }), + /`tabId` may not be used in conjunction with `url`/, + "Create call failed as expected" + ); + + browser.test.log( + "Try to create a window with both a tab and an invalid incognito setting" + ); + await browser.test.assertRejects( + browser.windows.create({ tabId: tab.id, incognito: true }), + /`incognito` property must match the incognito state of tab/, + "Create call failed as expected" + ); + + browser.test.log("Try to create a window with an invalid tabId"); + await browser.test.assertRejects( + browser.windows.create({ tabId: 0 }), + /Invalid tab ID: 0/, + "Create call failed as expected" + ); + + browser.test.log("Try to create a window with two URLs"); + let readyPromise = Promise.all([ + // tabs.onUpdated can be invoked between the call of windows.create and + // the invocation of its callback/promise, so set up the listeners + // before creating the window. + promiseTabUpdated("http://example.com/"), + promiseTabUpdated("http://example.org/"), + ]); + + window = await browser.windows.create({ + url: ["http://example.com/", "http://example.org/"], + }); + await readyPromise; + + browser.test.assertEq( + 2, + window.tabs.length, + "2 tabs were opened in new window" + ); + browser.test.assertEq( + "about:blank", + window.tabs[0].url, + "about:blank, page not loaded yet" + ); + browser.test.assertEq( + "about:blank", + window.tabs[1].url, + "about:blank, page not loaded yet" + ); + + window = await browser.windows.get(window.id, { populate: true }); + + browser.test.assertEq( + 2, + window.tabs.length, + "2 tabs were opened in new window" + ); + browser.test.assertEq( + "http://example.com/", + window.tabs[0].url, + "Correct URL was loaded in tab 1" + ); + browser.test.assertEq( + "http://example.org/", + window.tabs[1].url, + "Correct URL was loaded in tab 2" + ); + + await browser.windows.remove(window.id); + + browser.test.notifyPass("window-create"); + } catch (e) { + browser.test.fail(`${e} :: ${e.stack}`); + browser.test.notifyFail("window-create"); + } + } + + let extension = ExtensionTestUtils.loadExtension({ + incognitoOverride: "spanning", + manifest: { + permissions: ["tabs"], + }, + + background, + }); + + await extension.startup(); + await extension.awaitFinish("window-create"); + await extension.unload(); + + assertNoLeaksInTabTracker(); +}); + +add_task(async function testWebNavigationOnWindowCreateTabId() { + async function background() { + const webNavEvents = []; + const onceTabsAttached = []; + + let promiseTabAttached = tab => { + return new Promise(resolve => { + browser.tabs.onAttached.addListener(function listener(tabId) { + if (tabId !== tab.id) { + return; + } + browser.tabs.onAttached.removeListener(listener); + resolve(); + }); + }); + }; + + // Listen to webNavigation.onCompleted events to ensure that + // it is not going to be fired when we move the existent tabs + // to new windows. + browser.webNavigation.onCompleted.addListener(data => { + webNavEvents.push(data); + }); + + // Wait for the list of urls needed to select the test tabs, + // and then move these tabs to a new window and assert that + // no webNavigation.onCompleted events should be received + // while the tabs are being adopted into the new windows. + browser.test.onMessage.addListener(async (msg, testTabURLs) => { + if (msg !== "testTabURLs") { + return; + } + + // Retrieve the tabs list and filter out the tabs that should + // not be moved into a new window. + let allTabs = await browser.tabs.query({}); + let testTabs = allTabs.filter(tab => { + return testTabURLs.includes(tab.url); + }); + + browser.test.assertEq( + 2, + testTabs.length, + "Got the expected number of test tabs" + ); + + for (let tab of testTabs) { + onceTabsAttached.push(promiseTabAttached(tab)); + await browser.windows.create({ tabId: tab.id }); + } + + // Wait the tabs to have been attached to the new window and then assert that no + // webNavigation.onCompleted event has been received. + browser.test.log("Waiting tabs move to new window to be attached"); + await Promise.all(onceTabsAttached); + + browser.test.assertEq( + "[]", + JSON.stringify(webNavEvents), + "No webNavigation.onCompleted event should have been received" + ); + + // Remove all the test tabs before exiting the test successfully. + for (let tab of testTabs) { + await browser.tabs.remove(tab.id); + } + + browser.test.notifyPass("webNavigation-on-window-create-tabId"); + }); + } + + const testURLs = ["http://example.com/", "http://example.org/"]; + + for (let url of testURLs) { + await BrowserTestUtils.openNewForegroundTab(gBrowser, url); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs", "webNavigation"], + }, + background, + }); + + await extension.startup(); + + await extension.sendMessage("testTabURLs", testURLs); + + await extension.awaitFinish("webNavigation-on-window-create-tabId"); + await extension.unload(); + + assertNoLeaksInTabTracker(); +}); + +add_task(async function testGetLastFocusedDoesNotLeakDuringTabAdoption() { + async function background() { + const allTabs = await browser.tabs.query({}); + + browser.test.onMessage.addListener(async (msg, testTabURL) => { + if (msg !== "testTabURL") { + return; + } + + let tab = allTabs.filter(tab => tab.url === testTabURL).pop(); + + // Keep calling getLastFocused while browser.windows.create is creating + // a new window to adopt the test tab, so that the test recreates + // conditions similar to the extension that has been triggered this leak + // (See Bug 1458918 for a rationale). + // The while loop is explicited exited right before the notifyPass + // (but unloading the extension will stop it in any case). + let stopGetLastFocusedLoop = false; + Promise.resolve().then(async () => { + while (!stopGetLastFocusedLoop) { + browser.windows.getLastFocused({ populate: true }); + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(resolve => setTimeout(resolve, 50)); + } + }); + + // Create a new window which adopt an existent tab and wait the tab to + // be fully attached to the new window. + await Promise.all([ + new Promise(resolve => { + const listener = () => { + browser.tabs.onAttached.removeListener(listener); + resolve(); + }; + browser.tabs.onAttached.addListener(listener); + }), + browser.windows.create({ tabId: tab.id }), + ]); + + // Check that getLastFocused populate the tabs property once the tab adoption + // has been completed. + const lastFocusedPopulate = await browser.windows.getLastFocused({ + populate: true, + }); + browser.test.assertEq( + 1, + lastFocusedPopulate.tabs.length, + "Got the expected number of tabs from windows.getLastFocused" + ); + + // Remove the test tab. + await browser.tabs.remove(tab.id); + + stopGetLastFocusedLoop = true; + + browser.test.notifyPass("tab-adopted"); + }); + } + + await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/"); + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs", "webNavigation"], + }, + background, + }); + + await extension.startup(); + + extension.sendMessage("testTabURL", "http://example.com/"); + + await extension.awaitFinish("tab-adopted"); + + await extension.unload(); + + assertNoLeaksInTabTracker(); +}); -- cgit v1.2.3