summaryrefslogtreecommitdiffstats
path: root/browser/components/sessionstore/test/browser_pinned_tabs.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /browser/components/sessionstore/test/browser_pinned_tabs.js
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/sessionstore/test/browser_pinned_tabs.js')
-rw-r--r--browser/components/sessionstore/test/browser_pinned_tabs.js347
1 files changed, 347 insertions, 0 deletions
diff --git a/browser/components/sessionstore/test/browser_pinned_tabs.js b/browser/components/sessionstore/test/browser_pinned_tabs.js
new file mode 100644
index 0000000000..a4d1cce85b
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_pinned_tabs.js
@@ -0,0 +1,347 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const REMOTE_URL = "https://www.example.com/";
+const ABOUT_ROBOTS_URL = "about:robots";
+const NO_TITLE_URL = "data:text/plain,foo";
+
+const BACKUP_STATE = SessionStore.getBrowserState();
+registerCleanupFunction(() => promiseBrowserState(BACKUP_STATE));
+
+/**
+ * This is regrettable, but when `promiseBrowserState` resolves, we're still
+ * midway through loading the tabs. To avoid race conditions in URLs for tabs
+ * being available, wait for all the loads to finish:
+ */
+function promiseSessionStoreLoads(numberOfLoads) {
+ let loadsSeen = 0;
+ return new Promise(resolve => {
+ Services.obs.addObserver(function obs(browser) {
+ loadsSeen++;
+ if (loadsSeen == numberOfLoads) {
+ resolve();
+ }
+ // The typeof check is here to avoid one test messing with everything else by
+ // keeping the observer indefinitely.
+ if (typeof info == "undefined" || loadsSeen >= numberOfLoads) {
+ Services.obs.removeObserver(obs, "sessionstore-debug-tab-restored");
+ }
+ info("Saw load for " + browser.currentURI.spec);
+ }, "sessionstore-debug-tab-restored");
+ });
+}
+
+add_setup(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.sessionstore.restore_on_demand", true],
+ ["browser.sessionstore.restore_tabs_lazily", true],
+ ],
+ });
+});
+
+/**
+ * When implementing batch insertion of tabs as part of session restore,
+ * we started reversing the insertion order of pinned tabs (bug 1607441).
+ * This test checks we don't regress that again.
+ */
+add_task(async function test_pinned_tabs_order() {
+ // we expect 3 pinned tabs plus the selected tab get content restored.
+ let allTabsRestored = promiseSessionStoreLoads(4);
+ await promiseBrowserState({
+ windows: [
+ {
+ selected: 4, // SessionStore uses 1-based indexing.
+ tabs: [
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: ABOUT_ROBOTS_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: NO_TITLE_URL, triggeringPrincipal_base64 }],
+ },
+ { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
+ { entries: [{ url: "about:blank", triggeringPrincipal_base64 }] },
+ ],
+ },
+ ],
+ });
+ await allTabsRestored;
+ let [tab1, tab2, tab3, tab4, tab5] = gBrowser.tabs;
+ ok(tab1.pinned, "First tab is pinned");
+ ok(tab2.pinned, "Second tab is pinned");
+ ok(tab3.pinned, "Third tab is pinned");
+ ok(!tab4.pinned, "Fourth tab is not pinned");
+ ok(!tab5.pinned, "Fifth tab is not pinned");
+
+ ok(tab4.selected, "Fourth tab is selected");
+ is(
+ tab1.linkedBrowser.currentURI.spec,
+ REMOTE_URL,
+ "First tab has matching URL"
+ );
+ is(
+ tab2.linkedBrowser.currentURI.spec,
+ ABOUT_ROBOTS_URL,
+ "Second tab has matching URL"
+ );
+ is(
+ tab3.linkedBrowser.currentURI.spec,
+ NO_TITLE_URL,
+ "Third tab has matching URL"
+ );
+ // Clean up for the next task.
+ await promiseBrowserState(BACKUP_STATE);
+});
+
+/**
+ * When fixing the previous regression, pinned tabs started disappearing out
+ * of sessions with selected pinned tabs. This test checks that case.
+ */
+add_task(async function test_selected_pinned_tab_dataloss() {
+ // we expect 3 pinned tabs (one of which is selected) get content restored.
+ let allTabsRestored = promiseSessionStoreLoads(3);
+ await promiseBrowserState({
+ windows: [
+ {
+ selected: 1, // SessionStore uses 1-based indexing.
+ tabs: [
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: ABOUT_ROBOTS_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: NO_TITLE_URL, triggeringPrincipal_base64 }],
+ },
+ { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
+ { entries: [{ url: "about:blank", triggeringPrincipal_base64 }] },
+ ],
+ },
+ ],
+ });
+ await allTabsRestored;
+ let [tab1, tab2, tab3, tab4, tab5] = gBrowser.tabs;
+ ok(tab5, "Should have 5 tabs");
+ ok(tab1.pinned, "First tab is pinned");
+ ok(tab2.pinned, "Second tab is pinned");
+ ok(tab3.pinned, "Third tab is pinned");
+ ok(tab4 && !tab4.pinned, "Fourth tab is not pinned");
+ ok(tab5 && !tab5.pinned, "Fifth tab is not pinned");
+
+ ok(tab1 && tab1.selected, "First (pinned) tab is selected");
+ is(
+ tab1.linkedBrowser.currentURI.spec,
+ REMOTE_URL,
+ "First tab has matching URL"
+ );
+ is(
+ tab2.linkedBrowser.currentURI.spec,
+ ABOUT_ROBOTS_URL,
+ "Second tab has matching URL"
+ );
+ is(
+ tab3.linkedBrowser.currentURI.spec,
+ NO_TITLE_URL,
+ "Third tab has matching URL"
+ );
+ // Clean up for the next task.
+ await promiseBrowserState(BACKUP_STATE);
+});
+
+/**
+ * While we're here, it seems useful to have a test for mixed pinned and
+ * unpinned tabs in session store state, as well as userContextId.
+ */
+add_task(async function test_mixed_pinned_unpinned() {
+ // we expect 3 pinned tabs plus the selected tab get content restored.
+ let allTabsRestored = promiseSessionStoreLoads(4);
+ await promiseBrowserState({
+ windows: [
+ {
+ selected: 4, // SessionStore uses 1-based indexing.
+ tabs: [
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
+ {
+ pinned: true,
+ entries: [{ url: ABOUT_ROBOTS_URL, triggeringPrincipal_base64 }],
+ },
+ { entries: [{ url: "about:blank", triggeringPrincipal_base64 }] },
+ {
+ pinned: true,
+ entries: [{ url: NO_TITLE_URL, triggeringPrincipal_base64 }],
+ },
+ ],
+ },
+ ],
+ });
+ await allTabsRestored;
+ let [tab1, tab2, tab3, tab4, tab5] = gBrowser.tabs;
+ ok(tab1.pinned, "First tab is pinned");
+ ok(tab2.pinned, "Second tab is pinned");
+ ok(tab3.pinned, "Third tab is pinned");
+ ok(!tab4.pinned, "Fourth tab is not pinned");
+ ok(!tab5.pinned, "Fifth tab is not pinned");
+
+ // This is confusing to read - the 4th entry in the session data is
+ // selected. But the 5th entry is pinned, so it moves to the start of the
+ // tabstrip, so when we fetch `gBrowser.tabs`, the 4th entry in the list
+ // is actually the 5th tab.
+ ok(tab5.selected, "Fifth tab is selected");
+ is(
+ tab1.linkedBrowser.currentURI.spec,
+ REMOTE_URL,
+ "First tab has matching URL"
+ );
+ is(
+ tab2.linkedBrowser.currentURI.spec,
+ ABOUT_ROBOTS_URL,
+ "Second tab has matching URL"
+ );
+ is(
+ tab3.linkedBrowser.currentURI.spec,
+ NO_TITLE_URL,
+ "Third tab has matching URL"
+ );
+ // Clean up for the next task.
+ await promiseBrowserState(BACKUP_STATE);
+});
+
+/**
+ * After session restore, if we crash an unpinned tab, we noticed pinned tabs
+ * created in the same process would lose all data (Bug 1624511). This test
+ * checks that case.
+ */
+add_task(async function test_pinned_tab_dataloss() {
+ // We do not run if there are no crash reporters to avoid
+ // problems with the intentional crash.
+ if (!AppConstants.MOZ_CRASHREPORTER) {
+ return;
+ }
+ // If we end up increasing the process count limit in future,
+ // we want to ensure that we don't stop testing this case
+ // of pinned tab data loss.
+ if (SpecialPowers.getIntPref("dom.ipc.processCount") > 8) {
+ ok(
+ false,
+ "Process count is greater than 8, update the number of pinned tabs in test."
+ );
+ }
+
+ // We expect 17 pinned tabs plus the selected tab get content restored.
+ // Given that the default process count is currently 8, we need this
+ // number of pinned tabs to reproduce the data loss. If this changes,
+ // please add more pinned tabs.
+ let allTabsRestored = promiseSessionStoreLoads(18);
+ await promiseBrowserState({
+ windows: [
+ {
+ selected: 18, // SessionStore uses 1-based indexing.
+ tabs: [
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ {
+ pinned: true,
+ entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
+ },
+ { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
+ ],
+ },
+ ],
+ });
+ await allTabsRestored;
+
+ let tabs = gBrowser.tabs;
+ BrowserTestUtils.crashFrame(tabs[17].linkedBrowser);
+
+ await TestUtils.topicObserved("sessionstore-state-write-complete");
+
+ for (let i = 0; i < tabs.length; i++) {
+ let tab = tabs[i];
+ is(
+ tab.linkedBrowser.currentURI.spec,
+ REMOTE_URL,
+ `Tab ${i + 1} should have matching URL`
+ );
+ }
+
+ // Clean up for the next task.
+ await promiseBrowserState(BACKUP_STATE);
+});