| <!DOCTYPE html> |
| <title>Service Worker: Fetch Event Waits for Activate Event</title> |
| <meta name=timeout content=long> |
| <script src="/resources/testharness.js"></script> |
| <script src="resources/testharness-helpers.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/common/get-host-info.sub.js"></script> |
| <script src="resources/test-helpers.sub.js"></script> |
| <body> |
| <script> |
| |
| const worker_url = 'resources/fetch-waits-for-activate-worker.js'; |
| const normalized_worker_url = normalizeURL(worker_url); |
| const worker_scope = 'resources/fetch-waits-for-activate/'; |
| |
| // Resolves with the Service Worker's registration once it's reached the |
| // "activating" state. (The Service Worker should remain "activating" until |
| // explicitly told advance to the "activated" state). |
| async function registerAndWaitForActivating(t) { |
| const registration = await service_worker_unregister_and_register( |
| t, worker_url, worker_scope); |
| t.add_cleanup(() => service_worker_unregister(t, worker_scope)); |
| |
| await wait_for_state(t, registration.installing, 'activating'); |
| |
| return registration; |
| } |
| |
| // Attempts to ensure that the "Handle Fetch" algorithm has reached the step |
| // |
| // "If activeWorker’s state is "activating", wait for activeWorker’s state to |
| // become "activated"." |
| // |
| // by waiting for some time to pass. |
| // |
| // WARNING: whether the algorithm has reached that step isn't directly |
| // observable, so this is best effort and can race. Note that this can only |
| // result in false positives (where the algorithm hasn't reached that step yet |
| // and any functional events haven't actually been handled by the Service |
| // Worker). |
| async function ensureFunctionalEventsAreWaiting(registration) { |
| await (new Promise(resolve => { setTimeout(resolve, 1000); })); |
| |
| assert_equals(registration.active.scriptURL, normalized_worker_url, |
| 'active worker should be present'); |
| assert_equals(registration.active.state, 'activating', |
| 'active worker should be in activating state'); |
| } |
| |
| promise_test(async t => { |
| const registration = await registerAndWaitForActivating(t); |
| |
| let frame = null; |
| t.add_cleanup(() => { |
| if (frame) { |
| frame.remove(); |
| } |
| }); |
| |
| // This should block until we message the worker to tell it to complete |
| // the activate event. |
| const frameLoadPromise = with_iframe(worker_scope).then(function(f) { |
| frame = f; |
| }); |
| |
| await ensureFunctionalEventsAreWaiting(registration); |
| assert_equals(frame, null, 'frame should not be loaded'); |
| |
| registration.active.postMessage('ACTIVATE'); |
| |
| await frameLoadPromise; |
| assert_equals(frame.contentWindow.navigator.serviceWorker.controller.scriptURL, |
| normalized_worker_url, |
| 'frame should now be loaded and controlled'); |
| assert_equals(registration.active.state, 'activated', |
| 'active worker should be in activated state'); |
| }, 'Navigation fetch events should wait for the activate event to complete.'); |
| |
| promise_test(async t => { |
| const frame = await with_iframe(worker_scope); |
| t.add_cleanup(() => { frame.remove(); }); |
| |
| const registration = await registerAndWaitForActivating(t); |
| |
| // Make the Service Worker control the frame so the frame can perform an |
| // intercepted fetch. |
| await (new Promise(resolve => { |
| navigator.serviceWorker.onmessage = e => { |
| assert_equals( |
| frame.contentWindow.navigator.serviceWorker.controller.scriptURL, |
| normalized_worker_url, 'frame should be controlled'); |
| resolve(); |
| }; |
| |
| registration.active.postMessage('CLAIM'); |
| })); |
| |
| const fetch_url = `${worker_scope}non/existent/path`; |
| const expected_fetch_result = 'Hello world'; |
| let fetch_promise_settled = false; |
| |
| // This should block until we message the worker to tell it to complete |
| // the activate event. |
| const fetchPromise = frame.contentWindow.fetch(fetch_url, { |
| method: 'POST', |
| body: expected_fetch_result, |
| }).then(response => { |
| fetch_promise_settled = true; |
| return response; |
| }); |
| |
| await ensureFunctionalEventsAreWaiting(registration); |
| assert_false(fetch_promise_settled, |
| "fetch()-ing a Service Worker-controlled scope shouldn't have " + |
| "settled yet"); |
| |
| registration.active.postMessage('ACTIVATE'); |
| |
| const response = await fetchPromise; |
| assert_equals(await response.text(), expected_fetch_result, |
| "Service Worker should have responded to request to" + |
| fetch_url) |
| assert_equals(registration.active.state, 'activated', |
| 'active worker should be in activated state'); |
| }, 'Subresource fetch events should wait for the activate event to complete.'); |
| |
| </script> |
| </body> |