update-not-allowed.https.html (5643B)
1 <!DOCTYPE html> 2 <script src="/resources/testharness.js"></script> 3 <script src="/resources/testharnessreport.js"></script> 4 <script src="resources/test-helpers.sub.js"></script> 5 <script> 6 'use strict'; 7 8 function send_message_to_worker_and_wait_for_response(worker, message) { 9 return new Promise(resolve => { 10 // Use a dedicated channel for every request to avoid race conditions on 11 // concurrent requests. 12 const channel = new MessageChannel(); 13 worker.postMessage(channel.port1, [channel.port1]); 14 15 let messageReceived = false; 16 channel.port2.onmessage = event => { 17 assert_false(messageReceived, 'Already received response for ' + message); 18 messageReceived = true; 19 resolve(event.data); 20 }; 21 channel.port2.postMessage(message); 22 }); 23 } 24 25 async function ensure_install_event_fired(worker) { 26 const response = await send_message_to_worker_and_wait_for_response(worker, 'awaitInstallEvent'); 27 assert_equals('installEventFired', response); 28 assert_equals('installing', worker.state, 'Expected worker to be installing.'); 29 } 30 31 async function finish_install(worker) { 32 await ensure_install_event_fired(worker); 33 const response = await send_message_to_worker_and_wait_for_response(worker, 'finishInstall'); 34 assert_equals('installFinished', response); 35 } 36 37 async function activate_service_worker(t, worker) { 38 await finish_install(worker); 39 // By waiting for both states at the same time, the test fails 40 // quickly if the installation fails, avoiding a timeout. 41 await Promise.race([wait_for_state(t, worker, 'activated'), 42 wait_for_state(t, worker, 'redundant')]); 43 assert_equals('activated', worker.state, 'Service worker should be activated.'); 44 } 45 46 async function update_within_service_worker(worker) { 47 // This function returns a Promise that resolves when update() 48 // has been called but is not necessarily finished yet. 49 // Call finish() on the returned object to wait for update() settle. 50 const port = await send_message_to_worker_and_wait_for_response(worker, 'callUpdate'); 51 let messageReceived = false; 52 return { 53 finish: () => { 54 return new Promise(resolve => { 55 port.onmessage = event => { 56 assert_false(messageReceived, 'Update already finished.'); 57 messageReceived = true; 58 resolve(event.data); 59 }; 60 }); 61 }, 62 }; 63 } 64 65 async function update_from_client_and_await_installing_version(test, registration) { 66 const updatefound = wait_for_update(test, registration); 67 registration.update(); 68 await updatefound; 69 return registration.installing; 70 } 71 72 async function spin_up_service_worker(test) { 73 const script = 'resources/update-during-installation-worker.py'; 74 const scope = 'resources/blank.html'; 75 76 const registration = await service_worker_unregister_and_register(test, script, scope); 77 test.add_cleanup(async () => { 78 if (registration.installing) { 79 // If there is an installing worker, we need to finish installing it. 80 // Otherwise, the tests fails with an timeout because unregister() blocks 81 // until the install-event-handler finishes. 82 const worker = registration.installing; 83 await send_message_to_worker_and_wait_for_response(worker, 'awaitInstallEvent'); 84 await send_message_to_worker_and_wait_for_response(worker, 'finishInstall'); 85 } 86 return registration.unregister(); 87 }); 88 89 return registration; 90 } 91 92 promise_test(async t => { 93 const registration = await spin_up_service_worker(t); 94 const worker = registration.installing; 95 await ensure_install_event_fired(worker); 96 97 const result = registration.update(); 98 await activate_service_worker(t, worker); 99 return result; 100 }, 'ServiceWorkerRegistration.update() from client succeeds while installing service worker.'); 101 102 promise_test(async t => { 103 const registration = await spin_up_service_worker(t); 104 const worker = registration.installing; 105 await ensure_install_event_fired(worker); 106 107 // Add event listener to fail the test if update() succeeds. 108 const updatefound = t.step_func(async () => { 109 registration.removeEventListener('updatefound', updatefound); 110 // Activate new worker so non-compliant browsers don't fail with timeout. 111 await activate_service_worker(t, registration.installing); 112 assert_unreached("update() should have failed"); 113 }); 114 registration.addEventListener('updatefound', updatefound); 115 116 const update = await update_within_service_worker(worker); 117 // Activate worker to ensure update() finishes and the test doesn't timeout 118 // in non-compliant browsers. 119 await activate_service_worker(t, worker); 120 121 const response = await update.finish(); 122 assert_false(response.success, 'update() should have failed.'); 123 assert_equals('InvalidStateError', response.exception, 'update() should have thrown InvalidStateError.'); 124 }, 'ServiceWorkerRegistration.update() from installing service worker throws.'); 125 126 promise_test(async t => { 127 const registration = await spin_up_service_worker(t); 128 const worker1 = registration.installing; 129 await activate_service_worker(t, worker1); 130 131 const worker2 = await update_from_client_and_await_installing_version(t, registration); 132 await ensure_install_event_fired(worker2); 133 134 const update = await update_within_service_worker(worker1); 135 // Activate the new version so that update() finishes and the test doesn't timeout. 136 await activate_service_worker(t, worker2); 137 const response = await update.finish(); 138 assert_true(response.success, 'update() from active service worker should have succeeded.'); 139 }, 'ServiceWorkerRegistration.update() from active service worker succeeds while installing service worker.'); 140 </script>