summaryrefslogtreecommitdiffstats
path: root/dom/base/test/test_script_loader_js_cache.html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/base/test/test_script_loader_js_cache.html
parentInitial commit. (diff)
downloadfirefox-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/base/test/test_script_loader_js_cache.html')
-rw-r--r--dom/base/test/test_script_loader_js_cache.html264
1 files changed, 264 insertions, 0 deletions
diff --git a/dom/base/test/test_script_loader_js_cache.html b/dom/base/test/test_script_loader_js_cache.html
new file mode 100644
index 0000000000..de168ad109
--- /dev/null
+++ b/dom/base/test/test_script_loader_js_cache.html
@@ -0,0 +1,264 @@
+<!DOCTYPE html>
+<html>
+<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=900784 -->
+<!-- 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 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_evaluate_module": {
+ "scriptloader_encode": {
+ "scriptloader_bytecode_saved": "module_bytecode_saved",
+ "scriptloader_bytecode_failed": "module_bytecode_failed"
+ },
+ "scriptloader_no_encode": "module_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_evaluate_module": {
+ "scriptloader_encode": {
+ "scriptloader_bytecode_saved": "module_fallback_bytecode_saved",
+ "scriptloader_bytecode_failed": "module_fallback_bytecode_failed",
+ },
+ "scriptloader_no_encode": "module_fallback_source_exec",
+ },
+ }
+ },
+ "scriptloader_execute": "bytecode_exec",
+ "scriptloader_evaluate_module": "module_bytecode_exec",
+ }
+ };
+
+ 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_evaluate_module", 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;
+ }
+
+ async function basicTest(isModule) {
+ const prefix = isModule ? "module_" : "";
+ const name = isModule ? "module" : "script";
+
+ // 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.script_loader.bytecode_cache.enabled', true],
+ ['dom.expose_test_interfaces', true],
+ ['dom.script_loader.bytecode_cache.strategy', -1]
+ ]});
+
+ // 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_${prefix}js_cache.html`);
+ assert_equals(await stateMachineResult, `${prefix}bytecode_saved`,
+ `[1-${name}] 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_${prefix}js_cache.html`);
+ assert_equals(await stateMachineResult, `${prefix}bytecode_exec`,
+ `[2-${name}] 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_${prefix}js_cache_with_sri.html`);
+ assert_equals(await stateMachineResult, `${prefix}fallback_bytecode_saved`,
+ `[3-${name}] 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_${prefix}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_${prefix}js_cache.html`);
+
+ assert_equals(await stateMachineResult1, `${prefix}bytecode_exec`,
+ `[4-${name}] ScriptLoadRequest status after same SRI hash`);
+ assert_equals(await stateMachineResult2, `${prefix}bytecode_exec`,
+ `[5-${name}] ScriptLoadRequest status after visit with no SRI`);
+
+ if (!isModule) {
+ // Load a page that uses the same script as a module and verify that we
+ // re-parse it from source.
+ stateMachineResult = WaitForScriptTagEvent("file_js_cache_module.html");
+ assert_equals(await stateMachineResult, "module_bytecode_saved",
+ `[6-${name}] ScriptLoadRequest status for a module`);
+ } else {
+ // Load a page that uses the same module script as a regular script and
+ // verify that we re-parse it from source.
+ stateMachineResult = WaitForScriptTagEvent("file_module_js_cache_no_module.html");
+ assert_equals(await stateMachineResult, "bytecode_saved",
+ `[6-${name}] ScriptLoadRequest status for a script`);
+ }
+ }
+
+ promise_test(async function() {
+ await basicTest(false);
+ }, "Check the JS bytecode cache for script");
+
+ promise_test(async function() {
+ await basicTest(true);
+ }, "Check the JS bytecode cache for module");
+
+ promise_test(async function() {
+ // (see above)
+ await SpecialPowers.pushPrefEnv({set: [
+ ['dom.script_loader.bytecode_cache.enabled', true],
+ ['dom.expose_test_interfaces', true],
+ ['dom.script_loader.bytecode_cache.strategy', -1]
+ ]});
+
+ // 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");
+
+ }, "Save bytecode after the initialization of the page");
+
+ promise_test(async function() {
+ // (see above)
+ await SpecialPowers.pushPrefEnv({set: [
+ ['dom.script_loader.bytecode_cache.enabled', true],
+ ['dom.expose_test_interfaces', true],
+ ['dom.script_loader.bytecode_cache.strategy', -1]
+ ]});
+
+ // 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");
+
+ }, "Do not save bytecode on compilation errors");
+
+ promise_test(async function() {
+ // (see above)
+ await SpecialPowers.pushPrefEnv({set: [
+ ['dom.script_loader.bytecode_cache.enabled', true],
+ ['dom.expose_test_interfaces', true],
+ ['dom.script_loader.bytecode_cache.strategy', -1],
+ ['browser.cache.jsbc_compression_level', 2]
+ ]});
+
+ // 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 compressed bytecode.
+ var stateMachineResult = WaitForScriptTagEvent(`file_js_cache.html`);
+ assert_equals(await stateMachineResult, `bytecode_saved`,
+ `[1-script] 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 compressed bytecode, decompressing it, and executing it.
+ stateMachineResult = WaitForScriptTagEvent(`file_js_cache.html`);
+ assert_equals(await stateMachineResult, `bytecode_exec`,
+ `[2-script] ScriptLoadRequest status after the second visit`);
+ }, "Check the JS bytecode cache can save and load compressed bytecode");
+
+ done();
+ </script>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=900784">Mozilla Bug 900784</a>
+</body>
+</html>