tor-browser

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

tee.any.js (33562B)


      1 // META: global=window,worker,shadowrealm
      2 // META: script=../resources/rs-utils.js
      3 // META: script=../resources/test-utils.js
      4 // META: script=../resources/recording-streams.js
      5 // META: script=../resources/rs-test-templates.js
      6 'use strict';
      7 
      8 test(() => {
      9 
     10  const rs = new ReadableStream({ type: 'bytes' });
     11  const result = rs.tee();
     12 
     13  assert_true(Array.isArray(result), 'return value should be an array');
     14  assert_equals(result.length, 2, 'array should have length 2');
     15  assert_equals(result[0].constructor, ReadableStream, '0th element should be a ReadableStream');
     16  assert_equals(result[1].constructor, ReadableStream, '1st element should be a ReadableStream');
     17 
     18 }, 'ReadableStream teeing with byte source: rs.tee() returns an array of two ReadableStreams');
     19 
     20 promise_test(async t => {
     21 
     22  const rs = new ReadableStream({
     23    type: 'bytes',
     24    start(c) {
     25      c.enqueue(new Uint8Array([0x01]));
     26      c.enqueue(new Uint8Array([0x02]));
     27      c.close();
     28    }
     29  });
     30 
     31  const [branch1, branch2] = rs.tee();
     32  const reader1 = branch1.getReader({ mode: 'byob' });
     33  const reader2 = branch2.getReader({ mode: 'byob' });
     34 
     35  reader2.closed.then(t.unreached_func('branch2 should not be closed'));
     36 
     37  {
     38    const result = await reader1.read(new Uint8Array(1));
     39    assert_equals(result.done, false, 'done');
     40    assert_typed_array_equals(result.value, new Uint8Array([0x01]), 'value');
     41  }
     42 
     43  {
     44    const result = await reader1.read(new Uint8Array(1));
     45    assert_equals(result.done, false, 'done');
     46    assert_typed_array_equals(result.value, new Uint8Array([0x02]), 'value');
     47  }
     48 
     49  {
     50    const result = await reader1.read(new Uint8Array(1));
     51    assert_equals(result.done, true, 'done');
     52    assert_typed_array_equals(result.value, new Uint8Array([0]).subarray(0, 0), 'value');
     53  }
     54 
     55  {
     56    const result = await reader2.read(new Uint8Array(1));
     57    assert_equals(result.done, false, 'done');
     58    assert_typed_array_equals(result.value, new Uint8Array([0x01]), 'value');
     59  }
     60 
     61  await reader1.closed;
     62 
     63 }, 'ReadableStream teeing with byte source: should be able to read one branch to the end without affecting the other');
     64 
     65 promise_test(async () => {
     66 
     67  let pullCount = 0;
     68  const enqueuedChunk = new Uint8Array([0x01]);
     69  const rs = new ReadableStream({
     70    type: 'bytes',
     71    pull(c) {
     72      ++pullCount;
     73      if (pullCount === 1) {
     74        c.enqueue(enqueuedChunk);
     75      }
     76    }
     77  });
     78 
     79  const [branch1, branch2] = rs.tee();
     80  const reader1 = branch1.getReader();
     81  const reader2 = branch2.getReader();
     82 
     83  const [result1, result2] = await Promise.all([reader1.read(), reader2.read()]);
     84  assert_equals(result1.done, false, 'reader1 done');
     85  assert_equals(result2.done, false, 'reader2 done');
     86 
     87  const view1 = result1.value;
     88  const view2 = result2.value;
     89  assert_typed_array_equals(view1, new Uint8Array([0x01]), 'reader1 value');
     90  assert_typed_array_equals(view2, new Uint8Array([0x01]), 'reader2 value');
     91 
     92  assert_not_equals(view1.buffer, view2.buffer, 'chunks should have different buffers');
     93  assert_not_equals(enqueuedChunk.buffer, view1.buffer, 'enqueued chunk and branch1\'s chunk should have different buffers');
     94  assert_not_equals(enqueuedChunk.buffer, view2.buffer, 'enqueued chunk and branch2\'s chunk should have different buffers');
     95 
     96 }, 'ReadableStream teeing with byte source: chunks should be cloned for each branch');
     97 
     98 promise_test(async () => {
     99 
    100  let pullCount = 0;
    101  const rs = new ReadableStream({
    102    type: 'bytes',
    103    pull(c) {
    104      ++pullCount;
    105      if (pullCount === 1) {
    106        c.byobRequest.view[0] = 0x01;
    107        c.byobRequest.respond(1);
    108      }
    109    }
    110  });
    111 
    112  const [branch1, branch2] = rs.tee();
    113  const reader1 = branch1.getReader({ mode: 'byob' });
    114  const reader2 = branch2.getReader();
    115  const buffer = new Uint8Array([42, 42, 42]).buffer;
    116 
    117  {
    118    const result = await reader1.read(new Uint8Array(buffer, 0, 1));
    119    assert_equals(result.done, false, 'done');
    120    assert_typed_array_equals(result.value, new Uint8Array([0x01, 42, 42]).subarray(0, 1), 'value');
    121  }
    122 
    123  {
    124    const result = await reader2.read();
    125    assert_equals(result.done, false, 'done');
    126    assert_typed_array_equals(result.value, new Uint8Array([0x01]), 'value');
    127  }
    128 
    129 }, 'ReadableStream teeing with byte source: chunks for BYOB requests from branch 1 should be cloned to branch 2');
    130 
    131 promise_test(async t => {
    132 
    133  const theError = { name: 'boo!' };
    134  const rs = new ReadableStream({
    135    type: 'bytes',
    136    start(c) {
    137      c.enqueue(new Uint8Array([0x01]));
    138      c.enqueue(new Uint8Array([0x02]));
    139    },
    140    pull() {
    141      throw theError;
    142    }
    143  });
    144 
    145  const [branch1, branch2] = rs.tee();
    146  const reader1 = branch1.getReader({ mode: 'byob' });
    147  const reader2 = branch2.getReader({ mode: 'byob' });
    148 
    149  {
    150    const result = await reader1.read(new Uint8Array(1));
    151    assert_equals(result.done, false, 'first read from branch1 should not be done');
    152    assert_typed_array_equals(result.value, new Uint8Array([0x01]), 'first read from branch1');
    153  }
    154 
    155  {
    156    const result = await reader1.read(new Uint8Array(1));
    157    assert_equals(result.done, false, 'second read from branch1 should not be done');
    158    assert_typed_array_equals(result.value, new Uint8Array([0x02]), 'second read from branch1');
    159  }
    160 
    161  await promise_rejects_exactly(t, theError, reader1.read(new Uint8Array(1)));
    162  await promise_rejects_exactly(t, theError, reader2.read(new Uint8Array(1)));
    163 
    164  await Promise.all([
    165    promise_rejects_exactly(t, theError, reader1.closed),
    166    promise_rejects_exactly(t, theError, reader2.closed)
    167  ]);
    168 
    169 }, 'ReadableStream teeing with byte source: errors in the source should propagate to both branches');
    170 
    171 promise_test(async () => {
    172 
    173  const rs = new ReadableStream({
    174    type: 'bytes',
    175    start(c) {
    176      c.enqueue(new Uint8Array([0x01]));
    177      c.enqueue(new Uint8Array([0x02]));
    178      c.close();
    179    }
    180  });
    181 
    182  const [branch1, branch2] = rs.tee();
    183  branch1.cancel();
    184 
    185  const [chunks1, chunks2] = await Promise.all([readableStreamToArray(branch1), readableStreamToArray(branch2)]);
    186  assert_array_equals(chunks1, [], 'branch1 should have no chunks');
    187  assert_equals(chunks2.length, 2, 'branch2 should have two chunks');
    188  assert_typed_array_equals(chunks2[0], new Uint8Array([0x01]), 'first chunk from branch2');
    189  assert_typed_array_equals(chunks2[1], new Uint8Array([0x02]), 'second chunk from branch2');
    190 
    191 }, 'ReadableStream teeing with byte source: canceling branch1 should not impact branch2');
    192 
    193 promise_test(async () => {
    194 
    195  const rs = new ReadableStream({
    196    type: 'bytes',
    197    start(c) {
    198      c.enqueue(new Uint8Array([0x01]));
    199      c.enqueue(new Uint8Array([0x02]));
    200      c.close();
    201    }
    202  });
    203 
    204  const [branch1, branch2] = rs.tee();
    205  branch2.cancel();
    206 
    207  const [chunks1, chunks2] = await Promise.all([readableStreamToArray(branch1), readableStreamToArray(branch2)]);
    208  assert_equals(chunks1.length, 2, 'branch1 should have two chunks');
    209  assert_typed_array_equals(chunks1[0], new Uint8Array([0x01]), 'first chunk from branch1');
    210  assert_typed_array_equals(chunks1[1], new Uint8Array([0x02]), 'second chunk from branch1');
    211  assert_array_equals(chunks2, [], 'branch2 should have no chunks');
    212 
    213 }, 'ReadableStream teeing with byte source: canceling branch2 should not impact branch1');
    214 
    215 templatedRSTeeCancel('ReadableStream teeing with byte source', (extras) => {
    216  return new ReadableStream({ type: 'bytes', ...extras });
    217 });
    218 
    219 promise_test(async () => {
    220 
    221  let controller;
    222  const rs = new ReadableStream({
    223    type: 'bytes',
    224    start(c) {
    225      controller = c;
    226    }
    227  });
    228 
    229  const [branch1, branch2] = rs.tee();
    230  const reader1 = branch1.getReader({ mode: 'byob' });
    231  const reader2 = branch2.getReader({ mode: 'byob' });
    232 
    233  const promise = Promise.all([reader1.closed, reader2.closed]);
    234 
    235  controller.close();
    236 
    237  // The branches are created with HWM 0, so we need to read from at least one of them
    238  // to observe the stream becoming closed.
    239  const read1 = await reader1.read(new Uint8Array(1));
    240  assert_equals(read1.done, true, 'first read from branch1 should be done');
    241 
    242  await promise;
    243 
    244 }, 'ReadableStream teeing with byte source: closing the original should close the branches');
    245 
    246 promise_test(async t => {
    247 
    248  let controller;
    249  const rs = new ReadableStream({
    250    type: 'bytes',
    251    start(c) {
    252      controller = c;
    253    }
    254  });
    255 
    256  const [branch1, branch2] = rs.tee();
    257  const reader1 = branch1.getReader({ mode: 'byob' });
    258  const reader2 = branch2.getReader({ mode: 'byob' });
    259 
    260  const theError = { name: 'boo!' };
    261  const promise = Promise.all([
    262    promise_rejects_exactly(t, theError, reader1.closed),
    263    promise_rejects_exactly(t, theError, reader2.closed)
    264  ]);
    265 
    266  controller.error(theError);
    267  await promise;
    268 
    269 }, 'ReadableStream teeing with byte source: erroring the original should immediately error the branches');
    270 
    271 promise_test(async t => {
    272 
    273  let controller;
    274  const rs = new ReadableStream({
    275    type: 'bytes',
    276    start(c) {
    277      controller = c;
    278    }
    279  });
    280 
    281  const [branch1, branch2] = rs.tee();
    282  const reader1 = branch1.getReader();
    283  const reader2 = branch2.getReader();
    284 
    285  const theError = { name: 'boo!' };
    286  const promise = Promise.all([
    287    promise_rejects_exactly(t, theError, reader1.read()),
    288    promise_rejects_exactly(t, theError, reader2.read())
    289  ]);
    290 
    291  controller.error(theError);
    292  await promise;
    293 
    294 }, 'ReadableStream teeing with byte source: erroring the original should error pending reads from default reader');
    295 
    296 promise_test(async t => {
    297 
    298  let controller;
    299  const rs = new ReadableStream({
    300    type: 'bytes',
    301    start(c) {
    302      controller = c;
    303    }
    304  });
    305 
    306  const [branch1, branch2] = rs.tee();
    307  const reader1 = branch1.getReader({ mode: 'byob' });
    308  const reader2 = branch2.getReader({ mode: 'byob' });
    309 
    310  const theError = { name: 'boo!' };
    311  const promise = Promise.all([
    312    promise_rejects_exactly(t, theError, reader1.read(new Uint8Array(1))),
    313    promise_rejects_exactly(t, theError, reader2.read(new Uint8Array(1)))
    314  ]);
    315 
    316  controller.error(theError);
    317  await promise;
    318 
    319 }, 'ReadableStream teeing with byte source: erroring the original should error pending reads from BYOB reader');
    320 
    321 promise_test(async () => {
    322 
    323  let controller;
    324  const rs = new ReadableStream({
    325    type: 'bytes',
    326    start(c) {
    327      controller = c;
    328    }
    329  });
    330 
    331  const [branch1, branch2] = rs.tee();
    332  const reader1 = branch1.getReader({ mode: 'byob' });
    333  const reader2 = branch2.getReader({ mode: 'byob' });
    334  const cancelPromise = reader2.cancel();
    335 
    336  controller.enqueue(new Uint8Array([0x01]));
    337 
    338  const read1 = await reader1.read(new Uint8Array(1));
    339  assert_equals(read1.done, false, 'first read() from branch1 should not be done');
    340  assert_typed_array_equals(read1.value, new Uint8Array([0x01]), 'first read() from branch1');
    341 
    342  controller.close();
    343 
    344  const read2 = await reader1.read(new Uint8Array(1));
    345  assert_equals(read2.done, true, 'second read() from branch1 should be done');
    346 
    347  await Promise.all([
    348    reader1.closed,
    349    cancelPromise
    350  ]);
    351 
    352 }, 'ReadableStream teeing with byte source: canceling branch1 should finish when branch2 reads until end of stream');
    353 
    354 promise_test(async t => {
    355 
    356  let controller;
    357  const theError = { name: 'boo!' };
    358  const rs = new ReadableStream({
    359    type: 'bytes',
    360    start(c) {
    361      controller = c;
    362    }
    363  });
    364 
    365  const [branch1, branch2] = rs.tee();
    366  const reader1 = branch1.getReader({ mode: 'byob' });
    367  const reader2 = branch2.getReader({ mode: 'byob' });
    368  const cancelPromise = reader2.cancel();
    369 
    370  controller.error(theError);
    371 
    372  await Promise.all([
    373    promise_rejects_exactly(t, theError, reader1.read(new Uint8Array(1))),
    374    cancelPromise
    375  ]);
    376 
    377 }, 'ReadableStream teeing with byte source: canceling branch1 should finish when original stream errors');
    378 
    379 promise_test(async () => {
    380 
    381  const rs = recordingReadableStream({ type: 'bytes' });
    382 
    383  // Create two branches, each with a HWM of 0. This should result in no chunks being pulled.
    384  rs.tee();
    385 
    386  await flushAsyncEvents();
    387  assert_array_equals(rs.events, [], 'pull should not be called');
    388 
    389 }, 'ReadableStream teeing with byte source: should not pull any chunks if no branches are reading');
    390 
    391 promise_test(async () => {
    392 
    393  const rs = recordingReadableStream({
    394    type: 'bytes',
    395    pull(controller) {
    396      controller.enqueue(new Uint8Array([0x01]));
    397    }
    398  });
    399 
    400  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    401  await Promise.all([
    402    reader1.read(new Uint8Array(1)),
    403    reader2.read(new Uint8Array(1))
    404  ]);
    405  assert_array_equals(rs.events, ['pull'], 'pull should be called once');
    406 
    407 }, 'ReadableStream teeing with byte source: should only pull enough to fill the emptiest queue');
    408 
    409 promise_test(async t => {
    410 
    411  const rs = recordingReadableStream({ type: 'bytes' });
    412  const theError = { name: 'boo!' };
    413 
    414  rs.controller.error(theError);
    415 
    416  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    417 
    418  await flushAsyncEvents();
    419  assert_array_equals(rs.events, [], 'pull should not be called');
    420 
    421  await Promise.all([
    422    promise_rejects_exactly(t, theError, reader1.closed),
    423    promise_rejects_exactly(t, theError, reader2.closed)
    424  ]);
    425 
    426 }, 'ReadableStream teeing with byte source: should not pull when original is already errored');
    427 
    428 for (const branch of [1, 2]) {
    429  promise_test(async t => {
    430 
    431    const rs = recordingReadableStream({ type: 'bytes' });
    432    const theError = { name: 'boo!' };
    433 
    434    const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    435 
    436    await flushAsyncEvents();
    437    assert_array_equals(rs.events, [], 'pull should not be called');
    438 
    439    const reader = (branch === 1) ? reader1 : reader2;
    440    const read1 = reader.read(new Uint8Array(1));
    441 
    442    await flushAsyncEvents();
    443    assert_array_equals(rs.events, ['pull'], 'pull should be called once');
    444 
    445    rs.controller.error(theError);
    446 
    447    await Promise.all([
    448      promise_rejects_exactly(t, theError, read1),
    449      promise_rejects_exactly(t, theError, reader1.closed),
    450      promise_rejects_exactly(t, theError, reader2.closed)
    451    ]);
    452 
    453    await flushAsyncEvents();
    454    assert_array_equals(rs.events, ['pull'], 'pull should be called once');
    455 
    456  }, `ReadableStream teeing with byte source: stops pulling when original stream errors while branch ${branch} is reading`);
    457 }
    458 
    459 promise_test(async t => {
    460 
    461  const rs = recordingReadableStream({ type: 'bytes' });
    462  const theError = { name: 'boo!' };
    463 
    464  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    465 
    466  await flushAsyncEvents();
    467  assert_array_equals(rs.events, [], 'pull should not be called');
    468 
    469  const read1 = reader1.read(new Uint8Array(1));
    470  const read2 = reader2.read(new Uint8Array(1));
    471 
    472  await flushAsyncEvents();
    473  assert_array_equals(rs.events, ['pull'], 'pull should be called once');
    474 
    475  rs.controller.error(theError);
    476 
    477  await Promise.all([
    478    promise_rejects_exactly(t, theError, read1),
    479    promise_rejects_exactly(t, theError, read2),
    480    promise_rejects_exactly(t, theError, reader1.closed),
    481    promise_rejects_exactly(t, theError, reader2.closed)
    482  ]);
    483 
    484  await flushAsyncEvents();
    485  assert_array_equals(rs.events, ['pull'], 'pull should be called once');
    486 
    487 }, 'ReadableStream teeing with byte source: stops pulling when original stream errors while both branches are reading');
    488 
    489 promise_test(async () => {
    490 
    491  const rs = recordingReadableStream({ type: 'bytes' });
    492 
    493  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    494 
    495  const read1 = reader1.read(new Uint8Array([0x11]));
    496  const read2 = reader2.read(new Uint8Array([0x22]));
    497 
    498  const cancel1 = reader1.cancel();
    499  await flushAsyncEvents();
    500  const cancel2 = reader2.cancel();
    501 
    502  const result1 = await read1;
    503  assert_object_equals(result1, { value: undefined, done: true });
    504  const result2 = await read2;
    505  assert_object_equals(result2, { value: undefined, done: true });
    506 
    507  await Promise.all([cancel1, cancel2]);
    508 
    509 }, 'ReadableStream teeing with byte source: canceling both branches in sequence with delay');
    510 
    511 promise_test(async t => {
    512 
    513  const theError = { name: 'boo!' };
    514  const rs = new ReadableStream({
    515    type: 'bytes',
    516    cancel() {
    517      throw theError;
    518    }
    519  });
    520 
    521  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    522 
    523  const read1 = reader1.read(new Uint8Array([0x11]));
    524  const read2 = reader2.read(new Uint8Array([0x22]));
    525 
    526  const cancel1 = reader1.cancel();
    527  await flushAsyncEvents();
    528  const cancel2 = reader2.cancel();
    529 
    530  const result1 = await read1;
    531  assert_object_equals(result1, { value: undefined, done: true });
    532  const result2 = await read2;
    533  assert_object_equals(result2, { value: undefined, done: true });
    534 
    535  await Promise.all([
    536    promise_rejects_exactly(t, theError, cancel1),
    537    promise_rejects_exactly(t, theError, cancel2)
    538  ]);
    539 
    540 }, 'ReadableStream teeing with byte source: failing to cancel when canceling both branches in sequence with delay');
    541 
    542 promise_test(async () => {
    543 
    544  let cancelResolve;
    545  const cancelCalled = new Promise((resolve) => {
    546    cancelResolve = resolve;
    547  });
    548  const rs = recordingReadableStream({
    549    type: 'bytes',
    550    cancel() {
    551      cancelResolve();
    552    }
    553  });
    554 
    555  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    556 
    557  const read1 = reader1.read(new Uint8Array([0x11]));
    558  await flushAsyncEvents();
    559  const read2 = reader2.read(new Uint8Array([0x22]));
    560  await flushAsyncEvents();
    561 
    562  // We are reading into branch1's buffer.
    563  const byobRequest1 = rs.controller.byobRequest;
    564  assert_not_equals(byobRequest1, null);
    565  assert_typed_array_equals(byobRequest1.view, new Uint8Array([0x11]), 'byobRequest1.view');
    566 
    567  // Cancelling branch1 should not affect the BYOB request.
    568  const cancel1 = reader1.cancel();
    569  const result1 = await read1;
    570  assert_equals(result1.done, true);
    571  assert_equals(result1.value, undefined);
    572  await flushAsyncEvents();
    573  const byobRequest2 = rs.controller.byobRequest;
    574  assert_typed_array_equals(byobRequest2.view, new Uint8Array([0x11]), 'byobRequest2.view');
    575 
    576  // Cancelling branch1 should invalidate the BYOB request.
    577  const cancel2 = reader2.cancel();
    578  await cancelCalled;
    579  const byobRequest3 = rs.controller.byobRequest;
    580  assert_equals(byobRequest3, null);
    581  const result2 = await read2;
    582  assert_equals(result2.done, true);
    583  assert_equals(result2.value, undefined);
    584 
    585  await Promise.all([cancel1, cancel2]);
    586 
    587 }, 'ReadableStream teeing with byte source: read from branch1 and branch2, cancel branch1, cancel branch2');
    588 
    589 promise_test(async () => {
    590 
    591  let cancelResolve;
    592  const cancelCalled = new Promise((resolve) => {
    593    cancelResolve = resolve;
    594  });
    595  const rs = recordingReadableStream({
    596    type: 'bytes',
    597    cancel() {
    598      cancelResolve();
    599    }
    600  });
    601 
    602  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    603 
    604  const read1 = reader1.read(new Uint8Array([0x11]));
    605  await flushAsyncEvents();
    606  const read2 = reader2.read(new Uint8Array([0x22]));
    607  await flushAsyncEvents();
    608 
    609  // We are reading into branch1's buffer.
    610  const byobRequest1 = rs.controller.byobRequest;
    611  assert_not_equals(byobRequest1, null);
    612  assert_typed_array_equals(byobRequest1.view, new Uint8Array([0x11]), 'byobRequest1.view');
    613 
    614  // Cancelling branch2 should not affect the BYOB request.
    615  const cancel2 = reader2.cancel();
    616  const result2 = await read2;
    617  assert_equals(result2.done, true);
    618  assert_equals(result2.value, undefined);
    619  await flushAsyncEvents();
    620  const byobRequest2 = rs.controller.byobRequest;
    621  assert_typed_array_equals(byobRequest2.view, new Uint8Array([0x11]), 'byobRequest2.view');
    622 
    623  // Cancelling branch1 should invalidate the BYOB request.
    624  const cancel1 = reader1.cancel();
    625  await cancelCalled;
    626  const byobRequest3 = rs.controller.byobRequest;
    627  assert_equals(byobRequest3, null);
    628  const result1 = await read1;
    629  assert_equals(result1.done, true);
    630  assert_equals(result1.value, undefined);
    631 
    632  await Promise.all([cancel1, cancel2]);
    633 
    634 }, 'ReadableStream teeing with byte source: read from branch1 and branch2, cancel branch2, cancel branch1');
    635 
    636 promise_test(async () => {
    637 
    638  const rs = recordingReadableStream({ type: 'bytes' });
    639 
    640  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    641 
    642  const read1 = reader1.read(new Uint8Array([0x11]));
    643  await flushAsyncEvents();
    644  const read2 = reader2.read(new Uint8Array([0x22]));
    645  await flushAsyncEvents();
    646 
    647  // We are reading into branch1's buffer.
    648  assert_typed_array_equals(rs.controller.byobRequest.view, new Uint8Array([0x11]), 'first byobRequest.view');
    649 
    650  // Cancelling branch2 should not affect the BYOB request.
    651  reader2.cancel();
    652  const result2 = await read2;
    653  assert_equals(result2.done, true);
    654  assert_equals(result2.value, undefined);
    655  await flushAsyncEvents();
    656  assert_typed_array_equals(rs.controller.byobRequest.view, new Uint8Array([0x11]), 'second byobRequest.view');
    657 
    658  // Respond to the BYOB request.
    659  rs.controller.byobRequest.view[0] = 0x33;
    660  rs.controller.byobRequest.respond(1);
    661 
    662  // branch1 should receive the read chunk.
    663  const result1 = await read1;
    664  assert_equals(result1.done, false);
    665  assert_typed_array_equals(result1.value, new Uint8Array([0x33]), 'first read() from branch1');
    666 
    667 }, 'ReadableStream teeing with byte source: read from branch1 and branch2, cancel branch2, enqueue to branch1');
    668 
    669 promise_test(async () => {
    670 
    671  const rs = recordingReadableStream({ type: 'bytes' });
    672 
    673  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    674 
    675  const read1 = reader1.read(new Uint8Array([0x11]));
    676  await flushAsyncEvents();
    677  const read2 = reader2.read(new Uint8Array([0x22]));
    678  await flushAsyncEvents();
    679 
    680  // We are reading into branch1's buffer.
    681  assert_typed_array_equals(rs.controller.byobRequest.view, new Uint8Array([0x11]), 'first byobRequest.view');
    682 
    683  // Cancelling branch1 should not affect the BYOB request.
    684  reader1.cancel();
    685  const result1 = await read1;
    686  assert_equals(result1.done, true);
    687  assert_equals(result1.value, undefined);
    688  await flushAsyncEvents();
    689  assert_typed_array_equals(rs.controller.byobRequest.view, new Uint8Array([0x11]), 'second byobRequest.view');
    690 
    691  // Respond to the BYOB request.
    692  rs.controller.byobRequest.view[0] = 0x33;
    693  rs.controller.byobRequest.respond(1);
    694 
    695  // branch2 should receive the read chunk.
    696  const result2 = await read2;
    697  assert_equals(result2.done, false);
    698  assert_typed_array_equals(result2.value, new Uint8Array([0x33]), 'first read() from branch2');
    699 
    700 }, 'ReadableStream teeing with byte source: read from branch1 and branch2, cancel branch1, respond to branch2');
    701 
    702 promise_test(async () => {
    703 
    704  let pullCount = 0;
    705  const byobRequestDefined = [];
    706  const rs = new ReadableStream({
    707    type: 'bytes',
    708    pull(c) {
    709      ++pullCount;
    710      byobRequestDefined.push(c.byobRequest !== null);
    711      c.enqueue(new Uint8Array([pullCount]));
    712    }
    713  });
    714 
    715  const [branch1, _] = rs.tee();
    716  const reader1 = branch1.getReader({ mode: 'byob' });
    717 
    718  const result1 = await reader1.read(new Uint8Array([0x11]));
    719  assert_equals(result1.done, false, 'first read should not be done');
    720  assert_typed_array_equals(result1.value, new Uint8Array([0x1]), 'first read');
    721  assert_equals(pullCount, 1, 'pull() should be called once');
    722  assert_equals(byobRequestDefined[0], true, 'should have created a BYOB request for first read');
    723 
    724  reader1.releaseLock();
    725  const reader2 = branch1.getReader();
    726 
    727  const result2 = await reader2.read();
    728  assert_equals(result2.done, false, 'second read should not be done');
    729  assert_typed_array_equals(result2.value, new Uint8Array([0x2]), 'second read');
    730  assert_equals(pullCount, 2, 'pull() should be called twice');
    731  assert_equals(byobRequestDefined[1], false, 'should not have created a BYOB request for second read');
    732 
    733 }, 'ReadableStream teeing with byte source: pull with BYOB reader, then pull with default reader');
    734 
    735 promise_test(async () => {
    736 
    737  let pullCount = 0;
    738  const byobRequestDefined = [];
    739  const rs = new ReadableStream({
    740    type: 'bytes',
    741    pull(c) {
    742      ++pullCount;
    743      byobRequestDefined.push(c.byobRequest !== null);
    744      c.enqueue(new Uint8Array([pullCount]));
    745    }
    746  });
    747 
    748  const [branch1, _] = rs.tee();
    749  const reader1 = branch1.getReader();
    750 
    751  const result1 = await reader1.read();
    752  assert_equals(result1.done, false, 'first read should not be done');
    753  assert_typed_array_equals(result1.value, new Uint8Array([0x1]), 'first read');
    754  assert_equals(pullCount, 1, 'pull() should be called once');
    755  assert_equals(byobRequestDefined[0], false, 'should not have created a BYOB request for first read');
    756 
    757  reader1.releaseLock();
    758  const reader2 = branch1.getReader({ mode: 'byob' });
    759 
    760  const result2 = await reader2.read(new Uint8Array([0x22]));
    761  assert_equals(result2.done, false, 'second read should not be done');
    762  assert_typed_array_equals(result2.value, new Uint8Array([0x2]), 'second read');
    763  assert_equals(pullCount, 2, 'pull() should be called twice');
    764  assert_equals(byobRequestDefined[1], true, 'should have created a BYOB request for second read');
    765 
    766 }, 'ReadableStream teeing with byte source: pull with default reader, then pull with BYOB reader');
    767 
    768 promise_test(async () => {
    769 
    770  const rs = recordingReadableStream({
    771    type: 'bytes'
    772  });
    773  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    774 
    775  // Wait for each branch's start() promise to resolve.
    776  await flushAsyncEvents();
    777 
    778  const read2 = reader2.read(new Uint8Array([0x22]));
    779  const read1 = reader1.read(new Uint8Array([0x11]));
    780  await flushAsyncEvents();
    781 
    782  // branch2 should provide the BYOB request.
    783  const byobRequest = rs.controller.byobRequest;
    784  assert_typed_array_equals(byobRequest.view, new Uint8Array([0x22]), 'first BYOB request');
    785  byobRequest.view[0] = 0x01;
    786  byobRequest.respond(1);
    787 
    788  const result1 = await read1;
    789  assert_equals(result1.done, false, 'first read should not be done');
    790  assert_typed_array_equals(result1.value, new Uint8Array([0x1]), 'first read');
    791 
    792  const result2 = await read2;
    793  assert_equals(result2.done, false, 'second read should not be done');
    794  assert_typed_array_equals(result2.value, new Uint8Array([0x1]), 'second read');
    795 
    796 }, 'ReadableStream teeing with byte source: read from branch2, then read from branch1');
    797 
    798 promise_test(async () => {
    799 
    800  const rs = recordingReadableStream({ type: 'bytes' });
    801  const [branch1, branch2] = rs.tee();
    802  const reader1 = branch1.getReader();
    803  const reader2 = branch2.getReader({ mode: 'byob' });
    804  await flushAsyncEvents();
    805 
    806  const read1 = reader1.read();
    807  const read2 = reader2.read(new Uint8Array([0x22]));
    808  await flushAsyncEvents();
    809 
    810  // There should be no BYOB request.
    811  assert_equals(rs.controller.byobRequest, null, 'first BYOB request');
    812 
    813  // Close the stream.
    814  rs.controller.close();
    815 
    816  const result1 = await read1;
    817  assert_equals(result1.done, true, 'read from branch1 should be done');
    818  assert_equals(result1.value, undefined, 'read from branch1');
    819 
    820  // branch2 should get its buffer back.
    821  const result2 = await read2;
    822  assert_equals(result2.done, true, 'read from branch2 should be done');
    823  assert_typed_array_equals(result2.value, new Uint8Array([0x22]).subarray(0, 0), 'read from branch2');
    824 
    825 }, 'ReadableStream teeing with byte source: read from branch1 with default reader, then close while branch2 has pending BYOB read');
    826 
    827 promise_test(async () => {
    828 
    829  const rs = recordingReadableStream({ type: 'bytes' });
    830  const [branch1, branch2] = rs.tee();
    831  const reader1 = branch1.getReader({ mode: 'byob' });
    832  const reader2 = branch2.getReader();
    833  await flushAsyncEvents();
    834 
    835  const read2 = reader2.read();
    836  const read1 = reader1.read(new Uint8Array([0x11]));
    837  await flushAsyncEvents();
    838 
    839  // There should be no BYOB request.
    840  assert_equals(rs.controller.byobRequest, null, 'first BYOB request');
    841 
    842  // Close the stream.
    843  rs.controller.close();
    844 
    845  const result2 = await read2;
    846  assert_equals(result2.done, true, 'read from branch2 should be done');
    847  assert_equals(result2.value, undefined, 'read from branch2');
    848 
    849  // branch1 should get its buffer back.
    850  const result1 = await read1;
    851  assert_equals(result1.done, true, 'read from branch1 should be done');
    852  assert_typed_array_equals(result1.value, new Uint8Array([0x11]).subarray(0, 0), 'read from branch1');
    853 
    854 }, 'ReadableStream teeing with byte source: read from branch2 with default reader, then close while branch1 has pending BYOB read');
    855 
    856 promise_test(async () => {
    857 
    858  const rs = recordingReadableStream({ type: 'bytes' });
    859  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    860  await flushAsyncEvents();
    861 
    862  const read1 = reader1.read(new Uint8Array([0x11]));
    863  const read2 = reader2.read(new Uint8Array([0x22]));
    864  await flushAsyncEvents();
    865 
    866  // branch1 should provide the BYOB request.
    867  const byobRequest = rs.controller.byobRequest;
    868  assert_typed_array_equals(byobRequest.view, new Uint8Array([0x11]), 'first BYOB request');
    869 
    870  // Close the stream.
    871  rs.controller.close();
    872  byobRequest.respond(0);
    873 
    874  // Both branches should get their buffers back.
    875  const result1 = await read1;
    876  assert_equals(result1.done, true, 'first read should be done');
    877  assert_typed_array_equals(result1.value, new Uint8Array([0x11]).subarray(0, 0), 'first read');
    878 
    879  const result2 = await read2;
    880  assert_equals(result2.done, true, 'second read should be done');
    881  assert_typed_array_equals(result2.value, new Uint8Array([0x22]).subarray(0, 0), 'second read');
    882 
    883 }, 'ReadableStream teeing with byte source: close when both branches have pending BYOB reads');
    884 
    885 promise_test(async () => {
    886 
    887  const rs = recordingReadableStream({ type: 'bytes' });
    888 
    889  const [reader1, reader2] = rs.tee().map(branch => branch.getReader());
    890  const branch1Reads = [reader1.read(), reader1.read()];
    891  const branch2Reads = [reader2.read(), reader2.read()];
    892 
    893  await flushAsyncEvents();
    894  rs.controller.enqueue(new Uint8Array([0x11]));
    895  rs.controller.close();
    896 
    897  const result1 = await branch1Reads[0];
    898  assert_equals(result1.done, false, 'first read() from branch1 should be not done');
    899  assert_typed_array_equals(result1.value, new Uint8Array([0x11]), 'first chunk from branch1 should be correct');
    900  const result2 = await branch2Reads[0];
    901  assert_equals(result2.done, false, 'first read() from branch2 should be not done');
    902  assert_typed_array_equals(result2.value, new Uint8Array([0x11]), 'first chunk from branch2 should be correct');
    903 
    904  assert_object_equals(await branch1Reads[1], { value: undefined, done: true }, 'second read() from branch1 should be done');
    905  assert_object_equals(await branch2Reads[1], { value: undefined, done: true }, 'second read() from branch2 should be done');
    906 
    907 }, 'ReadableStream teeing with byte source: enqueue() and close() while both branches are pulling');
    908 
    909 promise_test(async () => {
    910 
    911  const rs = recordingReadableStream({ type: 'bytes' });
    912 
    913  const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' }));
    914  const branch1Reads = [reader1.read(new Uint8Array(1)), reader1.read(new Uint8Array(1))];
    915  const branch2Reads = [reader2.read(new Uint8Array(1)), reader2.read(new Uint8Array(1))];
    916 
    917  await flushAsyncEvents();
    918  rs.controller.byobRequest.view[0] = 0x11;
    919  rs.controller.byobRequest.respond(1);
    920  rs.controller.close();
    921 
    922  const result1 = await branch1Reads[0];
    923  assert_equals(result1.done, false, 'first read() from branch1 should be not done');
    924  assert_typed_array_equals(result1.value, new Uint8Array([0x11]), 'first chunk from branch1 should be correct');
    925  const result2 = await branch2Reads[0];
    926  assert_equals(result2.done, false, 'first read() from branch2 should be not done');
    927  assert_typed_array_equals(result2.value, new Uint8Array([0x11]), 'first chunk from branch2 should be correct');
    928 
    929  const result3 = await branch1Reads[1];
    930  assert_equals(result3.done, true, 'second read() from branch1 should be done');
    931  assert_typed_array_equals(result3.value, new Uint8Array([0]).subarray(0, 0), 'second chunk from branch1 should be correct');
    932  const result4 = await branch2Reads[1];
    933  assert_equals(result4.done, true, 'second read() from branch2 should be done');
    934  assert_typed_array_equals(result4.value, new Uint8Array([0]).subarray(0, 0), 'second chunk from branch2 should be correct');
    935 
    936 }, 'ReadableStream teeing with byte source: respond() and close() while both branches are pulling');
    937 
    938 promise_test(async t => {
    939  let pullCount = 0;
    940  const arrayBuffer = new Uint8Array([0x01, 0x02, 0x03]).buffer;
    941  const enqueuedChunk = new Uint8Array(arrayBuffer, 2);
    942  assert_equals(enqueuedChunk.length, 1);
    943  assert_equals(enqueuedChunk.byteOffset, 2);
    944  const rs = new ReadableStream({
    945    type: 'bytes',
    946    pull(c) {
    947      ++pullCount;
    948      if (pullCount === 1) {
    949        c.enqueue(enqueuedChunk);
    950      }
    951    }
    952  });
    953 
    954  const [branch1, branch2] = rs.tee();
    955  const reader1 = branch1.getReader();
    956  const reader2 = branch2.getReader();
    957 
    958  const [result1, result2] = await Promise.all([reader1.read(), reader2.read()]);
    959  assert_equals(result1.done, false, 'reader1 done');
    960  assert_equals(result2.done, false, 'reader2 done');
    961 
    962  const view1 = result1.value;
    963  const view2 = result2.value;
    964  // The first stream has the transferred buffer, but the second stream has the
    965  // cloned buffer.
    966  const underlying = new Uint8Array([0x01, 0x02, 0x03]).buffer;
    967  assert_typed_array_equals(view1, new Uint8Array(underlying, 2), 'reader1 value');
    968  assert_typed_array_equals(view2, new Uint8Array([0x03]), 'reader2 value');
    969 }, 'ReadableStream teeing with byte source: reading an array with a byte offset should clone correctly');