"use strict"; const TOPIC = "browsing-context-discarded"; async function observeDiscarded(browsingContexts, callback) { let discarded = new Map(); let promise = BrowserUtils.promiseObserved(TOPIC, (subject, why) => { ok(BrowsingContext.isInstance(subject), "subject to be a BrowsingContext"); discarded.set(subject, why); return browsingContexts.every(item => discarded.has(item)); }); await callback(); await promise; return discarded; } function check_reason(discardedContexts, expected) { discardedContexts.forEach((why, browsingContext) => { if (expected.has(browsingContext)) { is(why, expected.get(browsingContext)); } }); } add_task(async function toplevelForNewWindow() { const win = await BrowserTestUtils.openNewBrowserWindow(); const browsingContext = win.gBrowser.selectedBrowser.browsingContext; const expected = new Map([ [win.browsingContext, "discard"], [browsingContext, "discard"], ]); const discarded = await observeDiscarded([...expected.keys()], async () => { await BrowserTestUtils.closeWindow(win); }); check_reason(discarded, expected); }); add_task(async function toplevelForNewTab() { const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); const browsingContext = tab.linkedBrowser.browsingContext; const expected = new Map([[browsingContext, "discard"]]); const discarded = await observeDiscarded([...expected.keys()], () => { BrowserTestUtils.removeTab(tab); }); ok( !discarded.has(window.browsingContext), "no notification for the current window's chrome browsing context" ); check_reason(discarded, expected); }); add_task(async function subframe() { const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); const frameBrowsingContext = await SpecialPowers.spawn( tab.linkedBrowser, [], () => { const iframe = content.document.createElement("iframe"); iframe.src = "https://example.com/"; content.document.body.appendChild(iframe); return iframe.browsingContext; } ); const expected = new Map([[frameBrowsingContext, "discard"]]); const discarded = await observeDiscarded([...expected.keys()], async () => { await SpecialPowers.spawn(tab.linkedBrowser, [], () => { let iframe = content.document.querySelector("iframe"); iframe.remove(); }); }); ok( !discarded.has(tab.linkedBrowser.browsingContext), "no notification for toplevel browsing context" ); ok( !discarded.has(window.browsingContext), "no notification for the current window's chrome browsing context" ); check_reason(discarded, expected); BrowserTestUtils.removeTab(tab); }); add_task(async function replaceToplevel() { const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); // Force load about:blank such that BC::HasLoadedNonInitialDocument is true // which means it's BFCache eligible and will be replaced BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, "about:blank"); await BrowserTestUtils.browserLoaded(tab.linkedBrowser, { wantLoad: "about:blank", }); // Make it transient so that it doesn't stay in SH and the BC is discarded tab.linkedBrowser.browsingContext.sessionHistory .getEntryAtIndex(0) .setTransient(); const browsingContext = tab.linkedBrowser.browsingContext; const expected = new Map([[browsingContext, "replace"]]); const discarded = await observeDiscarded([...expected.keys()], async () => { await SpecialPowers.spawn(tab.linkedBrowser, [], () => { content.location = "about:newtab"; }); }); ok( !discarded.has(window.browsingContext), "no notification for the current window's chrome browsing context" ); check_reason(discarded, expected); BrowserTestUtils.removeTab(tab); });