tor-browser

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

aborting.any.js (50491B)


      1 // META: global=window,worker,shadowrealm
      2 // META: script=../resources/test-utils.js
      3 // META: script=../resources/recording-streams.js
      4 'use strict';
      5 
      6 const error1 = new Error('error1');
      7 error1.name = 'error1';
      8 
      9 const error2 = new Error('error2');
     10 error2.name = 'error2';
     11 
     12 promise_test(t => {
     13  const ws = new WritableStream({
     14    write: t.unreached_func('write() should not be called')
     15  });
     16 
     17  const writer = ws.getWriter();
     18  const writePromise = writer.write('a');
     19 
     20  const readyPromise = writer.ready;
     21 
     22  writer.abort(error1);
     23 
     24  assert_equals(writer.ready, readyPromise, 'the ready promise property should not change');
     25 
     26  return Promise.all([
     27    promise_rejects_exactly(t, error1, readyPromise, 'the ready promise should reject with error1'),
     28    promise_rejects_exactly(t, error1, writePromise, 'the write() promise should reject with error1')
     29  ]);
     30 }, 'Aborting a WritableStream before it starts should cause the writer\'s unsettled ready promise to reject');
     31 
     32 promise_test(t => {
     33  const ws = new WritableStream();
     34 
     35  const writer = ws.getWriter();
     36  writer.write('a');
     37 
     38  const readyPromise = writer.ready;
     39 
     40  return readyPromise.then(() => {
     41    writer.abort(error1);
     42 
     43    assert_not_equals(writer.ready, readyPromise, 'the ready promise property should change');
     44    return promise_rejects_exactly(t, error1, writer.ready, 'the ready promise should reject with error1');
     45  });
     46 }, 'Aborting a WritableStream should cause the writer\'s fulfilled ready promise to reset to a rejected one');
     47 
     48 promise_test(t => {
     49  const ws = new WritableStream();
     50  const writer = ws.getWriter();
     51 
     52  writer.releaseLock();
     53 
     54  return promise_rejects_js(t, TypeError, writer.abort(), 'abort() should reject with a TypeError');
     55 }, 'abort() on a released writer rejects');
     56 
     57 promise_test(t => {
     58  const ws = recordingWritableStream();
     59 
     60  return delay(0)
     61    .then(() => {
     62      const writer = ws.getWriter();
     63 
     64      const abortPromise = writer.abort(error1);
     65 
     66      return Promise.all([
     67        promise_rejects_exactly(t, error1, writer.write(1), 'write(1) must reject with error1'),
     68        promise_rejects_exactly(t, error1, writer.write(2), 'write(2) must reject with error1'),
     69        abortPromise
     70      ]);
     71    })
     72    .then(() => {
     73      assert_array_equals(ws.events, ['abort', error1]);
     74    });
     75 }, 'Aborting a WritableStream immediately prevents future writes');
     76 
     77 promise_test(t => {
     78  const ws = recordingWritableStream();
     79  const results = [];
     80 
     81  return delay(0)
     82    .then(() => {
     83      const writer = ws.getWriter();
     84 
     85      results.push(
     86        writer.write(1),
     87        promise_rejects_exactly(t, error1, writer.write(2), 'write(2) must reject with error1'),
     88        promise_rejects_exactly(t, error1, writer.write(3), 'write(3) must reject with error1')
     89      );
     90 
     91      const abortPromise = writer.abort(error1);
     92 
     93      results.push(
     94        promise_rejects_exactly(t, error1, writer.write(4), 'write(4) must reject with error1'),
     95        promise_rejects_exactly(t, error1, writer.write(5), 'write(5) must reject with error1')
     96      );
     97 
     98      return abortPromise;
     99    }).then(() => {
    100      assert_array_equals(ws.events, ['write', 1, 'abort', error1]);
    101 
    102      return Promise.all(results);
    103    });
    104 }, 'Aborting a WritableStream prevents further writes after any that are in progress');
    105 
    106 promise_test(() => {
    107  const ws = new WritableStream({
    108    abort() {
    109      return 'Hello';
    110    }
    111  });
    112  const writer = ws.getWriter();
    113 
    114  return writer.abort('a').then(value => {
    115    assert_equals(value, undefined, 'fulfillment value must be undefined');
    116  });
    117 }, 'Fulfillment value of writer.abort() call must be undefined even if the underlying sink returns a non-undefined ' +
    118   'value');
    119 
    120 promise_test(t => {
    121  const ws = new WritableStream({
    122    abort() {
    123      throw error1;
    124    }
    125  });
    126  const writer = ws.getWriter();
    127 
    128  return promise_rejects_exactly(t, error1, writer.abort(undefined),
    129    'rejection reason of abortPromise must be the error thrown by abort');
    130 }, 'WritableStream if sink\'s abort throws, the promise returned by writer.abort() rejects');
    131 
    132 promise_test(t => {
    133  const ws = new WritableStream({
    134    abort() {
    135      throw error1;
    136    }
    137  });
    138  const writer = ws.getWriter();
    139 
    140  const abortPromise1 = writer.abort(undefined);
    141  const abortPromise2 = writer.abort(undefined);
    142 
    143  assert_equals(abortPromise1, abortPromise2, 'the promises must be the same');
    144 
    145  return promise_rejects_exactly(t, error1, abortPromise1, 'promise must have matching rejection');
    146 }, 'WritableStream if sink\'s abort throws, the promise returned by multiple writer.abort()s is the same and rejects');
    147 
    148 promise_test(t => {
    149  const ws = new WritableStream({
    150    abort() {
    151      throw error1;
    152    }
    153  });
    154 
    155  return promise_rejects_exactly(t, error1, ws.abort(undefined),
    156    'rejection reason of abortPromise must be the error thrown by abort');
    157 }, 'WritableStream if sink\'s abort throws, the promise returned by ws.abort() rejects');
    158 
    159 promise_test(t => {
    160  let resolveWritePromise;
    161  const ws = new WritableStream({
    162    write() {
    163      return new Promise(resolve => {
    164        resolveWritePromise = resolve;
    165      });
    166    },
    167    abort() {
    168      throw error1;
    169    }
    170  });
    171 
    172  const writer = ws.getWriter();
    173 
    174  writer.write().catch(() => {});
    175  return flushAsyncEvents().then(() => {
    176    const abortPromise = writer.abort(undefined);
    177 
    178    resolveWritePromise();
    179    return promise_rejects_exactly(t, error1, abortPromise,
    180      'rejection reason of abortPromise must be the error thrown by abort');
    181  });
    182 }, 'WritableStream if sink\'s abort throws, for an abort performed during a write, the promise returned by ' +
    183   'ws.abort() rejects');
    184 
    185 promise_test(() => {
    186  const ws = recordingWritableStream();
    187  const writer = ws.getWriter();
    188 
    189  return writer.abort(error1).then(() => {
    190    assert_array_equals(ws.events, ['abort', error1]);
    191  });
    192 }, 'Aborting a WritableStream passes through the given reason');
    193 
    194 promise_test(t => {
    195  const ws = new WritableStream();
    196  const writer = ws.getWriter();
    197 
    198  const abortPromise = writer.abort(error1);
    199 
    200  const events = [];
    201  writer.ready.catch(() => {
    202    events.push('ready');
    203  });
    204  writer.closed.catch(() => {
    205    events.push('closed');
    206  });
    207 
    208  return Promise.all([
    209    abortPromise,
    210    promise_rejects_exactly(t, error1, writer.write(), 'writing should reject with error1'),
    211    promise_rejects_exactly(t, error1, writer.close(), 'closing should reject with error1'),
    212    promise_rejects_exactly(t, error1, writer.ready, 'ready should reject with error1'),
    213    promise_rejects_exactly(t, error1, writer.closed, 'closed should reject with error1')
    214  ]).then(() => {
    215    assert_array_equals(['ready', 'closed'], events, 'ready should reject before closed');
    216  });
    217 }, 'Aborting a WritableStream puts it in an errored state with the error passed to abort()');
    218 
    219 promise_test(t => {
    220  const ws = new WritableStream();
    221  const writer = ws.getWriter();
    222 
    223  const writePromise = promise_rejects_exactly(t, error1, writer.write('a'),
    224    'writing should reject with error1');
    225 
    226  writer.abort(error1);
    227 
    228  return writePromise;
    229 }, 'Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied');
    230 
    231 promise_test(t => {
    232  const ws = recordingWritableStream();
    233  const writer = ws.getWriter();
    234 
    235  const closePromise = writer.close();
    236  const abortPromise = writer.abort(error1);
    237 
    238  return Promise.all([
    239    promise_rejects_exactly(t, error1, writer.closed, 'closed should reject with error1'),
    240    promise_rejects_exactly(t, error1, closePromise, 'close() should reject with error1'),
    241    abortPromise
    242  ]).then(() => {
    243    assert_array_equals(ws.events, ['abort', error1]);
    244  });
    245 }, 'Closing but then immediately aborting a WritableStream causes the stream to error');
    246 
    247 promise_test(() => {
    248  let resolveClose;
    249  const ws = new WritableStream({
    250    close() {
    251      return new Promise(resolve => {
    252        resolveClose = resolve;
    253      });
    254    }
    255  });
    256  const writer = ws.getWriter();
    257 
    258  const closePromise = writer.close();
    259 
    260  return delay(0).then(() => {
    261    const abortPromise = writer.abort(error1);
    262    resolveClose();
    263    return Promise.all([
    264      writer.closed,
    265      abortPromise,
    266      closePromise
    267    ]);
    268  });
    269 }, 'Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt');
    270 
    271 promise_test(() => {
    272  const ws = new WritableStream();
    273  const writer = ws.getWriter();
    274 
    275  writer.close();
    276 
    277  return delay(0).then(() => writer.abort());
    278 }, 'Aborting a WritableStream after it is closed is a no-op');
    279 
    280 promise_test(t => {
    281  // Testing that per https://github.com/whatwg/streams/issues/620#issuecomment-263483953 the fallback to close was
    282  // removed.
    283 
    284  // Cannot use recordingWritableStream since it always has an abort
    285  let closeCalled = false;
    286  const ws = new WritableStream({
    287    close() {
    288      closeCalled = true;
    289    }
    290  });
    291 
    292  const writer = ws.getWriter();
    293 
    294  writer.abort(error1);
    295 
    296  return promise_rejects_exactly(t, error1, writer.closed, 'closed should reject with error1').then(() => {
    297    assert_false(closeCalled, 'close must not have been called');
    298  });
    299 }, 'WritableStream should NOT call underlying sink\'s close if no abort is supplied (historical)');
    300 
    301 promise_test(() => {
    302  let thenCalled = false;
    303  const ws = new WritableStream({
    304    abort() {
    305      return {
    306        then(onFulfilled) {
    307          thenCalled = true;
    308          onFulfilled();
    309        }
    310      };
    311    }
    312  });
    313  const writer = ws.getWriter();
    314  return writer.abort().then(() => assert_true(thenCalled, 'then() should be called'));
    315 }, 'returning a thenable from abort() should work');
    316 
    317 promise_test(t => {
    318  const ws = new WritableStream({
    319    write() {
    320      return flushAsyncEvents();
    321    }
    322  });
    323  const writer = ws.getWriter();
    324  return writer.ready.then(() => {
    325    const writePromise = writer.write('a');
    326    writer.abort(error1);
    327    let closedRejected = false;
    328    return Promise.all([
    329      writePromise.then(() => assert_false(closedRejected, '.closed should not resolve before write()')),
    330      promise_rejects_exactly(t, error1, writer.closed, '.closed should reject').then(() => {
    331        closedRejected = true;
    332      })
    333    ]);
    334  });
    335 }, '.closed should not resolve before fulfilled write()');
    336 
    337 promise_test(t => {
    338  const ws = new WritableStream({
    339    write() {
    340      return Promise.reject(error1);
    341    }
    342  });
    343  const writer = ws.getWriter();
    344  return writer.ready.then(() => {
    345    const writePromise = writer.write('a');
    346    const abortPromise = writer.abort(error2);
    347    let closedRejected = false;
    348    return Promise.all([
    349      promise_rejects_exactly(t, error1, writePromise, 'write() should reject')
    350          .then(() => assert_false(closedRejected, '.closed should not resolve before write()')),
    351      promise_rejects_exactly(t, error2, writer.closed, '.closed should reject')
    352          .then(() => {
    353            closedRejected = true;
    354          }),
    355      abortPromise
    356    ]);
    357  });
    358 }, '.closed should not resolve before rejected write(); write() error should not overwrite abort() error');
    359 
    360 promise_test(t => {
    361  const ws = new WritableStream({
    362    write() {
    363      return flushAsyncEvents();
    364    }
    365  }, new CountQueuingStrategy({ highWaterMark: 4 }));
    366  const writer = ws.getWriter();
    367  return writer.ready.then(() => {
    368    const settlementOrder = [];
    369    return Promise.all([
    370      writer.write('1').then(() => settlementOrder.push(1)),
    371      promise_rejects_exactly(t, error1, writer.write('2'), 'first queued write should be rejected')
    372          .then(() => settlementOrder.push(2)),
    373      promise_rejects_exactly(t, error1, writer.write('3'), 'second queued write should be rejected')
    374          .then(() => settlementOrder.push(3)),
    375      writer.abort(error1)
    376    ]).then(() => assert_array_equals([1, 2, 3], settlementOrder, 'writes should be satisfied in order'));
    377  });
    378 }, 'writes should be satisfied in order when aborting');
    379 
    380 promise_test(t => {
    381  const ws = new WritableStream({
    382    write() {
    383      return Promise.reject(error1);
    384    }
    385  }, new CountQueuingStrategy({ highWaterMark: 4 }));
    386  const writer = ws.getWriter();
    387  return writer.ready.then(() => {
    388    const settlementOrder = [];
    389    return Promise.all([
    390      promise_rejects_exactly(t, error1, writer.write('1'), 'in-flight write should be rejected')
    391          .then(() => settlementOrder.push(1)),
    392      promise_rejects_exactly(t, error2, writer.write('2'), 'first queued write should be rejected')
    393          .then(() => settlementOrder.push(2)),
    394      promise_rejects_exactly(t, error2, writer.write('3'), 'second queued write should be rejected')
    395          .then(() => settlementOrder.push(3)),
    396      writer.abort(error2)
    397    ]).then(() => assert_array_equals([1, 2, 3], settlementOrder, 'writes should be satisfied in order'));
    398  });
    399 }, 'writes should be satisfied in order after rejected write when aborting');
    400 
    401 promise_test(t => {
    402  const ws = new WritableStream({
    403    write() {
    404      return Promise.reject(error1);
    405    }
    406  });
    407  const writer = ws.getWriter();
    408  return writer.ready.then(() => {
    409    return Promise.all([
    410      promise_rejects_exactly(t, error1, writer.write('a'), 'writer.write() should reject with error from underlying write()'),
    411      promise_rejects_exactly(t, error2, writer.close(),
    412                              'writer.close() should reject with error from underlying write()'),
    413      writer.abort(error2)
    414    ]);
    415  });
    416 }, 'close() should reject with abort reason why abort() is first error');
    417 
    418 promise_test(() => {
    419  let resolveWrite;
    420  const ws = recordingWritableStream({
    421    write() {
    422      return new Promise(resolve => {
    423        resolveWrite = resolve;
    424      });
    425    }
    426  });
    427 
    428  const writer = ws.getWriter();
    429  return writer.ready.then(() => {
    430    writer.write('a');
    431    const abortPromise = writer.abort('b');
    432    return flushAsyncEvents().then(() => {
    433      assert_array_equals(ws.events, ['write', 'a'], 'abort should not be called while write is in-flight');
    434      resolveWrite();
    435      return abortPromise.then(() => {
    436        assert_array_equals(ws.events, ['write', 'a', 'abort', 'b'], 'abort should be called after the write finishes');
    437      });
    438    });
    439  });
    440 }, 'underlying abort() should not be called until underlying write() completes');
    441 
    442 promise_test(() => {
    443  let resolveClose;
    444  const ws = recordingWritableStream({
    445    close() {
    446      return new Promise(resolve => {
    447        resolveClose = resolve;
    448      });
    449    }
    450  });
    451 
    452  const writer = ws.getWriter();
    453  return writer.ready.then(() => {
    454    writer.close();
    455    const abortPromise = writer.abort();
    456    return flushAsyncEvents().then(() => {
    457      assert_array_equals(ws.events, ['close'], 'abort should not be called while close is in-flight');
    458      resolveClose();
    459      return abortPromise.then(() => {
    460        assert_array_equals(ws.events, ['close'], 'abort should not be called');
    461      });
    462    });
    463  });
    464 }, 'underlying abort() should not be called if underlying close() has started');
    465 
    466 promise_test(t => {
    467  let rejectClose;
    468  let abortCalled = false;
    469  const ws = new WritableStream({
    470    close() {
    471      return new Promise((resolve, reject) => {
    472        rejectClose = reject;
    473      });
    474    },
    475    abort() {
    476      abortCalled = true;
    477    }
    478  });
    479 
    480  const writer = ws.getWriter();
    481  return writer.ready.then(() => {
    482    const closePromise = writer.close();
    483    const abortPromise = writer.abort();
    484    return flushAsyncEvents().then(() => {
    485      assert_false(abortCalled, 'underlying abort should not be called while close is in-flight');
    486      rejectClose(error1);
    487      return promise_rejects_exactly(t, error1, abortPromise, 'abort should reject with the same reason').then(() => {
    488        return promise_rejects_exactly(t, error1, closePromise, 'close should reject with the same reason');
    489      }).then(() => {
    490        assert_false(abortCalled, 'underlying abort should not be called after close completes');
    491      });
    492    });
    493  });
    494 }, 'if underlying close() has started and then rejects, the abort() and close() promises should reject with the ' +
    495   'underlying close rejection reason');
    496 
    497 promise_test(t => {
    498  let resolveWrite;
    499  const ws = recordingWritableStream({
    500    write() {
    501      return new Promise(resolve => {
    502        resolveWrite = resolve;
    503      });
    504    }
    505  });
    506 
    507  const writer = ws.getWriter();
    508  return writer.ready.then(() => {
    509    writer.write('a');
    510    const closePromise = writer.close();
    511    const abortPromise = writer.abort(error1);
    512 
    513    return flushAsyncEvents().then(() => {
    514      assert_array_equals(ws.events, ['write', 'a'], 'abort should not be called while write is in-flight');
    515      resolveWrite();
    516      return abortPromise.then(() => {
    517        assert_array_equals(ws.events, ['write', 'a', 'abort', error1], 'abort should be called after write completes');
    518        return promise_rejects_exactly(t, error1, closePromise, 'promise returned by close() should be rejected');
    519      });
    520    });
    521  });
    522 }, 'an abort() that happens during a write() should trigger the underlying abort() even with a close() queued');
    523 
    524 promise_test(t => {
    525  const ws = new WritableStream({
    526    write() {
    527      return new Promise(() => {});
    528    }
    529  });
    530 
    531  const writer = ws.getWriter();
    532  return writer.ready.then(() => {
    533    writer.write('a');
    534    writer.abort(error1);
    535    writer.releaseLock();
    536    const writer2 = ws.getWriter();
    537    return promise_rejects_exactly(t, error1, writer2.ready,
    538                                   'ready of the second writer should be rejected with error1');
    539  });
    540 }, 'if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error');
    541 
    542 promise_test(() => {
    543  const ws = new WritableStream();
    544  const writer = ws.getWriter();
    545  return writer.ready.then(() => {
    546    const closePromise = writer.close();
    547    const abortPromise = writer.abort();
    548    const events = [];
    549    return Promise.all([
    550      closePromise.then(() => { events.push('close'); }),
    551      abortPromise.then(() => { events.push('abort'); })
    552    ]).then(() => {
    553      assert_array_equals(events, ['close', 'abort']);
    554    });
    555  });
    556 }, 'writer close() promise should resolve before abort() promise');
    557 
    558 promise_test(t => {
    559  const ws = new WritableStream({
    560    write(chunk, controller) {
    561      controller.error(error1);
    562      return new Promise(() => {});
    563    }
    564  });
    565  const writer = ws.getWriter();
    566  return writer.ready.then(() => {
    567    writer.write('a');
    568    return promise_rejects_exactly(t, error1, writer.ready, 'writer.ready should reject');
    569  });
    570 }, 'writer.ready should reject on controller error without waiting for underlying write');
    571 
    572 promise_test(t => {
    573  let rejectWrite;
    574  const ws = new WritableStream({
    575    write() {
    576      return new Promise((resolve, reject) => {
    577        rejectWrite = reject;
    578      });
    579    }
    580  });
    581 
    582  let writePromise;
    583  let abortPromise;
    584 
    585  const events = [];
    586 
    587  const writer = ws.getWriter();
    588 
    589  writer.closed.catch(() => {
    590    events.push('closed');
    591  });
    592 
    593  // Wait for ws to start
    594  return flushAsyncEvents().then(() => {
    595    writePromise = writer.write('a');
    596    writePromise.catch(() => {
    597      events.push('writePromise');
    598    });
    599 
    600    abortPromise = writer.abort(error1);
    601    abortPromise.then(() => {
    602      events.push('abortPromise');
    603    });
    604 
    605    const writePromise2 = writer.write('a');
    606 
    607    return Promise.all([
    608      promise_rejects_exactly(t, error1, writePromise2, 'writePromise2 must reject with the error from abort'),
    609      promise_rejects_exactly(t, error1, writer.ready, 'writer.ready must reject with the error from abort'),
    610      flushAsyncEvents()
    611    ]);
    612  }).then(() => {
    613    assert_array_equals(events, [], 'writePromise, abortPromise and writer.closed must not be rejected yet');
    614 
    615    rejectWrite(error2);
    616 
    617    return Promise.all([
    618      promise_rejects_exactly(t, error2, writePromise,
    619                              'writePromise must reject with the error returned from the sink\'s write method'),
    620      abortPromise,
    621      promise_rejects_exactly(t, error1, writer.closed,
    622                              'writer.closed must reject with the error from abort'),
    623      flushAsyncEvents()
    624    ]);
    625  }).then(() => {
    626    assert_array_equals(events, ['writePromise', 'abortPromise', 'closed'],
    627                        'writePromise, abortPromise and writer.closed must settle');
    628 
    629    const writePromise3 = writer.write('a');
    630 
    631    return Promise.all([
    632      promise_rejects_exactly(t, error1, writePromise3,
    633                              'writePromise3 must reject with the error from abort'),
    634      promise_rejects_exactly(t, error1, writer.ready,
    635                              'writer.ready must be still rejected with the error indicating abort')
    636    ]);
    637  }).then(() => {
    638    writer.releaseLock();
    639 
    640    return Promise.all([
    641      promise_rejects_js(t, TypeError, writer.ready,
    642                      'writer.ready must be rejected with an error indicating release'),
    643      promise_rejects_js(t, TypeError, writer.closed,
    644                      'writer.closed must be rejected with an error indicating release')
    645    ]);
    646  });
    647 }, 'writer.abort() while there is an in-flight write, and then finish the write with rejection');
    648 
    649 promise_test(t => {
    650  let resolveWrite;
    651  let controller;
    652  const ws = new WritableStream({
    653    write(chunk, c) {
    654      controller = c;
    655      return new Promise(resolve => {
    656        resolveWrite = resolve;
    657      });
    658    }
    659  });
    660 
    661  let writePromise;
    662  let abortPromise;
    663 
    664  const events = [];
    665 
    666  const writer = ws.getWriter();
    667 
    668  writer.closed.catch(() => {
    669    events.push('closed');
    670  });
    671 
    672  // Wait for ws to start
    673  return flushAsyncEvents().then(() => {
    674    writePromise = writer.write('a');
    675    writePromise.then(() => {
    676      events.push('writePromise');
    677    });
    678 
    679    abortPromise = writer.abort(error1);
    680    abortPromise.then(() => {
    681      events.push('abortPromise');
    682    });
    683 
    684    const writePromise2 = writer.write('a');
    685 
    686    return Promise.all([
    687      promise_rejects_exactly(t, error1, writePromise2, 'writePromise2 must reject with the error from abort'),
    688      promise_rejects_exactly(t, error1, writer.ready, 'writer.ready must reject with the error from abort'),
    689      flushAsyncEvents()
    690    ]);
    691  }).then(() => {
    692    assert_array_equals(events, [], 'writePromise, abortPromise and writer.closed must not be fulfilled/rejected yet');
    693 
    694    // This error is too late to change anything. abort() has already changed the stream state to 'erroring'.
    695    controller.error(error2);
    696 
    697    const writePromise3 = writer.write('a');
    698 
    699    return Promise.all([
    700      promise_rejects_exactly(t, error1, writePromise3,
    701                              'writePromise3 must reject with the error from abort'),
    702      promise_rejects_exactly(t, error1, writer.ready,
    703                              'writer.ready must be still rejected with the error indicating abort'),
    704      flushAsyncEvents()
    705    ]);
    706  }).then(() => {
    707    assert_array_equals(
    708        events, [],
    709        'writePromise, abortPromise and writer.closed must not be fulfilled/rejected yet even after ' +
    710            'controller.error() call');
    711 
    712    resolveWrite();
    713 
    714    return Promise.all([
    715      writePromise,
    716      abortPromise,
    717      promise_rejects_exactly(t, error1, writer.closed,
    718                              'writer.closed must reject with the error from abort'),
    719      flushAsyncEvents()
    720    ]);
    721  }).then(() => {
    722    assert_array_equals(events, ['writePromise', 'abortPromise', 'closed'],
    723                        'writePromise, abortPromise and writer.closed must settle');
    724 
    725    const writePromise4 = writer.write('a');
    726 
    727    return Promise.all([
    728      writePromise,
    729      promise_rejects_exactly(t, error1, writePromise4,
    730                              'writePromise4 must reject with the error from abort'),
    731      promise_rejects_exactly(t, error1, writer.ready,
    732                              'writer.ready must be still rejected with the error indicating abort')
    733    ]);
    734  }).then(() => {
    735    writer.releaseLock();
    736 
    737    return Promise.all([
    738      promise_rejects_js(t, TypeError, writer.ready,
    739                      'writer.ready must be rejected with an error indicating release'),
    740      promise_rejects_js(t, TypeError, writer.closed,
    741                      'writer.closed must be rejected with an error indicating release')
    742    ]);
    743  });
    744 }, 'writer.abort(), controller.error() while there is an in-flight write, and then finish the write');
    745 
    746 promise_test(t => {
    747  let resolveClose;
    748  let controller;
    749  const ws = new WritableStream({
    750    start(c) {
    751      controller = c;
    752    },
    753    close() {
    754      return new Promise(resolve => {
    755        resolveClose = resolve;
    756      });
    757    }
    758  });
    759 
    760  let closePromise;
    761  let abortPromise;
    762 
    763  const events = [];
    764 
    765  const writer = ws.getWriter();
    766 
    767  writer.closed.then(() => {
    768    events.push('closed');
    769  });
    770 
    771  // Wait for ws to start
    772  return flushAsyncEvents().then(() => {
    773    closePromise = writer.close();
    774    closePromise.then(() => {
    775      events.push('closePromise');
    776    });
    777 
    778    abortPromise = writer.abort(error1);
    779    abortPromise.then(() => {
    780      events.push('abortPromise');
    781    });
    782 
    783    return Promise.all([
    784      promise_rejects_js(t, TypeError, writer.close(),
    785        'writer.close() must reject with an error indicating already closing'),
    786      promise_rejects_exactly(t, error1, writer.ready, 'writer.ready must reject with the error from abort'),
    787      flushAsyncEvents()
    788    ]);
    789  }).then(() => {
    790    assert_array_equals(events, [], 'closePromise, abortPromise and writer.closed must not be fulfilled/rejected yet');
    791 
    792    controller.error(error2);
    793 
    794    return Promise.all([
    795      promise_rejects_js(t, TypeError, writer.close(),
    796        'writer.close() must reject with an error indicating already closing'),
    797      promise_rejects_exactly(t, error1, writer.ready,
    798                              'writer.ready must be still rejected with the error indicating abort'),
    799      flushAsyncEvents()
    800    ]);
    801  }).then(() => {
    802    assert_array_equals(
    803        events, [],
    804        'closePromise, abortPromise and writer.closed must not be fulfilled/rejected yet even after ' +
    805            'controller.error() call');
    806 
    807    resolveClose();
    808 
    809    return Promise.all([
    810      closePromise,
    811      abortPromise,
    812      writer.closed,
    813      flushAsyncEvents()
    814    ]);
    815  }).then(() => {
    816    assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
    817                        'closedPromise, abortPromise and writer.closed must fulfill');
    818 
    819    return Promise.all([
    820      promise_rejects_js(t, TypeError, writer.close(),
    821        'writer.close() must reject with an error indicating already closing'),
    822      promise_rejects_exactly(t, error1, writer.ready,
    823                              'writer.ready must be still rejected with the error indicating abort')
    824    ]);
    825  }).then(() => {
    826    writer.releaseLock();
    827 
    828    return Promise.all([
    829      promise_rejects_js(t, TypeError, writer.close(),
    830        'writer.close() must reject with an error indicating release'),
    831      promise_rejects_js(t, TypeError, writer.ready,
    832                      'writer.ready must be rejected with an error indicating release'),
    833      promise_rejects_js(t, TypeError, writer.closed,
    834                      'writer.closed must be rejected with an error indicating release')
    835    ]);
    836  });
    837 }, 'writer.abort(), controller.error() while there is an in-flight close, and then finish the close');
    838 
    839 promise_test(t => {
    840  let resolveWrite;
    841  let controller;
    842  const ws = recordingWritableStream({
    843    write(chunk, c) {
    844      controller = c;
    845      return new Promise(resolve => {
    846        resolveWrite = resolve;
    847      });
    848    }
    849  });
    850 
    851  let writePromise;
    852  let abortPromise;
    853 
    854  const events = [];
    855 
    856  const writer = ws.getWriter();
    857 
    858  writer.closed.catch(() => {
    859    events.push('closed');
    860  });
    861 
    862  // Wait for ws to start
    863  return flushAsyncEvents().then(() => {
    864    writePromise = writer.write('a');
    865    writePromise.then(() => {
    866      events.push('writePromise');
    867    });
    868 
    869    controller.error(error2);
    870 
    871    const writePromise2 = writer.write('a');
    872 
    873    return Promise.all([
    874      promise_rejects_exactly(t, error2, writePromise2,
    875                              'writePromise2 must reject with the error passed to the controller\'s error method'),
    876      promise_rejects_exactly(t, error2, writer.ready,
    877                              'writer.ready must reject with the error passed to the controller\'s error method'),
    878      flushAsyncEvents()
    879    ]);
    880  }).then(() => {
    881    assert_array_equals(events, [], 'writePromise and writer.closed must not be fulfilled/rejected yet');
    882 
    883    abortPromise = writer.abort(error1);
    884    abortPromise.catch(() => {
    885      events.push('abortPromise');
    886    });
    887 
    888    const writePromise3 = writer.write('a');
    889 
    890    return Promise.all([
    891      promise_rejects_exactly(t, error2, writePromise3,
    892                              'writePromise3 must reject with the error passed to the controller\'s error method'),
    893      flushAsyncEvents()
    894    ]);
    895  }).then(() => {
    896    assert_array_equals(
    897        events, [],
    898        'writePromise and writer.closed must not be fulfilled/rejected yet even after writer.abort()');
    899 
    900    resolveWrite();
    901 
    902    return Promise.all([
    903      promise_rejects_exactly(t, error2, abortPromise,
    904                              'abort() must reject with the error passed to the controller\'s error method'),
    905      promise_rejects_exactly(t, error2, writer.closed,
    906                              'writer.closed must reject with the error passed to the controller\'s error method'),
    907      flushAsyncEvents()
    908    ]);
    909  }).then(() => {
    910    assert_array_equals(events, ['writePromise', 'abortPromise', 'closed'],
    911                        'writePromise, abortPromise and writer.closed must fulfill/reject');
    912    assert_array_equals(ws.events, ['write', 'a'], 'sink abort() should not be called');
    913 
    914    const writePromise4 = writer.write('a');
    915 
    916    return Promise.all([
    917      writePromise,
    918      promise_rejects_exactly(t, error2, writePromise4,
    919                              'writePromise4 must reject with the error passed to the controller\'s error method'),
    920      promise_rejects_exactly(t, error2, writer.ready,
    921                              'writer.ready must be still rejected with the error passed to the controller\'s error method')
    922    ]);
    923  }).then(() => {
    924    writer.releaseLock();
    925 
    926    return Promise.all([
    927      promise_rejects_js(t, TypeError, writer.ready,
    928                      'writer.ready must be rejected with an error indicating release'),
    929      promise_rejects_js(t, TypeError, writer.closed,
    930                      'writer.closed must be rejected with an error indicating release')
    931    ]);
    932  });
    933 }, 'controller.error(), writer.abort() while there is an in-flight write, and then finish the write');
    934 
    935 promise_test(t => {
    936  let resolveClose;
    937  let controller;
    938  const ws = new WritableStream({
    939    start(c) {
    940      controller = c;
    941    },
    942    close() {
    943      return new Promise(resolve => {
    944        resolveClose = resolve;
    945      });
    946    }
    947  });
    948 
    949  let closePromise;
    950  let abortPromise;
    951 
    952  const events = [];
    953 
    954  const writer = ws.getWriter();
    955 
    956  writer.closed.then(() => {
    957    events.push('closed');
    958  });
    959 
    960  // Wait for ws to start
    961  return flushAsyncEvents().then(() => {
    962    closePromise = writer.close();
    963    closePromise.then(() => {
    964      events.push('closePromise');
    965    });
    966 
    967    controller.error(error2);
    968 
    969    return flushAsyncEvents();
    970  }).then(() => {
    971    assert_array_equals(events, [], 'closePromise must not be fulfilled/rejected yet');
    972 
    973    abortPromise = writer.abort(error1);
    974    abortPromise.then(() => {
    975      events.push('abortPromise');
    976    });
    977 
    978    return Promise.all([
    979      promise_rejects_exactly(t, error2, writer.ready,
    980                              'writer.ready must reject with the error passed to the controller\'s error method'),
    981      flushAsyncEvents()
    982    ]);
    983  }).then(() => {
    984    assert_array_equals(
    985        events, [],
    986        'closePromise and writer.closed must not be fulfilled/rejected yet even after writer.abort()');
    987 
    988    resolveClose();
    989 
    990    return Promise.all([
    991      closePromise,
    992      promise_rejects_exactly(t, error2, writer.ready,
    993                              'writer.ready must be still rejected with the error passed to the controller\'s error method'),
    994      writer.closed,
    995      flushAsyncEvents()
    996    ]);
    997  }).then(() => {
    998    assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
    999                        'abortPromise, closePromise and writer.closed must fulfill/reject');
   1000  }).then(() => {
   1001    writer.releaseLock();
   1002 
   1003    return Promise.all([
   1004      promise_rejects_js(t, TypeError, writer.ready,
   1005                      'writer.ready must be rejected with an error indicating release'),
   1006      promise_rejects_js(t, TypeError, writer.closed,
   1007                      'writer.closed must be rejected with an error indicating release')
   1008    ]);
   1009  });
   1010 }, 'controller.error(), writer.abort() while there is an in-flight close, and then finish the close');
   1011 
   1012 promise_test(t => {
   1013  let resolveWrite;
   1014  const ws = new WritableStream({
   1015    write() {
   1016      return new Promise(resolve => {
   1017        resolveWrite = resolve;
   1018      });
   1019    }
   1020  });
   1021  const writer = ws.getWriter();
   1022  return writer.ready.then(() => {
   1023    const writePromise = writer.write('a');
   1024    const closed = writer.closed;
   1025    const abortPromise = writer.abort();
   1026    writer.releaseLock();
   1027    resolveWrite();
   1028    return Promise.all([
   1029      writePromise,
   1030      abortPromise,
   1031      promise_rejects_js(t, TypeError, closed, 'closed should reject')]);
   1032  });
   1033 }, 'releaseLock() while aborting should reject the original closed promise');
   1034 
   1035 // TODO(ricea): Consider removing this test if it is no longer useful.
   1036 promise_test(t => {
   1037  let resolveWrite;
   1038  let resolveAbort;
   1039  let resolveAbortStarted;
   1040  const abortStarted = new Promise(resolve => {
   1041    resolveAbortStarted = resolve;
   1042  });
   1043  const ws = new WritableStream({
   1044    write() {
   1045      return new Promise(resolve => {
   1046        resolveWrite = resolve;
   1047      });
   1048    },
   1049    abort() {
   1050      resolveAbortStarted();
   1051      return new Promise(resolve => {
   1052        resolveAbort = resolve;
   1053      });
   1054    }
   1055  });
   1056  const writer = ws.getWriter();
   1057  return writer.ready.then(() => {
   1058    const writePromise = writer.write('a');
   1059    const closed = writer.closed;
   1060    const abortPromise = writer.abort();
   1061    resolveWrite();
   1062    return abortStarted.then(() => {
   1063      writer.releaseLock();
   1064      assert_equals(writer.closed, closed, 'closed promise should not have changed');
   1065      resolveAbort();
   1066      return Promise.all([
   1067        writePromise,
   1068        abortPromise,
   1069        promise_rejects_js(t, TypeError, closed, 'closed should reject')]);
   1070    });
   1071  });
   1072 }, 'releaseLock() during delayed async abort() should reject the writer.closed promise');
   1073 
   1074 promise_test(() => {
   1075  let resolveStart;
   1076  const ws = recordingWritableStream({
   1077    start() {
   1078      return new Promise(resolve => {
   1079        resolveStart = resolve;
   1080      });
   1081    }
   1082  });
   1083  const abortPromise = ws.abort('done');
   1084  return flushAsyncEvents().then(() => {
   1085    assert_array_equals(ws.events, [], 'abort() should not be called during start()');
   1086    resolveStart();
   1087    return abortPromise.then(() => {
   1088      assert_array_equals(ws.events, ['abort', 'done'], 'abort() should be called after start() is done');
   1089    });
   1090  });
   1091 }, 'sink abort() should not be called until sink start() is done');
   1092 
   1093 promise_test(() => {
   1094  let resolveStart;
   1095  let controller;
   1096  const ws = recordingWritableStream({
   1097    start(c) {
   1098      controller = c;
   1099      return new Promise(resolve => {
   1100        resolveStart = resolve;
   1101      });
   1102    }
   1103  });
   1104  const abortPromise = ws.abort('done');
   1105  controller.error(error1);
   1106  resolveStart();
   1107  return abortPromise.then(() =>
   1108      assert_array_equals(ws.events, ['abort', 'done'],
   1109                          'abort() should still be called if start() errors the controller'));
   1110 }, 'if start attempts to error the controller after abort() has been called, then it should lose');
   1111 
   1112 promise_test(() => {
   1113  const ws = recordingWritableStream({
   1114    start() {
   1115      return Promise.reject(error1);
   1116    }
   1117  });
   1118  return ws.abort('done').then(() =>
   1119      assert_array_equals(ws.events, ['abort', 'done'], 'abort() should still be called if start() rejects'));
   1120 }, 'stream abort() promise should still resolve if sink start() rejects');
   1121 
   1122 promise_test(t => {
   1123  const ws = new WritableStream();
   1124  const writer = ws.getWriter();
   1125  const writerReady1 = writer.ready;
   1126  writer.abort(error1);
   1127  const writerReady2 = writer.ready;
   1128  assert_not_equals(writerReady1, writerReady2, 'abort() should replace the ready promise with a rejected one');
   1129  return Promise.all([writerReady1,
   1130                      promise_rejects_exactly(t, error1, writerReady2, 'writerReady2 should reject')]);
   1131 }, 'writer abort() during sink start() should replace the writer.ready promise synchronously');
   1132 
   1133 promise_test(t => {
   1134  const events = [];
   1135  const ws = recordingWritableStream();
   1136  const writer = ws.getWriter();
   1137  const writePromise1 = writer.write(1);
   1138  const abortPromise = writer.abort(error1);
   1139  const writePromise2 = writer.write(2);
   1140  const closePromise = writer.close();
   1141  writePromise1.catch(() => events.push('write1'));
   1142  abortPromise.then(() => events.push('abort'));
   1143  writePromise2.catch(() => events.push('write2'));
   1144  closePromise.catch(() => events.push('close'));
   1145  return Promise.all([
   1146    promise_rejects_exactly(t, error1, writePromise1, 'first write() should reject'),
   1147    abortPromise,
   1148    promise_rejects_exactly(t, error1, writePromise2, 'second write() should reject'),
   1149    promise_rejects_exactly(t, error1, closePromise, 'close() should reject')
   1150  ])
   1151  .then(() => {
   1152    assert_array_equals(events, ['write2', 'write1', 'abort', 'close'],
   1153                        'promises should resolve in the standard order');
   1154    assert_array_equals(ws.events, ['abort', error1], 'underlying sink write() should not be called');
   1155  });
   1156 }, 'promises returned from other writer methods should be rejected when writer abort() happens during sink start()');
   1157 
   1158 promise_test(t => {
   1159  let writeReject;
   1160  let controller;
   1161  const ws = new WritableStream({
   1162    write(chunk, c) {
   1163      controller = c;
   1164      return new Promise((resolve, reject) => {
   1165        writeReject = reject;
   1166      });
   1167    }
   1168  });
   1169  const writer = ws.getWriter();
   1170  return writer.ready.then(() => {
   1171    const writePromise = writer.write('a');
   1172    const abortPromise = writer.abort();
   1173    controller.error(error1);
   1174    writeReject(error2);
   1175    return Promise.all([
   1176      promise_rejects_exactly(t, error2, writePromise, 'write() should reject with error2'),
   1177      abortPromise
   1178    ]);
   1179  });
   1180 }, 'abort() should succeed despite rejection from write');
   1181 
   1182 promise_test(t => {
   1183  let closeReject;
   1184  let controller;
   1185  const ws = new WritableStream({
   1186    start(c) {
   1187      controller = c;
   1188    },
   1189    close() {
   1190      return new Promise((resolve, reject) => {
   1191        closeReject = reject;
   1192      });
   1193    }
   1194  });
   1195  const writer = ws.getWriter();
   1196  return writer.ready.then(() => {
   1197    const closePromise = writer.close();
   1198    const abortPromise = writer.abort();
   1199    controller.error(error1);
   1200    closeReject(error2);
   1201    return Promise.all([
   1202      promise_rejects_exactly(t, error2, closePromise, 'close() should reject with error2'),
   1203      promise_rejects_exactly(t, error2, abortPromise, 'abort() should reject with error2')
   1204    ]);
   1205  });
   1206 }, 'abort() should be rejected with the rejection returned from close()');
   1207 
   1208 promise_test(t => {
   1209  let rejectWrite;
   1210  const ws = recordingWritableStream({
   1211    write() {
   1212      return new Promise((resolve, reject) => {
   1213        rejectWrite = reject;
   1214      });
   1215    }
   1216  });
   1217  const writer = ws.getWriter();
   1218  return writer.ready.then(() => {
   1219    const writePromise = writer.write('1');
   1220    const abortPromise = writer.abort(error2);
   1221    rejectWrite(error1);
   1222    return Promise.all([
   1223      promise_rejects_exactly(t, error1, writePromise, 'write should reject'),
   1224      abortPromise,
   1225      promise_rejects_exactly(t, error2, writer.closed, 'closed should reject with error2')
   1226    ]);
   1227  }).then(() => {
   1228    assert_array_equals(ws.events, ['write', '1', 'abort', error2], 'abort sink method should be called');
   1229  });
   1230 }, 'a rejecting sink.write() should not prevent sink.abort() from being called');
   1231 
   1232 promise_test(() => {
   1233  const ws = recordingWritableStream({
   1234    start() {
   1235      return Promise.reject(error1);
   1236    }
   1237  });
   1238  return ws.abort(error2)
   1239      .then(() => {
   1240        assert_array_equals(ws.events, ['abort', error2]);
   1241      });
   1242 }, 'when start errors after stream abort(), underlying sink abort() should be called anyway');
   1243 
   1244 promise_test(() => {
   1245  const ws = new WritableStream();
   1246  const abortPromise1 = ws.abort();
   1247  const abortPromise2 = ws.abort();
   1248  assert_equals(abortPromise1, abortPromise2, 'the promises must be the same');
   1249 
   1250  return abortPromise1.then(
   1251      v => assert_equals(v, undefined, 'abort() should fulfill with undefined'));
   1252 }, 'when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined');
   1253 
   1254 promise_test(() => {
   1255  const ws = new WritableStream();
   1256  const abortPromise1 = ws.abort();
   1257 
   1258  return abortPromise1.then(v1 => {
   1259    assert_equals(v1, undefined, 'first abort() should fulfill with undefined');
   1260 
   1261    const abortPromise2 = ws.abort();
   1262    assert_not_equals(abortPromise2, abortPromise1, 'because we waited, the second promise should be a new promise');
   1263 
   1264    return abortPromise2.then(v2 => {
   1265      assert_equals(v2, undefined, 'second abort() should fulfill with undefined');
   1266    });
   1267  });
   1268 }, 'when calling abort() twice on the same stream, but sequentially so so there\'s no pending abort the second time, ' +
   1269   'both should fulfill with undefined');
   1270 
   1271 promise_test(t => {
   1272  const ws = new WritableStream({
   1273    start(c) {
   1274      c.error(error1);
   1275    }
   1276  });
   1277 
   1278  const writer = ws.getWriter();
   1279 
   1280  return promise_rejects_exactly(t, error1, writer.closed, 'writer.closed should reject').then(() => {
   1281    return writer.abort().then(
   1282      v => assert_equals(v, undefined, 'abort() should fulfill with undefined'));
   1283  });
   1284 }, 'calling abort() on an errored stream should fulfill with undefined');
   1285 
   1286 promise_test(t => {
   1287  let controller;
   1288  let resolveWrite;
   1289  const ws = recordingWritableStream({
   1290    start(c) {
   1291      controller = c;
   1292    },
   1293    write() {
   1294      return new Promise(resolve => {
   1295        resolveWrite = resolve;
   1296      });
   1297    }
   1298  });
   1299  const writer = ws.getWriter();
   1300  return writer.ready.then(() => {
   1301    const writePromise = writer.write('chunk');
   1302    controller.error(error1);
   1303    const abortPromise = writer.abort(error2);
   1304    resolveWrite();
   1305    return Promise.all([
   1306      writePromise,
   1307      promise_rejects_exactly(t, error1, abortPromise, 'abort() should reject')
   1308    ]).then(() => {
   1309      assert_array_equals(ws.events, ['write', 'chunk'], 'sink abort() should not be called');
   1310    });
   1311  });
   1312 }, 'sink abort() should not be called if stream was erroring due to controller.error() before abort() was called');
   1313 
   1314 promise_test(t => {
   1315  let resolveWrite;
   1316  let size = 1;
   1317  const ws = recordingWritableStream({
   1318    write() {
   1319      return new Promise(resolve => {
   1320        resolveWrite = resolve;
   1321      });
   1322    }
   1323  }, {
   1324    size() {
   1325      return size;
   1326    },
   1327    highWaterMark: 1
   1328  });
   1329  const writer = ws.getWriter();
   1330  return writer.ready.then(() => {
   1331    const writePromise1 = writer.write('chunk1');
   1332    size = NaN;
   1333    const writePromise2 = writer.write('chunk2');
   1334    const abortPromise = writer.abort(error2);
   1335    resolveWrite();
   1336    return Promise.all([
   1337      writePromise1,
   1338      promise_rejects_js(t, RangeError, writePromise2, 'second write() should reject'),
   1339      promise_rejects_js(t, RangeError, abortPromise, 'abort() should reject')
   1340    ]).then(() => {
   1341      assert_array_equals(ws.events, ['write', 'chunk1'], 'sink abort() should not be called');
   1342    });
   1343  });
   1344 }, 'sink abort() should not be called if stream was erroring due to bad strategy before abort() was called');
   1345 
   1346 promise_test(t => {
   1347  const ws = new WritableStream();
   1348  return ws.abort().then(() => {
   1349    const writer = ws.getWriter();
   1350    return writer.closed.then(t.unreached_func('closed promise should not fulfill'),
   1351                              e => assert_equals(e, undefined, 'e should be undefined'));
   1352  });
   1353 }, 'abort with no arguments should set the stored error to undefined');
   1354 
   1355 promise_test(t => {
   1356  const ws = new WritableStream();
   1357  return ws.abort(undefined).then(() => {
   1358    const writer = ws.getWriter();
   1359    return writer.closed.then(t.unreached_func('closed promise should not fulfill'),
   1360                              e => assert_equals(e, undefined, 'e should be undefined'));
   1361  });
   1362 }, 'abort with an undefined argument should set the stored error to undefined');
   1363 
   1364 promise_test(t => {
   1365  const ws = new WritableStream();
   1366  return ws.abort('string argument').then(() => {
   1367    const writer = ws.getWriter();
   1368    return writer.closed.then(t.unreached_func('closed promise should not fulfill'),
   1369                              e => assert_equals(e, 'string argument', 'e should be \'string argument\''));
   1370  });
   1371 }, 'abort with a string argument should set the stored error to that argument');
   1372 
   1373 promise_test(t => {
   1374  const ws = new WritableStream();
   1375  const writer = ws.getWriter();
   1376  return promise_rejects_js(t, TypeError, ws.abort(), 'abort should reject')
   1377    .then(() => writer.ready);
   1378 }, 'abort on a locked stream should reject');
   1379 
   1380 test(t => {
   1381  let ctrl;
   1382  const ws = new WritableStream({start(c) { ctrl = c; }});
   1383  const e = Error('hello');
   1384 
   1385  assert_true(ctrl.signal instanceof AbortSignal);
   1386  assert_false(ctrl.signal.aborted);
   1387  assert_equals(ctrl.signal.reason, undefined, 'signal.reason before abort');
   1388  ws.abort(e);
   1389  assert_true(ctrl.signal.aborted);
   1390  assert_equals(ctrl.signal.reason, e);
   1391 }, 'WritableStreamDefaultController.signal');
   1392 
   1393 promise_test(async t => {
   1394  let ctrl;
   1395  let resolve;
   1396  const called = new Promise(r => resolve = r);
   1397 
   1398  const ws = new WritableStream({
   1399    start(c) { ctrl = c; },
   1400    write() { resolve(); return new Promise(() => {}); }
   1401  });
   1402  const writer = ws.getWriter();
   1403 
   1404  writer.write(99);
   1405  await called;
   1406 
   1407  assert_false(ctrl.signal.aborted);
   1408  assert_equals(ctrl.signal.reason, undefined, 'signal.reason before abort');
   1409  writer.abort();
   1410  assert_true(ctrl.signal.aborted);
   1411  assert_true(ctrl.signal.reason instanceof DOMException, 'signal.reason is a DOMException');
   1412  assert_equals(ctrl.signal.reason.name, 'AbortError', 'signal.reason is an AbortError');
   1413 }, 'the abort signal is signalled synchronously - write');
   1414 
   1415 promise_test(async t => {
   1416  let ctrl;
   1417  let resolve;
   1418  const called = new Promise(r => resolve = r);
   1419 
   1420  const ws = new WritableStream({
   1421    start(c) { ctrl = c; },
   1422    close() { resolve(); return new Promise(() => {}); }
   1423  });
   1424  const writer = ws.getWriter();
   1425 
   1426  writer.close(99);
   1427  await called;
   1428 
   1429  assert_false(ctrl.signal.aborted);
   1430  writer.abort();
   1431  assert_true(ctrl.signal.aborted);
   1432 }, 'the abort signal is signalled synchronously - close');
   1433 
   1434 promise_test(async t => {
   1435  let ctrl;
   1436  const ws = new WritableStream({start(c) { ctrl = c; }});
   1437  const writer = ws.getWriter();
   1438 
   1439  const e = TypeError();
   1440  ctrl.error(e);
   1441  await promise_rejects_exactly(t, e, writer.closed);
   1442  assert_false(ctrl.signal.aborted);
   1443 }, 'the abort signal is not signalled on error');
   1444 
   1445 promise_test(async t => {
   1446  let ctrl;
   1447  const e = TypeError();
   1448  const ws = new WritableStream({
   1449    start(c) { ctrl = c; },
   1450    async write() { throw e; }
   1451  });
   1452  const writer = ws.getWriter();
   1453 
   1454  await promise_rejects_exactly(t, e, writer.write('hello'), 'write result');
   1455  await promise_rejects_exactly(t, e, writer.closed, 'closed');
   1456  assert_false(ctrl.signal.aborted);
   1457 }, 'the abort signal is not signalled on write failure');
   1458 
   1459 promise_test(async t => {
   1460  let ctrl;
   1461  const e = TypeError();
   1462  const ws = new WritableStream({
   1463    start(c) { ctrl = c; },
   1464    async close() { throw e; }
   1465  });
   1466  const writer = ws.getWriter();
   1467 
   1468  await promise_rejects_exactly(t, e, writer.close(), 'close result');
   1469  await promise_rejects_exactly(t, e, writer.closed, 'closed');
   1470  assert_false(ctrl.signal.aborted);
   1471 }, 'the abort signal is not signalled on close failure');
   1472 
   1473 promise_test(async t => {
   1474  let ctrl;
   1475  let abortPromise;
   1476  let abortPromiseFromSignal;
   1477  const e1 = SyntaxError();
   1478  const e2 = TypeError();
   1479  const ws = new WritableStream({
   1480    start(c) { ctrl = c; },
   1481  });
   1482 
   1483  const writer = ws.getWriter();
   1484  ctrl.signal.addEventListener('abort', () => {
   1485    abortPromiseFromSignal = writer.abort(e2);
   1486  });
   1487  abortPromise = writer.abort(e1);
   1488  assert_true(ctrl.signal.aborted);
   1489 
   1490  await Promise.all([
   1491    abortPromise,
   1492    abortPromiseFromSignal,
   1493    promise_rejects_exactly(t, e2, writer.closed, 'closed')
   1494  ]);
   1495 }, 'recursive abort() call from abort() aborting signal (not started)');
   1496 
   1497 promise_test(async t => {
   1498  let ctrl;
   1499  let abortPromise;
   1500  let abortPromiseFromSignal;
   1501  const e1 = SyntaxError();
   1502  const e2 = TypeError();
   1503  const ws = new WritableStream({
   1504    start(c) { ctrl = c; },
   1505  });
   1506  await flushAsyncEvents(); // ensure stream is started
   1507 
   1508  const writer = ws.getWriter();
   1509  ctrl.signal.addEventListener('abort', () => {
   1510    abortPromiseFromSignal = writer.abort(e2);
   1511  });
   1512  abortPromise = writer.abort(e1);
   1513  assert_true(ctrl.signal.aborted);
   1514 
   1515  await Promise.all([
   1516    abortPromise,
   1517    abortPromiseFromSignal,
   1518    promise_rejects_exactly(t, e2, writer.closed, 'closed')
   1519  ]);
   1520 }, 'recursive abort() call from abort() aborting signal');
   1521 
   1522 promise_test(async t => {
   1523  let ctrl;
   1524  let abortPromise;
   1525  let closePromiseFromSignal;
   1526  const theError = SyntaxError();
   1527  const ws = new WritableStream({
   1528    start(c) { ctrl = c; },
   1529  });
   1530 
   1531  const writer = ws.getWriter();
   1532  ctrl.signal.addEventListener('abort', () => {
   1533    closePromiseFromSignal = writer.close();
   1534  });
   1535  abortPromise = writer.abort(theError);
   1536  assert_true(ctrl.signal.aborted);
   1537 
   1538  await Promise.all([
   1539    abortPromise,
   1540    promise_rejects_exactly(t, theError, closePromiseFromSignal, 'closed'),
   1541    promise_rejects_exactly(t, theError, writer.closed, 'closed')
   1542  ]);
   1543 }, 'recursive close() call from abort() aborting signal (not started)');
   1544 
   1545 promise_test(async t => {
   1546  let ctrl;
   1547  let abortPromise;
   1548  let closePromiseFromSignal;
   1549  const theError = SyntaxError();
   1550  const ws = new WritableStream({
   1551    start(c) { ctrl = c; },
   1552  });
   1553  await flushAsyncEvents(); // ensure stream is started
   1554 
   1555  const writer = ws.getWriter();
   1556  ctrl.signal.addEventListener('abort', () => {
   1557    closePromiseFromSignal = writer.close();
   1558  });
   1559  abortPromise = writer.abort(theError);
   1560  assert_true(ctrl.signal.aborted);
   1561 
   1562  await Promise.all([
   1563    abortPromise,
   1564    closePromiseFromSignal,
   1565    writer.closed
   1566  ]);
   1567 }, 'recursive close() call from abort() aborting signal');