tor-browser

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

WritableStream.cpp (31513B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/WritableStream.h"
      8 
      9 #include "StreamUtils.h"
     10 #include "js/Array.h"
     11 #include "js/PropertyAndElement.h"
     12 #include "js/TypeDecls.h"
     13 #include "js/Value.h"
     14 #include "mozilla/AlreadyAddRefed.h"
     15 #include "mozilla/Assertions.h"
     16 #include "mozilla/Attributes.h"
     17 #include "mozilla/CycleCollectedJSContext.h"
     18 #include "mozilla/HoldDropJSObjects.h"
     19 #include "mozilla/dom/AbortSignal.h"
     20 #include "mozilla/dom/BindingCallContext.h"
     21 #include "mozilla/dom/Promise-inl.h"
     22 #include "mozilla/dom/QueueWithSizes.h"
     23 #include "mozilla/dom/QueuingStrategyBinding.h"
     24 #include "mozilla/dom/ReadRequest.h"
     25 #include "mozilla/dom/RootedDictionary.h"
     26 #include "mozilla/dom/UnderlyingSinkBinding.h"
     27 #include "mozilla/dom/WritableStreamBinding.h"
     28 #include "mozilla/dom/WritableStreamDefaultController.h"
     29 #include "mozilla/dom/WritableStreamDefaultWriter.h"
     30 #include "nsCOMPtr.h"
     31 #include "nsIGlobalObject.h"
     32 #include "nsISupports.h"
     33 
     34 namespace mozilla::dom {
     35 
     36 using namespace streams_abstract;
     37 
     38 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(
     39    WritableStream,
     40    (mGlobal, mCloseRequest, mController, mInFlightWriteRequest,
     41     mInFlightCloseRequest, mPendingAbortRequestPromise, mWriter,
     42     mWriteRequests),
     43    (mPendingAbortRequestReason, mStoredError))
     44 
     45 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStream)
     46 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(WritableStream,
     47                                                   LastRelease())
     48 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStream)
     49  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     50  NS_INTERFACE_MAP_ENTRY(nsISupports)
     51 NS_INTERFACE_MAP_END
     52 
     53 WritableStream::WritableStream(nsIGlobalObject* aGlobal,
     54                               HoldDropJSObjectsCaller aHoldDropCaller)
     55    : mGlobal(aGlobal), mHoldDropCaller(aHoldDropCaller) {
     56  if (mHoldDropCaller == HoldDropJSObjectsCaller::Implicit) {
     57    mozilla::HoldJSObjects(this);
     58  }
     59 }
     60 
     61 WritableStream::WritableStream(const GlobalObject& aGlobal,
     62                               HoldDropJSObjectsCaller aHoldDropCaller)
     63    : mGlobal(do_QueryInterface(aGlobal.GetAsSupports())),
     64      mHoldDropCaller(aHoldDropCaller) {
     65  if (mHoldDropCaller == HoldDropJSObjectsCaller::Implicit) {
     66    mozilla::HoldJSObjects(this);
     67  }
     68 }
     69 
     70 WritableStream::~WritableStream() {
     71  if (mHoldDropCaller == HoldDropJSObjectsCaller::Implicit) {
     72    mozilla::DropJSObjects(this);
     73  }
     74 }
     75 
     76 JSObject* WritableStream::WrapObject(JSContext* aCx,
     77                                     JS::Handle<JSObject*> aGivenProto) {
     78  return WritableStream_Binding::Wrap(aCx, this, aGivenProto);
     79 }
     80 
     81 // https://streams.spec.whatwg.org/#writable-stream-deal-with-rejection
     82 void WritableStream::DealWithRejection(JSContext* aCx,
     83                                       JS::Handle<JS::Value> aError,
     84                                       ErrorResult& aRv) {
     85  // Step 1. Let state be stream.[[state]].
     86  // Step 2. If state is "writable",
     87  if (mState == WriterState::Writable) {
     88    // Step 2.1. Perform ! WritableStreamStartErroring(stream, error).
     89    StartErroring(aCx, aError, aRv);
     90 
     91    // Step 2.2. Return.
     92    return;
     93  }
     94 
     95  // Step 3. Assert: state is "erroring".
     96  MOZ_ASSERT(mState == WriterState::Erroring);
     97 
     98  // Step 4. Perform ! WritableStreamFinishErroring(stream).
     99  FinishErroring(aCx, aRv);
    100 }
    101 
    102 // https://streams.spec.whatwg.org/#writable-stream-finish-erroring
    103 void WritableStream::FinishErroring(JSContext* aCx, ErrorResult& aRv) {
    104  // Step 1. Assert: stream.[[state]] is "erroring".
    105  MOZ_ASSERT(mState == WriterState::Erroring);
    106 
    107  // Step 2. Assert: ! WritableStreamHasOperationMarkedInFlight(stream) is
    108  // false.
    109  MOZ_ASSERT(!HasOperationMarkedInFlight());
    110 
    111  // Step 3. Set stream.[[state]] to "errored".
    112  mState = WriterState::Errored;
    113 
    114  // Step 4. Perform ! stream.[[controller]].[[ErrorSteps]]().
    115  Controller()->ErrorSteps();
    116 
    117  // Step 5. Let storedError be stream.[[storedError]].
    118  JS::Rooted<JS::Value> storedError(aCx, mStoredError);
    119 
    120  // Step 6. For each writeRequest of stream.[[writeRequests]]:
    121  while (!mWriteRequests.IsEmpty()) {
    122    // Step 6.1. Reject writeRequest with storedError.
    123    mWriteRequests.Pop()->MaybeReject(storedError);
    124  }
    125 
    126  // Step 7. Set stream.[[writeRequests]] to an empty list.
    127  // (Popping should make it empty)
    128 
    129  // Step 8. If stream.[[pendingAbortRequest]] is undefined,
    130  if (!mPendingAbortRequestPromise) {
    131    // Step 8.1. Perform !
    132    // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
    133    RejectCloseAndClosedPromiseIfNeeded();
    134 
    135    // Step 8.2. Return.
    136    return;
    137  }
    138 
    139  // Step 9. Let abortRequest be stream.[[pendingAbortRequest]].
    140  RefPtr<Promise> abortPromise = mPendingAbortRequestPromise;
    141  JS::Rooted<JS::Value> abortReason(aCx, mPendingAbortRequestReason);
    142  bool abortWasAlreadyErroring = mPendingAbortRequestWasAlreadyErroring;
    143 
    144  // Step 10. Set stream.[[pendingAbortRequest]] to undefined.
    145  SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue, false);
    146 
    147  // Step 11. If abortRequest’s was already erroring is true,
    148  if (abortWasAlreadyErroring) {
    149    // Step 11.1. Reject abortRequest’s promise with storedError.
    150    abortPromise->MaybeReject(storedError);
    151 
    152    // Step 11.2. Perform !
    153    // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
    154    RejectCloseAndClosedPromiseIfNeeded();
    155 
    156    // Step 11.3. Return.
    157    return;
    158  }
    159 
    160  // Step 12. Let promise be !
    161  // stream.[[controller]].[[AbortSteps]](abortRequest’s reason).
    162  RefPtr<WritableStreamDefaultController> controller = mController;
    163  RefPtr<Promise> promise = controller->AbortSteps(aCx, abortReason, aRv);
    164  if (aRv.Failed()) {
    165    return;
    166  }
    167 
    168  // Step 13 + 14.
    169  promise->AddCallbacksWithCycleCollectedArgs(
    170      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    171         Promise* aAbortRequestPromise, WritableStream* aStream) {
    172        // Step 13. Upon fulfillment of promise,
    173        // Step 13.1. Resolve abortRequest’s promise with undefined.
    174        aAbortRequestPromise->MaybeResolveWithUndefined();
    175 
    176        // Step 13.2. Perform !
    177        // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
    178        aStream->RejectCloseAndClosedPromiseIfNeeded();
    179      },
    180      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    181         Promise* aAbortRequestPromise, WritableStream* aStream) {
    182        // Step 14. Upon rejection of promise with reason reason,
    183        // Step 14.1. Reject abortRequest’s promise with reason.
    184        aAbortRequestPromise->MaybeReject(aValue);
    185 
    186        // Step 14.2. Perform !
    187        // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
    188        aStream->RejectCloseAndClosedPromiseIfNeeded();
    189      },
    190      RefPtr(abortPromise), RefPtr(this));
    191 }
    192 
    193 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close
    194 void WritableStream::FinishInFlightClose() {
    195  // Step 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
    196  MOZ_ASSERT(mInFlightCloseRequest);
    197 
    198  // Step 2. Resolve stream.[[inFlightCloseRequest]] with undefined.
    199  mInFlightCloseRequest->MaybeResolveWithUndefined();
    200 
    201  // Step 3. Set stream.[[inFlightCloseRequest]] to undefined.
    202  mInFlightCloseRequest = nullptr;
    203 
    204  // Step 4. Let state be stream.[[state]].
    205  // Step 5. Assert: stream.[[state]] is "writable" or "erroring".
    206  MOZ_ASSERT(mState == WriterState::Writable ||
    207             mState == WriterState::Erroring);
    208 
    209  // Step 6. If state is "erroring",
    210  if (mState == WriterState::Erroring) {
    211    // Step 6.1. Set stream.[[storedError]] to undefined.
    212    mStoredError.setUndefined();
    213 
    214    // Step 6.2. If stream.[[pendingAbortRequest]] is not undefined,
    215    if (mPendingAbortRequestPromise) {
    216      // Step 6.2.1. Resolve stream.[[pendingAbortRequest]]'s promise with
    217      // undefined.
    218      mPendingAbortRequestPromise->MaybeResolveWithUndefined();
    219 
    220      // Step 6.2.2. Set stream.[[pendingAbortRequest]] to undefined.
    221      SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue, false);
    222    }
    223  }
    224 
    225  // Step 7. Set stream.[[state]] to "closed".
    226  mState = WriterState::Closed;
    227 
    228  // Step 8. Let writer be stream.[[writer]].
    229  // Step 9. If writer is not undefined, resolve writer.[[closedPromise]] with
    230  // undefined.
    231  if (mWriter) {
    232    mWriter->ClosedPromise()->MaybeResolveWithUndefined();
    233  }
    234 
    235  // Step 10. Assert: stream.[[pendingAbortRequest]] is undefined.
    236  MOZ_ASSERT(!mPendingAbortRequestPromise);
    237  // Assert: stream.[[storedError]] is undefined.
    238  MOZ_ASSERT(mStoredError.isUndefined());
    239 }
    240 
    241 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close-with-error
    242 void WritableStream::FinishInFlightCloseWithError(JSContext* aCx,
    243                                                  JS::Handle<JS::Value> aError,
    244                                                  ErrorResult& aRv) {
    245  // Step 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
    246  MOZ_ASSERT(mInFlightCloseRequest);
    247 
    248  // Step 2. Reject stream.[[inFlightCloseRequest]] with error.
    249  mInFlightCloseRequest->MaybeReject(aError);
    250 
    251  // Step 3. Set stream.[[inFlightCloseRequest]] to undefined.
    252  mInFlightCloseRequest = nullptr;
    253 
    254  // Step 4. Assert: stream.[[state]] is "writable" or "erroring".
    255  MOZ_ASSERT(mState == WriterState::Writable ||
    256             mState == WriterState::Erroring);
    257 
    258  // Step 5. If stream.[[pendingAbortRequest]] is not undefined,
    259  if (mPendingAbortRequestPromise) {
    260    // Step 5.1. Reject stream.[[pendingAbortRequest]]'s promise with error.
    261    mPendingAbortRequestPromise->MaybeReject(aError);
    262 
    263    // Step 5.2. Set stream.[[pendingAbortRequest]] to undefined.
    264    SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue, false);
    265  }
    266 
    267  // Step 6. Perform ! WritableStreamDealWithRejection(stream, error).
    268  DealWithRejection(aCx, aError, aRv);
    269 }
    270 
    271 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write
    272 void WritableStream::FinishInFlightWrite() {
    273  // Step 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
    274  MOZ_ASSERT(mInFlightWriteRequest);
    275 
    276  // Step 2. Resolve stream.[[inFlightWriteRequest]] with undefined.
    277  mInFlightWriteRequest->MaybeResolveWithUndefined();
    278 
    279  // Step 3. Set stream.[[inFlightWriteRequest]] to undefined.
    280  mInFlightWriteRequest = nullptr;
    281 }
    282 
    283 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write-with-error
    284 void WritableStream::FinishInFlightWriteWithError(JSContext* aCx,
    285                                                  JS::Handle<JS::Value> aError,
    286                                                  ErrorResult& aRv) {
    287  // Step 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
    288  MOZ_ASSERT(mInFlightWriteRequest);
    289 
    290  // Step 2. Reject stream.[[inFlightWriteRequest]] with error.
    291  mInFlightWriteRequest->MaybeReject(aError);
    292 
    293  // Step 3. Set stream.[[inFlightWriteRequest]] to undefined.
    294  mInFlightWriteRequest = nullptr;
    295 
    296  // Step 4. Assert: stream.[[state]] is "writable" or "erroring".
    297  MOZ_ASSERT(mState == WriterState::Writable ||
    298             mState == WriterState::Erroring);
    299 
    300  // Step 5. Perform ! WritableStreamDealWithRejection(stream, error).
    301  DealWithRejection(aCx, aError, aRv);
    302 }
    303 
    304 // https://streams.spec.whatwg.org/#writable-stream-mark-close-request-in-flight
    305 void WritableStream::MarkCloseRequestInFlight() {
    306  // Step 1. Assert: stream.[[inFlightCloseRequest]] is undefined.
    307  MOZ_ASSERT(!mInFlightCloseRequest);
    308 
    309  // Step 2. Assert: stream.[[closeRequest]] is not undefined.
    310  MOZ_ASSERT(mCloseRequest);
    311 
    312  // Step 3. Set stream.[[inFlightCloseRequest]] to stream.[[closeRequest]].
    313  mInFlightCloseRequest = mCloseRequest;
    314 
    315  // Step 4. Set stream.[[closeRequest]] to undefined.
    316  mCloseRequest = nullptr;
    317 }
    318 
    319 // https://streams.spec.whatwg.org/#writable-stream-mark-first-write-request-in-flight
    320 void WritableStream::MarkFirstWriteRequestInFlight() {
    321  // Step 1. Assert: stream.[[inFlightWriteRequest]] is undefined.
    322  MOZ_ASSERT(!mInFlightWriteRequest);
    323 
    324  // Step 2. Assert: stream.[[writeRequests]] is not empty.
    325  MOZ_ASSERT(!mWriteRequests.IsEmpty());
    326 
    327  // Step 3. Let writeRequest be stream.[[writeRequests]][0].
    328  // Step 4. Remove writeRequest from stream.[[writeRequests]].
    329  RefPtr<Promise> writeRequest = mWriteRequests.Pop();
    330 
    331  // Step 5. Set stream.[[inFlightWriteRequest]] to writeRequest.
    332  mInFlightWriteRequest = writeRequest;
    333 }
    334 
    335 // https://streams.spec.whatwg.org/#writable-stream-reject-close-and-closed-promise-if-needed
    336 void WritableStream::RejectCloseAndClosedPromiseIfNeeded() {
    337  // Step 1. Assert: stream.[[state]] is "errored".
    338  MOZ_ASSERT(mState == WriterState::Errored);
    339 
    340  JS::Rooted<JS::Value> storedError(RootingCx(), mStoredError);
    341  // Step 2. If stream.[[closeRequest]] is not undefined,
    342  if (mCloseRequest) {
    343    // Step 2.1. Assert: stream.[[inFlightCloseRequest]] is undefined.
    344    MOZ_ASSERT(!mInFlightCloseRequest);
    345 
    346    // Step 2.2. Reject stream.[[closeRequest]] with stream.[[storedError]].
    347    mCloseRequest->MaybeReject(storedError);
    348 
    349    // Step 2.3. Set stream.[[closeRequest]] to undefined.
    350    mCloseRequest = nullptr;
    351  }
    352 
    353  // Step 3. Let writer be stream.[[writer]].
    354  RefPtr<WritableStreamDefaultWriter> writer = mWriter;
    355 
    356  // Step 4. If writer is not undefined,
    357  if (writer) {
    358    // Step 4.1. Reject writer.[[closedPromise]] with stream.[[storedError]].
    359    RefPtr<Promise> closedPromise = writer->ClosedPromise();
    360    closedPromise->MaybeReject(storedError);
    361 
    362    // Step 4.2. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
    363    closedPromise->SetSettledPromiseIsHandled();
    364  }
    365 }
    366 
    367 // https://streams.spec.whatwg.org/#writable-stream-start-erroring
    368 void WritableStream::StartErroring(JSContext* aCx,
    369                                   JS::Handle<JS::Value> aReason,
    370                                   ErrorResult& aRv) {
    371  // Step 1. Assert: stream.[[storedError]] is undefined.
    372  MOZ_ASSERT(mStoredError.isUndefined());
    373 
    374  // Step 2. Assert: stream.[[state]] is "writable".
    375  MOZ_ASSERT(mState == WriterState::Writable);
    376 
    377  // Step 3. Let controller be stream.[[controller]].
    378  RefPtr<WritableStreamDefaultController> controller = mController;
    379  // Step 4. Assert: controller is not undefined.
    380  MOZ_ASSERT(controller);
    381 
    382  // Step 5. Set stream.[[state]] to "erroring".
    383  mState = WriterState::Erroring;
    384 
    385  // Step 6. Set stream.[[storedError]] to reason.
    386  mStoredError = aReason;
    387 
    388  // Step 7. Let writer be stream.[[writer]].
    389  RefPtr<WritableStreamDefaultWriter> writer = mWriter;
    390  // Step 8. If writer is not undefined, perform !
    391  // WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason).
    392  if (writer) {
    393    WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, aReason);
    394  }
    395 
    396  // Step 9. If ! WritableStreamHasOperationMarkedInFlight(stream) is false
    397  // and controller.[[started]] is true,
    398  // perform !WritableStreamFinishErroring(stream).
    399  if (!HasOperationMarkedInFlight() && controller->Started()) {
    400    FinishErroring(aCx, aRv);
    401  }
    402 }
    403 
    404 // https://streams.spec.whatwg.org/#writable-stream-update-backpressure
    405 void WritableStream::UpdateBackpressure(bool aBackpressure) {
    406  // Step 1. Assert: stream.[[state]] is "writable".
    407  MOZ_ASSERT(mState == WriterState::Writable);
    408  // Step 2. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
    409  MOZ_ASSERT(!CloseQueuedOrInFlight());
    410 
    411  // Step 3. Let writer be stream.[[writer]].
    412  RefPtr<WritableStreamDefaultWriter> writer = mWriter;
    413 
    414  // Step 4. If writer is not undefined and backpressure is not
    415  // stream.[[backpressure]],
    416  if (writer && aBackpressure != mBackpressure) {
    417    // Step 4.1. If backpressure is true, set writer.[[readyPromise]] to a new
    418    // promise.
    419    if (aBackpressure) {
    420      RefPtr<Promise> promise =
    421          Promise::CreateInfallible(writer->GetParentObject());
    422      writer->SetReadyPromise(promise);
    423    } else {
    424      // Step 4.2. Otherwise,
    425      // Step 4.2.1. Assert: backpressure is false.
    426      // Step 4.2.2. Resolve writer.[[readyPromise]] with undefined.
    427      writer->ReadyPromise()->MaybeResolveWithUndefined();
    428    }
    429  }
    430 
    431  // Step 5. Set stream.[[backpressure]] to backpressure.
    432  mBackpressure = aBackpressure;
    433 }
    434 
    435 // https://streams.spec.whatwg.org/#ws-constructor
    436 already_AddRefed<WritableStream> WritableStream::Constructor(
    437    const GlobalObject& aGlobal,
    438    const Optional<JS::Handle<JSObject*>>& aUnderlyingSink,
    439    const QueuingStrategy& aStrategy, ErrorResult& aRv) {
    440  // Step 1. If underlyingSink is missing, set it to null.
    441  JS::Rooted<JSObject*> underlyingSinkObj(
    442      aGlobal.Context(),
    443      aUnderlyingSink.WasPassed() ? aUnderlyingSink.Value() : nullptr);
    444 
    445  // Step 2. Let underlyingSinkDict be underlyingSink, converted to
    446  //         an IDL value of type UnderlyingSink.
    447  RootedDictionary<UnderlyingSink> underlyingSinkDict(aGlobal.Context());
    448  if (underlyingSinkObj) {
    449    JS::Rooted<JS::Value> objValue(aGlobal.Context(),
    450                                   JS::ObjectValue(*underlyingSinkObj));
    451    dom::BindingCallContext callCx(aGlobal.Context(),
    452                                   "WritableStream.constructor");
    453    aRv.MightThrowJSException();
    454    if (!underlyingSinkDict.Init(callCx, objValue)) {
    455      aRv.StealExceptionFromJSContext(aGlobal.Context());
    456      return nullptr;
    457    }
    458  }
    459 
    460  // Step 3. If underlyingSinkDict["type"] exists, throw a RangeError exception.
    461  if (!underlyingSinkDict.mType.isUndefined()) {
    462    aRv.ThrowRangeError("Implementation preserved member 'type'");
    463    return nullptr;
    464  }
    465 
    466  // Step 4. Perform ! InitializeWritableStream(this).
    467  RefPtr<WritableStream> writableStream =
    468      new WritableStream(aGlobal, HoldDropJSObjectsCaller::Implicit);
    469 
    470  // Step 5. Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy).
    471  //
    472  // Implementation Note: The specification demands that if the size doesn't
    473  // exist, we instead would provide an algorithm that returns 1. Instead, we
    474  // will teach callers that a missing callback should simply return 1, rather
    475  // than gin up a fake callback here.
    476  //
    477  // This decision may need to be revisited if the default action ever diverges
    478  // within the specification.
    479  RefPtr<QueuingStrategySize> sizeAlgorithm =
    480      aStrategy.mSize.WasPassed() ? &aStrategy.mSize.Value() : nullptr;
    481 
    482  // Step 6. Let highWaterMark be ? ExtractHighWaterMark(strategy, 1).
    483  double highWaterMark = ExtractHighWaterMark(aStrategy, 1, aRv);
    484  if (aRv.Failed()) {
    485    return nullptr;
    486  }
    487 
    488  // Step 7. Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink(
    489  // this, underlyingSink, underlyingSinkDict, highWaterMark, sizeAlgorithm).
    490  SetUpWritableStreamDefaultControllerFromUnderlyingSink(
    491      aGlobal.Context(), writableStream, underlyingSinkObj, underlyingSinkDict,
    492      highWaterMark, sizeAlgorithm, aRv);
    493  if (aRv.Failed()) {
    494    return nullptr;
    495  }
    496 
    497  return writableStream.forget();
    498 }
    499 
    500 namespace streams_abstract {
    501 // https://streams.spec.whatwg.org/#writable-stream-abort
    502 already_AddRefed<Promise> WritableStreamAbort(JSContext* aCx,
    503                                              WritableStream* aStream,
    504                                              JS::Handle<JS::Value> aReason,
    505                                              ErrorResult& aRv) {
    506  // Step 1. If stream.[[state]] is "closed" or "errored", return a promise
    507  // resolved with undefined.
    508  if (aStream->State() == WritableStream::WriterState::Closed ||
    509      aStream->State() == WritableStream::WriterState::Errored) {
    510    RefPtr<Promise> promise =
    511        Promise::CreateInfallible(aStream->GetParentObject());
    512    promise->MaybeResolveWithUndefined();
    513    return promise.forget();
    514  }
    515 
    516  // Step 2. Signal abort on stream.[[controller]].[[signal]] with reason.
    517  RefPtr<WritableStreamDefaultController> controller = aStream->Controller();
    518  controller->Signal()->SignalAbort(aReason);
    519 
    520  // Step 3. Let state be stream.[[state]].
    521  WritableStream::WriterState state = aStream->State();
    522 
    523  // Step 4. If state is "closed" or "errored", return a promise resolved with
    524  // undefined. Note: We re-check the state because signaling abort runs author
    525  // code and that might have changed the state.
    526  if (aStream->State() == WritableStream::WriterState::Closed ||
    527      aStream->State() == WritableStream::WriterState::Errored) {
    528    RefPtr<Promise> promise =
    529        Promise::CreateInfallible(aStream->GetParentObject());
    530    promise->MaybeResolveWithUndefined();
    531    return promise.forget();
    532  }
    533 
    534  // Step 5. If stream.[[pendingAbortRequest]] is not undefined, return
    535  // stream.[[pendingAbortRequest]]'s promise.
    536  if (aStream->GetPendingAbortRequestPromise()) {
    537    RefPtr<Promise> promise = aStream->GetPendingAbortRequestPromise();
    538    return promise.forget();
    539  }
    540 
    541  // Step 6. Assert: state is "writable" or "erroring".
    542  MOZ_ASSERT(state == WritableStream::WriterState::Writable ||
    543             state == WritableStream::WriterState::Erroring);
    544 
    545  // Step 7. Let wasAlreadyErroring be false.
    546  bool wasAlreadyErroring = false;
    547 
    548  // Step 8. If state is "erroring",
    549  JS::Rooted<JS::Value> reason(aCx, aReason);
    550  if (state == WritableStream::WriterState::Erroring) {
    551    // Step 8.1. Set wasAlreadyErroring to true.
    552    wasAlreadyErroring = true;
    553    // Step 8.2. Set reason to undefined.
    554    reason.setUndefined();
    555  }
    556 
    557  // Step 9. Let promise be a new promise.
    558  RefPtr<Promise> promise =
    559      Promise::CreateInfallible(aStream->GetParentObject());
    560 
    561  // Step 10. Set stream.[[pendingAbortRequest]] to a new pending abort request
    562  // whose promise is promise, reason is reason, and was already erroring is
    563  // wasAlreadyErroring.
    564  aStream->SetPendingAbortRequest(promise, reason, wasAlreadyErroring);
    565 
    566  // Step 11. If wasAlreadyErroring is false, perform !
    567  // WritableStreamStartErroring(stream, reason).
    568  if (!wasAlreadyErroring) {
    569    aStream->StartErroring(aCx, reason, aRv);
    570    if (aRv.Failed()) {
    571      return nullptr;
    572    }
    573  }
    574 
    575  // Step 12. Return promise.
    576  return promise.forget();
    577 }
    578 }  // namespace streams_abstract
    579 
    580 // https://streams.spec.whatwg.org/#ws-abort
    581 already_AddRefed<Promise> WritableStream::Abort(JSContext* aCx,
    582                                                JS::Handle<JS::Value> aReason,
    583                                                ErrorResult& aRv) {
    584  // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
    585  // rejected with a TypeError exception.
    586  if (Locked()) {
    587    return Promise::CreateRejectedWithTypeError(
    588        GetParentObject(), "Canceled Locked Stream"_ns, aRv);
    589  }
    590 
    591  // Step 2. Return ! WritableStreamAbort(this, reason).
    592  RefPtr<WritableStream> thisRefPtr = this;
    593  return WritableStreamAbort(aCx, thisRefPtr, aReason, aRv);
    594 }
    595 
    596 namespace streams_abstract {
    597 // https://streams.spec.whatwg.org/#writable-stream-close
    598 already_AddRefed<Promise> WritableStreamClose(JSContext* aCx,
    599                                              WritableStream* aStream,
    600                                              ErrorResult& aRv) {
    601  // Step 1. Let state be stream.[[state]].
    602  WritableStream::WriterState state = aStream->State();
    603 
    604  // Step 2. If state is "closed" or "errored", return a promise rejected with a
    605  // TypeError exception.
    606  if (state == WritableStream::WriterState::Closed ||
    607      state == WritableStream::WriterState::Errored) {
    608    return Promise::CreateRejectedWithTypeError(
    609        aStream->GetParentObject(),
    610        "Can not close stream after closing or error"_ns, aRv);
    611  }
    612 
    613  // Step 3. Assert: state is "writable" or "erroring".
    614  MOZ_ASSERT(state == WritableStream::WriterState::Writable ||
    615             state == WritableStream::WriterState::Erroring);
    616 
    617  // Step 4. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
    618  MOZ_ASSERT(!aStream->CloseQueuedOrInFlight());
    619 
    620  // Step 5. Let promise be a new promise.
    621  RefPtr<Promise> promise =
    622      Promise::CreateInfallible(aStream->GetParentObject());
    623 
    624  // Step 6. Set stream.[[closeRequest]] to promise.
    625  aStream->SetCloseRequest(promise);
    626 
    627  // Step 7. Let writer be stream.[[writer]].
    628  RefPtr<WritableStreamDefaultWriter> writer = aStream->GetWriter();
    629 
    630  // Step 8. If writer is not undefined, and stream.[[backpressure]] is true,
    631  // and state is "writable", resolve writer.[[readyPromise]] with undefined.
    632  if (writer && aStream->Backpressure() &&
    633      state == WritableStream::WriterState::Writable) {
    634    writer->ReadyPromise()->MaybeResolveWithUndefined();
    635  }
    636 
    637  // Step 9.
    638  // Perform ! WritableStreamDefaultControllerClose(stream.[[controller]]).
    639  RefPtr<WritableStreamDefaultController> controller = aStream->Controller();
    640  WritableStreamDefaultControllerClose(aCx, controller, aRv);
    641  if (aRv.Failed()) {
    642    return nullptr;
    643  }
    644 
    645  // Step 10. Return promise.
    646  return promise.forget();
    647 }
    648 }  // namespace streams_abstract
    649 
    650 // https://streams.spec.whatwg.org/#ws-close
    651 already_AddRefed<Promise> WritableStream::Close(JSContext* aCx,
    652                                                ErrorResult& aRv) {
    653  // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
    654  // rejected with a TypeError exception.
    655  if (Locked()) {
    656    return Promise::CreateRejectedWithTypeError(
    657        GetParentObject(), "Can not close locked stream"_ns, aRv);
    658  }
    659 
    660  // Step 2. If ! WritableStreamCloseQueuedOrInFlight(this) is true, return a
    661  // promise rejected with a TypeError exception.
    662  if (CloseQueuedOrInFlight()) {
    663    return Promise::CreateRejectedWithTypeError(
    664        GetParentObject(), "Stream is already closing"_ns, aRv);
    665  }
    666 
    667  // Step 3. Return ! WritableStreamClose(this).
    668  RefPtr<WritableStream> thisRefPtr = this;
    669  return WritableStreamClose(aCx, thisRefPtr, aRv);
    670 }
    671 
    672 namespace streams_abstract {
    673 // https://streams.spec.whatwg.org/#acquire-writable-stream-default-writer
    674 already_AddRefed<WritableStreamDefaultWriter>
    675 AcquireWritableStreamDefaultWriter(WritableStream* aStream, ErrorResult& aRv) {
    676  // Step 1. Let writer be a new WritableStreamDefaultWriter.
    677  RefPtr<WritableStreamDefaultWriter> writer =
    678      new WritableStreamDefaultWriter(aStream->GetParentObject());
    679 
    680  // Step 2. Perform ? SetUpWritableStreamDefaultWriter(writer, stream).
    681  SetUpWritableStreamDefaultWriter(writer, aStream, aRv);
    682  if (aRv.Failed()) {
    683    return nullptr;
    684  }
    685 
    686  // Step 3. Return writer.
    687  return writer.forget();
    688 }
    689 }  // namespace streams_abstract
    690 
    691 // https://streams.spec.whatwg.org/#create-writable-stream
    692 already_AddRefed<WritableStream> WritableStream::CreateAbstract(
    693    JSContext* aCx, nsIGlobalObject* aGlobal,
    694    UnderlyingSinkAlgorithmsBase* aAlgorithms, double aHighWaterMark,
    695    QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
    696  // Step 1: Assert: ! IsNonNegativeNumber(highWaterMark) is true.
    697  MOZ_ASSERT(IsNonNegativeNumber(aHighWaterMark));
    698 
    699  // Step 2: Let stream be a new WritableStream.
    700  // Step 3: Perform ! InitializeWritableStream(stream).
    701  RefPtr<WritableStream> stream = new WritableStream(
    702      aGlobal, WritableStream::HoldDropJSObjectsCaller::Implicit);
    703 
    704  // Step 4: Let controller be a new WritableStreamDefaultController.
    705  auto controller =
    706      MakeRefPtr<WritableStreamDefaultController>(aGlobal, *stream);
    707 
    708  // Step 5: Perform ? SetUpWritableStreamDefaultController(stream, controller,
    709  // startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm,
    710  // highWaterMark, sizeAlgorithm).
    711  SetUpWritableStreamDefaultController(aCx, stream, controller, aAlgorithms,
    712                                       aHighWaterMark, aSizeAlgorithm, aRv);
    713  if (aRv.Failed()) {
    714    return nullptr;
    715  }
    716 
    717  // Step 6: Return stream.
    718  return stream.forget();
    719 }
    720 
    721 already_AddRefed<WritableStreamDefaultWriter> WritableStream::GetWriter(
    722    ErrorResult& aRv) {
    723  return AcquireWritableStreamDefaultWriter(this, aRv);
    724 }
    725 
    726 namespace streams_abstract {
    727 // https://streams.spec.whatwg.org/#writable-stream-add-write-request
    728 already_AddRefed<Promise> WritableStreamAddWriteRequest(
    729    WritableStream* aStream) {
    730  // Step 1. Assert: ! IsWritableStreamLocked(stream) is true.
    731  MOZ_ASSERT(IsWritableStreamLocked(aStream));
    732 
    733  // Step 2. Assert: stream.[[state]] is "writable".
    734  MOZ_ASSERT(aStream->State() == WritableStream::WriterState::Writable);
    735 
    736  // Step 3. Let promise be a new promise.
    737  RefPtr<Promise> promise =
    738      Promise::CreateInfallible(aStream->GetParentObject());
    739 
    740  // Step 4. Append promise to stream.[[writeRequests]].
    741  aStream->AppendWriteRequest(promise);
    742 
    743  // Step 5. Return promise.
    744  return promise.forget();
    745 }
    746 }  // namespace streams_abstract
    747 
    748 // https://streams.spec.whatwg.org/#writablestream-set-up
    749 // _BOUNDARY because `aAlgorithms->StartCallback` (called by
    750 // SetUpWritableStreamDefaultController below) should not be able to run script
    751 // in this case.
    752 MOZ_CAN_RUN_SCRIPT_BOUNDARY void WritableStream::SetUpNative(
    753    JSContext* aCx, UnderlyingSinkAlgorithmsWrapper& aAlgorithms,
    754    Maybe<double> aHighWaterMark, QueuingStrategySize* aSizeAlgorithm,
    755    ErrorResult& aRv) {
    756  // an optional number highWaterMark (default 1)
    757  double highWaterMark = aHighWaterMark.valueOr(1);
    758  // and if given, highWaterMark must be a non-negative, non-NaN number.
    759  MOZ_ASSERT(IsNonNegativeNumber(highWaterMark));
    760 
    761  // Step 1: Let startAlgorithm be an algorithm that returns undefined.
    762  // Step 2: Let closeAlgorithmWrapper be an algorithm that runs these steps:
    763  // Step 3: Let abortAlgorithmWrapper be an algorithm that runs these steps:
    764  // (Covered by UnderlyingSinkAlgorithmsWrapper)
    765 
    766  // Step 4: If sizeAlgorithm was not given, then set it to an algorithm that
    767  // returns 1. (Callers will treat nullptr as such, see
    768  // WritableStream::Constructor for details)
    769 
    770  // Step 5: Perform ! InitializeWritableStream(stream).
    771  // (Covered by the constructor)
    772 
    773  // Step 6: Let controller be a new WritableStreamDefaultController.
    774  auto controller =
    775      MakeRefPtr<WritableStreamDefaultController>(GetParentObject(), *this);
    776 
    777  // Step 7: Perform ! SetUpWritableStreamDefaultController(stream, controller,
    778  // startAlgorithm, writeAlgorithm, closeAlgorithmWrapper,
    779  // abortAlgorithmWrapper, highWaterMark, sizeAlgorithm).
    780  SetUpWritableStreamDefaultController(aCx, this, controller, &aAlgorithms,
    781                                       highWaterMark, aSizeAlgorithm, aRv);
    782 }
    783 
    784 already_AddRefed<WritableStream> WritableStream::CreateNative(
    785    JSContext* aCx, nsIGlobalObject& aGlobal,
    786    UnderlyingSinkAlgorithmsWrapper& aAlgorithms, Maybe<double> aHighWaterMark,
    787    QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
    788  RefPtr<WritableStream> stream = new WritableStream(
    789      &aGlobal, WritableStream::HoldDropJSObjectsCaller::Implicit);
    790  stream->SetUpNative(aCx, aAlgorithms, aHighWaterMark, aSizeAlgorithm, aRv);
    791  if (aRv.Failed()) {
    792    return nullptr;
    793  }
    794  return stream.forget();
    795 }
    796 
    797 // https://streams.spec.whatwg.org/#writablestream-error
    798 // To error a WritableStream stream given a JavaScript value e, perform !
    799 // WritableStreamDefaultControllerErrorIfNeeded(stream.[[controller]], e).
    800 void WritableStream::ErrorNative(JSContext* aCx, JS::Handle<JS::Value> aError,
    801                                 ErrorResult& aRv) {
    802  // MOZ_KnownLive here instead of MOZ_KNOWN_LIVE at the field, because
    803  // mController is set outside of the constructor
    804  WritableStreamDefaultControllerErrorIfNeeded(aCx, MOZ_KnownLive(mController),
    805                                               aError, aRv);
    806 }
    807 
    808 }  // namespace mozilla::dom