tor-browser

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

pipe-through.any.js (10144B)


      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 'use strict';
      6 
      7 function duckTypedPassThroughTransform() {
      8  let enqueueInReadable;
      9  let closeReadable;
     10 
     11  return {
     12    writable: new WritableStream({
     13      write(chunk) {
     14        enqueueInReadable(chunk);
     15      },
     16 
     17      close() {
     18        closeReadable();
     19      }
     20    }),
     21 
     22    readable: new ReadableStream({
     23      start(c) {
     24        enqueueInReadable = c.enqueue.bind(c);
     25        closeReadable = c.close.bind(c);
     26      }
     27    })
     28  };
     29 }
     30 
     31 function uninterestingReadableWritablePair() {
     32  return { writable: new WritableStream(), readable: new ReadableStream() };
     33 }
     34 
     35 promise_test(() => {
     36  const readableEnd = sequentialReadableStream(5).pipeThrough(duckTypedPassThroughTransform());
     37 
     38  return readableStreamToArray(readableEnd).then(chunks =>
     39    assert_array_equals(chunks, [1, 2, 3, 4, 5]), 'chunks should match');
     40 }, 'Piping through a duck-typed pass-through transform stream should work');
     41 
     42 promise_test(() => {
     43  const transform = {
     44    writable: new WritableStream({
     45      start(c) {
     46        c.error(new Error('this rejection should not be reported as unhandled'));
     47      }
     48    }),
     49    readable: new ReadableStream()
     50  };
     51 
     52  sequentialReadableStream(5).pipeThrough(transform);
     53 
     54  // The test harness should complain about unhandled rejections by then.
     55  return flushAsyncEvents();
     56 
     57 }, 'Piping through a transform errored on the writable end does not cause an unhandled promise rejection');
     58 
     59 test(() => {
     60  let calledPipeTo = false;
     61  class BadReadableStream extends ReadableStream {
     62    pipeTo() {
     63      calledPipeTo = true;
     64    }
     65  }
     66 
     67  const brs = new BadReadableStream({
     68    start(controller) {
     69      controller.close();
     70    }
     71  });
     72  const readable = new ReadableStream();
     73  const writable = new WritableStream();
     74  const result = brs.pipeThrough({ readable, writable });
     75 
     76  assert_false(calledPipeTo, 'the overridden pipeTo should not have been called');
     77  assert_equals(result, readable, 'return value should be the passed readable property');
     78 }, 'pipeThrough should not call pipeTo on this');
     79 
     80 test(t => {
     81  let calledFakePipeTo = false;
     82  const realPipeTo = ReadableStream.prototype.pipeTo;
     83  t.add_cleanup(() => {
     84    ReadableStream.prototype.pipeTo = realPipeTo;
     85  });
     86  ReadableStream.prototype.pipeTo = () => {
     87    calledFakePipeTo = true;
     88  };
     89  const rs = new ReadableStream();
     90  const readable = new ReadableStream();
     91  const writable = new WritableStream();
     92  const result = rs.pipeThrough({ readable, writable });
     93 
     94  assert_false(calledFakePipeTo, 'the monkey-patched pipeTo should not have been called');
     95  assert_equals(result, readable, 'return value should be the passed readable property');
     96 
     97 }, 'pipeThrough should not call pipeTo on the ReadableStream prototype');
     98 
     99 const badReadables = [null, undefined, 0, NaN, true, 'ReadableStream', Object.create(ReadableStream.prototype)];
    100 for (const readable of badReadables) {
    101  test(() => {
    102    assert_throws_js(TypeError,
    103                     ReadableStream.prototype.pipeThrough.bind(readable, uninterestingReadableWritablePair()),
    104                     'pipeThrough should throw');
    105  }, `pipeThrough should brand-check this and not allow '${readable}'`);
    106 
    107  test(() => {
    108    const rs = new ReadableStream();
    109    let writableGetterCalled = false;
    110    assert_throws_js(
    111      TypeError,
    112      () => rs.pipeThrough({
    113        get writable() {
    114          writableGetterCalled = true;
    115          return new WritableStream();
    116        },
    117        readable
    118      }),
    119      'pipeThrough should brand-check readable'
    120    );
    121    assert_false(writableGetterCalled, 'writable should not have been accessed');
    122  }, `pipeThrough should brand-check readable and not allow '${readable}'`);
    123 }
    124 
    125 const badWritables = [null, undefined, 0, NaN, true, 'WritableStream', Object.create(WritableStream.prototype)];
    126 for (const writable of badWritables) {
    127  test(() => {
    128    const rs = new ReadableStream({
    129      start(c) {
    130        c.close();
    131      }
    132    });
    133    let readableGetterCalled = false;
    134    assert_throws_js(TypeError, () => rs.pipeThrough({
    135      get readable() {
    136        readableGetterCalled = true;
    137        return new ReadableStream();
    138      },
    139      writable
    140    }),
    141                  'pipeThrough should brand-check writable');
    142    assert_true(readableGetterCalled, 'readable should have been accessed');
    143  }, `pipeThrough should brand-check writable and not allow '${writable}'`);
    144 }
    145 
    146 test(t => {
    147  const error = new Error();
    148  error.name = 'custom';
    149 
    150  const rs = new ReadableStream({
    151    pull: t.unreached_func('pull should not be called')
    152  }, { highWaterMark: 0 });
    153 
    154  const throwingWritable = {
    155    readable: rs,
    156    get writable() {
    157      throw error;
    158    }
    159  };
    160  assert_throws_exactly(error,
    161                        () => ReadableStream.prototype.pipeThrough.call(rs, throwingWritable, {}),
    162                        'pipeThrough should rethrow the error thrown by the writable getter');
    163 
    164  const throwingReadable = {
    165    get readable() {
    166      throw error;
    167    },
    168    writable: {}
    169  };
    170  assert_throws_exactly(error,
    171                        () => ReadableStream.prototype.pipeThrough.call(rs, throwingReadable, {}),
    172                        'pipeThrough should rethrow the error thrown by the readable getter');
    173 
    174 }, 'pipeThrough should rethrow errors from accessing readable or writable');
    175 
    176 const badSignals = [null, 0, NaN, true, 'AbortSignal', Object.create(AbortSignal.prototype)];
    177 for (const signal of badSignals) {
    178  test(() => {
    179    const rs = new ReadableStream();
    180    assert_throws_js(TypeError, () => rs.pipeThrough(uninterestingReadableWritablePair(), { signal }),
    181                     'pipeThrough should throw');
    182  }, `invalid values of signal should throw; specifically '${signal}'`);
    183 }
    184 
    185 test(() => {
    186  const rs = new ReadableStream();
    187  const controller = new AbortController();
    188  const signal = controller.signal;
    189  rs.pipeThrough(uninterestingReadableWritablePair(), { signal });
    190 }, 'pipeThrough should accept a real AbortSignal');
    191 
    192 test(() => {
    193  const rs = new ReadableStream();
    194  rs.getReader();
    195  assert_throws_js(TypeError, () => rs.pipeThrough(uninterestingReadableWritablePair()),
    196                   'pipeThrough should throw');
    197 }, 'pipeThrough should throw if this is locked');
    198 
    199 test(() => {
    200  const rs = new ReadableStream();
    201  const writable = new WritableStream();
    202  const readable = new ReadableStream();
    203  writable.getWriter();
    204  assert_throws_js(TypeError, () => rs.pipeThrough({writable, readable}),
    205                   'pipeThrough should throw');
    206 }, 'pipeThrough should throw if writable is locked');
    207 
    208 test(() => {
    209  const rs = new ReadableStream();
    210  const writable = new WritableStream();
    211  const readable = new ReadableStream();
    212  readable.getReader();
    213  assert_equals(rs.pipeThrough({ writable, readable }), readable,
    214                'pipeThrough should not throw');
    215 }, 'pipeThrough should not care if readable is locked');
    216 
    217 promise_test(() => {
    218  const rs = recordingReadableStream();
    219  const writable = new WritableStream({
    220    start(controller) {
    221      controller.error();
    222    }
    223  });
    224  const readable = new ReadableStream();
    225  rs.pipeThrough({ writable, readable }, { preventCancel: true });
    226  return flushAsyncEvents(0).then(() => {
    227    assert_array_equals(rs.events, ['pull'], 'cancel should not have been called');
    228  });
    229 }, 'preventCancel should work');
    230 
    231 promise_test(() => {
    232  const rs = new ReadableStream({
    233    start(controller) {
    234      controller.close();
    235    }
    236  });
    237  const writable = recordingWritableStream();
    238  const readable = new ReadableStream();
    239  rs.pipeThrough({ writable, readable }, { preventClose: true });
    240  return flushAsyncEvents(0).then(() => {
    241    assert_array_equals(writable.events, [], 'writable should not be closed');
    242  });
    243 }, 'preventClose should work');
    244 
    245 promise_test(() => {
    246  const rs = new ReadableStream({
    247    start(controller) {
    248      controller.error();
    249    }
    250  });
    251  const writable = recordingWritableStream();
    252  const readable = new ReadableStream();
    253  rs.pipeThrough({ writable, readable }, { preventAbort: true });
    254  return flushAsyncEvents(0).then(() => {
    255    assert_array_equals(writable.events, [], 'writable should not be aborted');
    256  });
    257 }, 'preventAbort should work');
    258 
    259 test(() => {
    260  const rs = new ReadableStream();
    261  const readable = new ReadableStream();
    262  const writable = new WritableStream();
    263  assert_throws_js(TypeError, () => rs.pipeThrough({readable, writable}, {
    264    get preventAbort() {
    265      writable.getWriter();
    266    }
    267  }), 'pipeThrough should throw');
    268 }, 'pipeThrough() should throw if an option getter grabs a writer');
    269 
    270 test(() => {
    271  const rs = new ReadableStream();
    272  const readable = new ReadableStream();
    273  const writable = new WritableStream();
    274  rs.pipeThrough({readable, writable}, null);
    275 }, 'pipeThrough() should not throw if option is null');
    276 
    277 test(() => {
    278  const rs = new ReadableStream();
    279  const readable = new ReadableStream();
    280  const writable = new WritableStream();
    281  rs.pipeThrough({readable, writable}, {signal:undefined});
    282 }, 'pipeThrough() should not throw if signal is undefined');
    283 
    284 function tryPipeThrough(pair, options)
    285 {
    286  const rs = new ReadableStream();
    287  if (!pair)
    288    pair = {readable:new ReadableStream(), writable:new WritableStream()};
    289  try {
    290    rs.pipeThrough(pair, options)
    291  } catch (e) {
    292    return e;
    293  }
    294 }
    295 
    296 test(() => {
    297  let result = tryPipeThrough({
    298    get readable() {
    299      return new ReadableStream();
    300    },
    301    get writable() {
    302      throw "writable threw";
    303    }
    304  }, { });
    305  assert_equals(result, "writable threw");
    306 
    307  result = tryPipeThrough({
    308    get readable() {
    309      throw "readable threw";
    310    },
    311    get writable() {
    312      throw "writable threw";
    313    }
    314  }, { });
    315  assert_equals(result, "readable threw");
    316 
    317  result = tryPipeThrough({
    318    get readable() {
    319      throw "readable threw";
    320    },
    321    get writable() {
    322      throw "writable threw";
    323    }
    324  }, {
    325    get preventAbort() {
    326      throw "preventAbort threw";
    327    }
    328  });
    329  assert_equals(result, "readable threw");
    330 
    331 }, 'pipeThrough() should throw if readable/writable getters throw');