tor-browser

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

yield-inherit-across-promises.any.js (4955B)


      1 'use strict';
      2 
      3 function postInheritPriorityTestTask(config) {
      4  const ids = [];
      5  const task = scheduler.postTask(async () => {
      6    await new Promise(resolve => setTimeout(resolve));
      7    await fetch('/common/blank.html');
      8    await new Promise(resolve => setTimeout(resolve));
      9    const subtask = scheduler.postTask(() => { ids.push('subtask'); }, {priority: config.subTaskPriority});
     10    await scheduler.yield();
     11    ids.push('yield');
     12    await subtask;
     13  }, config.taskOptions);
     14  return {task, ids}
     15 }
     16 
     17 for (let priority of ['user-blocking', 'background']) {
     18  const expected = priority == 'user-blocking' ? 'yield,subtask' : 'subtask,yield';
     19  promise_test(async t => {
     20    const config = {
     21      taskOptions: {priority},
     22      subTaskPriority: 'user-blocking',
     23    };
     24    const {task, ids} = postInheritPriorityTestTask(config);
     25    await task;
     26    assert_equals(ids.join(), expected);
     27  }, `yield() inherits priority (string) across promises (${priority})`);
     28 
     29  promise_test(async t => {
     30    const signal = (new TaskController({priority})).signal;
     31    const config = {
     32      taskOptions: {signal},
     33      subTaskPriority: 'user-blocking',
     34    };
     35    const {task, ids} = postInheritPriorityTestTask(config);
     36    await task;
     37    assert_equals(ids.join(), expected);
     38  }, `yield() inherits priority (signal) across promises (${priority})`);
     39 }
     40 
     41 promise_test(async t => {
     42  const controller = new TaskController();
     43  const signal = controller.signal;
     44  return scheduler.postTask(async () => {
     45    await new Promise(resolve => setTimeout(resolve));
     46    await fetch('/common/blank.html');
     47    await new Promise(resolve => setTimeout(resolve));
     48    controller.abort();
     49    const p = scheduler.yield();
     50    await promise_rejects_dom(t, 'AbortError', p);
     51  }, {signal});
     52 }, `yield() inherits abort across promises`);
     53 
     54 promise_test(async t => {
     55  const ids = [];
     56  let {promise: p1, resolve} = Promise.withResolvers();
     57  // For promises, the scheduling state is bound to the future microtask when
     58  // the promise is awaited or .then() is called on it. This tests that the
     59  // right scheduling state is used, i.e. not the "resolve time" state.
     60  //
     61  // First, create a pending continuation (.then(...)) bound to the current
     62  // (null) scheduling state. The continuation calls yield(), which should
     63  // inherit the null scheduling state.
     64  p1 = p1.then(async () => {
     65    await scheduler.yield();
     66    ids.push('continuation');
     67  });
     68  // Next, resolve `p1` in a user-blocking task. The user-blocking scheduling
     69  // state should not be propagated to the continuation above.
     70  await scheduler.postTask(resolve, {priority: 'user-blocking'});
     71  // Finally, to test this, race another user-blocking task with the `p1`
     72  // continuation above. The continuation should run after this task, since it
     73  // should not inherit the user-blocking priority.
     74  const p2 = scheduler.postTask(() => {
     75    ids.push('task');
     76  }, {priority: 'user-blocking'});
     77 
     78  const result = await Promise.all([p1, p2]);
     79  assert_equals(ids.toString(), 'task,continuation');
     80 }, 'yield() inherits .then() context, not resolve context');
     81 
     82 promise_test(async t => {
     83  // This tests that non-promise microtasks also inherit scheduling state by
     84  // checking that the scheduling state is propagated from queueMicrotask() to
     85  // the subsequent microtask.
     86  //
     87  // First, create a pending continuation (.then(...)) which will be bound to
     88  // the current (null) context. The yield() below should have default priority.
     89  const ids = [];
     90  let {promise: p1, resolve} = Promise.withResolvers();
     91  p1 = p1.then(async () => {
     92    ids.push('p1-start');
     93    await scheduler.yield();
     94    ids.push('p1-continuation');
     95  });
     96 
     97  // Next, schedule a task which resolves `p1` and then calls queueMicrotask().
     98  // This is done to interleave the microtasks in a way that we can ensure
     99  // queueMicrotask() actually propagates scheduling state, rather than using
    100  // the state set when the postTask() callback starts.
    101  //
    102  // The yield() below should inherit the user-blocking priority.
    103  const p2 = scheduler.postTask(async () => {
    104    resolve();
    105    queueMicrotask(async () => {
    106      ids.push('p2-start');
    107      await scheduler.yield();
    108      ids.push('p2-continuation');
    109    })
    110  }, {priority: 'user-blocking'});
    111 
    112  // Finally, schedule another task to race with the contents of the `p2` task
    113  // above. Both yield() calls above happen during the `p2` task microtask
    114  // checkpoint, so both continuations are scheduled when the `p3` task below
    115  // runs. The p2-continuation (user-blocking continuation) should run before
    116  // the `p3` task, and the p1-continuation (default prioriy continuation)
    117  // should run after.
    118  const p3 = scheduler.postTask(() => {
    119    ids.push('p3');
    120  }, {priority: 'user-blocking'});
    121 
    122  await Promise.all([p1, p2, p3]);
    123  assert_equals(
    124      ids.toString(), "p1-start,p2-start,p2-continuation,p3,p1-continuation");
    125 }, 'yield() inherits priority in queueMicrotask()');