1 // This worker calls waitUntil() and respondWith() asynchronously and
2 // reports back to the test whether they threw.
4 // These test cases are confusing. Bear in mind that the event is active
5 // (calling waitUntil() is allowed) if:
6 // * The pending promise count is not 0, or
7 // * The event dispatch flag is set.
9 // Controlled by 'init'/'done' messages.
10 var resolveLockPromise;
13 self.addEventListener('message', function(event) {
15 var resolveTestPromise;
17 switch (event.data.step) {
19 event.waitUntil(new Promise((res) => { resolveLockPromise = res; }));
20 port = event.data.port;
26 // Throws because waitUntil() is called in a task after event dispatch
28 case 'no-current-extension-different-task':
29 async_task_waituntil(event).then(reportResultExpecting('InvalidStateError'));
32 // OK because waitUntil() is called in a microtask that runs after the
33 // event handler runs, while the event dispatch flag is still set.
34 case 'no-current-extension-different-microtask':
35 async_microtask_waituntil(event).then(reportResultExpecting('OK'));
38 // OK because the second waitUntil() is called while the first waitUntil()
39 // promise is still pending.
40 case 'current-extension-different-task':
41 event.waitUntil(new Promise((res) => { resolveTestPromise = res; }));
42 async_task_waituntil(event).then(reportResultExpecting('OK')).then(resolveTestPromise);
45 // OK because all promises involved resolve "immediately", so the second
46 // waitUntil() is called during the microtask checkpoint at the end of
47 // event dispatching, when the event dispatch flag is still set.
48 case 'during-event-dispatch-current-extension-expired-same-microtask-turn':
49 waitPromise = Promise.resolve();
50 event.waitUntil(waitPromise);
51 waitPromise.then(() => { return sync_waituntil(event); })
52 .then(reportResultExpecting('OK'))
55 // OK for the same reason as above.
56 case 'during-event-dispatch-current-extension-expired-same-microtask-turn-extra':
57 waitPromise = Promise.resolve();
58 event.waitUntil(waitPromise);
59 waitPromise.then(() => { return async_microtask_waituntil(event); })
60 .then(reportResultExpecting('OK'))
64 // OK because the pending promise count is decremented in a microtask
65 // queued upon fulfillment of the first waitUntil() promise, so the second
66 // waitUntil() is called while the pending promise count is still
68 case 'after-event-dispatch-current-extension-expired-same-microtask-turn':
69 waitPromise = makeNewTaskPromise();
70 event.waitUntil(waitPromise);
71 waitPromise.then(() => { return sync_waituntil(event); })
72 .then(reportResultExpecting('OK'))
75 // Throws because the second waitUntil() is called after the pending
76 // promise count was decremented to 0.
77 case 'after-event-dispatch-current-extension-expired-same-microtask-turn-extra':
78 waitPromise = makeNewTaskPromise();
79 event.waitUntil(waitPromise);
80 waitPromise.then(() => { return async_microtask_waituntil(event); })
81 .then(reportResultExpecting('InvalidStateError'))
84 // Throws because the second waitUntil() is called in a new task, after
85 // first waitUntil() promise settled and the event dispatch flag is unset.
86 case 'current-extension-expired-different-task':
87 event.waitUntil(Promise.resolve());
88 async_task_waituntil(event).then(reportResultExpecting('InvalidStateError'));
91 case 'script-extendable-event':
92 self.dispatchEvent(new ExtendableEvent('nontrustedevent'));
96 event.source.postMessage('ACK');
99 self.addEventListener('fetch', function(event) {
100 const path = new URL(event.request.url).pathname;
101 const step = path.substring(path.lastIndexOf('/') + 1);
104 // OK because waitUntil() is called while the respondWith() promise is still
105 // unsettled, so the pending promise count is positive.
106 case 'pending-respondwith-async-waituntil':
108 response = new Promise((res) => { resolveFetch = res; });
109 event.respondWith(response);
110 async_task_waituntil(event)
111 .then(reportResultExpecting('OK'))
112 .then(() => { resolveFetch(new Response('OK')); });
115 // OK because all promises involved resolve "immediately", so waitUntil() is
116 // called during the microtask checkpoint at the end of event dispatching,
117 // when the event dispatch flag is still set.
118 case 'during-event-dispatch-respondwith-microtask-sync-waituntil':
119 response = Promise.resolve(new Response('RESP'));
120 event.respondWith(response);
121 response.then(() => { return sync_waituntil(event); })
122 .then(reportResultExpecting('OK'));
125 // OK because all promises involved resolve "immediately", so waitUntil() is
126 // called during the microtask checkpoint at the end of event dispatching,
127 // when the event dispatch flag is still set.
128 case 'during-event-dispatch-respondwith-microtask-async-waituntil':
129 response = Promise.resolve(new Response('RESP'));
130 event.respondWith(response);
131 response.then(() => { return async_microtask_waituntil(event); })
132 .then(reportResultExpecting('OK'));
135 // OK because the pending promise count is decremented in a microtask queued
136 // upon fulfillment of the respondWith() promise, so waitUntil() is called
137 // while the pending promise count is still positive.
138 case 'after-event-dispatch-respondwith-microtask-sync-waituntil':
139 response = makeNewTaskPromise().then(() => {return new Response('RESP');});
140 event.respondWith(response);
141 response.then(() => { return sync_waituntil(event); })
142 .then(reportResultExpecting('OK'));
146 // Throws because waitUntil() is called after the pending promise count was
148 case 'after-event-dispatch-respondwith-microtask-async-waituntil':
149 response = makeNewTaskPromise().then(() => {return new Response('RESP');});
150 event.respondWith(response);
151 response.then(() => { return async_microtask_waituntil(event); })
152 .then(reportResultExpecting('InvalidStateError'))
157 self.addEventListener('nontrustedevent', function(event) {
158 sync_waituntil(event).then(reportResultExpecting('InvalidStateError'));
161 function reportResultExpecting(expectedResult) {
162 return function (result) {
163 port.postMessage({result : result, expected: expectedResult});
168 function sync_waituntil(event) {
169 return new Promise((res, rej) => {
171 event.waitUntil(Promise.resolve());
179 function async_microtask_waituntil(event) {
180 return new Promise((res, rej) => {
181 Promise.resolve().then(() => {
183 event.waitUntil(Promise.resolve());
192 function async_task_waituntil(event) {
193 return new Promise((res, rej) => {
196 event.waitUntil(Promise.resolve());
205 // Returns a promise that settles in a separate task.
206 function makeNewTaskPromise() {
207 return new Promise(resolve => {
208 setTimeout(resolve, 0);