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