diff options
Diffstat (limited to 'testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html')
-rw-r--r-- | testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html b/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html new file mode 100644 index 0000000000..ed9bc973e8 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html @@ -0,0 +1,212 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Aborting fetch when intercepted by a service worker</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="../../../service-workers/service-worker/resources/test-helpers.sub.js"></script> +</head> +<body> +<script> + // Duplicating this resource to make service worker scoping simpler. + const SCOPE = '../resources/basic.html'; + const BODY_METHODS = ['arrayBuffer', 'blob', 'formData', 'json', 'text']; + + const error1 = new Error('error1'); + error1.name = 'error1'; + + async function setupRegistration(t, scope, service_worker) { + const reg = await navigator.serviceWorker.register(service_worker, { scope }); + await wait_for_state(t, reg.installing, 'activated'); + add_completion_callback(_ => reg.unregister()); + return reg; + } + + promise_test(async t => { + const suffix = "?q=aborted-not-intercepted"; + const scope = SCOPE + suffix; + await setupRegistration(t, scope, '../resources/sw-intercept.js'); + const iframe = await with_iframe(scope); + add_completion_callback(_ => iframe.remove()); + const w = iframe.contentWindow; + + const controller = new w.AbortController(); + const signal = controller.signal; + controller.abort(); + + const nextData = new Promise(resolve => { + w.navigator.serviceWorker.addEventListener('message', function once(event) { + // The message triggered by the iframe's document's fetch + // request cannot get dispatched by the time we add the event + // listener, so we have to guard against it. + if (!event.data.endsWith(suffix)) { + w.navigator.serviceWorker.removeEventListener('message', once); + resolve(event.data); + } + }) + }); + + const fetchPromise = w.fetch('data.json', { signal }); + + await promise_rejects_dom(t, "AbortError", w.DOMException, fetchPromise); + + await w.fetch('data.json?no-abort'); + + assert_true((await nextData).endsWith('?no-abort'), "Aborted request does not go through service worker"); + }, "Already aborted request does not land in service worker"); + + for (const bodyMethod of BODY_METHODS) { + promise_test(async t => { + const scope = SCOPE + "?q=aborted-" + bodyMethod + "-rejects"; + await setupRegistration(t, scope, '../resources/sw-intercept.js'); + const iframe = await with_iframe(scope); + add_completion_callback(_ => iframe.remove()); + const w = iframe.contentWindow; + + const controller = new w.AbortController(); + const signal = controller.signal; + + const log = []; + const response = await w.fetch('data.json', { signal }); + + controller.abort(); + + const bodyPromise = response[bodyMethod](); + + await Promise.all([ + bodyPromise.catch(() => log.push(`${bodyMethod}-reject`)), + Promise.resolve().then(() => log.push('next-microtask')) + ]); + + await promise_rejects_dom(t, "AbortError", w.DOMException, bodyPromise); + + assert_array_equals(log, [`${bodyMethod}-reject`, 'next-microtask']); + }, `response.${bodyMethod}() rejects if already aborted`); + } + + promise_test(async t => { + const scope = SCOPE + "?q=aborted-stream-errors"; + await setupRegistration(t, scope, '../resources/sw-intercept.js'); + const iframe = await with_iframe(scope); + add_completion_callback(_ => iframe.remove()); + const w = iframe.contentWindow; + + const controller = new w.AbortController(); + const signal = controller.signal; + + const response = await w.fetch('data.json', { signal }); + const reader = response.body.getReader(); + + controller.abort(); + + await promise_rejects_dom(t, "AbortError", w.DOMException, reader.read()); + await promise_rejects_dom(t, "AbortError", w.DOMException, reader.closed); + }, "Stream errors once aborted."); + + promise_test(async t => { + const scope = SCOPE + "?q=aborted-with-abort-reason"; + await setupRegistration(t, scope, '../resources/sw-intercept.js'); + const iframe = await with_iframe(scope); + add_completion_callback(_ => iframe.remove()); + const w = iframe.contentWindow; + + const controller = new w.AbortController(); + const signal = controller.signal; + + const fetchPromise = w.fetch('data.json', { signal }); + + controller.abort(error1); + + await promise_rejects_exactly(t, error1, fetchPromise); + }, "fetch() rejects with abort reason"); + + + promise_test(async t => { + const scope = SCOPE + "?q=aborted-with-abort-reason-in-body"; + await setupRegistration(t, scope, '../resources/sw-intercept.js'); + const iframe = await with_iframe(scope); + add_completion_callback(_ => iframe.remove()); + const w = iframe.contentWindow; + + const controller = new w.AbortController(); + const signal = controller.signal; + + const fetchResponse = await w.fetch('data.json', { signal }); + const bodyPromise = fetchResponse.body.getReader().read(); + controller.abort(error1); + + await promise_rejects_exactly(t, error1, bodyPromise); + }, "fetch() response body has abort reason"); + + promise_test(async t => { + const scope = SCOPE + "?q=service-worker-observes-abort-reason"; + await setupRegistration(t, scope, '../resources/sw-intercept-abort.js'); + const iframe = await with_iframe(scope); + add_completion_callback(_ => iframe.remove()); + const w = iframe.contentWindow; + + const controller = new w.AbortController(); + const signal = controller.signal; + + const fetchPromise = w.fetch('data.json', { signal }); + + await new Promise(resolve => { + w.navigator.serviceWorker.addEventListener('message', t.step_func(event => { + assert_equals(event.data, "fetch event has arrived"); + resolve(); + }), {once: true}); + }); + + controller.abort(error1); + + await new Promise(resolve => { + w.navigator.serviceWorker.addEventListener('message', t.step_func(event => { + assert_equals(event.data.message, error1.message); + resolve(); + }), {once: true}); + }); + + await promise_rejects_exactly(t, error1, fetchPromise); + }, "Service Worker can observe the fetch abort and associated abort reason"); + + promise_test(async t => { + let incrementing_error = new Error('error1'); + incrementing_error.name = 'error1'; + + const scope = SCOPE + "?q=serialization-on-abort"; + await setupRegistration(t, scope, '../resources/sw-intercept-abort.js'); + const iframe = await with_iframe(scope); + add_completion_callback(_ => iframe.remove()); + const w = iframe.contentWindow; + + const controller = new w.AbortController(); + const signal = controller.signal; + + const fetchPromise = w.fetch('data.json', { signal }); + + await new Promise(resolve => { + w.navigator.serviceWorker.addEventListener('message', t.step_func(event => { + assert_equals(event.data, "fetch event has arrived"); + resolve(); + }), {once: true}); + }); + + controller.abort(incrementing_error); + + const original_error_name = incrementing_error.name; + + incrementing_error.name = 'error2'; + + await new Promise(resolve => { + w.navigator.serviceWorker.addEventListener('message', t.step_func(event => { + assert_equals(event.data.name, original_error_name); + resolve(); + }), {once: true}); + }); + + await promise_rejects_exactly(t, incrementing_error, fetchPromise); + }, "Abort reason serialization happens on abort"); +</script> +</body> +</html> |