diff options
Diffstat (limited to 'docshell/test/navigation/test_blockBFCache.html')
-rw-r--r-- | docshell/test/navigation/test_blockBFCache.html | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/docshell/test/navigation/test_blockBFCache.html b/docshell/test/navigation/test_blockBFCache.html new file mode 100644 index 0000000000..3d4a418369 --- /dev/null +++ b/docshell/test/navigation/test_blockBFCache.html @@ -0,0 +1,294 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Blocking pages from entering BFCache</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> +</head> +<body onload=""> +<script> + +const getUserMediaPrefs = { + set: [ + ["media.devices.insecure.enabled", true], + ["media.getusermedia.insecure.enabled", true], + ["media.navigator.permission.disabled", true], + ], +}; +const msePrefs = { + set: [ + ["media.mediasource.enabled", true], + ["media.audio-max-decode-error", 0], + ["media.video-max-decode-error", 0], + ] +}; + +const blockBFCacheTests = [ + { + name: "Request", + test: () => { + return new Promise((resolve) => { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "slow.sjs"); + xhr.addEventListener("progress", () => { resolve(xhr); }, { once: true }); + xhr.send(); + }); + }, + }, + { + name: "Background request", + test: () => { + return new Promise((resolve) => { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "slow.sjs"); + xhr.addEventListener("readystatechange", () => { if (xhr.readyState == xhr.HEADERS_RECEIVED) resolve(xhr); }); + xhr.send(); + }); + }, + }, + { + name: "getUserMedia", + prefs: getUserMediaPrefs, + test: () => { + return navigator.mediaDevices.getUserMedia({ audio: true, fake: true }); + }, + }, + { + name: "RTCPeerConnection", + test: () => { + let pc = new RTCPeerConnection(); + return pc.createOffer(); + }, + }, + { + name: "MSE", + prefs: msePrefs, + test: () => { + const ms = new MediaSource(); + const el = document.createElement("video"); + el.src = URL.createObjectURL(ms); + el.preload = "auto"; + return el; + }, + }, + { + name: "WebSpeech", + test: () => { + return new Promise((resolve) => { + const utterance = new SpeechSynthesisUtterance('bfcache'); + utterance.lang = 'it-IT-noend'; + utterance.addEventListener('start', () => { resolve(utterance); }) + speechSynthesis.speak(utterance); + }); + }, + }, + { + name: "WebVR", + prefs: { + set: [ + ["dom.vr.test.enabled", true], + ["dom.vr.puppet.enabled", true], + ["dom.vr.require-gesture", false], + ], + }, + test: () => { + return navigator.requestVRServiceTest(); + } + }, +]; + +if (SpecialPowers.Services.appinfo.fissionAutostart) { + blockBFCacheTests.push({ + name: "Loading OOP iframe", + test: () => { + return new Promise((resolve) => { + const el = document.body.appendChild(document.createElement("iframe")); + el.id = "frame"; + addEventListener("message", ({ data }) => { + if (data == "onload") { + resolve(); + } + }); + el.src = "https://example.com/tests/docshell/test/navigation/iframe_slow_onload.html"; + }); + }, + waitForDone: () => { + SimpleTest.requestFlakyTimeout("Test has a loop in an onload handler that runs for 5000ms, we need to make sure the loop is done before moving to the next test."); + return new Promise(resolve => { + setTimeout(resolve, 5000); + }); + }, + }); +} + +const dontBlockBFCacheTests = [ + { + name: "getUserMedia", + prefs: getUserMediaPrefs, + test: () => { + return navigator.mediaDevices.getUserMedia({ video: true, fake: true }).then(stream => { + stream.getTracks().forEach(track => track.stop()); + return stream; + }); + }, + }, +/* + Disabled because MediaKeys rely on being destroyed by the CC before they + notify their window, so the test would intermittently fail depending on + when the CC runs. + + { + name: "MSE", + prefs: msePrefs, + test: () => { + return new Promise((resolve) => { + const ms = new MediaSource(); + const el = document.createElement("video"); + ms.addEventListener("sourceopen", () => { resolve(el) }, { once: true }); + el.src = URL.createObjectURL(ms); + el.preload = "auto"; + }).then(el => { + el.src = ""; + return el; + }); + }, + }, +*/ +]; + + + +function executeTest() { + + let bc = new BroadcastChannel("bfcache_blocking"); + + function promiseMessage(type) { + return new Promise((resolve, reject) => { + bc.addEventListener("message", (e) => { + if (e.data.type == type) { + resolve(e.data); + } + }, { once: true }); + }); + } + + function promisePageShow(shouldBePersisted) { + return promiseMessage("pageshow").then(data => data.persisted == shouldBePersisted); + } + + function promisePageShowFromBFCache(e) { + return promisePageShow(true); + } + + function promisePageShowNotFromBFCache(e) { + return promisePageShow(false); + } + + function runTests(testArray, shouldBlockBFCache) { + for (const { name, prefs = {}, test, waitForDone } of testArray) { + add_task(async function() { + await SpecialPowers.pushPrefEnv(prefs, async function() { + // Load a mostly blank page that we can communicate with over + // BroadcastChannel (though it will close the BroadcastChannel after + // receiving the next "load" message, to avoid blocking BFCache). + let loaded = promisePageShowNotFromBFCache(); + window.open("file_blockBFCache.html", "", "noopener"); + await loaded; + + // Load the same page with a different URL. + loaded = promisePageShowNotFromBFCache(); + bc.postMessage({ message: "load", url: `file_blockBFCache.html?${name}_${shouldBlockBFCache}` }); + await loaded; + + // Run test script in the second page. + bc.postMessage({ message: "runScript", fun: test.toString() }); + await promiseMessage("runScriptDone"); + + // Go back to the first page (this should just come from the BFCache). + let goneBack = promisePageShowFromBFCache(); + bc.postMessage({ message: "back" }); + await goneBack; + + // Go forward again to the second page and check that it does/doesn't come + // from the BFCache. + let goneForward = promisePageShow(!shouldBlockBFCache); + bc.postMessage({ message: "forward" }); + let result = await goneForward; + ok(result, `Page ${shouldBlockBFCache ? "should" : "should not"} have been blocked from going into the BFCache (${name})`); + + // If the test will keep running after navigation, then we need to make + // sure it's completely done before moving to the next test, to avoid + // interfering with any following tests. If waitForDone is defined then + // it'll return a Promise that we can use to wait for the end of the + // test. + if (waitForDone) { + await waitForDone(); + } + + // Do a similar test, but replace the bfcache test page with a new page, + // not a page coming from the session history. + + // Load the same page with a different URL. + loaded = promisePageShowNotFromBFCache(); + bc.postMessage({ message: "load", url: `file_blockBFCache.html?p2_${name}_${shouldBlockBFCache}` }); + await loaded; + + // Run the test script. + bc.postMessage({ message: "runScript", fun: test.toString() }); + await promiseMessage("runScriptDone"); + + // Load a new page. + loaded = promisePageShowNotFromBFCache(); + bc.postMessage({ message: "load", url: "file_blockBFCache.html" }); + await loaded; + + // Go back to the previous page and check that it does/doesn't come + // from the BFCache. + goneBack = promisePageShow(!shouldBlockBFCache); + bc.postMessage({ message: "back" }); + result = await goneBack; + ok(result, `Page ${shouldBlockBFCache ? "should" : "should not"} have been blocked from going into the BFCache (${name})`); + + if (waitForDone) { + await waitForDone(); + } + + bc.postMessage({ message: "close" }); + + SpecialPowers.popPrefEnv(); + }); + }); + } + } + + // If Fission is disabled, the pref is no-op. + SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => { + runTests(blockBFCacheTests, true); + runTests(dontBlockBFCacheTests, false); + }); +} + +if (isXOrigin) { + // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5) + // Acquire storage access permission here so that the BroadcastChannel used to + // communicate with the opened windows works in xorigin tests. Otherwise, + // the iframe containing this page is isolated from first-party storage access, + // which isolates BroadcastChannel communication. + SpecialPowers.wrap(document).notifyUserGestureActivation(); + SpecialPowers.addPermission("storageAccessAPI", true, window.location.href).then(() => { + SpecialPowers.wrap(document).requestStorageAccess().then(() => { + SpecialPowers.pushPrefEnv({ + set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]] + }).then(() => { + executeTest(); + }); + }); + }); +} else { + executeTest(); +} + +</script> +</body> +</html> |