tor-browser

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

default-reader.any.js (14046B)


      1 // META: global=window,worker,shadowrealm
      2 // META: script=../resources/rs-utils.js
      3 'use strict';
      4 
      5 test(() => {
      6 
      7  assert_throws_js(TypeError, () => new ReadableStreamDefaultReader('potato'));
      8  assert_throws_js(TypeError, () => new ReadableStreamDefaultReader({}));
      9  assert_throws_js(TypeError, () => new ReadableStreamDefaultReader());
     10 
     11 }, 'ReadableStreamDefaultReader constructor should get a ReadableStream object as argument');
     12 
     13 test(() => {
     14 
     15  const rsReader = new ReadableStreamDefaultReader(new ReadableStream());
     16  assert_equals(rsReader.closed, rsReader.closed, 'closed should return the same promise');
     17 
     18 }, 'ReadableStreamDefaultReader closed should always return the same promise object');
     19 
     20 test(() => {
     21 
     22  const rs = new ReadableStream();
     23  new ReadableStreamDefaultReader(rs); // Constructing directly the first time should be fine.
     24  assert_throws_js(TypeError, () => new ReadableStreamDefaultReader(rs),
     25                   'constructing directly the second time should fail');
     26 
     27 }, 'Constructing a ReadableStreamDefaultReader directly should fail if the stream is already locked (via direct ' +
     28   'construction)');
     29 
     30 test(() => {
     31 
     32  const rs = new ReadableStream();
     33  new ReadableStreamDefaultReader(rs); // Constructing directly should be fine.
     34  assert_throws_js(TypeError, () => rs.getReader(), 'getReader() should fail');
     35 
     36 }, 'Getting a ReadableStreamDefaultReader via getReader should fail if the stream is already locked (via direct ' +
     37   'construction)');
     38 
     39 test(() => {
     40 
     41  const rs = new ReadableStream();
     42  rs.getReader(); // getReader() should be fine.
     43  assert_throws_js(TypeError, () => new ReadableStreamDefaultReader(rs), 'constructing directly should fail');
     44 
     45 }, 'Constructing a ReadableStreamDefaultReader directly should fail if the stream is already locked (via getReader)');
     46 
     47 test(() => {
     48 
     49  const rs = new ReadableStream();
     50  rs.getReader(); // getReader() should be fine.
     51  assert_throws_js(TypeError, () => rs.getReader(), 'getReader() should fail');
     52 
     53 }, 'Getting a ReadableStreamDefaultReader via getReader should fail if the stream is already locked (via getReader)');
     54 
     55 test(() => {
     56 
     57  const rs = new ReadableStream({
     58    start(c) {
     59      c.close();
     60    }
     61  });
     62 
     63  new ReadableStreamDefaultReader(rs); // Constructing directly should not throw.
     64 
     65 }, 'Constructing a ReadableStreamDefaultReader directly should be OK if the stream is closed');
     66 
     67 test(() => {
     68 
     69  const theError = new Error('don\'t say i didn\'t warn ya');
     70  const rs = new ReadableStream({
     71    start(c) {
     72      c.error(theError);
     73    }
     74  });
     75 
     76  new ReadableStreamDefaultReader(rs); // Constructing directly should not throw.
     77 
     78 }, 'Constructing a ReadableStreamDefaultReader directly should be OK if the stream is errored');
     79 
     80 promise_test(() => {
     81 
     82  let controller;
     83  const rs = new ReadableStream({
     84    start(c) {
     85      controller = c;
     86    }
     87  });
     88  const reader = rs.getReader();
     89 
     90  const promise = reader.read().then(result => {
     91    assert_object_equals(result, { value: 'a', done: false }, 'read() should fulfill with the enqueued chunk');
     92  });
     93 
     94  controller.enqueue('a');
     95  return promise;
     96 
     97 }, 'Reading from a reader for an empty stream will wait until a chunk is available');
     98 
     99 promise_test(() => {
    100 
    101  let cancelCalled = false;
    102  const passedReason = new Error('it wasn\'t the right time, sorry');
    103  const rs = new ReadableStream({
    104    cancel(reason) {
    105      assert_true(rs.locked, 'the stream should still be locked');
    106      assert_throws_js(TypeError, () => rs.getReader(), 'should not be able to get another reader');
    107      assert_equals(reason, passedReason, 'the cancellation reason is passed through to the underlying source');
    108      cancelCalled = true;
    109    }
    110  });
    111 
    112  const reader = rs.getReader();
    113  return reader.cancel(passedReason).then(() => assert_true(cancelCalled));
    114 
    115 }, 'cancel() on a reader does not release the reader');
    116 
    117 promise_test(() => {
    118 
    119  let controller;
    120  const rs = new ReadableStream({
    121    start(c) {
    122      controller = c;
    123    }
    124  });
    125 
    126  const reader = rs.getReader();
    127  const promise = reader.closed;
    128 
    129  controller.close();
    130  return promise;
    131 
    132 }, 'closed should be fulfilled after stream is closed (.closed access before acquiring)');
    133 
    134 promise_test(t => {
    135 
    136  let controller;
    137  const rs = new ReadableStream({
    138    start(c) {
    139      controller = c;
    140    }
    141  });
    142 
    143  const reader1 = rs.getReader();
    144 
    145  reader1.releaseLock();
    146 
    147  const reader2 = rs.getReader();
    148  controller.close();
    149 
    150  return Promise.all([
    151    promise_rejects_js(t, TypeError, reader1.closed),
    152    reader2.closed
    153  ]);
    154 
    155 }, 'closed should be rejected after reader releases its lock (multiple stream locks)');
    156 
    157 promise_test(t => {
    158 
    159  let controller;
    160  const rs = new ReadableStream({
    161    start(c) {
    162      controller = c;
    163    }
    164  });
    165 
    166  const reader = rs.getReader();
    167  const promise1 = reader.closed;
    168 
    169  controller.close();
    170 
    171  reader.releaseLock();
    172  const promise2 = reader.closed;
    173 
    174  assert_not_equals(promise1, promise2, '.closed should be replaced');
    175  return Promise.all([
    176    promise1,
    177    promise_rejects_js(t, TypeError, promise2, '.closed after releasing lock'),
    178  ]);
    179 
    180 }, 'closed is replaced when stream closes and reader releases its lock');
    181 
    182 promise_test(t => {
    183 
    184  const theError = { name: 'unique error' };
    185  let controller;
    186  const rs = new ReadableStream({
    187    start(c) {
    188      controller = c;
    189    }
    190  });
    191 
    192  const reader = rs.getReader();
    193  const promise1 = reader.closed;
    194 
    195  controller.error(theError);
    196 
    197  reader.releaseLock();
    198  const promise2 = reader.closed;
    199 
    200  assert_not_equals(promise1, promise2, '.closed should be replaced');
    201  return Promise.all([
    202    promise_rejects_exactly(t, theError, promise1, '.closed before releasing lock'),
    203    promise_rejects_js(t, TypeError, promise2, '.closed after releasing lock')
    204  ]);
    205 
    206 }, 'closed is replaced when stream errors and reader releases its lock');
    207 
    208 promise_test(() => {
    209 
    210  const rs = new ReadableStream({
    211    start(c) {
    212      c.enqueue('a');
    213      c.enqueue('b');
    214      c.close();
    215    }
    216  });
    217 
    218  const reader1 = rs.getReader();
    219  const promise1 = reader1.read().then(r => {
    220    assert_object_equals(r, { value: 'a', done: false }, 'reading the first chunk from reader1 works');
    221  });
    222  reader1.releaseLock();
    223 
    224  const reader2 = rs.getReader();
    225  const promise2 = reader2.read().then(r => {
    226    assert_object_equals(r, { value: 'b', done: false }, 'reading the second chunk from reader2 works');
    227  });
    228  reader2.releaseLock();
    229 
    230  return Promise.all([promise1, promise2]);
    231 
    232 }, 'Multiple readers can access the stream in sequence');
    233 
    234 promise_test(() => {
    235  const rs = new ReadableStream({
    236    start(c) {
    237      c.enqueue('a');
    238    }
    239  });
    240 
    241  const reader1 = rs.getReader();
    242  reader1.releaseLock();
    243 
    244  const reader2 = rs.getReader();
    245 
    246  // Should be a no-op
    247  reader1.releaseLock();
    248 
    249  return reader2.read().then(result => {
    250    assert_object_equals(result, { value: 'a', done: false },
    251                         'read() should still work on reader2 even after reader1 is released');
    252  });
    253 
    254 }, 'Cannot use an already-released reader to unlock a stream again');
    255 
    256 promise_test(t => {
    257 
    258  const rs = new ReadableStream({
    259    start(c) {
    260      c.enqueue('a');
    261    },
    262    cancel() {
    263      assert_unreached('underlying source cancel should not be called');
    264    }
    265  });
    266 
    267  const reader = rs.getReader();
    268  reader.releaseLock();
    269  const cancelPromise = reader.cancel();
    270 
    271  const reader2 = rs.getReader();
    272  const readPromise = reader2.read().then(r => {
    273    assert_object_equals(r, { value: 'a', done: false }, 'a new reader should be able to read a chunk');
    274  });
    275 
    276  return Promise.all([
    277    promise_rejects_js(t, TypeError, cancelPromise),
    278    readPromise
    279  ]);
    280 
    281 }, 'cancel() on a released reader is a no-op and does not pass through');
    282 
    283 promise_test(t => {
    284 
    285  const promiseAsserts = [];
    286 
    287  let controller;
    288  const theError = { name: 'unique error' };
    289  const rs = new ReadableStream({
    290    start(c) {
    291      controller = c;
    292    }
    293  });
    294 
    295  const reader1 = rs.getReader();
    296 
    297  promiseAsserts.push(
    298    promise_rejects_exactly(t, theError, reader1.closed),
    299    promise_rejects_exactly(t, theError, reader1.read())
    300  );
    301 
    302  assert_throws_js(TypeError, () => rs.getReader(), 'trying to get another reader before erroring should throw');
    303 
    304  controller.error(theError);
    305 
    306  reader1.releaseLock();
    307 
    308  const reader2 = rs.getReader();
    309 
    310  promiseAsserts.push(
    311    promise_rejects_exactly(t, theError, reader2.closed),
    312    promise_rejects_exactly(t, theError, reader2.read())
    313  );
    314 
    315  return Promise.all(promiseAsserts);
    316 
    317 }, 'Getting a second reader after erroring the stream and releasing the reader should succeed');
    318 
    319 promise_test(t => {
    320 
    321  let controller;
    322  const rs = new ReadableStream({
    323    start(c) {
    324      controller = c;
    325    }
    326  });
    327 
    328  const promise = rs.getReader().closed.then(
    329    t.unreached_func('closed promise should not be fulfilled when stream is errored'),
    330    err => {
    331      assert_equals(err, undefined, 'passed error should be undefined as it was');
    332    }
    333  );
    334 
    335  controller.error();
    336  return promise;
    337 
    338 }, 'ReadableStreamDefaultReader closed promise should be rejected with undefined if that is the error');
    339 
    340 
    341 promise_test(t => {
    342 
    343  const rs = new ReadableStream({
    344    start() {
    345      return Promise.reject();
    346    }
    347  });
    348 
    349  return rs.getReader().read().then(
    350    t.unreached_func('read promise should not be fulfilled when stream is errored'),
    351    err => {
    352      assert_equals(err, undefined, 'passed error should be undefined as it was');
    353    }
    354  );
    355 
    356 }, 'ReadableStreamDefaultReader: if start rejects with no parameter, it should error the stream with an undefined ' +
    357    'error');
    358 
    359 promise_test(t => {
    360 
    361  const theError = { name: 'unique string' };
    362  let controller;
    363  const rs = new ReadableStream({
    364    start(c) {
    365      controller = c;
    366    }
    367  });
    368 
    369  const promise = promise_rejects_exactly(t, theError, rs.getReader().closed);
    370 
    371  controller.error(theError);
    372  return promise;
    373 
    374 }, 'Erroring a ReadableStream after checking closed should reject ReadableStreamDefaultReader closed promise');
    375 
    376 promise_test(t => {
    377 
    378  const theError = { name: 'unique string' };
    379  let controller;
    380  const rs = new ReadableStream({
    381    start(c) {
    382      controller = c;
    383    }
    384  });
    385 
    386  controller.error(theError);
    387 
    388  // Let's call getReader twice for extra test coverage of this code path.
    389  rs.getReader().releaseLock();
    390 
    391  return promise_rejects_exactly(t, theError, rs.getReader().closed);
    392 
    393 }, 'Erroring a ReadableStream before checking closed should reject ReadableStreamDefaultReader closed promise');
    394 
    395 promise_test(() => {
    396 
    397  let controller;
    398  const rs = new ReadableStream({
    399    start(c) {
    400      controller = c;
    401    }
    402  });
    403  const reader = rs.getReader();
    404 
    405  const promise = Promise.all([
    406    reader.read().then(result => {
    407      assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (1)');
    408    }),
    409    reader.read().then(result => {
    410      assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (2)');
    411    }),
    412    reader.closed
    413  ]);
    414 
    415  controller.close();
    416  return promise;
    417 
    418 }, 'Reading twice on a stream that gets closed');
    419 
    420 promise_test(() => {
    421 
    422  let controller;
    423  const rs = new ReadableStream({
    424    start(c) {
    425      controller = c;
    426    }
    427  });
    428 
    429  controller.close();
    430  const reader = rs.getReader();
    431 
    432  return Promise.all([
    433    reader.read().then(result => {
    434      assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (1)');
    435    }),
    436    reader.read().then(result => {
    437      assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (2)');
    438    }),
    439    reader.closed
    440  ]);
    441 
    442 }, 'Reading twice on a closed stream');
    443 
    444 promise_test(t => {
    445 
    446  let controller;
    447  const rs = new ReadableStream({
    448    start(c) {
    449      controller = c;
    450    }
    451  });
    452 
    453  const myError = { name: 'mashed potatoes' };
    454  controller.error(myError);
    455 
    456  const reader = rs.getReader();
    457 
    458  return Promise.all([
    459    promise_rejects_exactly(t, myError, reader.read()),
    460    promise_rejects_exactly(t, myError, reader.read()),
    461    promise_rejects_exactly(t, myError, reader.closed)
    462  ]);
    463 
    464 }, 'Reading twice on an errored stream');
    465 
    466 promise_test(t => {
    467 
    468  let controller;
    469  const rs = new ReadableStream({
    470    start(c) {
    471      controller = c;
    472    }
    473  });
    474 
    475  const myError = { name: 'mashed potatoes' };
    476  const reader = rs.getReader();
    477 
    478  const promise = Promise.all([
    479    promise_rejects_exactly(t, myError, reader.read()),
    480    promise_rejects_exactly(t, myError, reader.read()),
    481    promise_rejects_exactly(t, myError, reader.closed)
    482  ]);
    483 
    484  controller.error(myError);
    485  return promise;
    486 
    487 }, 'Reading twice on a stream that gets errored');
    488 
    489 test(() => {
    490  const rs = new ReadableStream();
    491  let toStringCalled = false;
    492  const mode = {
    493    toString() {
    494      toStringCalled = true;
    495      return '';
    496    }
    497  };
    498  assert_throws_js(TypeError, () => rs.getReader({ mode }), 'getReader() should throw');
    499  assert_true(toStringCalled, 'toString() should be called');
    500 }, 'getReader() should call ToString() on mode');
    501 
    502 promise_test(() => {
    503  const rs = new ReadableStream({
    504    pull(controller) {
    505      controller.close();
    506    }
    507  });
    508 
    509  const reader = rs.getReader();
    510  return reader.read().then(() => {
    511    // The test passes if releaseLock() does not throw.
    512    reader.releaseLock();
    513  });
    514 }, 'controller.close() should clear the list of pending read requests');
    515 
    516 promise_test(t => {
    517 
    518  let controller;
    519  const rs = new ReadableStream({
    520    start(c) {
    521      controller = c;
    522    }
    523  });
    524 
    525  const reader1 = rs.getReader();
    526  const promise1 = promise_rejects_js(t, TypeError, reader1.read(), 'read() from reader1 should reject when reader1 is released');
    527  reader1.releaseLock();
    528 
    529  controller.enqueue('a');
    530 
    531  const reader2 = rs.getReader();
    532  const promise2 = reader2.read().then(r => {
    533    assert_object_equals(r, { value: 'a', done: false }, 'read() from reader2 should resolve with enqueued chunk');
    534  })
    535  reader2.releaseLock();
    536 
    537  return Promise.all([promise1, promise2]);
    538 
    539 }, 'Second reader can read chunks after first reader was released with pending read requests');