diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/serviceworkers/test/test_script_loader_intercepted_js_cache.html | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/serviceworkers/test/test_script_loader_intercepted_js_cache.html')
-rw-r--r-- | dom/serviceworkers/test/test_script_loader_intercepted_js_cache.html | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/dom/serviceworkers/test/test_script_loader_intercepted_js_cache.html b/dom/serviceworkers/test/test_script_loader_intercepted_js_cache.html new file mode 100644 index 0000000000..d0073705bb --- /dev/null +++ b/dom/serviceworkers/test/test_script_loader_intercepted_js_cache.html @@ -0,0 +1,224 @@ +<!DOCTYPE html> +<html> +<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1350359 --> +<!-- The JS bytecode cache is not supposed to be observable. To make it + observable, the ScriptLoader is instrumented to trigger events on the + script tag. These events are followed to reconstruct the code path taken by + the script loader and associate a simple name which is checked in these + test cases. +--> +<head> + <meta charset="utf-8"> + <title>Test for saving and loading bytecode in/from the necko cache</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="utils.js"></script> + <script type="application/javascript"> + + // This is the state machine of the trace events produced by the + // ScriptLoader. This state machine is used to give a name to each + // code path, such that we can assert each code path with a single word. + var scriptLoaderStateMachine = { + "scriptloader_load_source": { + "scriptloader_execute": { + "scriptloader_encode": { + "scriptloader_bytecode_saved": "bytecode_saved", + "scriptloader_bytecode_failed": "bytecode_failed" + }, + "scriptloader_no_encode": "source_exec" + } + }, + "scriptloader_load_bytecode": { + "scriptloader_fallback": { + // Replicate the top-level state machine without + // "scriptloader_load_bytecode" transition. + "scriptloader_load_source": { + "scriptloader_execute": { + "scriptloader_encode": { + "scriptloader_bytecode_saved": "fallback_bytecode_saved", + "scriptloader_bytecode_failed": "fallback_bytecode_failed" + }, + "scriptloader_no_encode": "fallback_source_exec" + } + } + }, + "scriptloader_execute": "bytecode_exec" + } + }; + + var gScript = SpecialPowers. + loadChromeScript('http://mochi.test:8888/tests/dom/serviceworkers/test/file_js_cache_cleanup.js'); + + function WaitForScriptTagEvent(url) { + var iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + + var stateMachine = scriptLoaderStateMachine; + var stateHistory = []; + var stateMachineResolve, stateMachineReject; + var statePromise = new Promise((resolve, reject) => { + stateMachineResolve = resolve; + stateMachineReject = reject; + }); + var ping = 0; + + // Walk the script loader state machine with the emitted events. + function log_event(evt) { + // If we have multiple script tags in the loaded source, make sure + // we only watch a single one. + if (evt.target.id != "watchme") + return; + + dump("## ScriptLoader event: " + evt.type + "\n"); + stateHistory.push(evt.type) + if (typeof stateMachine == "object") + stateMachine = stateMachine[evt.type]; + if (typeof stateMachine == "string") { + // We arrived to a final state, report the name of it. + var result = stateMachine; + if (ping) { + result = `${result} & ping(=${ping})`; + } + stateMachineResolve(result); + } else if (stateMachine === undefined) { + // We followed an unknown transition, report the known history. + stateMachineReject(stateHistory); + } + } + + var iwin = iframe.contentWindow; + iwin.addEventListener("scriptloader_load_source", log_event); + iwin.addEventListener("scriptloader_load_bytecode", log_event); + iwin.addEventListener("scriptloader_generate_bytecode", log_event); + iwin.addEventListener("scriptloader_execute", log_event); + iwin.addEventListener("scriptloader_encode", log_event); + iwin.addEventListener("scriptloader_no_encode", log_event); + iwin.addEventListener("scriptloader_bytecode_saved", log_event); + iwin.addEventListener("scriptloader_bytecode_failed", log_event); + iwin.addEventListener("scriptloader_fallback", log_event); + iwin.addEventListener("ping", (evt) => { + ping += 1; + dump(`## Content event: ${evt.type} (=${ping})\n`); + }); + iframe.src = url; + + statePromise.then(() => { + document.body.removeChild(iframe); + }); + return statePromise; + } + + promise_test(async function() { + // Setting dom.expose_test_interfaces pref causes the + // nsScriptLoadRequest to fire event on script tags, with information + // about its internal state. The ScriptLoader source send events to + // trace these and resolve a promise with the path taken by the + // script loader. + // + // Setting dom.script_loader.bytecode_cache.strategy to -1 causes the + // nsScriptLoadRequest to force all the conditions necessary to make a + // script be saved as bytecode in the alternate data storage provided + // by the channel (necko cache). + await SpecialPowers.pushPrefEnv({set: [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ['dom.script_loader.bytecode_cache.enabled', true], + ['dom.expose_test_interfaces', true], + ['dom.script_loader.bytecode_cache.strategy', -1] + ]}); + + // Register the service worker that perform the pass-through fetch. + var registration = await navigator.serviceWorker + .register("fetch.js", {scope: "./"}); + let sw = registration.installing || registration.active; + + // wait for service worker be activated + await waitForState(sw, 'activated'); + + await testCheckTheJSBytecodeCache(); + await testSavebytecodeAfterTheInitializationOfThePage(); + await testDoNotSaveBytecodeOnCompilationErrors(); + + await registration.unregister(); + await teardown(); + }); + + function teardown() { + return new Promise((resolve, reject) => { + gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() { + gScript.removeMessageListener("teardown-complete", teardownCompleteHandler); + gScript.destroy(); + resolve(); + }); + gScript.sendAsyncMessage("teardown"); + }); + } + + async function testCheckTheJSBytecodeCache() { + dump("## Test: Check the JS bytecode cache\n"); + + // Load the test page, and verify that the code path taken by the + // nsScriptLoadRequest corresponds to the code path which is loading a + // source and saving it as bytecode. + var stateMachineResult = WaitForScriptTagEvent("file_js_cache.html"); + assert_equals(await stateMachineResult, "bytecode_saved", + "[1] ScriptLoadRequest status after the first visit"); + + // Reload the same test page, and verify that the code path taken by + // the nsScriptLoadRequest corresponds to the code path which is + // loading bytecode and executing it. + stateMachineResult = WaitForScriptTagEvent("file_js_cache.html"); + assert_equals(await stateMachineResult, "bytecode_exec", + "[2] ScriptLoadRequest status after the second visit"); + + // Load another page which loads the same script with an SRI, while + // the cached bytecode does not have any. This should fallback to + // loading the source before saving the bytecode once more. + stateMachineResult = WaitForScriptTagEvent("file_js_cache_with_sri.html"); + assert_equals(await stateMachineResult, "fallback_bytecode_saved", + "[3] ScriptLoadRequest status after the SRI hash"); + + // Loading a page, which has the same SRI should verify the SRI and + // continue by executing the bytecode. + var stateMachineResult1 = WaitForScriptTagEvent("file_js_cache_with_sri.html"); + + // Loading a page which does not have a SRI while we have one in the + // cache should not change anything. We should also be able to load + // the cache simultanesouly. + var stateMachineResult2 = WaitForScriptTagEvent("file_js_cache.html"); + + assert_equals(await stateMachineResult1, "bytecode_exec", + "[4] ScriptLoadRequest status after same SRI hash"); + assert_equals(await stateMachineResult2, "bytecode_exec", + "[5] ScriptLoadRequest status after visit with no SRI"); + } + + async function testSavebytecodeAfterTheInitializationOfThePage() { + dump("## Test: Save bytecode after the initialization of the page"); + + // The test page add a new script which generate a "ping" event, which + // should be recorded before the bytecode is stored in the cache. + var stateMachineResult = + WaitForScriptTagEvent("file_js_cache_save_after_load.html"); + assert_equals(await stateMachineResult, "bytecode_saved & ping(=3)", + "Wait on all scripts to be executed"); + } + + async function testDoNotSaveBytecodeOnCompilationErrors() { + dump("## Test: Do not save bytecode on compilation errors"); + + // The test page loads a script which contains a syntax error, we should + // not attempt to encode any bytecode for it. + var stateMachineResult = + WaitForScriptTagEvent("file_js_cache_syntax_error.html"); + assert_equals(await stateMachineResult, "source_exec", + "Check the lack of bytecode encoding"); + } + + done(); + </script> +</head> +<body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1350359">Mozilla Bug 1350359</a> +</body> +</html> |