summaryrefslogtreecommitdiffstats
path: root/browser/components/firefoxview/tests/browser/browser_opentabs_changes.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/firefoxview/tests/browser/browser_opentabs_changes.js')
-rw-r--r--browser/components/firefoxview/tests/browser/browser_opentabs_changes.js541
1 files changed, 541 insertions, 0 deletions
diff --git a/browser/components/firefoxview/tests/browser/browser_opentabs_changes.js b/browser/components/firefoxview/tests/browser/browser_opentabs_changes.js
new file mode 100644
index 0000000000..c293afa8cd
--- /dev/null
+++ b/browser/components/firefoxview/tests/browser/browser_opentabs_changes.js
@@ -0,0 +1,541 @@
+const { NonPrivateTabs, getTabsTargetForWindow } = ChromeUtils.importESModule(
+ "resource:///modules/OpenTabs.sys.mjs"
+);
+let privateTabsChanges;
+
+const tabURL1 = "data:text/html,<title>Tab1</title>Tab1";
+const tabURL2 = "data:text/html,<title>Tab2</title>Tab2";
+const tabURL3 = "data:text/html,<title>Tab3</title>Tab3";
+const tabURL4 = "data:text/html,<title>Tab4</title>Tab4";
+
+const nonPrivateListener = sinon.stub();
+const privateListener = sinon.stub();
+
+function tabUrl(tab) {
+ return tab.linkedBrowser.currentURI?.spec;
+}
+
+function getWindowId(win) {
+ return win.windowGlobalChild.innerWindowId;
+}
+
+async function setup(tabChangeEventName) {
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+
+ NonPrivateTabs.addEventListener(tabChangeEventName, nonPrivateListener);
+
+ await TestUtils.waitForTick();
+ is(
+ NonPrivateTabs.currentWindows.length,
+ 1,
+ "NonPrivateTabs has 1 window a tick after adding the event listener"
+ );
+
+ info("Opening new windows");
+ let win0 = window,
+ win1 = await BrowserTestUtils.openNewBrowserWindow(),
+ privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ BrowserTestUtils.startLoadingURIString(
+ win1.gBrowser.selectedBrowser,
+ tabURL1
+ );
+ await BrowserTestUtils.browserLoaded(win1.gBrowser.selectedBrowser);
+
+ // load a tab with a title/label we can easily verify
+ BrowserTestUtils.startLoadingURIString(
+ privateWin.gBrowser.selectedBrowser,
+ tabURL2
+ );
+ await BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser);
+
+ is(
+ win1.gBrowser.selectedTab.label,
+ "Tab1",
+ "Check the tab label in the new non-private window"
+ );
+ is(
+ privateWin.gBrowser.selectedTab.label,
+ "Tab2",
+ "Check the tab label in the new private window"
+ );
+
+ privateTabsChanges = getTabsTargetForWindow(privateWin);
+ privateTabsChanges.addEventListener(tabChangeEventName, privateListener);
+ is(
+ privateTabsChanges,
+ getTabsTargetForWindow(privateWin),
+ "getTabsTargetForWindow reuses a single instance per exclusive window"
+ );
+
+ await TestUtils.waitForTick();
+ is(
+ NonPrivateTabs.currentWindows.length,
+ 2,
+ "NonPrivateTabs has 2 windows once openNewBrowserWindow resolves"
+ );
+ is(
+ privateTabsChanges.currentWindows.length,
+ 1,
+ "privateTabsChanges has 1 window once openNewBrowserWindow resolves"
+ );
+
+ await SimpleTest.promiseFocus(win0);
+ info("setup, win0 has id: " + getWindowId(win0));
+ info("setup, win1 has id: " + getWindowId(win1));
+ info("setup, privateWin has id: " + getWindowId(privateWin));
+ info("setup,waiting for both private and nonPrivateListener to be called");
+ await TestUtils.waitForCondition(() => {
+ return nonPrivateListener.called && privateListener.called;
+ });
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+
+ const cleanup = async eventName => {
+ NonPrivateTabs.removeEventListener(eventName, nonPrivateListener);
+ privateTabsChanges.removeEventListener(eventName, privateListener);
+ await SimpleTest.promiseFocus(window);
+ await promiseAllButPrimaryWindowClosed();
+ };
+ return { windows: [win0, win1, privateWin], cleanup };
+}
+
+add_task(async function test_TabChanges() {
+ const { windows, cleanup } = await setup("TabChange");
+ const [win0, win1, privateWin] = windows;
+ let tabChangeRaised;
+ let changeEvent;
+
+ info(
+ "Verify that manipulating tabs in a non-private window dispatches events on the correct target"
+ );
+ for (let win of [win0, win1]) {
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ NonPrivateTabs,
+ "TabChange"
+ );
+ let newTab = await BrowserTestUtils.openNewForegroundTab(
+ win.gBrowser,
+ tabURL1
+ );
+ changeEvent = await tabChangeRaised;
+ Assert.deepEqual(
+ changeEvent.detail.windowIds,
+ [getWindowId(win)],
+ "The event had the correct window id"
+ );
+
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ NonPrivateTabs,
+ "TabChange"
+ );
+ const navigateUrl = "https://example.org/";
+ BrowserTestUtils.startLoadingURIString(newTab.linkedBrowser, navigateUrl);
+ await BrowserTestUtils.browserLoaded(
+ newTab.linkedBrowser,
+ null,
+ navigateUrl
+ );
+ // navigation in a tab changes the label which should produce a change event
+ changeEvent = await tabChangeRaised;
+ Assert.deepEqual(
+ changeEvent.detail.windowIds,
+ [getWindowId(win)],
+ "The event had the correct window id"
+ );
+
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ NonPrivateTabs,
+ "TabChange"
+ );
+ BrowserTestUtils.removeTab(newTab);
+ // navigation in a tab changes the label which should produce a change event
+ changeEvent = await tabChangeRaised;
+ Assert.deepEqual(
+ changeEvent.detail.windowIds,
+ [getWindowId(win)],
+ "The event had the correct window id"
+ );
+ }
+
+ info(
+ "make sure a change to a private window doesnt dispatch on a nonprivate target"
+ );
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ privateTabsChanges,
+ "TabChange"
+ );
+ BrowserTestUtils.addTab(privateWin.gBrowser, tabURL1);
+ changeEvent = await tabChangeRaised;
+ info(
+ `Check windowIds adding tab to private window: ${getWindowId(
+ privateWin
+ )}: ${JSON.stringify(changeEvent.detail.windowIds)}`
+ );
+ Assert.deepEqual(
+ changeEvent.detail.windowIds,
+ [getWindowId(privateWin)],
+ "The event had the correct window id"
+ );
+ await TestUtils.waitForTick();
+ Assert.ok(
+ nonPrivateListener.notCalled,
+ "A private tab change shouldnt raise a tab change event on the non-private target"
+ );
+
+ info("testTabChanges complete");
+ await cleanup("TabChange");
+});
+
+add_task(async function test_TabRecencyChange() {
+ const { windows, cleanup } = await setup("TabRecencyChange");
+ const [win0, win1, privateWin] = windows;
+
+ let tabChangeRaised;
+ let changeEvent;
+ let sortedTabs;
+
+ info("Open some tabs in the non-private windows");
+ for (let win of [win0, win1]) {
+ for (let url of [tabURL1, tabURL2]) {
+ let tab = BrowserTestUtils.addTab(win.gBrowser, url);
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ NonPrivateTabs,
+ "TabChange"
+ );
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ await tabChangeRaised;
+ }
+ }
+
+ info("Verify switching tabs produces the expected event and result");
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ NonPrivateTabs,
+ "TabRecencyChange"
+ );
+ BrowserTestUtils.switchTab(win0.gBrowser, win0.gBrowser.tabs.at(-1));
+ changeEvent = await tabChangeRaised;
+
+ Assert.deepEqual(
+ changeEvent.detail.windowIds,
+ [getWindowId(win0)],
+ "The recency change event had the correct window id"
+ );
+ Assert.ok(
+ nonPrivateListener.called,
+ "Sanity check that the non-private tabs listener was called"
+ );
+ Assert.ok(
+ privateListener.notCalled,
+ "The private tabs listener was not called"
+ );
+
+ sortedTabs = NonPrivateTabs.getRecentTabs();
+ is(
+ sortedTabs[0],
+ win0.gBrowser.selectedTab,
+ "The most-recent tab is the selected tab"
+ );
+
+ info("Verify switching window produces the expected event and result");
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ NonPrivateTabs,
+ "TabRecencyChange"
+ );
+ await SimpleTest.promiseFocus(win1);
+ changeEvent = await tabChangeRaised;
+ Assert.deepEqual(
+ changeEvent.detail.windowIds,
+ [getWindowId(win1)],
+ "The recency change event had the correct window id"
+ );
+ Assert.ok(
+ nonPrivateListener.called,
+ "Sanity check that the non-private tabs listener was called"
+ );
+ Assert.ok(
+ privateListener.notCalled,
+ "The private tabs listener was not called"
+ );
+
+ sortedTabs = NonPrivateTabs.getRecentTabs();
+ is(
+ sortedTabs[0],
+ win1.gBrowser.selectedTab,
+ "The most-recent tab is the selected tab in the current window"
+ );
+
+ info("Verify behavior with private window changes");
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ privateTabsChanges,
+ "TabRecencyChange"
+ );
+ await SimpleTest.promiseFocus(privateWin);
+ changeEvent = await tabChangeRaised;
+ Assert.deepEqual(
+ changeEvent.detail.windowIds,
+ [getWindowId(privateWin)],
+ "The recency change event had the correct window id"
+ );
+ Assert.ok(
+ nonPrivateListener.notCalled,
+ "The non-private listener got no recency-change events from the private window"
+ );
+ Assert.ok(
+ privateListener.called,
+ "Sanity check the private tabs listener was called"
+ );
+
+ sortedTabs = privateTabsChanges.getRecentTabs();
+ is(
+ sortedTabs[0],
+ privateWin.gBrowser.selectedTab,
+ "The most-recent tab is the selected tab in the current window"
+ );
+ sortedTabs = NonPrivateTabs.getRecentTabs();
+ is(
+ sortedTabs[0],
+ win1.gBrowser.selectedTab,
+ "The most-recent non-private tab is still the selected tab in the previous non-private window"
+ );
+
+ info("Verify adding a tab to a private window does the right thing");
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ privateTabsChanges,
+ "TabRecencyChange"
+ );
+ await BrowserTestUtils.openNewForegroundTab(privateWin.gBrowser, tabURL3);
+ changeEvent = await tabChangeRaised;
+ Assert.deepEqual(
+ changeEvent.detail.windowIds,
+ [getWindowId(privateWin)],
+ "The event had the correct window id"
+ );
+ Assert.ok(
+ nonPrivateListener.notCalled,
+ "The non-private listener got no recency-change events from the private window"
+ );
+ sortedTabs = privateTabsChanges.getRecentTabs();
+ is(
+ tabUrl(sortedTabs[0]),
+ tabURL3,
+ "The most-recent tab is the tab we just opened in the private window"
+ );
+
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ privateTabsChanges,
+ "TabRecencyChange"
+ );
+ BrowserTestUtils.switchTab(privateWin.gBrowser, privateWin.gBrowser.tabs[0]);
+ changeEvent = await tabChangeRaised;
+ Assert.deepEqual(
+ changeEvent.detail.windowIds,
+ [getWindowId(privateWin)],
+ "The event had the correct window id"
+ );
+ Assert.ok(
+ nonPrivateListener.notCalled,
+ "The non-private listener got no recency-change events from the private window"
+ );
+ sortedTabs = privateTabsChanges.getRecentTabs();
+ is(
+ sortedTabs[0],
+ privateWin.gBrowser.selectedTab,
+ "The most-recent tab is the selected tab in the private window"
+ );
+
+ info("Verify switching back to a non-private does the right thing");
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+
+ tabChangeRaised = BrowserTestUtils.waitForEvent(
+ NonPrivateTabs,
+ "TabRecencyChange"
+ );
+ await SimpleTest.promiseFocus(win1);
+ await tabChangeRaised;
+ if (privateListener.called) {
+ info(`The private listener was called ${privateListener.callCount} times`);
+ }
+ Assert.ok(
+ privateListener.notCalled,
+ "The private listener got no recency-change events for the non-private window"
+ );
+ Assert.ok(
+ nonPrivateListener.called,
+ "Sanity-check the non-private listener got a recency-change event for the non-private window"
+ );
+
+ sortedTabs = privateTabsChanges.getRecentTabs();
+ is(
+ sortedTabs[0],
+ privateWin.gBrowser.selectedTab,
+ "The most-recent private tab is unchanged"
+ );
+
+ sortedTabs = NonPrivateTabs.getRecentTabs();
+ is(
+ sortedTabs[0],
+ win1.gBrowser.selectedTab,
+ "The most-recent non-private tab is the selected tab in the current window"
+ );
+
+ await cleanup("TabRecencyChange");
+ while (win0.gBrowser.tabs.length > 1) {
+ info(
+ "Removing last tab:" +
+ win0.gBrowser.tabs.at(-1).linkedBrowser.currentURI.spec
+ );
+ BrowserTestUtils.removeTab(win0.gBrowser.tabs.at(-1));
+ info("Removed, tabs.length:" + win0.gBrowser.tabs.length);
+ }
+});
+
+add_task(async function test_tabNavigations() {
+ const { windows, cleanup } = await setup("TabChange");
+ const [, win1, privateWin] = windows;
+
+ // also listen for TabRecencyChange events
+ const nonPrivateRecencyListener = sinon.stub();
+ const privateRecencyListener = sinon.stub();
+ privateTabsChanges.addEventListener(
+ "TabRecencyChange",
+ privateRecencyListener
+ );
+ NonPrivateTabs.addEventListener(
+ "TabRecencyChange",
+ nonPrivateRecencyListener
+ );
+
+ info(
+ `Verify navigating in tab generates TabChange & TabRecencyChange events`
+ );
+ let loaded = BrowserTestUtils.browserLoaded(win1.gBrowser.selectedBrowser);
+ win1.gBrowser.selectedBrowser.loadURI(Services.io.newURI(tabURL4), {
+ triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ });
+ info("waiting for the load into win1 tab to complete");
+ await loaded;
+ info("waiting for listeners to be called");
+ await BrowserTestUtils.waitForCondition(() => {
+ return nonPrivateListener.called && nonPrivateRecencyListener.called;
+ });
+ ok(!privateListener.called, "The private TabChange listener was not called");
+ ok(
+ !privateRecencyListener.called,
+ "The private TabRecencyChange listener was not called"
+ );
+
+ nonPrivateListener.resetHistory();
+ privateListener.resetHistory();
+ nonPrivateRecencyListener.resetHistory();
+ privateRecencyListener.resetHistory();
+
+ // Now verify the same with a private window
+ info(
+ `Verify navigating in private tab generates TabChange & TabRecencyChange events`
+ );
+ ok(
+ !nonPrivateListener.called,
+ "The non-private TabChange listener is not yet called"
+ );
+
+ loaded = BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser);
+ privateWin.gBrowser.selectedBrowser.loadURI(
+ Services.io.newURI("about:robots"),
+ {
+ triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ }
+ );
+ info("waiting for the load into privateWin tab to complete");
+ await loaded;
+ info("waiting for the privateListeners to be called");
+ await BrowserTestUtils.waitForCondition(() => {
+ return privateListener.called && privateRecencyListener.called;
+ });
+ ok(
+ !nonPrivateListener.called,
+ "The non-private TabChange listener was not called"
+ );
+ ok(
+ !nonPrivateRecencyListener.called,
+ "The non-private TabRecencyChange listener was not called"
+ );
+
+ // cleanup
+ privateTabsChanges.removeEventListener(
+ "TabRecencyChange",
+ privateRecencyListener
+ );
+ NonPrivateTabs.removeEventListener(
+ "TabRecencyChange",
+ nonPrivateRecencyListener
+ );
+
+ await cleanup();
+});
+
+add_task(async function test_tabsFromPrivateWindows() {
+ const { cleanup } = await setup("TabChange");
+ const private2Listener = sinon.stub();
+
+ const private2Win = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ waitForTabURL: "about:privatebrowsing",
+ });
+ const private2TabsChanges = getTabsTargetForWindow(private2Win);
+ private2TabsChanges.addEventListener("TabChange", private2Listener);
+ ok(
+ privateTabsChanges !== getTabsTargetForWindow(private2Win),
+ "getTabsTargetForWindow creates a distinct instance for a different private window"
+ );
+
+ await BrowserTestUtils.waitForCondition(() => private2Listener.called);
+
+ ok(
+ !privateListener.called,
+ "No TabChange event was raised by opening a different private window"
+ );
+ privateListener.resetHistory();
+ private2Listener.resetHistory();
+
+ BrowserTestUtils.addTab(private2Win.gBrowser, tabURL1);
+ await BrowserTestUtils.waitForCondition(() => private2Listener.called);
+ ok(
+ !privateListener.called,
+ "No TabChange event was raised by adding tab to a different private window"
+ );
+
+ is(
+ privateTabsChanges.getRecentTabs().length,
+ 1,
+ "The recent tab count for the first private window tab target only reports the tabs for its associated windodw"
+ );
+ is(
+ private2TabsChanges.getRecentTabs().length,
+ 2,
+ "The recent tab count for a 2nd private window tab target only reports the tabs for its associated windodw"
+ );
+
+ await cleanup("TabChange");
+});