requestLongerTimeout(2); const DUMMY_PATH = "browser/browser/base/content/test/general/dummy_page.html"; const gExpectedHistory = { index: -1, entries: [], }; async function get_remote_history(browser) { if (SpecialPowers.Services.appinfo.sessionHistoryInParent) { let sessionHistory = browser.browsingContext?.sessionHistory; if (!sessionHistory) { return null; } let result = { index: sessionHistory.index, entries: [], }; for (let i = 0; i < sessionHistory.count; i++) { let entry = sessionHistory.getEntryAtIndex(i); result.entries.push({ uri: entry.URI.spec, title: entry.title, }); } return result; } return SpecialPowers.spawn(browser, [], () => { let webNav = content.docShell.QueryInterface(Ci.nsIWebNavigation); let sessionHistory = webNav.sessionHistory; let result = { index: sessionHistory.index, entries: [], }; for (let i = 0; i < sessionHistory.count; i++) { let entry = sessionHistory.legacySHistory.getEntryAtIndex(i); result.entries.push({ uri: entry.URI.spec, title: entry.title, }); } return result; }); } var check_history = async function () { let sessionHistory = await get_remote_history(gBrowser.selectedBrowser); let count = sessionHistory.entries.length; is( count, gExpectedHistory.entries.length, "Should have the right number of history entries" ); is( sessionHistory.index, gExpectedHistory.index, "Should have the right history index" ); for (let i = 0; i < count; i++) { let entry = sessionHistory.entries[i]; info("Checking History Entry: " + entry.uri); is(entry.uri, gExpectedHistory.entries[i].uri, "Should have the right URI"); is( entry.title, gExpectedHistory.entries[i].title, "Should have the right title" ); } }; function clear_history() { gExpectedHistory.index = -1; gExpectedHistory.entries = []; } // Waits for a load and updates the known history var waitForLoad = async function (uriString) { info("Loading " + uriString); // Longwinded but this ensures we don't just shortcut to LoadInNewProcess let loadURIOptions = { triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), }; gBrowser.selectedBrowser.webNavigation.loadURI( Services.io.newURI(uriString), loadURIOptions ); await BrowserTestUtils.browserStopped(gBrowser, uriString); // Some of the documents we're using in this test use Fluent, // and they may finish localization later. // To prevent this test from being intermittent, we'll // wait for the `document.l10n.ready` promise to resolve. if ( gBrowser.selectedBrowser.contentWindow && gBrowser.selectedBrowser.contentWindow.document.l10n ) { await gBrowser.selectedBrowser.contentWindow.document.l10n.ready; } gExpectedHistory.index++; gExpectedHistory.entries.push({ uri: gBrowser.currentURI.spec, title: gBrowser.contentTitle, }); }; // Waits for a load and updates the known history var waitForLoadWithFlags = async function ( uriString, flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE ) { info("Loading " + uriString + " flags = " + flags); gBrowser.selectedBrowser.loadURI(Services.io.newURI(uriString), { flags, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), }); await BrowserTestUtils.browserStopped(gBrowser, uriString); if (!(flags & Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY)) { if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY) { gExpectedHistory.entries.pop(); } else { gExpectedHistory.index++; } gExpectedHistory.entries.push({ uri: gBrowser.currentURI.spec, title: gBrowser.contentTitle, }); } }; var back = async function () { info("Going back"); gBrowser.goBack(); await BrowserTestUtils.browserStopped(gBrowser); gExpectedHistory.index--; }; var forward = async function () { info("Going forward"); gBrowser.goForward(); await BrowserTestUtils.browserStopped(gBrowser); gExpectedHistory.index++; }; // Tests that navigating from a page that should be in the remote process and // a page that should be in the main process works and retains history add_task(async function test_navigation() { await SpecialPowers.pushPrefEnv({ set: [["browser.navigation.requireUserInteraction", false]], }); let expectedRemote = gMultiProcessBrowser; info("1"); // Create a tab and load a remote page in it gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true, }); let { permanentKey } = gBrowser.selectedBrowser; // eslint-disable-next-line @microsoft/sdl/no-insecure-url await waitForLoad("http://example.org/" + DUMMY_PATH); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); info("2"); // Load another page // eslint-disable-next-line @microsoft/sdl/no-insecure-url await waitForLoad("http://example.com/" + DUMMY_PATH); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); await check_history(); info("3"); // Load a non-remote page await waitForLoad("about:robots"); await TestUtils.waitForCondition( () => !!gBrowser.selectedBrowser.contentTitle.length, "Waiting for about:robots title to update" ); is( gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); await check_history(); info("4"); // Load a remote page // eslint-disable-next-line @microsoft/sdl/no-insecure-url await waitForLoad("http://example.org/" + DUMMY_PATH); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); await check_history(); info("5"); await back(); is( gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); await TestUtils.waitForCondition( () => !!gBrowser.selectedBrowser.contentTitle.length, "Waiting for about:robots title to update" ); await check_history(); info("6"); await back(); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); await check_history(); info("7"); await forward(); is( gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); await TestUtils.waitForCondition( () => !!gBrowser.selectedBrowser.contentTitle.length, "Waiting for about:robots title to update" ); await check_history(); info("8"); await forward(); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); await check_history(); info("9"); await back(); await TestUtils.waitForCondition( () => !!gBrowser.selectedBrowser.contentTitle.length, "Waiting for about:robots title to update" ); is( gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); await check_history(); info("10"); // Load a new remote page, this should replace the last history entry gExpectedHistory.entries.splice(gExpectedHistory.entries.length - 1, 1); // eslint-disable-next-line @microsoft/sdl/no-insecure-url await waitForLoad("http://example.com/" + DUMMY_PATH); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); await check_history(); info("11"); gBrowser.removeCurrentTab(); clear_history(); }); // Tests that calling gBrowser.loadURI or browser.loadURI to load a page in a // different process updates the browser synchronously add_task(async function test_synchronous() { let expectedRemote = gMultiProcessBrowser; info("1"); // Create a tab and load a remote page in it gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true, }); let { permanentKey } = gBrowser.selectedBrowser; // eslint-disable-next-line @microsoft/sdl/no-insecure-url await waitForLoad("http://example.org/" + DUMMY_PATH); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); info("2"); // Load another page info("Loading about:robots"); BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, "about:robots"); await BrowserTestUtils.browserStopped(gBrowser); is( gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); info("3"); // Load the remote page again info("Loading http://example.org/" + DUMMY_PATH); BrowserTestUtils.loadURIString( gBrowser.selectedBrowser, // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.org/" + DUMMY_PATH ); await BrowserTestUtils.browserStopped(gBrowser); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); is( gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same" ); info("4"); gBrowser.removeCurrentTab(); clear_history(); }); // Tests that load flags are correctly passed through to the child process with // normal loads add_task(async function test_loadflags() { let expectedRemote = gMultiProcessBrowser; info("1"); // Create a tab and load a remote page in it gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true, }); await waitForLoadWithFlags("about:robots"); await TestUtils.waitForCondition( () => gBrowser.selectedBrowser.contentTitle != "about:robots", "Waiting for about:robots title to update" ); is( gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct" ); await check_history(); info("2"); // Load a page in the remote process with some custom flags await waitForLoadWithFlags( // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.com/" + DUMMY_PATH, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY ); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); await check_history(); info("3"); // Load a non-remote page await waitForLoadWithFlags("about:robots"); await TestUtils.waitForCondition( () => !!gBrowser.selectedBrowser.contentTitle.length, "Waiting for about:robots title to update" ); is( gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct" ); await check_history(); info("4"); // Load another remote page await waitForLoadWithFlags( // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.org/" + DUMMY_PATH, Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY ); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); await check_history(); info("5"); // Load another remote page from a different origin await waitForLoadWithFlags( // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.com/" + DUMMY_PATH, Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY ); is( gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct" ); await check_history(); is( gExpectedHistory.entries.length, 2, "Should end with the right number of history entries" ); info("6"); gBrowser.removeCurrentTab(); clear_history(); });