summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js')
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js210
1 files changed, 210 insertions, 0 deletions
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js b/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
new file mode 100644
index 0000000000..8a975b0d2e
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
@@ -0,0 +1,210 @@
+// This worker calls waitUntil() and respondWith() asynchronously and
+// reports back to the test whether they threw.
+//
+// These test cases are confusing. Bear in mind that the event is active
+// (calling waitUntil() is allowed) if:
+// * The pending promise count is not 0, or
+// * The event dispatch flag is set.
+
+// Controlled by 'init'/'done' messages.
+var resolveLockPromise;
+var port;
+
+self.addEventListener('message', function(event) {
+ var waitPromise;
+ var resolveTestPromise;
+
+ switch (event.data.step) {
+ case 'init':
+ event.waitUntil(new Promise((res) => { resolveLockPromise = res; }));
+ port = event.data.port;
+ break;
+ case 'done':
+ resolveLockPromise();
+ break;
+
+ // Throws because waitUntil() is called in a task after event dispatch
+ // finishes.
+ case 'no-current-extension-different-task':
+ async_task_waituntil(event).then(reportResultExpecting('InvalidStateError'));
+ break;
+
+ // OK because waitUntil() is called in a microtask that runs after the
+ // event handler runs, while the event dispatch flag is still set.
+ case 'no-current-extension-different-microtask':
+ async_microtask_waituntil(event).then(reportResultExpecting('OK'));
+ break;
+
+ // OK because the second waitUntil() is called while the first waitUntil()
+ // promise is still pending.
+ case 'current-extension-different-task':
+ event.waitUntil(new Promise((res) => { resolveTestPromise = res; }));
+ async_task_waituntil(event).then(reportResultExpecting('OK')).then(resolveTestPromise);
+ break;
+
+ // OK because all promises involved resolve "immediately", so the second
+ // waitUntil() is called during the microtask checkpoint at the end of
+ // event dispatching, when the event dispatch flag is still set.
+ case 'during-event-dispatch-current-extension-expired-same-microtask-turn':
+ waitPromise = Promise.resolve();
+ event.waitUntil(waitPromise);
+ waitPromise.then(() => { return sync_waituntil(event); })
+ .then(reportResultExpecting('OK'))
+ break;
+
+ // OK for the same reason as above.
+ case 'during-event-dispatch-current-extension-expired-same-microtask-turn-extra':
+ waitPromise = Promise.resolve();
+ event.waitUntil(waitPromise);
+ waitPromise.then(() => { return async_microtask_waituntil(event); })
+ .then(reportResultExpecting('OK'))
+ break;
+
+
+ // OK because the pending promise count is decremented in a microtask
+ // queued upon fulfillment of the first waitUntil() promise, so the second
+ // waitUntil() is called while the pending promise count is still
+ // positive.
+ case 'after-event-dispatch-current-extension-expired-same-microtask-turn':
+ waitPromise = makeNewTaskPromise();
+ event.waitUntil(waitPromise);
+ waitPromise.then(() => { return sync_waituntil(event); })
+ .then(reportResultExpecting('OK'))
+ break;
+
+ // Throws because the second waitUntil() is called after the pending
+ // promise count was decremented to 0.
+ case 'after-event-dispatch-current-extension-expired-same-microtask-turn-extra':
+ waitPromise = makeNewTaskPromise();
+ event.waitUntil(waitPromise);
+ waitPromise.then(() => { return async_microtask_waituntil(event); })
+ .then(reportResultExpecting('InvalidStateError'))
+ break;
+
+ // Throws because the second waitUntil() is called in a new task, after
+ // first waitUntil() promise settled and the event dispatch flag is unset.
+ case 'current-extension-expired-different-task':
+ event.waitUntil(Promise.resolve());
+ async_task_waituntil(event).then(reportResultExpecting('InvalidStateError'));
+ break;
+
+ case 'script-extendable-event':
+ self.dispatchEvent(new ExtendableEvent('nontrustedevent'));
+ break;
+ }
+
+ event.source.postMessage('ACK');
+ });
+
+self.addEventListener('fetch', function(event) {
+ const path = new URL(event.request.url).pathname;
+ const step = path.substring(path.lastIndexOf('/') + 1);
+ let response;
+ switch (step) {
+ // OK because waitUntil() is called while the respondWith() promise is still
+ // unsettled, so the pending promise count is positive.
+ case 'pending-respondwith-async-waituntil':
+ var resolveFetch;
+ response = new Promise((res) => { resolveFetch = res; });
+ event.respondWith(response);
+ async_task_waituntil(event)
+ .then(reportResultExpecting('OK'))
+ .then(() => { resolveFetch(new Response('OK')); });
+ break;
+
+ // OK because all promises involved resolve "immediately", so waitUntil() is
+ // called during the microtask checkpoint at the end of event dispatching,
+ // when the event dispatch flag is still set.
+ case 'during-event-dispatch-respondwith-microtask-sync-waituntil':
+ response = Promise.resolve(new Response('RESP'));
+ event.respondWith(response);
+ response.then(() => { return sync_waituntil(event); })
+ .then(reportResultExpecting('OK'));
+ break;
+
+ // OK because all promises involved resolve "immediately", so waitUntil() is
+ // called during the microtask checkpoint at the end of event dispatching,
+ // when the event dispatch flag is still set.
+ case 'during-event-dispatch-respondwith-microtask-async-waituntil':
+ response = Promise.resolve(new Response('RESP'));
+ event.respondWith(response);
+ response.then(() => { return async_microtask_waituntil(event); })
+ .then(reportResultExpecting('OK'));
+ break;
+
+ // OK because the pending promise count is decremented in a microtask queued
+ // upon fulfillment of the respondWith() promise, so waitUntil() is called
+ // while the pending promise count is still positive.
+ case 'after-event-dispatch-respondwith-microtask-sync-waituntil':
+ response = makeNewTaskPromise().then(() => {return new Response('RESP');});
+ event.respondWith(response);
+ response.then(() => { return sync_waituntil(event); })
+ .then(reportResultExpecting('OK'));
+ break;
+
+
+ // Throws because waitUntil() is called after the pending promise count was
+ // decremented to 0.
+ case 'after-event-dispatch-respondwith-microtask-async-waituntil':
+ response = makeNewTaskPromise().then(() => {return new Response('RESP');});
+ event.respondWith(response);
+ response.then(() => { return async_microtask_waituntil(event); })
+ .then(reportResultExpecting('InvalidStateError'))
+ break;
+ }
+});
+
+self.addEventListener('nontrustedevent', function(event) {
+ sync_waituntil(event).then(reportResultExpecting('InvalidStateError'));
+ });
+
+function reportResultExpecting(expectedResult) {
+ return function (result) {
+ port.postMessage({result : result, expected: expectedResult});
+ return result;
+ };
+}
+
+function sync_waituntil(event) {
+ return new Promise((res, rej) => {
+ try {
+ event.waitUntil(Promise.resolve());
+ res('OK');
+ } catch (error) {
+ res(error.name);
+ }
+ });
+}
+
+function async_microtask_waituntil(event) {
+ return new Promise((res, rej) => {
+ Promise.resolve().then(() => {
+ try {
+ event.waitUntil(Promise.resolve());
+ res('OK');
+ } catch (error) {
+ res(error.name);
+ }
+ });
+ });
+}
+
+function async_task_waituntil(event) {
+ return new Promise((res, rej) => {
+ setTimeout(() => {
+ try {
+ event.waitUntil(Promise.resolve());
+ res('OK');
+ } catch (error) {
+ res(error.name);
+ }
+ }, 0);
+ });
+}
+
+// Returns a promise that settles in a separate task.
+function makeNewTaskPromise() {
+ return new Promise(resolve => {
+ setTimeout(resolve, 0);
+ });
+}