summaryrefslogtreecommitdiffstats
path: root/dom/serviceworkers/test/test_script_loader_intercepted_js_cache.html
diff options
context:
space:
mode:
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.html224
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>