/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const URL = "about:blank"; async function getBrowsingContextId(browser, id) { return SpecialPowers.spawn(browser, [id], async function (id) { let contextId = content.window.docShell.browsingContext.id; let frames = [content.window]; while (frames.length) { let frame = frames.pop(); let target = frame.document.getElementById(id); if (target) { contextId = target.docShell.browsingContext.id; break; } frames = frames.concat(Array.from(frame.frames)); } return contextId; }); } async function addFrame(browser, id, parentId) { return SpecialPowers.spawn( browser, [{ parentId, id }], async function ({ parentId, id }) { let parent = null; if (parentId) { let frames = [content.window]; while (frames.length) { let frame = frames.pop(); let target = frame.document.getElementById(parentId); if (target) { parent = target.contentWindow.document.body; break; } frames = frames.concat(Array.from(frame.frames)); } } else { parent = content.document.body; } let frame = await new Promise(resolve => { let frame = content.document.createElement("iframe"); frame.id = id || ""; frame.url = "about:blank"; frame.onload = () => resolve(frame); parent.appendChild(frame); }); return frame.contentWindow.docShell.browsingContext.id; } ); } async function removeFrame(browser, id) { return SpecialPowers.spawn(browser, [id], async function (id) { let frames = [content.window]; while (frames.length) { let frame = frames.pop(); let target = frame.document.getElementById(id); if (target) { target.remove(); break; } frames = frames.concat(Array.from(frame.frames)); } }); } function getBrowsingContextById(id) { return BrowsingContext.get(id); } add_task(async function () { await BrowserTestUtils.withNewTab( { gBrowser, url: URL }, async function (browser) { let topId = await getBrowsingContextId(browser, ""); let topContext = getBrowsingContextById(topId); isnot(topContext, null); is(topContext.parent, null); is( topId, browser.browsingContext.id, " has the correct browsingContext" ); is( browser.browserId, topContext.browserId, "browsing context should have a correct id" ); let id0 = await addFrame(browser, "frame0"); let browsingContext0 = getBrowsingContextById(id0); isnot(browsingContext0, null); is(browsingContext0.parent, topContext); await removeFrame(browser, "frame0"); is(topContext.children.indexOf(browsingContext0), -1); // TODO(farre): Handle browsingContext removal [see Bug 1486719]. todo_isnot(browsingContext0.parent, topContext); } ); }); add_task(async function () { // If Fission is disabled, the pref is no-op. await SpecialPowers.pushPrefEnv({ set: [["fission.bfcacheInParent", true]] }); await BrowserTestUtils.withNewTab( { gBrowser, url: getRootDirectory(gTestPath).replace( "chrome://mochitests/content", // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.com" ) + "dummy_page.html", }, async function (browser) { let path = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.com" ); await SpecialPowers.spawn(browser, [path], async function (path) { var bc = new content.BroadcastChannel("browser_browsingContext"); function waitForMessage(command) { let p = new Promise(resolve => { bc.addEventListener("message", e => resolve(e), { once: true }); }); command(); return p; } // Open a new window and wait for the message. let e1 = await waitForMessage(_ => content.window.open(path + "onpageshow_message.html", "", "noopener") ); is(e1.data, "pageshow", "Got page show"); let e2 = await waitForMessage(_ => bc.postMessage("createiframe")); is(e2.data.framesLength, 1, "Here we should have an iframe"); let e3 = await waitForMessage(_ => bc.postMessage("nextpage")); is(e3.data.event, "load"); is(e3.data.framesLength, 0, "Here there shouldn't be an iframe"); // Return to the previous document. N.B. we expect to trigger // BFCache here, hence we wait for pageshow. let e4 = await waitForMessage(_ => bc.postMessage("back")); is(e4.data, "pageshow"); let e5 = await waitForMessage(_ => bc.postMessage("queryframes")); is(e5.data.framesLength, 1, "And again there should be an iframe"); is(e5.outerWindowId, e2.outerWindowId, "BF cache cached outer window"); is(e5.browsingContextId, e2.browsingContextId, "BF cache cached BC"); let e6 = await waitForMessage(_ => bc.postMessage("close")); is(e6.data, "closed"); bc.close(); }); } ); });