Skip to content
This repository was archived by the owner on Mar 19, 2021. It is now read-only.

Commit 1875674

Browse files
perryjiangjgraham
authored andcommitted
Add test for handling of "pending" fetch events
This patch factors out some of the initial test case in fetch-waits-for-activate.https.html (which only tests "pending" fetch events for a navigation request) to share with a new test case that tests "pending" fetch events for a subresource request with a request body. Both tests in the file have a high-level structure of: 1) Register a Service Worker and wait until its state is "activating" but don't let its state reach "activated". 2) Fire a fetch event that will be controlled by that Service Worker, which should wait until the Service Worker's state advances to "activated". 3) Wait for the fetch to see that the worker isn't "activated". This step isn't directly observable by content, so the test's method to determine this can have false posities (but should never cause the test to unexpectedly fail). 4) Tell the Service Worker to advance to "activated". 5) Verify the fetch that was dispatched while the Service Worker was "activating" is successfully handled. Differential Revision: https://phabricator.services.mozilla.com/D49031 bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1578919 gecko-commit: 0464faa83b959a5f00f2e6e7951d2818f419c6ac gecko-integration-branch: autoland gecko-reviewers: asuth
1 parent 26ff9e8 commit 1875674

File tree

2 files changed

+130
-54
lines changed

2 files changed

+130
-54
lines changed

service-workers/service-worker/fetch-waits-for-activate.https.html

Lines changed: 113 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,58 +9,120 @@
99
<body>
1010
<script>
1111

12-
var worker = 'resources/fetch-waits-for-activate-worker.js';
13-
var expected_url = normalizeURL(worker);
14-
var scope = 'resources/fetch-waits-for-activate/';
15-
16-
promise_test(function(t) {
17-
var registration;
18-
var frameLoadPromise;
19-
var frame;
20-
return service_worker_unregister_and_register(t, worker, scope).then(function(reg) {
21-
t.add_cleanup(function() {
22-
return service_worker_unregister(t, scope);
23-
});
24-
25-
registration = reg;
26-
return wait_for_state(t, reg.installing, 'activating');
27-
}).then(function() {
28-
assert_equals(registration.active.scriptURL, expected_url,
29-
'active worker should be present');
30-
assert_equals(registration.active.state, 'activating',
31-
'active worker should be in activating state');
32-
33-
// This should block until we message the worker to tell it to complete
34-
// the activate event.
35-
frameLoadPromise = with_iframe(scope).then(function(f) {
36-
frame = f;
37-
});
38-
39-
// Wait some time to allow frame loading to proceed. It should not,
40-
// however, if the fetch event is blocked on the activate. I don't
41-
// see any way to force this race without a timeout, unfortunately.
42-
return new Promise(function(resolve) {
43-
setTimeout(resolve, 1000);
44-
});
45-
}).then(function() {
46-
assert_equals(frame, undefined, 'frame should not be loaded');
47-
assert_equals(registration.active.scriptURL, expected_url,
48-
'active worker should be present');
49-
assert_equals(registration.active.state, 'activating',
50-
'active worker should be in activating state');
51-
52-
// This signals the activate event to complete. The frame should now
53-
// load.
54-
registration.active.postMessage('GO');
55-
return frameLoadPromise;
56-
}).then(function() {
57-
assert_equals(frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
58-
expected_url, 'frame should now be loaded and controlled');
59-
assert_equals(registration.active.state, 'activated',
60-
'active worker should be in activated state');
61-
frame.remove();
12+
const worker_url = 'resources/fetch-waits-for-activate-worker.js';
13+
const normalized_worker_url = normalizeURL(worker_url);
14+
const worker_scope = 'resources/fetch-waits-for-activate/';
15+
16+
// Resolves with the Service Worker's registration once it's reached the
17+
// "activating" state. (The Service Worker should remain "activating" until
18+
// explicitly told advance to the "activated" state).
19+
async function registerAndWaitForActivating(t) {
20+
const registration = await service_worker_unregister_and_register(
21+
t, worker_url, worker_scope);
22+
t.add_cleanup(() => service_worker_unregister(t, worker_scope));
23+
24+
await wait_for_state(t, registration.installing, 'activating');
25+
26+
return registration;
27+
}
28+
29+
// Attempts to ensure that the "Handle Fetch" algorithm has reached the step
30+
//
31+
// "If activeWorker’s state is "activating", wait for activeWorker’s state to
32+
// become "activated"."
33+
//
34+
// by waiting for some time to pass.
35+
//
36+
// WARNING: whether the algorithm has reached that step isn't directly
37+
// observable, so this is best effort and can race. Note that this can only
38+
// result in false positives (where the algorithm hasn't reached that step yet
39+
// and any functional events haven't actually been handled by the Service
40+
// Worker).
41+
async function ensureFunctionalEventsAreWaiting(registration) {
42+
await (new Promise(resolve => { setTimeout(resolve, 1000); }));
43+
44+
assert_equals(registration.active.scriptURL, normalized_worker_url,
45+
'active worker should be present');
46+
assert_equals(registration.active.state, 'activating',
47+
'active worker should be in activating state');
48+
}
49+
50+
promise_test(async t => {
51+
const registration = await registerAndWaitForActivating(t);
52+
53+
let frame = null;
54+
t.add_cleanup(() => {
55+
if (frame) {
56+
frame.remove();
57+
}
58+
});
59+
60+
// This should block until we message the worker to tell it to complete
61+
// the activate event.
62+
const frameLoadPromise = with_iframe(worker_scope).then(function(f) {
63+
frame = f;
64+
});
65+
66+
await ensureFunctionalEventsAreWaiting(registration);
67+
assert_equals(frame, null, 'frame should not be loaded');
68+
69+
registration.active.postMessage('ACTIVATE');
70+
71+
await frameLoadPromise;
72+
assert_equals(frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
73+
normalized_worker_url,
74+
'frame should now be loaded and controlled');
75+
assert_equals(registration.active.state, 'activated',
76+
'active worker should be in activated state');
77+
}, 'Navigation fetch events should wait for the activate event to complete.');
78+
79+
promise_test(async t => {
80+
const frame = await with_iframe(worker_scope);
81+
t.add_cleanup(() => { frame.remove(); });
82+
83+
const registration = await registerAndWaitForActivating(t);
84+
85+
// Make the Service Worker control the frame so the frame can perform an
86+
// intercepted fetch.
87+
await (new Promise(resolve => {
88+
navigator.serviceWorker.onmessage = e => {
89+
assert_equals(
90+
frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
91+
normalized_worker_url, 'frame should be controlled');
92+
resolve();
93+
};
94+
95+
registration.active.postMessage('CLAIM');
96+
}));
97+
98+
const fetch_url = `${worker_scope}non/existent/path`;
99+
const expected_fetch_result = 'Hello world';
100+
let fetch_promise_settled = false;
101+
102+
// This should block until we message the worker to tell it to complete
103+
// the activate event.
104+
const fetchPromise = frame.contentWindow.fetch(fetch_url, {
105+
method: 'POST',
106+
body: expected_fetch_result,
107+
}).then(response => {
108+
fetch_promise_settled = true;
109+
return response;
62110
});
63-
}, 'Fetch events should wait for the activate event to complete.');
111+
112+
await ensureFunctionalEventsAreWaiting(registration);
113+
assert_false(fetch_promise_settled,
114+
"fetch()-ing a Service Worker-controlled scope shouldn't have " +
115+
"settled yet");
116+
117+
registration.active.postMessage('ACTIVATE');
118+
119+
const response = await fetchPromise;
120+
assert_equals(await response.text(), expected_fetch_result,
121+
"Service Worker should have responded to request to" +
122+
fetch_url)
123+
assert_equals(registration.active.state, 'activated',
124+
'active worker should be in activated state');
125+
}, 'Subresource fetch events should wait for the activate event to complete.');
64126

65127
</script>
66128
</body>

service-workers/service-worker/resources/fetch-waits-for-activate-worker.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,23 @@ addEventListener('activate', function(evt) {
66
}));
77
});
88

9-
addEventListener('message', function(evt) {
10-
if (typeof activatePromiseResolve === 'function') {
11-
activatePromiseResolve();
9+
addEventListener('message', async function(evt) {
10+
switch (evt.data) {
11+
case 'CLAIM':
12+
evt.waitUntil(new Promise(async resolve => {
13+
await clients.claim();
14+
evt.source.postMessage('CLAIMED');
15+
resolve();
16+
}));
17+
break;
18+
case 'ACTIVATE':
19+
if (typeof activatePromiseResolve !== 'function') {
20+
throw new Error('Not activating!');
21+
}
22+
activatePromiseResolve();
23+
break;
24+
default:
25+
throw new Error('Unknown message!');
1226
}
1327
});
1428

0 commit comments

Comments
 (0)