tor-browser

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

workers.html (13120B)


      1 <!DOCTYPE html>
      2 <meta charset=utf-8>
      3 <script src="/resources/testharness.js"></script>
      4 <script src="/resources/testharnessreport.js"></script>
      5 <script>
      6 
      7 async_test(t => {
      8    let c1 = new BroadcastChannel('worker');
      9    let c2 = new BroadcastChannel('worker');
     10    let events = [];
     11 
     12    c1.onmessage = e => events.push(e);
     13    c2.onmessage = e => events.push(e);
     14 
     15    let doneCount = 0;
     16    c2.addEventListener('message', t.step_func(e => {
     17        if (e.data == 'from worker') {
     18          c2.postMessage('from c2');
     19          c1.postMessage('done');
     20        } else if (e.data == 'done') {
     21          assert_equals(events.length, 4);
     22          assert_equals(events[0].data, 'from worker');
     23          assert_equals(events[0].target, c1);
     24          assert_equals(events[1].data, 'from worker');
     25          assert_equals(events[1].target, c2);
     26          assert_equals(events[2].data, 'from c2');
     27          assert_equals(events[3].data, 'done');
     28          if (++doneCount == 2) t.done();
     29        }
     30      }));
     31 
     32    let worker = new Worker('resources/worker.js');
     33    worker.onmessage = t.step_func(e => {
     34        assert_array_equals(e.data, ['from c2', 'done']);
     35        if (++doneCount == 2) t.done();
     36      });
     37    worker.postMessage({channel: 'worker'});
     38 
     39  }, 'BroadcastChannel works in workers');
     40 
     41 async_test(t => {
     42    let c1 = new BroadcastChannel('shared worker');
     43    let c2 = new BroadcastChannel('shared worker');
     44    let events = [];
     45 
     46    c1.onmessage = e => events.push(e);
     47    c2.onmessage = e => events.push(e);
     48 
     49    let doneCount = 0;
     50    c2.addEventListener('message', t.step_func(e => {
     51        if (e.data == 'from worker') {
     52          c2.postMessage('from c2');
     53          c1.postMessage('done');
     54        } else if (e.data == 'done') {
     55          assert_equals(events.length, 4);
     56          assert_equals(events[0].data, 'from worker');
     57          assert_equals(events[0].target, c1);
     58          assert_equals(events[1].data, 'from worker');
     59          assert_equals(events[1].target, c2);
     60          assert_equals(events[2].data, 'from c2');
     61          assert_equals(events[3].data, 'done');
     62          if (++doneCount == 2) t.done();
     63        }
     64      }));
     65 
     66    let worker = new SharedWorker('resources/worker.js');
     67    worker.port.onmessage = t.step_func(e => {
     68        assert_array_equals(e.data, ['from c2', 'done']);
     69        if (++doneCount == 2) t.done();
     70      });
     71    worker.port.postMessage({channel: 'shared worker'});
     72 
     73  }, 'BroadcastChannel works in shared workers');
     74 
     75 async_test(t => {
     76    let c = new BroadcastChannel('worker-close');
     77    let events = [];
     78 
     79    c.onmessage = e => events.push('c1: ' + e.data);
     80 
     81    let worker = new Worker('resources/worker.js');
     82    worker.onmessage = t.step_func(e => {
     83        assert_array_equals(events,
     84                            ['c1: from worker', 'c2: ready', 'c2: echo'],
     85                            'messages in document');
     86        assert_array_equals(e.data, ['done'], 'messages in worker');
     87        t.done();
     88      });
     89    worker.onmessagerror =
     90        t.unreached_func('Worker\'s onmessageerror handler called');
     91 
     92    c.addEventListener('message', e => {
     93        if (e.data == 'from worker') {
     94          c.close();
     95          if (self.gc) self.gc();
     96          window.setTimeout(() => {
     97              let c2 = new BroadcastChannel('worker-close');
     98              c2.onmessage = e => {
     99                  events.push('c2: ' + e.data);
    100                  if (e.data === 'ready') {
    101                    worker.postMessage({ping: 'echo'});
    102                  } else {
    103                    c2.postMessage('done');
    104                    c2.close();
    105                  }
    106                };
    107              // For some implementations there may be a race condition between
    108              // when the BroadcastChannel instance above is created / ready to
    109              // receive messages and when the worker calls postMessage on it's
    110              // BroadcastChannel instance. To avoid this, confirm that our
    111              // instance can receive a message before indicating to the other
    112              // thread that we are ready. For more details, see:
    113              // https://github.com/whatwg/html/issues/7267
    114              let c3 = new BroadcastChannel('worker-close');
    115              c3.postMessage('ready');
    116              c3.close();
    117            }, 1);
    118        }
    119      });
    120 
    121    worker.postMessage({channel: 'worker-close'});
    122    t.add_cleanup(() => worker.terminate());
    123 
    124  }, 'Closing and re-opening a channel works.');
    125 
    126 async_test(t => {
    127  function workerCode() {
    128    close();
    129    try {
    130      var bc = new BroadcastChannel('worker-create-after-close');
    131    } catch (e) {
    132      postMessage(e);
    133      return;
    134    }
    135    postMessage(true);
    136  }
    137 
    138  var workerBlob = new Blob(
    139      [workerCode.toString() + ';workerCode();'],
    140      {type: 'application/javascript'});
    141 
    142  var w = new Worker(URL.createObjectURL(workerBlob));
    143  w.onmessage = t.step_func_done(function(e) {
    144    assert_equals(
    145        e.data, true,
    146        'BroadcastChannel creation in closed worker triggered exception: ' +
    147            e.data.message);
    148  });
    149  t.add_cleanup(() => w.terminate());
    150 }, 'BroadcastChannel created after a worker self.close()');
    151 
    152 
    153 function postMessageFromWorkerWorkerCode(workerName, channelName) {
    154  if (workerName === 'close-before-create-worker') {
    155    close();
    156  }
    157  let bc = new BroadcastChannel(channelName);
    158  if (workerName === 'close-after-create-worker') {
    159    close();
    160  }
    161  bc.postMessage(workerName + ' done');
    162  postMessage(true);
    163 }
    164 
    165 function doPostMessageFromWorkerTest(t, workerName, channelName) {
    166  var bc = new BroadcastChannel(channelName);
    167  bc.onmessage = t.step_func_done(function(e) {
    168    assert_equals(
    169        e.data, 'done-worker done',
    170        'BroadcastChannel message should only be received from the second worker');
    171  });
    172  t.add_cleanup(() => bc.close());
    173 
    174  var testMessageHandler = t.step_func(function(e) {
    175    assert_equals(
    176        e.data, true,
    177        'Worker sent postMessage indicating it sent a BroadcastChannel message');
    178 
    179    var w = createWorker(
    180        postMessageFromWorkerWorkerCode, 'done-worker', channelName);
    181    t.add_cleanup(() => w.terminate());
    182  });
    183  createWorker(
    184      postMessageFromWorkerWorkerCode, workerName, channelName,
    185      testMessageHandler);
    186 
    187  // To avoid calling t.step_timeout here, have the worker postMessage(true)
    188  // once it is finished and then we'll instantiate another worker that
    189  // performs the same test steps but doesn't close. By the time the
    190  // BroadcastChannel message in that worker gets sent successfully it should
    191  // be safe to assume that any BroadcastChannel messages from the previous
    192  // worker would have been sent if they were going to be.
    193 }
    194 
    195 function createWorker(workerCode, workerName, channelName, handler = null) {
    196  var workerCodeStr = workerCode.toString() +
    197      `;${workerCode.name}("${workerName}", "${channelName}");`;
    198  var workerBlob = new Blob([workerCodeStr], {type: 'application/javascript'});
    199  var w = new Worker(URL.createObjectURL(workerBlob));
    200  if (handler !== null) {
    201    w.onmessage = handler;
    202  }
    203  return w;
    204 }
    205 
    206 async_test(t => {
    207  const workerName = 'close-after-create-worker';
    208  const channelName = workerName + '-postmessage-from-worker';
    209  doPostMessageFromWorkerTest(t, workerName, channelName);
    210 }, 'BroadcastChannel messages from closed worker to parent should be ignored (BC created before closing)');
    211 
    212 async_test(t => {
    213  const workerName = 'close-before-create-worker';
    214  const channelName = workerName + '-postmessage-from-worker';
    215  doPostMessageFromWorkerTest(t, workerName, channelName);
    216 }, 'BroadcastChannel messages from closed worker to parent should be ignored (BC created after closing)');
    217 
    218 
    219 function postMessageToWorkerWorkerCode(workerName, channelName) {
    220  self.addEventListener('message', () => {
    221    if (workerName === 'close-before-create-worker') {
    222      close();
    223    }
    224    try {
    225      let bc1 = new BroadcastChannel(channelName);
    226      bc1.onmessage = e => {
    227        if (e.data === 'ready') {
    228          postMessage(e.data);
    229        } else if (e.data === 'test') {
    230          postMessage(workerName + ' done');
    231        }
    232      };
    233      bc1.onmessageerror = () => {
    234        postMessage('onmessageerror called from worker BroadcastChannel');
    235      };
    236      if (workerName === 'close-after-create-worker') {
    237        close();
    238      }
    239    } catch (e) {
    240      postMessage(e);
    241      return;
    242    }
    243 
    244    if (workerName === 'done-worker') {
    245      // For some implementations there may be a race condition between when
    246      // the BroadcastChannel instance above is created / ready to receive
    247      // messages and when the parent calls postMessage on it's
    248      // BroadcastChannel instance. To avoid this, confirm that our instance
    249      // can receive a message before indicating to the other thread that we
    250      // are ready. For more details, see:
    251      // https://github.com/whatwg/html/issues/7267
    252      let bc2 = new BroadcastChannel(channelName);
    253      bc2.postMessage('ready');
    254      bc2.close();
    255    } else {
    256      // Since the worker has closed, it's not expected that the
    257      // BroadcastChannel will receive messages (there's a separate test for
    258      // that), so just indicate directly that it's ready to test receiving
    259      // a message from the parent dispite the possibility of a race condition.
    260      postMessage('ready');
    261    }
    262  });
    263  self.addEventListener('messageerror', () => {
    264    postMessage('onmessageerror called from worker');
    265  });
    266 }
    267 
    268 function doPostMessageToWorkerTest(t, workerName, channelName) {
    269  var bc = new BroadcastChannel(channelName);
    270  t.add_cleanup(() => bc.close());
    271 
    272  var doneMessageHandler = t.step_func(function(e) {
    273    if (e.data === 'ready') {
    274      bc.postMessage('test');
    275    } else if (e.data === 'done-worker done') {
    276      t.done();
    277    } else {
    278      assert_unreached(
    279          'BroadcastChannel.postMessage triggered exception within second worker: ' +
    280          e.data.message);
    281    }
    282  });
    283  var testMessageHandler = t.step_func(function(e) {
    284    assert_equals(
    285        e.data, 'ready',
    286        'Worker sent postMessage indicating its BroadcastChannel instance is ready');
    287    bc.postMessage('test');
    288 
    289    var doneWorker = createWorker(
    290        postMessageToWorkerWorkerCode, 'done-worker', channelName,
    291        doneMessageHandler);
    292    t.add_cleanup(() => {
    293      doneWorker.terminate();
    294    });
    295    doneWorker.postMessage('start');
    296  });
    297  var testWorker = createWorker(
    298      postMessageToWorkerWorkerCode, workerName, channelName,
    299      testMessageHandler);
    300  testWorker.postMessage('start');
    301 }
    302 
    303 async_test(t => {
    304  const workerName = 'close-after-create-worker';
    305  const channelName = workerName + '-postmessage-to-worker';
    306  doPostMessageToWorkerTest(t, workerName, channelName);
    307 }, 'BroadcastChannel messages from parent to closed worker should be ignored (BC created before closing)');
    308 
    309 async_test(t => {
    310  const workerName = 'close-before-create-worker';
    311  const channelName = workerName + '-postmessage-to-worker';
    312  doPostMessageToWorkerTest(t, workerName, channelName);
    313 }, 'BroadcastChannel messages from parent to closed worker should be ignored (BC created after closing)');
    314 
    315 
    316 function postMessageWithinWorkerWorkerCode(workerName, channelName) {
    317  if (workerName === 'close-before-create-worker') {
    318    close();
    319  }
    320  try {
    321    let bc1 = new BroadcastChannel(channelName);
    322    let bc2 = new BroadcastChannel(channelName);
    323    bc1.onmessage = e => {
    324      postMessage(workerName + ' done')
    325    };
    326    if (workerName === 'close-after-create-worker') {
    327      close();
    328    }
    329    bc2.postMessage(true);
    330    postMessage(true);
    331  } catch (e) {
    332    postMessage(e);
    333  }
    334 }
    335 
    336 function doPostMessageWithinWorkerTest(t, workerName, channelName) {
    337  var doneMessageHandler = t.step_func(function(e) {
    338    if (e.data === true) {
    339      // Done worker has finished - no action needed
    340    } else if (e.data === 'done-worker done') {
    341      t.done();
    342    } else {
    343      assert_unreached(
    344          'BroadcastChannel.postMessage triggered exception within second worker: ' +
    345          e.data.message);
    346    }
    347  });
    348  var testMessageHandler = t.step_func(function(e) {
    349    assert_equals(
    350        e.data, true,
    351        'Worker indicated that the test procedures were executed successfully');
    352 
    353    var w = createWorker(
    354        postMessageWithinWorkerWorkerCode, 'done-worker', channelName,
    355        doneMessageHandler);
    356    t.add_cleanup(() => w.terminate());
    357  });
    358  createWorker(
    359      postMessageWithinWorkerWorkerCode, workerName, channelName,
    360      testMessageHandler);
    361 }
    362 
    363 async_test(t => {
    364  const workerName = 'close-after-create-worker';
    365  const channelName = workerName + '-postmessage-within-worker';
    366  doPostMessageWithinWorkerTest(t, workerName, channelName);
    367 }, 'BroadcastChannel messages within closed worker should be ignored (BCs created before closing)');
    368 
    369 async_test(t => {
    370  const workerName = 'close-before-create-worker';
    371  const channelName = workerName + '-postmessage-within-worker';
    372  doPostMessageWithinWorkerTest(t, workerName, channelName);
    373 }, 'BroadcastChannel messages within closed worker should be ignored (BCs created after closing)');
    374 
    375 </script>