test_blockBFCache.html (9250B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Blocking pages from entering BFCache</title> 6 <script src="/tests/SimpleTest/SimpleTest.js"></script> 7 <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> 8 </head> 9 <body onload=""> 10 <script> 11 12 const getUserMediaPrefs = { 13 set: [ 14 ["media.devices.insecure.enabled", true], 15 ["media.getusermedia.insecure.enabled", true], 16 ["media.navigator.permission.disabled", true], 17 ], 18 }; 19 const msePrefs = { 20 set: [ 21 ["media.mediasource.enabled", true], 22 ["media.audio-max-decode-error", 0], 23 ["media.video-max-decode-error", 0], 24 ] 25 }; 26 27 const blockBFCacheTests = [ 28 { 29 name: "Request", 30 test: () => { 31 return new Promise((resolve) => { 32 const xhr = new XMLHttpRequest(); 33 xhr.open("GET", "slow.sjs"); 34 xhr.addEventListener("progress", () => { resolve(xhr); }, { once: true }); 35 xhr.send(); 36 }); 37 }, 38 }, 39 { 40 name: "Background request", 41 test: () => { 42 return new Promise((resolve) => { 43 const xhr = new XMLHttpRequest(); 44 xhr.open("GET", "slow.sjs"); 45 xhr.addEventListener("readystatechange", () => { if (xhr.readyState == xhr.HEADERS_RECEIVED) resolve(xhr); }); 46 xhr.send(); 47 }); 48 }, 49 }, 50 { 51 name: "getUserMedia", 52 prefs: getUserMediaPrefs, 53 test: () => { 54 return navigator.mediaDevices.getUserMedia({ audio: true, fake: true }); 55 }, 56 }, 57 { 58 name: "RTCPeerConnection", 59 test: () => { 60 let pc = new RTCPeerConnection(); 61 return pc.createOffer(); 62 }, 63 }, 64 { 65 name: "MSE", 66 prefs: msePrefs, 67 test: () => { 68 const ms = new MediaSource(); 69 const el = document.createElement("video"); 70 el.src = URL.createObjectURL(ms); 71 el.preload = "auto"; 72 return el; 73 }, 74 }, 75 { 76 name: "WebSpeech", 77 test: () => { 78 return new Promise((resolve) => { 79 const utterance = new SpeechSynthesisUtterance('bfcache'); 80 utterance.lang = 'it-IT-noend'; 81 utterance.addEventListener('start', () => { resolve(utterance); }) 82 speechSynthesis.speak(utterance); 83 }); 84 }, 85 }, 86 { 87 name: "WebVR", 88 prefs: { 89 set: [ 90 ["dom.vr.test.enabled", true], 91 ["dom.vr.puppet.enabled", true], 92 ["dom.vr.require-gesture", false], 93 ], 94 }, 95 test: () => { 96 return navigator.requestVRServiceTest(); 97 } 98 }, 99 ]; 100 101 if (SpecialPowers.effectiveIsolationStrategy() == SpecialPowers.ISOLATION_STRATEGY.IsolateEverything) { 102 blockBFCacheTests.push({ 103 name: "Loading OOP iframe", 104 test: () => { 105 return new Promise((resolve) => { 106 const el = document.createElement("iframe"); 107 el.id = "frame"; 108 addEventListener("message", ({ data }) => { 109 if (data == "onload") { 110 resolve(); 111 } 112 }); 113 el.src = "https://example.com/tests/docshell/test/navigation/iframe_slow_onload.html"; 114 document.body.appendChild(el); 115 }); 116 }, 117 waitForDone: () => { 118 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."); 119 return new Promise(resolve => { 120 setTimeout(resolve, 5000); 121 }); 122 }, 123 }); 124 } 125 126 const dontBlockBFCacheTests = [ 127 { 128 name: "getUserMedia", 129 prefs: getUserMediaPrefs, 130 test: () => { 131 return navigator.mediaDevices.getUserMedia({ video: true, fake: true }).then(stream => { 132 stream.getTracks().forEach(track => track.stop()); 133 return stream; 134 }); 135 }, 136 }, 137 /* 138 Disabled because MediaKeys rely on being destroyed by the CC before they 139 notify their window, so the test would intermittently fail depending on 140 when the CC runs. 141 142 { 143 name: "MSE", 144 prefs: msePrefs, 145 test: () => { 146 return new Promise((resolve) => { 147 const ms = new MediaSource(); 148 const el = document.createElement("video"); 149 ms.addEventListener("sourceopen", () => { resolve(el) }, { once: true }); 150 el.src = URL.createObjectURL(ms); 151 el.preload = "auto"; 152 }).then(el => { 153 el.src = ""; 154 return el; 155 }); 156 }, 157 }, 158 */ 159 ]; 160 161 162 163 function executeTest() { 164 165 let bc = new BroadcastChannel("bfcache_blocking"); 166 167 function promiseMessage(type) { 168 return new Promise((resolve) => { 169 bc.addEventListener("message", (e) => { 170 if (e.data.type == type) { 171 resolve(e.data); 172 } 173 }, { once: true }); 174 }); 175 } 176 177 function promisePageShow(shouldBePersisted) { 178 return promiseMessage("pageshow").then(data => data.persisted == shouldBePersisted); 179 } 180 181 function promisePageShowFromBFCache() { 182 return promisePageShow(true); 183 } 184 185 function promisePageShowNotFromBFCache() { 186 return promisePageShow(false); 187 } 188 189 function runTests(testArray, shouldBlockBFCache) { 190 for (const { name, prefs = {}, test, waitForDone } of testArray) { 191 add_task(async function() { 192 await SpecialPowers.pushPrefEnv(prefs, async function() { 193 // Load a mostly blank page that we can communicate with over 194 // BroadcastChannel (though it will close the BroadcastChannel after 195 // receiving the next "load" message, to avoid blocking BFCache). 196 let loaded = promisePageShowNotFromBFCache(); 197 window.open("file_blockBFCache.html", "", "noopener"); 198 await loaded; 199 200 // Load the same page with a different URL. 201 loaded = promisePageShowNotFromBFCache(); 202 bc.postMessage({ message: "load", url: `file_blockBFCache.html?${name}_${shouldBlockBFCache}` }); 203 await loaded; 204 205 // Run test script in the second page. 206 bc.postMessage({ message: "runScript", fun: test.toString() }); 207 await promiseMessage("runScriptDone"); 208 209 // Go back to the first page (this should just come from the BFCache). 210 let goneBack = promisePageShowFromBFCache(); 211 bc.postMessage({ message: "back" }); 212 await goneBack; 213 214 // Go forward again to the second page and check that it does/doesn't come 215 // from the BFCache. 216 let goneForward = promisePageShow(!shouldBlockBFCache); 217 bc.postMessage({ message: "forward" }); 218 let result = await goneForward; 219 ok(result, `Page ${shouldBlockBFCache ? "should" : "should not"} have been blocked from going into the BFCache (${name})`); 220 221 // If the test will keep running after navigation, then we need to make 222 // sure it's completely done before moving to the next test, to avoid 223 // interfering with any following tests. If waitForDone is defined then 224 // it'll return a Promise that we can use to wait for the end of the 225 // test. 226 if (waitForDone) { 227 await waitForDone(); 228 } 229 230 // Do a similar test, but replace the bfcache test page with a new page, 231 // not a page coming from the session history. 232 233 // Load the same page with a different URL. 234 loaded = promisePageShowNotFromBFCache(); 235 bc.postMessage({ message: "load", url: `file_blockBFCache.html?p2_${name}_${shouldBlockBFCache}` }); 236 await loaded; 237 238 // Run the test script. 239 bc.postMessage({ message: "runScript", fun: test.toString() }); 240 await promiseMessage("runScriptDone"); 241 242 // Load a new page. 243 loaded = promisePageShowNotFromBFCache(); 244 bc.postMessage({ message: "load", url: "file_blockBFCache.html" }); 245 await loaded; 246 247 // Go back to the previous page and check that it does/doesn't come 248 // from the BFCache. 249 goneBack = promisePageShow(!shouldBlockBFCache); 250 bc.postMessage({ message: "back" }); 251 result = await goneBack; 252 ok(result, `Page ${shouldBlockBFCache ? "should" : "should not"} have been blocked from going into the BFCache (${name})`); 253 254 if (waitForDone) { 255 await waitForDone(); 256 } 257 258 bc.postMessage({ message: "close" }); 259 260 SpecialPowers.popPrefEnv(); 261 }); 262 }); 263 } 264 } 265 266 // If Fission is disabled, the pref is no-op. 267 SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => { 268 runTests(blockBFCacheTests, true); 269 runTests(dontBlockBFCacheTests, false); 270 }); 271 } 272 273 if (isXOrigin) { 274 // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5) 275 // Acquire storage access permission here so that the BroadcastChannel used to 276 // communicate with the opened windows works in xorigin tests. Otherwise, 277 // the iframe containing this page is isolated from first-party storage access, 278 // which isolates BroadcastChannel communication. 279 SpecialPowers.wrap(document).notifyUserGestureActivation(); 280 SpecialPowers.addPermission("storageAccessAPI", true, window.location.href).then(() => { 281 SpecialPowers.wrap(document).requestStorageAccess().then(() => { 282 SpecialPowers.pushPrefEnv({ 283 set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]] 284 }).then(() => { 285 executeTest(); 286 }); 287 }); 288 }); 289 } else { 290 executeTest(); 291 } 292 293 </script> 294 </body> 295 </html>