tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

serviceworker-intercepted.https.html (7486B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4  <meta charset="utf-8">
      5  <title>Aborting fetch when intercepted by a service worker</title>
      6  <script src="/resources/testharness.js"></script>
      7  <script src="/resources/testharnessreport.js"></script>
      8  <script src="../../../service-workers/service-worker/resources/test-helpers.sub.js"></script>
      9 </head>
     10 <body>
     11 <script>
     12  // Duplicating this resource to make service worker scoping simpler.
     13  const SCOPE = '../resources/basic.html';
     14  const BODY_METHODS = ['arrayBuffer', 'blob', 'bytes', 'formData', 'json', 'text'];
     15 
     16  const error1 = new Error('error1');
     17  error1.name = 'error1';
     18 
     19  async function setupRegistration(t, scope, service_worker) {
     20    const reg = await navigator.serviceWorker.register(service_worker, { scope });
     21    await wait_for_state(t, reg.installing, 'activated');
     22    add_completion_callback(_ => reg.unregister());
     23    return reg;
     24  }
     25 
     26  promise_test(async t => {
     27    const suffix = "?q=aborted-not-intercepted";
     28    const scope = SCOPE + suffix;
     29    await setupRegistration(t, scope, '../resources/sw-intercept.js');
     30    const iframe = await with_iframe(scope);
     31    add_completion_callback(_ => iframe.remove());
     32    const w = iframe.contentWindow;
     33 
     34    const controller = new w.AbortController();
     35    const signal = controller.signal;
     36    controller.abort();
     37 
     38    const nextData = new Promise(resolve => {
     39      w.navigator.serviceWorker.addEventListener('message', function once(event) {
     40        // The message triggered by the iframe's document's fetch
     41        // request cannot get dispatched by the time we add the event
     42        // listener, so we have to guard against it.
     43        if (!event.data.endsWith(suffix)) {
     44          w.navigator.serviceWorker.removeEventListener('message', once);
     45          resolve(event.data);
     46        }
     47      })
     48    });
     49 
     50    const fetchPromise = w.fetch('data.json', { signal });
     51 
     52    await promise_rejects_dom(t, "AbortError", w.DOMException, fetchPromise);
     53 
     54    await w.fetch('data.json?no-abort');
     55 
     56    assert_true((await nextData).endsWith('?no-abort'), "Aborted request does not go through service worker");
     57  }, "Already aborted request does not land in service worker");
     58 
     59  for (const bodyMethod of BODY_METHODS) {
     60    promise_test(async t => {
     61      const scope = SCOPE + "?q=aborted-" + bodyMethod + "-rejects";
     62      await setupRegistration(t, scope, '../resources/sw-intercept.js');
     63      const iframe = await with_iframe(scope);
     64      add_completion_callback(_ => iframe.remove());
     65      const w = iframe.contentWindow;
     66 
     67      const controller = new w.AbortController();
     68      const signal = controller.signal;
     69 
     70      const log = [];
     71      const response = await w.fetch('data.json', { signal });
     72 
     73      controller.abort();
     74 
     75      const bodyPromise = response[bodyMethod]();
     76 
     77      await Promise.all([
     78        bodyPromise.catch(() => log.push(`${bodyMethod}-reject`)),
     79        Promise.resolve().then(() => log.push('next-microtask'))
     80      ]);
     81 
     82      await promise_rejects_dom(t, "AbortError", w.DOMException, bodyPromise);
     83 
     84      assert_array_equals(log, [`${bodyMethod}-reject`, 'next-microtask']);
     85    }, `response.${bodyMethod}() rejects if already aborted`);
     86  }
     87 
     88  promise_test(async t => {
     89    const scope = SCOPE + "?q=aborted-stream-errors";
     90    await setupRegistration(t, scope, '../resources/sw-intercept.js');
     91    const iframe = await with_iframe(scope);
     92    add_completion_callback(_ => iframe.remove());
     93    const w = iframe.contentWindow;
     94 
     95    const controller = new w.AbortController();
     96    const signal = controller.signal;
     97 
     98    const response = await w.fetch('data.json', { signal });
     99    const reader = response.body.getReader();
    100 
    101    controller.abort();
    102 
    103    await promise_rejects_dom(t, "AbortError", w.DOMException, reader.read());
    104    await promise_rejects_dom(t, "AbortError", w.DOMException, reader.closed);
    105  }, "Stream errors once aborted.");
    106 
    107  promise_test(async t => {
    108    const scope = SCOPE + "?q=aborted-with-abort-reason";
    109    await setupRegistration(t, scope, '../resources/sw-intercept.js');
    110    const iframe = await with_iframe(scope);
    111    add_completion_callback(_ => iframe.remove());
    112    const w = iframe.contentWindow;
    113 
    114    const controller = new w.AbortController();
    115    const signal = controller.signal;
    116 
    117    const fetchPromise = w.fetch('data.json', { signal });
    118 
    119    controller.abort(error1);
    120 
    121    await promise_rejects_exactly(t, error1, fetchPromise);
    122  }, "fetch() rejects with abort reason");
    123 
    124 
    125  promise_test(async t => {
    126    const scope = SCOPE + "?q=aborted-with-abort-reason-in-body";
    127    await setupRegistration(t, scope, '../resources/sw-intercept.js');
    128    const iframe = await with_iframe(scope);
    129    add_completion_callback(_ => iframe.remove());
    130    const w = iframe.contentWindow;
    131 
    132    const controller = new w.AbortController();
    133    const signal = controller.signal;
    134 
    135    const fetchResponse = await w.fetch('data.json', { signal });
    136    const bodyPromise  = fetchResponse.body.getReader().read();
    137    controller.abort(error1);
    138 
    139    await promise_rejects_exactly(t, error1, bodyPromise);
    140    }, "fetch() response body has abort reason");
    141 
    142  promise_test(async t => {
    143    const scope = SCOPE + "?q=service-worker-observes-abort-reason";
    144    await setupRegistration(t, scope, '../resources/sw-intercept-abort.js');
    145    const iframe = await with_iframe(scope);
    146    add_completion_callback(_ => iframe.remove());
    147    const w = iframe.contentWindow;
    148 
    149    const controller = new w.AbortController();
    150    const signal = controller.signal;
    151 
    152    const fetchPromise = w.fetch('data.json', { signal });
    153 
    154    await new Promise(resolve => {
    155      w.navigator.serviceWorker.addEventListener('message', t.step_func(event => {
    156        assert_equals(event.data, "fetch event has arrived");
    157        resolve();
    158      }), {once: true});
    159    });
    160 
    161    controller.abort(error1);
    162 
    163    await new Promise(resolve => {
    164      w.navigator.serviceWorker.addEventListener('message', t.step_func(event => {
    165        assert_equals(event.data.message, error1.message);
    166        resolve();
    167      }), {once: true});
    168    });
    169 
    170    await promise_rejects_exactly(t, error1, fetchPromise);
    171  }, "Service Worker can observe the fetch abort and associated abort reason");
    172 
    173  promise_test(async t => {
    174    let incrementing_error = new Error('error1');
    175    incrementing_error.name = 'error1';
    176 
    177    const scope = SCOPE + "?q=serialization-on-abort";
    178    await setupRegistration(t, scope, '../resources/sw-intercept-abort.js');
    179    const iframe = await with_iframe(scope);
    180    add_completion_callback(_ => iframe.remove());
    181    const w = iframe.contentWindow;
    182 
    183    const controller = new w.AbortController();
    184    const signal = controller.signal;
    185 
    186    const fetchPromise = w.fetch('data.json', { signal });
    187 
    188    await new Promise(resolve => {
    189      w.navigator.serviceWorker.addEventListener('message', t.step_func(event => {
    190        assert_equals(event.data, "fetch event has arrived");
    191        resolve();
    192      }), {once: true});
    193    });
    194 
    195    controller.abort(incrementing_error);
    196 
    197    const original_error_name = incrementing_error.name;
    198 
    199    incrementing_error.name = 'error2';
    200 
    201    await new Promise(resolve => {
    202      w.navigator.serviceWorker.addEventListener('message', t.step_func(event => {
    203        assert_equals(event.data.name, original_error_name);
    204        resolve();
    205      }), {once: true});
    206    });
    207 
    208    await promise_rejects_exactly(t, incrementing_error, fetchPromise);
    209  }, "Abort reason serialization happens on abort");
    210 </script>
    211 </body>
    212 </html>