tor-browser

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

WritableStreamDefaultController.cpp (23060B)


      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/WritableStreamDefaultController.h"
      8 
      9 #include "js/Exception.h"
     10 #include "js/TypeDecls.h"
     11 #include "js/Value.h"
     12 #include "mozilla/AlreadyAddRefed.h"
     13 #include "mozilla/Attributes.h"
     14 #include "mozilla/dom/AbortSignal.h"
     15 #include "mozilla/dom/Promise-inl.h"
     16 #include "mozilla/dom/Promise.h"
     17 #include "mozilla/dom/UnderlyingSinkBinding.h"
     18 #include "mozilla/dom/WritableStream.h"
     19 #include "mozilla/dom/WritableStreamDefaultControllerBinding.h"
     20 #include "nsCycleCollectionParticipant.h"
     21 #include "nsDebug.h"
     22 #include "nsISupports.h"
     23 
     24 namespace mozilla::dom {
     25 
     26 using namespace streams_abstract;
     27 
     28 // Note: Using the individual macros vs NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE
     29 // because I need to specificy a manual implementation of
     30 // NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN.
     31 NS_IMPL_CYCLE_COLLECTION_CLASS(WritableStreamDefaultController)
     32 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WritableStreamDefaultController)
     33  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mSignal, mStrategySizeAlgorithm,
     34                                  mAlgorithms, mStream)
     35  tmp->mQueue.clear();
     36  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     37 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WritableStreamDefaultController)
     39  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mSignal, mStrategySizeAlgorithm,
     40                                    mAlgorithms, mStream)
     41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     42 
     43 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WritableStreamDefaultController)
     44  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
     45  // Trace the associated queue.
     46  for (const auto& queueEntry : tmp->mQueue) {
     47    aCallbacks.Trace(&queueEntry->mValue, "mQueue.mValue", aClosure);
     48  }
     49 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     50 
     51 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStreamDefaultController)
     52 NS_IMPL_CYCLE_COLLECTING_RELEASE(WritableStreamDefaultController)
     53 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStreamDefaultController)
     54  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     55  NS_INTERFACE_MAP_ENTRY(nsISupports)
     56 NS_INTERFACE_MAP_END
     57 
     58 WritableStreamDefaultController::WritableStreamDefaultController(
     59    nsISupports* aGlobal, WritableStream& aStream)
     60    : mGlobal(do_QueryInterface(aGlobal)), mStream(&aStream) {
     61  mozilla::HoldJSObjects(this);
     62 }
     63 
     64 WritableStreamDefaultController::~WritableStreamDefaultController() {
     65  // MG:XXX: LinkedLists are required to be empty at destruction, but it seems
     66  //         it is possible to have a controller be destructed while still
     67  //         having entries in its queue.
     68  //
     69  //         This needs to be verified as not indicating some other issue.
     70  mQueue.clear();
     71  mozilla::DropJSObjects(this);
     72 }
     73 
     74 JSObject* WritableStreamDefaultController::WrapObject(
     75    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
     76  return WritableStreamDefaultController_Binding::Wrap(aCx, this, aGivenProto);
     77 }
     78 
     79 // https://streams.spec.whatwg.org/#ws-default-controller-error
     80 void WritableStreamDefaultController::Error(JSContext* aCx,
     81                                            JS::Handle<JS::Value> aError,
     82                                            ErrorResult& aRv) {
     83  // Step 1. Let state be this.[[stream]].[[state]].
     84  // Step 2. If state is not "writable", return.
     85  if (mStream->State() != WritableStream::WriterState::Writable) {
     86    return;
     87  }
     88  // Step 3. Perform ! WritableStreamDefaultControllerError(this, e).
     89  RefPtr<WritableStreamDefaultController> thisRefPtr = this;
     90  WritableStreamDefaultControllerError(aCx, thisRefPtr, aError, aRv);
     91 }
     92 
     93 // https://streams.spec.whatwg.org/#ws-default-controller-private-abort
     94 already_AddRefed<Promise> WritableStreamDefaultController::AbortSteps(
     95    JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
     96  // Step 1. Let result be the result of performing this.[[abortAlgorithm]],
     97  // passing reason.
     98  RefPtr<UnderlyingSinkAlgorithmsBase> algorithms = mAlgorithms;
     99  Optional<JS::Handle<JS::Value>> optionalReason(aCx, aReason);
    100  RefPtr<Promise> abortPromise =
    101      algorithms->AbortCallback(aCx, optionalReason, aRv);
    102  if (aRv.Failed()) {
    103    return nullptr;
    104  }
    105 
    106  // Step 2. Perform ! WritableStreamDefaultControllerClearAlgorithms(this).
    107  ClearAlgorithms();
    108 
    109  // Step 3. Return result.
    110  return abortPromise.forget();
    111 }
    112 
    113 // https://streams.spec.whatwg.org/#ws-default-controller-private-error
    114 void WritableStreamDefaultController::ErrorSteps() {
    115  // Step 1. Perform ! ResetQueue(this).
    116  ResetQueue(this);
    117 }
    118 
    119 void WritableStreamDefaultController::SetSignal(AbortSignal* aSignal) {
    120  MOZ_ASSERT(aSignal);
    121  mSignal = aSignal;
    122 }
    123 
    124 namespace streams_abstract {
    125 
    126 MOZ_CAN_RUN_SCRIPT static void
    127 WritableStreamDefaultControllerAdvanceQueueIfNeeded(
    128    JSContext* aCx, WritableStreamDefaultController* aController,
    129    ErrorResult& aRv);
    130 
    131 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller
    132 void SetUpWritableStreamDefaultController(
    133    JSContext* aCx, WritableStream* aStream,
    134    WritableStreamDefaultController* aController,
    135    UnderlyingSinkAlgorithmsBase* aAlgorithms, double aHighWaterMark,
    136    QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
    137  // Step 1. Assert: stream implements WritableStream.
    138  // Step 2. Assert: stream.[[controller]] is undefined.
    139  MOZ_ASSERT(!aStream->Controller());
    140 
    141  // Step 3. Set controller.[[stream]] to stream.
    142  // Note: Already set in
    143  // SetUpWritableStreamDefaultControllerFromUnderlyingSink.
    144  MOZ_ASSERT(aController->Stream() == aStream);
    145 
    146  // Step 4. Set stream.[[controller]] to controller.
    147  aStream->SetController(*aController);
    148 
    149  // Step 5. Perform ! ResetQueue(controller).
    150  ResetQueue(aController);
    151 
    152  // Step 6. Set controller.[[signal]] to a new AbortSignal.
    153  RefPtr<AbortSignal> signal =
    154      AbortSignal::Create(aController->GetParentObject(), SignalAborted::No,
    155                          JS::UndefinedHandleValue);
    156 
    157  aController->SetSignal(signal);
    158 
    159  // Step 7. Set controller.[[started]] to false.
    160  aController->SetStarted(false);
    161 
    162  // Step 8. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm.
    163  aController->SetStrategySizeAlgorithm(aSizeAlgorithm);
    164 
    165  // Step 9. Set controller.[[strategyHWM]] to highWaterMark.
    166  aController->SetStrategyHWM(aHighWaterMark);
    167 
    168  // Step 10. Set controller.[[writeAlgorithm]] to writeAlgorithm.
    169  // Step 11. Set controller.[[closeAlgorithm]] to closeAlgorithm.
    170  // Step 12. Set controller.[[abortAlgorithm]] to abortAlgorithm.
    171  aController->SetAlgorithms(*aAlgorithms);
    172 
    173  // Step 13. Let backpressure be !
    174  // WritableStreamDefaultControllerGetBackpressure(controller).
    175  bool backpressure = aController->GetBackpressure();
    176 
    177  // Step 14. Perform ! WritableStreamUpdateBackpressure(stream, backpressure).
    178  aStream->UpdateBackpressure(backpressure);
    179 
    180  // Step 15. Let startResult be the result of performing startAlgorithm. (This
    181  // may throw an exception.)
    182  JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue());
    183  RefPtr<WritableStreamDefaultController> controller(aController);
    184  aAlgorithms->StartCallback(aCx, *controller, &startResult, aRv);
    185  if (aRv.Failed()) {
    186    return;
    187  }
    188 
    189  // Step 16. Let startPromise be a promise resolved with startResult.
    190  RefPtr<Promise> startPromise =
    191      Promise::CreateInfallible(aStream->GetParentObject());
    192  startPromise->MaybeResolve(startResult);
    193 
    194  // Step 17/18.
    195  startPromise->AddCallbacksWithCycleCollectedArgs(
    196      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    197         WritableStreamDefaultController* aController)
    198          MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    199            // Step 17. Upon fulfillment of startPromise,
    200            // Step 17.1. Assert: stream.[[state]] is "writable" or "erroring".
    201            MOZ_ASSERT(aController->Stream()->State() ==
    202                           WritableStream::WriterState::Writable ||
    203                       aController->Stream()->State() ==
    204                           WritableStream::WriterState::Erroring);
    205            // Step 17.2. Set controller.[[started]] to true.
    206            aController->SetStarted(true);
    207            // Step 17.3 Perform
    208            // !WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
    209            WritableStreamDefaultControllerAdvanceQueueIfNeeded(
    210                aCx, MOZ_KnownLive(aController), aRv);
    211          },
    212      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    213         WritableStreamDefaultController* aController)
    214          MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    215            RefPtr<WritableStream> stream = aController->Stream();
    216            // Step 18. Upon rejection of startPromise with reason r,
    217            // Step 18.1. Assert: stream.[[state]] is "writable" or "erroring".
    218            MOZ_ASSERT(
    219                stream->State() == WritableStream::WriterState::Writable ||
    220                stream->State() == WritableStream::WriterState::Erroring);
    221            // Step 18.2. Set controller.[[started]] to true.
    222            aController->SetStarted(true);
    223            // Step 18.3. Perform ! WritableStreamDealWithRejection(stream, r).
    224            stream->DealWithRejection(aCx, aValue, aRv);
    225          },
    226      RefPtr(aController));
    227 }
    228 
    229 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink
    230 void SetUpWritableStreamDefaultControllerFromUnderlyingSink(
    231    JSContext* aCx, WritableStream* aStream,
    232    JS::Handle<JSObject*> aUnderlyingSink, UnderlyingSink& aUnderlyingSinkDict,
    233    double aHighWaterMark, QueuingStrategySize* aSizeAlgorithm,
    234    ErrorResult& aRv) {
    235  // Step 1.
    236  RefPtr<WritableStreamDefaultController> controller =
    237      new WritableStreamDefaultController(aStream->GetParentObject(), *aStream);
    238 
    239  // Step 2 - 9.
    240  auto algorithms = MakeRefPtr<UnderlyingSinkAlgorithms>(
    241      aStream->GetParentObject(), aUnderlyingSink, aUnderlyingSinkDict);
    242 
    243  // Step 10.
    244  SetUpWritableStreamDefaultController(aCx, aStream, controller, algorithms,
    245                                       aHighWaterMark, aSizeAlgorithm, aRv);
    246 }
    247 
    248 // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close
    249 MOZ_CAN_RUN_SCRIPT static void WritableStreamDefaultControllerProcessClose(
    250    JSContext* aCx, WritableStreamDefaultController* aController,
    251    ErrorResult& aRv) {
    252  // Step 1. Let stream be controller.[[stream]].
    253  RefPtr<WritableStream> stream = aController->Stream();
    254 
    255  // Step 2. Perform ! WritableStreamMarkCloseRequestInFlight(stream).
    256  stream->MarkCloseRequestInFlight();
    257 
    258  // Step 3. Perform ! DequeueValue(controller).
    259  JS::Rooted<JS::Value> value(aCx);
    260  DequeueValue(aController, &value);
    261 
    262  // Step 4. Assert: controller.[[queue]] is empty.
    263  MOZ_ASSERT(aController->Queue().isEmpty());
    264 
    265  // Step 5. Let sinkClosePromise be the result of performing
    266  // controller.[[closeAlgorithm]].
    267  RefPtr<UnderlyingSinkAlgorithmsBase> algorithms =
    268      aController->GetAlgorithms();
    269  RefPtr<Promise> sinkClosePromise = algorithms->CloseCallback(aCx, aRv);
    270  if (aRv.Failed()) {
    271    return;
    272  }
    273 
    274  // Step 6. Perform !
    275  // WritableStreamDefaultControllerClearAlgorithms(controller).
    276  aController->ClearAlgorithms();
    277 
    278  // Step 7 + 8.
    279  sinkClosePromise->AddCallbacksWithCycleCollectedArgs(
    280      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    281         WritableStreamDefaultController* aController) {
    282        RefPtr<WritableStream> stream = aController->Stream();
    283        // Step 7. Upon fulfillment of sinkClosePromise,
    284        // Step 7.1. Perform ! WritableStreamFinishInFlightClose(stream).
    285        stream->FinishInFlightClose();
    286      },
    287      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    288         WritableStreamDefaultController* aController)
    289          MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    290            RefPtr<WritableStream> stream = aController->Stream();
    291            // Step 8. Upon rejection of sinkClosePromise with reason reason,
    292            // Step 8.1. Perform
    293            // ! WritableStreamFinishInFlightCloseWithError(stream, reason).
    294            stream->FinishInFlightCloseWithError(aCx, aValue, aRv);
    295          },
    296      RefPtr(aController));
    297 }
    298 
    299 // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write
    300 MOZ_CAN_RUN_SCRIPT static void WritableStreamDefaultControllerProcessWrite(
    301    JSContext* aCx, WritableStreamDefaultController* aController,
    302    JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
    303  // Step 1. Let stream be controller.[[stream]].
    304  RefPtr<WritableStream> stream = aController->Stream();
    305 
    306  // Step 2. Perform ! WritableStreamMarkFirstWriteRequestInFlight(stream).
    307  stream->MarkFirstWriteRequestInFlight();
    308 
    309  // Step 3. Let sinkWritePromise be the result of performing
    310  // controller.[[writeAlgorithm]], passing in chunk.
    311  RefPtr<UnderlyingSinkAlgorithmsBase> algorithms =
    312      aController->GetAlgorithms();
    313  RefPtr<Promise> sinkWritePromise =
    314      algorithms->WriteCallback(aCx, aChunk, *aController, aRv);
    315  if (aRv.Failed()) {
    316    return;
    317  }
    318 
    319  // Step 4 + 5:
    320  sinkWritePromise->AddCallbacksWithCycleCollectedArgs(
    321      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    322         WritableStreamDefaultController* aController)
    323          MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    324            RefPtr<WritableStream> stream = aController->Stream();
    325 
    326            // Step 4.1. Perform ! WritableStreamFinishInFlightWrite(stream).
    327            stream->FinishInFlightWrite();
    328 
    329            // Step 4.2. Let state be stream.[[state]].
    330            WritableStream::WriterState state = stream->State();
    331 
    332            // Step 4.3. Assert: state is "writable" or "erroring".
    333            MOZ_ASSERT(state == WritableStream::WriterState::Writable ||
    334                       state == WritableStream::WriterState::Erroring);
    335 
    336            // Step 4.4. Perform ! DequeueValue(controller).
    337            JS::Rooted<JS::Value> value(aCx);
    338            DequeueValue(aController, &value);
    339 
    340            // Step 4.5. If ! WritableStreamCloseQueuedOrInFlight(stream) is
    341            // false and state is "writable",
    342            if (!stream->CloseQueuedOrInFlight() &&
    343                state == WritableStream::WriterState::Writable) {
    344              // Step 4.5.1. Let backpressure be !
    345              // WritableStreamDefaultControllerGetBackpressure(controller).
    346              bool backpressure = aController->GetBackpressure();
    347              // Step 4.5.2. Perform ! WritableStreamUpdateBackpressure(stream,
    348              // backpressure).
    349              stream->UpdateBackpressure(backpressure);
    350            }
    351 
    352            // Step 4.6. Perform !
    353            // WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
    354            WritableStreamDefaultControllerAdvanceQueueIfNeeded(
    355                aCx, MOZ_KnownLive(aController), aRv);
    356          },
    357      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    358         WritableStreamDefaultController* aController)
    359          MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    360            RefPtr<WritableStream> stream = aController->Stream();
    361 
    362            // Step 5.1. If stream.[[state]] is "writable", perform !
    363            // WritableStreamDefaultControllerClearAlgorithms(controller).
    364            if (stream->State() == WritableStream::WriterState::Writable) {
    365              aController->ClearAlgorithms();
    366            }
    367 
    368            // Step 5.2. Perform !
    369            // WritableStreamFinishInFlightWriteWithError(stream, reason)
    370            stream->FinishInFlightWriteWithError(aCx, aValue, aRv);
    371          },
    372      RefPtr(aController));
    373 }
    374 
    375 // We use a JS::MagicValue to represent the close sentinel required by the spec.
    376 // Normal JavaScript code can not generate magic values, so we can use this
    377 // as a special value. However care has to be taken to not leak the magic value
    378 // to other code.
    379 constexpr JSWhyMagic CLOSE_SENTINEL = JS_GENERIC_MAGIC;
    380 
    381 // https://streams.spec.whatwg.org/#writable-stream-default-controller-advance-queue-if-needed
    382 static void WritableStreamDefaultControllerAdvanceQueueIfNeeded(
    383    JSContext* aCx, WritableStreamDefaultController* aController,
    384    ErrorResult& aRv) {
    385  // Step 1. Let stream be controller.[[stream]].
    386  RefPtr<WritableStream> stream = aController->Stream();
    387 
    388  // Step 2. If controller.[[started]] is false, return.
    389  if (!aController->Started()) {
    390    return;
    391  }
    392 
    393  // Step 3. If stream.[[inFlightWriteRequest]] is not undefined, return.
    394  if (stream->GetInFlightWriteRequest()) {
    395    return;
    396  }
    397 
    398  // Step 4. Let state be stream.[[state]].
    399  WritableStream::WriterState state = stream->State();
    400 
    401  // Step 5. Assert: state is not "closed" or "errored".
    402  MOZ_ASSERT(state != WritableStream::WriterState::Closed &&
    403             state != WritableStream::WriterState::Errored);
    404 
    405  // Step 6. If state is "erroring",
    406  if (state == WritableStream::WriterState::Erroring) {
    407    // Step 6.1. Perform ! WritableStreamFinishErroring(stream).
    408    stream->FinishErroring(aCx, aRv);
    409 
    410    // Step 6.2. Return.
    411    return;
    412  }
    413 
    414  // Step 7. If controller.[[queue]] is empty, return.
    415  if (aController->Queue().isEmpty()) {
    416    return;
    417  }
    418 
    419  // Step 8. Let value be ! PeekQueueValue(controller).
    420  JS::Rooted<JS::Value> value(aCx);
    421  PeekQueueValue(aController, &value);
    422 
    423  // Step 9. If value is the close sentinel, perform !
    424  // WritableStreamDefaultControllerProcessClose(controller).
    425  if (value.isMagic(CLOSE_SENTINEL)) {
    426    WritableStreamDefaultControllerProcessClose(aCx, aController, aRv);
    427    return;
    428  }
    429 
    430  // Step 10. Otherwise, perform !
    431  // WritableStreamDefaultControllerProcessWrite(controller, value).
    432  WritableStreamDefaultControllerProcessWrite(aCx, aController, value, aRv);
    433 }
    434 
    435 // https://streams.spec.whatwg.org/#writable-stream-default-controller-close
    436 void WritableStreamDefaultControllerClose(
    437    JSContext* aCx, WritableStreamDefaultController* aController,
    438    ErrorResult& aRv) {
    439  // Step 1. Perform ! EnqueueValueWithSize(controller, close sentinel, 0).
    440  JS::Rooted<JS::Value> aCloseSentinel(aCx, JS::MagicValue(CLOSE_SENTINEL));
    441  EnqueueValueWithSize(aController, aCloseSentinel, 0, aRv);
    442  MOZ_ASSERT(!aRv.Failed());
    443 
    444  // Step 2. Perform !
    445  // WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
    446  WritableStreamDefaultControllerAdvanceQueueIfNeeded(aCx, aController, aRv);
    447 }
    448 
    449 // https://streams.spec.whatwg.org/#writable-stream-default-controller-write
    450 void WritableStreamDefaultControllerWrite(
    451    JSContext* aCx, WritableStreamDefaultController* aController,
    452    JS::Handle<JS::Value> aChunk, double chunkSize, ErrorResult& aRv) {
    453  // Step 1. Let enqueueResult be EnqueueValueWithSize(controller, chunk,
    454  // chunkSize).
    455  IgnoredErrorResult rv;
    456  EnqueueValueWithSize(aController, aChunk, chunkSize, rv);
    457 
    458  // Step 2. If enqueueResult is an abrupt completion,
    459  if (rv.MaybeSetPendingException(aCx,
    460                                  "WritableStreamDefaultController.write")) {
    461    JS::Rooted<JS::Value> error(aCx);
    462    JS_GetPendingException(aCx, &error);
    463    JS_ClearPendingException(aCx);
    464 
    465    // Step 2.1. Perform !
    466    // WritableStreamDefaultControllerErrorIfNeeded(controller,
    467    // enqueueResult.[[Value]]).
    468    WritableStreamDefaultControllerErrorIfNeeded(aCx, aController, error, aRv);
    469 
    470    // Step 2.2. Return.
    471    return;
    472  }
    473 
    474  // Step 3. Let stream be controller.[[stream]].
    475  RefPtr<WritableStream> stream = aController->Stream();
    476 
    477  // Step 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and
    478  // stream.[[state]] is "writable",
    479  if (!stream->CloseQueuedOrInFlight() &&
    480      stream->State() == WritableStream::WriterState::Writable) {
    481    // Step 4.1. Let backpressure be
    482    // !WritableStreamDefaultControllerGetBackpressure(controller).
    483    bool backpressure = aController->GetBackpressure();
    484 
    485    // Step 4.2. Perform ! WritableStreamUpdateBackpressure(stream,
    486    // backpressure).
    487    stream->UpdateBackpressure(backpressure);
    488  }
    489 
    490  // Step 5. Perform
    491  // ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
    492  WritableStreamDefaultControllerAdvanceQueueIfNeeded(aCx, aController, aRv);
    493 }
    494 
    495 void WritableStreamDefaultControllerError(
    496    JSContext* aCx, WritableStreamDefaultController* aController,
    497    JS::Handle<JS::Value> aError, ErrorResult& aRv) {
    498  // Step 1. Let stream be controller.[[stream]].
    499  RefPtr<WritableStream> stream = aController->Stream();
    500 
    501  // Step 2. Assert: stream.[[state]] is "writable".
    502  MOZ_ASSERT(stream->State() == WritableStream::WriterState::Writable);
    503 
    504  // Step 3. Perform
    505  // ! WritableStreamDefaultControllerClearAlgorithms(controller).
    506  aController->ClearAlgorithms();
    507 
    508  // Step 4.Perform ! WritableStreamStartErroring(stream, error).
    509  stream->StartErroring(aCx, aError, aRv);
    510 }
    511 
    512 // https://streams.spec.whatwg.org/#writable-stream-default-controller-error-if-needed
    513 void WritableStreamDefaultControllerErrorIfNeeded(
    514    JSContext* aCx, WritableStreamDefaultController* aController,
    515    JS::Handle<JS::Value> aError, ErrorResult& aRv) {
    516  // Step 1. If controller.[[stream]].[[state]] is "writable", perform
    517  // !WritableStreamDefaultControllerError(controller, error).
    518  if (aController->Stream()->State() == WritableStream::WriterState::Writable) {
    519    WritableStreamDefaultControllerError(aCx, aController, aError, aRv);
    520  }
    521 }
    522 
    523 // https://streams.spec.whatwg.org/#writable-stream-default-controller-get-chunk-size
    524 double WritableStreamDefaultControllerGetChunkSize(
    525    JSContext* aCx, WritableStreamDefaultController* aController,
    526    JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
    527  // Step 1. Let returnValue be the result of performing
    528  // controller.[[strategySizeAlgorithm]], passing in chunk, and interpreting
    529  // the result as a completion record.
    530  RefPtr<QueuingStrategySize> sizeAlgorithm(
    531      aController->StrategySizeAlgorithm());
    532 
    533  // If !sizeAlgorithm, we return 1, which is inlined from
    534  // https://streams.spec.whatwg.org/#make-size-algorithm-from-size-function
    535  Optional<JS::Handle<JS::Value>> optionalChunk(aCx, aChunk);
    536 
    537  double chunkSize =
    538      sizeAlgorithm
    539          ? sizeAlgorithm->Call(
    540                optionalChunk, aRv,
    541                "WritableStreamDefaultController.[[strategySizeAlgorithm]]",
    542                CallbackObject::eRethrowExceptions)
    543          : 1.0;
    544 
    545  // Step 2. If returnValue is an abrupt completion,
    546  if (aRv.MaybeSetPendingException(
    547          aCx, "WritableStreamDefaultController.[[strategySizeAlgorithm]]")) {
    548    JS::Rooted<JS::Value> error(aCx);
    549    JS_GetPendingException(aCx, &error);
    550    JS_ClearPendingException(aCx);
    551 
    552    // Step 2.1. Perform !
    553    // WritableStreamDefaultControllerErrorIfNeeded(controller,
    554    // returnValue.[[Value]]).
    555    WritableStreamDefaultControllerErrorIfNeeded(aCx, aController, error, aRv);
    556 
    557    // Step 2.2. Return 1.
    558    return 1.0;
    559  }
    560 
    561  // Step 3. Return returnValue.[[Value]].
    562  return chunkSize;
    563 }
    564 
    565 }  // namespace streams_abstract
    566 
    567 }  // namespace mozilla::dom