test_abrupt_completion.html (4815B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title></title> 4 <script src="/tests/SimpleTest/SimpleTest.js"></script> 5 <script> 6 7 // Tests a _registered_ ServiceWorker whose script evaluation results in an 8 // "abrupt completion", e.g. threw an uncaught exception. Such a ServiceWorker's 9 // first script evaluation must result in a "normal completion", however, for 10 // the Update algorithm to not abort in its step 18 when registering: 11 // 12 // 18. If runResult is failure or an abrupt completion, then: [...] 13 14 const script = "./abrupt_completion_worker.js"; 15 const scope = "./empty.html"; 16 const expectedMessage = "handler-before-throw"; 17 let registration = null; 18 19 // Should only be called once registration.active is non-null. Uses 20 // implementation details by zero-ing the "idle timeout"s and then sending an 21 // event to the ServiceWorker, which should immediately cause its termination. 22 // The idle timeouts are restored after the ServiceWorker is terminated. 23 async function startAndStopServiceWorker() { 24 SpecialPowers.registerObservers("service-worker-shutdown"); 25 26 const spTopic = "specialpowers-service-worker-shutdown"; 27 28 const origIdleTimeout = 29 SpecialPowers.getIntPref("dom.serviceWorkers.idle_timeout"); 30 31 const origIdleExtendedTimeout = 32 SpecialPowers.getIntPref("dom.serviceWorkers.idle_extended_timeout"); 33 34 await new Promise(resolve => { 35 const observer = { 36 async observe(subject, topic, data) { 37 if (topic !== spTopic) { 38 return; 39 } 40 41 SpecialPowers.removeObserver(observer, spTopic); 42 43 await SpecialPowers.pushPrefEnv({ 44 set: [ 45 ["dom.serviceWorkers.idle_timeout", origIdleTimeout], 46 ["dom.serviceWorkers.idle_extended_timeout", origIdleExtendedTimeout] 47 ] 48 }); 49 50 resolve(); 51 }, 52 }; 53 54 // Speed things up. 55 SpecialPowers.pushPrefEnv({ 56 set: [ 57 ["dom.serviceWorkers.idle_timeout", 0], 58 ["dom.serviceWorkers.idle_extended_timeout", 0] 59 ] 60 }).then(() => { 61 SpecialPowers.addObserver(observer, spTopic); 62 63 registration.active.postMessage(""); 64 }); 65 }); 66 } 67 68 // eslint-disable-next-line mozilla/no-addtask-setup 69 add_task(async function setup() { 70 await SpecialPowers.pushPrefEnv({ 71 set: [ 72 ["dom.serviceWorkers.enabled", true], 73 ["dom.serviceWorkers.testing.enabled", true] 74 ] 75 }); 76 77 registration = await navigator.serviceWorker.register(script, { scope }); 78 SimpleTest.registerCleanupFunction(async function unregisterRegistration() { 79 await registration.unregister(); 80 }); 81 82 await new Promise(resolve => { 83 const serviceWorker = registration.installing; 84 85 serviceWorker.onstatechange = () => { 86 if (serviceWorker.state === "activated") { 87 resolve(); 88 } 89 }; 90 }); 91 92 ok(registration.active instanceof ServiceWorker, "ServiceWorker is activated"); 93 }); 94 95 // We expect that the restarted SW that experiences an abrupt completion at 96 // startup after adding its message handler 1) will be active in order to 97 // respond to our postMessage and 2) will respond with the global value set 98 // prior to the importScripts call that throws (and not the global value that 99 // would have been assigned after the importScripts call if it didn't throw). 100 add_task(async function testMessageHandler() { 101 await startAndStopServiceWorker(); 102 103 await new Promise(resolve => { 104 navigator.serviceWorker.onmessage = e => { 105 is(e.data, expectedMessage, "Correct message handler"); 106 resolve(); 107 }; 108 registration.active.postMessage(""); 109 }); 110 }); 111 112 // We expect that the restarted SW that experiences an abrupt completion at 113 // startup before adding its "fetch" listener will 1) successfully dispatch the 114 // event and 2) it will not be handled (respondWith() will not be called) so 115 // interception will be reset and the response will contain the contents of 116 // empty.html. Before the fix in bug 1603484 the SW would fail to properly start 117 // up and the fetch event would result in a NetworkError, breaking the 118 // controlled page. 119 add_task(async function testFetchHandler() { 120 await startAndStopServiceWorker(); 121 122 const iframe = document.createElement("iframe"); 123 SimpleTest.registerCleanupFunction(function removeIframe() { 124 iframe.remove(); 125 }); 126 127 await new Promise(resolve => { 128 iframe.src = scope; 129 iframe.onload = resolve; 130 document.body.appendChild(iframe); 131 }); 132 133 const response = await iframe.contentWindow.fetch(scope); 134 135 // NetworkError will have a status of 0, which is not "ok", and this is 136 // a stronger guarantee that should be true instead of just checking if there 137 // isn't a NetworkError. 138 ok(response.ok, "Fetch succeeded and didn't result in a NetworkError"); 139 140 const text = await response.text(); 141 is(text, "", "Correct response text"); 142 }); 143 144 </script>