tor-browser

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

extendable-event-async-waituntil.js (7811B)


      1 // This worker calls waitUntil() and respondWith() asynchronously and
      2 // reports back to the test whether they threw.
      3 //
      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.
      8 
      9 // Controlled by 'init'/'done' messages.
     10 var resolveLockPromise;
     11 var port;
     12 
     13 self.addEventListener('message', function(event) {
     14    var waitPromise;
     15    var resolveTestPromise;
     16 
     17    switch (event.data.step) {
     18      case 'init':
     19        event.waitUntil(new Promise((res) => { resolveLockPromise = res; }));
     20        port = event.data.port;
     21        break;
     22      case 'done':
     23        resolveLockPromise();
     24        break;
     25 
     26      // Throws because waitUntil() is called in a task after event dispatch
     27      // finishes.
     28      case 'no-current-extension-different-task':
     29        async_task_waituntil(event).then(reportResultExpecting('InvalidStateError'));
     30        break;
     31 
     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'));
     36        break;
     37 
     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);
     43        break;
     44 
     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'))
     53        break;
     54 
     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'))
     61        break;
     62 
     63 
     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
     67      // positive.
     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'))
     73        break;
     74 
     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'))
     82        break;
     83 
     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'));
     89        break;
     90 
     91      case 'script-extendable-event':
     92        self.dispatchEvent(new ExtendableEvent('nontrustedevent'));
     93        break;
     94    }
     95 
     96    event.source.postMessage('ACK');
     97  });
     98 
     99 self.addEventListener('fetch', function(event) {
    100  const path = new URL(event.request.url).pathname;
    101  const step = path.substring(path.lastIndexOf('/') + 1);
    102  let response;
    103  switch (step) {
    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':
    107      var resolveFetch;
    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')); });
    113      break;
    114 
    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'));
    123      break;
    124 
    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'));
    133      break;
    134 
    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'));
    143      break;
    144 
    145 
    146    // Throws because waitUntil() is called after the pending promise count was
    147    // decremented to 0.
    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'))
    153      break;
    154  }
    155 });
    156 
    157 self.addEventListener('nontrustedevent', function(event) {
    158    sync_waituntil(event).then(reportResultExpecting('InvalidStateError'));
    159  });
    160 
    161 function reportResultExpecting(expectedResult) {
    162  return function (result) {
    163    port.postMessage({result : result, expected: expectedResult});
    164    return result;
    165  };
    166 }
    167 
    168 function sync_waituntil(event) {
    169  return new Promise((res, rej) => {
    170    try {
    171      event.waitUntil(Promise.resolve());
    172      res('OK');
    173    } catch (error) {
    174      res(error.name);
    175    }
    176  });
    177 }
    178 
    179 function async_microtask_waituntil(event) {
    180  return new Promise((res, rej) => {
    181    Promise.resolve().then(() => {
    182      try {
    183        event.waitUntil(Promise.resolve());
    184        res('OK');
    185      } catch (error) {
    186        res(error.name);
    187      }
    188    });
    189  });
    190 }
    191 
    192 function async_task_waituntil(event) {
    193  return new Promise((res, rej) => {
    194    setTimeout(() => {
    195      try {
    196        event.waitUntil(Promise.resolve());
    197        res('OK');
    198      } catch (error) {
    199        res(error.name);
    200      }
    201    }, 0);
    202  });
    203 }
    204 
    205 // Returns a promise that settles in a separate task.
    206 function makeNewTaskPromise() {
    207  return new Promise(resolve => {
    208    setTimeout(resolve, 0);
    209  });
    210 }