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
|
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
'use strict';
function send_message_to_worker_and_wait_for_response(worker, message) {
return new Promise(resolve => {
// Use a dedicated channel for every request to avoid race conditions on
// concurrent requests.
const channel = new MessageChannel();
worker.postMessage(channel.port1, [channel.port1]);
let messageReceived = false;
channel.port2.onmessage = event => {
assert_false(messageReceived, 'Already received response for ' + message);
messageReceived = true;
resolve(event.data);
};
channel.port2.postMessage(message);
});
}
async function ensure_install_event_fired(worker) {
const response = await send_message_to_worker_and_wait_for_response(worker, 'awaitInstallEvent');
assert_equals('installEventFired', response);
assert_equals('installing', worker.state, 'Expected worker to be installing.');
}
async function finish_install(worker) {
await ensure_install_event_fired(worker);
const response = await send_message_to_worker_and_wait_for_response(worker, 'finishInstall');
assert_equals('installFinished', response);
}
async function activate_service_worker(t, worker) {
await finish_install(worker);
// By waiting for both states at the same time, the test fails
// quickly if the installation fails, avoiding a timeout.
await Promise.race([wait_for_state(t, worker, 'activated'),
wait_for_state(t, worker, 'redundant')]);
assert_equals('activated', worker.state, 'Service worker should be activated.');
}
async function update_within_service_worker(worker) {
// This function returns a Promise that resolves when update()
// has been called but is not necessarily finished yet.
// Call finish() on the returned object to wait for update() settle.
const port = await send_message_to_worker_and_wait_for_response(worker, 'callUpdate');
let messageReceived = false;
return {
finish: () => {
return new Promise(resolve => {
port.onmessage = event => {
assert_false(messageReceived, 'Update already finished.');
messageReceived = true;
resolve(event.data);
};
});
},
};
}
async function update_from_client_and_await_installing_version(test, registration) {
const updatefound = wait_for_update(test, registration);
registration.update();
await updatefound;
return registration.installing;
}
async function spin_up_service_worker(test) {
const script = 'resources/update-during-installation-worker.py';
const scope = 'resources/blank.html';
const registration = await service_worker_unregister_and_register(test, script, scope);
test.add_cleanup(async () => {
if (registration.installing) {
// If there is an installing worker, we need to finish installing it.
// Otherwise, the tests fails with an timeout because unregister() blocks
// until the install-event-handler finishes.
const worker = registration.installing;
await send_message_to_worker_and_wait_for_response(worker, 'awaitInstallEvent');
await send_message_to_worker_and_wait_for_response(worker, 'finishInstall');
}
return registration.unregister();
});
return registration;
}
promise_test(async t => {
const registration = await spin_up_service_worker(t);
const worker = registration.installing;
await ensure_install_event_fired(worker);
const result = registration.update();
await activate_service_worker(t, worker);
return result;
}, 'ServiceWorkerRegistration.update() from client succeeds while installing service worker.');
promise_test(async t => {
const registration = await spin_up_service_worker(t);
const worker = registration.installing;
await ensure_install_event_fired(worker);
// Add event listener to fail the test if update() succeeds.
const updatefound = t.step_func(async () => {
registration.removeEventListener('updatefound', updatefound);
// Activate new worker so non-compliant browsers don't fail with timeout.
await activate_service_worker(t, registration.installing);
assert_unreached("update() should have failed");
});
registration.addEventListener('updatefound', updatefound);
const update = await update_within_service_worker(worker);
// Activate worker to ensure update() finishes and the test doesn't timeout
// in non-compliant browsers.
await activate_service_worker(t, worker);
const response = await update.finish();
assert_false(response.success, 'update() should have failed.');
assert_equals('InvalidStateError', response.exception, 'update() should have thrown InvalidStateError.');
}, 'ServiceWorkerRegistration.update() from installing service worker throws.');
promise_test(async t => {
const registration = await spin_up_service_worker(t);
const worker1 = registration.installing;
await activate_service_worker(t, worker1);
const worker2 = await update_from_client_and_await_installing_version(t, registration);
await ensure_install_event_fired(worker2);
const update = await update_within_service_worker(worker1);
// Activate the new version so that update() finishes and the test doesn't timeout.
await activate_service_worker(t, worker2);
const response = await update.finish();
assert_true(response.success, 'update() from active service worker should have succeeded.');
}, 'ServiceWorkerRegistration.update() from active service worker succeeds while installing service worker.');
</script>
|