tor-browser

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

TransformStream.cpp (28799B)


      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/TransformStream.h"
      8 
      9 #include "StreamUtils.h"
     10 #include "TransformerCallbackHelpers.h"
     11 #include "UnderlyingSourceCallbackHelpers.h"
     12 #include "js/TypeDecls.h"
     13 #include "mozilla/Attributes.h"
     14 #include "mozilla/dom/Promise-inl.h"
     15 #include "mozilla/dom/Promise.h"
     16 #include "mozilla/dom/ReadableStream.h"
     17 #include "mozilla/dom/RootedDictionary.h"
     18 #include "mozilla/dom/TransformStreamBinding.h"
     19 #include "mozilla/dom/TransformerBinding.h"
     20 #include "mozilla/dom/WritableStream.h"
     21 #include "nsWrapperCache.h"
     22 
     23 namespace mozilla::dom {
     24 
     25 using namespace streams_abstract;
     26 
     27 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TransformStream, mGlobal,
     28                                      mBackpressureChangePromise, mController,
     29                                      mReadable, mWritable)
     30 NS_IMPL_CYCLE_COLLECTING_ADDREF(TransformStream)
     31 NS_IMPL_CYCLE_COLLECTING_RELEASE(TransformStream)
     32 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStream)
     33  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     34  NS_INTERFACE_MAP_ENTRY(nsISupports)
     35 NS_INTERFACE_MAP_END
     36 
     37 // https://streams.spec.whatwg.org/#transformstream-set-up
     38 // (except this instead creates a new TransformStream rather than accepting an
     39 // existing instance)
     40 already_AddRefed<TransformStream> TransformStream::CreateGeneric(
     41    const GlobalObject& aGlobal, TransformerAlgorithmsWrapper& aAlgorithms,
     42    ErrorResult& aRv) {
     43  // Step 1. Let writableHighWaterMark be 1.
     44  double writableHighWaterMark = 1;
     45 
     46  // Step 2. Let writableSizeAlgorithm be an algorithm that returns 1.
     47  // Note: Callers should recognize nullptr as a callback that returns 1. See
     48  // also WritableStream::Constructor for this design decision.
     49  RefPtr<QueuingStrategySize> writableSizeAlgorithm;
     50 
     51  // Step 3. Let readableHighWaterMark be 0.
     52  double readableHighWaterMark = 0;
     53 
     54  // Step 4. Let readableSizeAlgorithm be an algorithm that returns 1.
     55  // Note: Callers should recognize nullptr as a callback that returns 1. See
     56  // also ReadableStream::Constructor for this design decision.
     57  RefPtr<QueuingStrategySize> readableSizeAlgorithm;
     58 
     59  // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps
     60  // given a value chunk:
     61  // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps:
     62  // (Done by TransformerAlgorithmsWrapper)
     63 
     64  // Step 7. Let startPromise be a promise resolved with undefined.
     65  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
     66  RefPtr<Promise> startPromise =
     67      Promise::CreateResolvedWithUndefined(global, aRv);
     68  if (!startPromise) {
     69    return nullptr;
     70  }
     71 
     72  // Step 8. Perform ! InitializeTransformStream(stream, startPromise,
     73  // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark,
     74  // readableSizeAlgorithm).
     75  RefPtr<TransformStream> stream =
     76      new TransformStream(global, nullptr, nullptr);
     77  stream->Initialize(aGlobal.Context(), startPromise, writableHighWaterMark,
     78                     writableSizeAlgorithm, readableHighWaterMark,
     79                     readableSizeAlgorithm, aRv);
     80  if (aRv.Failed()) {
     81    return nullptr;
     82  }
     83 
     84  // Step 9. Let controller be a new TransformStreamDefaultController.
     85  auto controller = MakeRefPtr<TransformStreamDefaultController>(global);
     86 
     87  // Step 10. Perform ! SetUpTransformStreamDefaultController(stream,
     88  // controller, transformAlgorithmWrapper, flushAlgorithmWrapper).
     89  SetUpTransformStreamDefaultController(aGlobal.Context(), *stream, *controller,
     90                                        aAlgorithms);
     91 
     92  return stream.forget();
     93 }
     94 
     95 TransformStream::TransformStream(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) {
     96  mozilla::HoldJSObjects(this);
     97 }
     98 
     99 TransformStream::TransformStream(nsIGlobalObject* aGlobal,
    100                                 ReadableStream* aReadable,
    101                                 WritableStream* aWritable)
    102    : mGlobal(aGlobal), mReadable(aReadable), mWritable(aWritable) {
    103  mozilla::HoldJSObjects(this);
    104 }
    105 
    106 TransformStream::~TransformStream() { mozilla::DropJSObjects(this); }
    107 
    108 JSObject* TransformStream::WrapObject(JSContext* aCx,
    109                                      JS::Handle<JSObject*> aGivenProto) {
    110  return TransformStream_Binding::Wrap(aCx, this, aGivenProto);
    111 }
    112 
    113 namespace streams_abstract {
    114 
    115 // https://streams.spec.whatwg.org/#transform-stream-error-writable-and-unblock-write
    116 void TransformStreamErrorWritableAndUnblockWrite(JSContext* aCx,
    117                                                 TransformStream* aStream,
    118                                                 JS::Handle<JS::Value> aError,
    119                                                 ErrorResult& aRv) {
    120  // Step 1: Perform !
    121  // TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]).
    122  aStream->Controller()->SetAlgorithms(nullptr);
    123 
    124  // Step 2: Perform !
    125  // WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]],
    126  // e).
    127  // TODO: Remove MOZ_KnownLive (bug 1761577)
    128  WritableStreamDefaultControllerErrorIfNeeded(
    129      aCx, MOZ_KnownLive(aStream->Writable()->Controller()), aError, aRv);
    130  if (aRv.Failed()) {
    131    return;
    132  }
    133 
    134  // Step 3: If stream.[[backpressure]] is true, perform !
    135  // TransformStreamSetBackpressure(stream, false).
    136  if (aStream->Backpressure()) {
    137    aStream->SetBackpressure(false);
    138  }
    139 }
    140 
    141 // https://streams.spec.whatwg.org/#transform-stream-error
    142 void TransformStreamError(JSContext* aCx, TransformStream* aStream,
    143                          JS::Handle<JS::Value> aError, ErrorResult& aRv) {
    144  // Step 1: Perform !
    145  // ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]],
    146  // e).
    147  ReadableStreamDefaultControllerError(
    148      aCx, aStream->Readable()->Controller()->AsDefault(), aError, aRv);
    149  if (aRv.Failed()) {
    150    return;
    151  }
    152 
    153  // Step 2: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, e).
    154  TransformStreamErrorWritableAndUnblockWrite(aCx, aStream, aError, aRv);
    155 }
    156 
    157 }  // namespace streams_abstract
    158 
    159 // https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform
    160 MOZ_CAN_RUN_SCRIPT static already_AddRefed<Promise>
    161 TransformStreamDefaultControllerPerformTransform(
    162    JSContext* aCx, TransformStreamDefaultController* aController,
    163    JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
    164  // Step 1: Let transformPromise be the result of performing
    165  // controller.[[transformAlgorithm]], passing chunk.
    166  RefPtr<TransformerAlgorithmsBase> algorithms = aController->Algorithms();
    167  RefPtr<Promise> transformPromise =
    168      algorithms->TransformCallback(aCx, aChunk, *aController, aRv);
    169  if (aRv.Failed()) {
    170    return nullptr;
    171  }
    172 
    173  // Step 2: Return the result of reacting to transformPromise with the
    174  // following rejection steps given the argument r:
    175  auto result = transformPromise->CatchWithCycleCollectedArgs(
    176      [](JSContext* aCx, JS::Handle<JS::Value> aError, ErrorResult& aRv,
    177         const RefPtr<TransformStreamDefaultController>& aController)
    178          MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA -> already_AddRefed<Promise> {
    179            // Step 2.1: Perform ! TransformStreamError(controller.[[stream]],
    180            // r).
    181            // TODO: Remove MOZ_KnownLive (bug 1761577)
    182            TransformStreamError(aCx, MOZ_KnownLive(aController->Stream()),
    183                                 aError, aRv);
    184            if (aRv.Failed()) {
    185              return nullptr;
    186            }
    187 
    188            // Step 2.2: Throw r.
    189            JS::Rooted<JS::Value> r(aCx, aError);
    190            aRv.MightThrowJSException();
    191            aRv.ThrowJSException(aCx, r);
    192            return nullptr;
    193          },
    194      RefPtr(aController));
    195  if (result.isErr()) {
    196    aRv.Throw(result.unwrapErr());
    197    return nullptr;
    198  }
    199  return result.unwrap().forget();
    200 }
    201 
    202 // https://streams.spec.whatwg.org/#initialize-transform-stream
    203 class TransformStreamUnderlyingSinkAlgorithms final
    204    : public UnderlyingSinkAlgorithmsBase {
    205 public:
    206  NS_DECL_ISUPPORTS_INHERITED
    207  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
    208      TransformStreamUnderlyingSinkAlgorithms, UnderlyingSinkAlgorithmsBase)
    209 
    210  TransformStreamUnderlyingSinkAlgorithms(Promise* aStartPromise,
    211                                          TransformStream* aStream)
    212      : mStartPromise(aStartPromise), mStream(aStream) {}
    213 
    214  void StartCallback(JSContext* aCx,
    215                     WritableStreamDefaultController& aController,
    216                     JS::MutableHandle<JS::Value> aRetVal,
    217                     ErrorResult& aRv) override {
    218    // Step 1. Let startAlgorithm be an algorithm that returns startPromise.
    219    // (Same as TransformStreamUnderlyingSourceAlgorithms::StartCallback)
    220    aRetVal.setObject(*mStartPromise->PromiseObj());
    221  }
    222 
    223  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> WriteCallback(
    224      JSContext* aCx, JS::Handle<JS::Value> aChunk,
    225      WritableStreamDefaultController& aController, ErrorResult& aRv) override {
    226    // Step 2. Let writeAlgorithm be the following steps, taking a chunk
    227    // argument:
    228    // Step 2.1. Return ! TransformStreamDefaultSinkWriteAlgorithm(stream,
    229    // chunk).
    230 
    231    // Inlining TransformStreamDefaultSinkWriteAlgorithm here:
    232    // https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm
    233 
    234    // Step 1: Assert: stream.[[writable]].[[state]] is "writable".
    235    MOZ_ASSERT(mStream->Writable()->State() ==
    236               WritableStream::WriterState::Writable);
    237 
    238    // Step 2: Let controller be stream.[[controller]].
    239    RefPtr<TransformStreamDefaultController> controller = mStream->Controller();
    240 
    241    // Step 3: If stream.[[backpressure]] is true,
    242    if (mStream->Backpressure()) {
    243      // Step 3.1: Let backpressureChangePromise be
    244      // stream.[[backpressureChangePromise]].
    245      RefPtr<Promise> backpressureChangePromise =
    246          mStream->BackpressureChangePromise();
    247 
    248      // Step 3.2: Assert: backpressureChangePromise is not undefined.
    249      MOZ_ASSERT(backpressureChangePromise);
    250 
    251      // Step 3.3: Return the result of reacting to backpressureChangePromise
    252      // with the following fulfillment steps:
    253      auto result = backpressureChangePromise->ThenWithCycleCollectedArgsJS(
    254          [](JSContext* aCx, JS::Handle<JS::Value>, ErrorResult& aRv,
    255             const RefPtr<TransformStream>& aStream,
    256             const RefPtr<TransformStreamDefaultController>& aController,
    257             JS::Handle<JS::Value> aChunk)
    258              MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA -> already_AddRefed<Promise> {
    259                // Step 1: Let writable be stream.[[writable]].
    260                RefPtr<WritableStream> writable = aStream->Writable();
    261 
    262                // Step 2: Let state be writable.[[state]].
    263                WritableStream::WriterState state = writable->State();
    264 
    265                // Step 3: If state is "erroring", throw
    266                // writable.[[storedError]].
    267                if (state == WritableStream::WriterState::Erroring) {
    268                  JS::Rooted<JS::Value> storedError(aCx,
    269                                                    writable->StoredError());
    270                  aRv.MightThrowJSException();
    271                  aRv.ThrowJSException(aCx, storedError);
    272                  return nullptr;
    273                }
    274 
    275                // Step 4: Assert: state is "writable".
    276                MOZ_ASSERT(state == WritableStream::WriterState::Writable);
    277 
    278                // Step 5: Return !
    279                // TransformStreamDefaultControllerPerformTransform(controller,
    280                // chunk).
    281                return TransformStreamDefaultControllerPerformTransform(
    282                    aCx, aController, aChunk, aRv);
    283              },
    284          std::make_tuple(mStream, controller), std::make_tuple(aChunk));
    285 
    286      if (result.isErr()) {
    287        aRv.Throw(result.unwrapErr());
    288        return nullptr;
    289      }
    290      return result.unwrap().forget();
    291    }
    292 
    293    // Step 4: Return !
    294    // TransformStreamDefaultControllerPerformTransform(controller, chunk).
    295    return TransformStreamDefaultControllerPerformTransform(aCx, controller,
    296                                                            aChunk, aRv);
    297  }
    298 
    299  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> AbortCallback(
    300      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
    301      ErrorResult& aRv) override {
    302    // Step 3. Let abortAlgorithm be the following steps, taking a reason
    303    // argument:
    304    // Step 3.1. Return ! TransformStreamDefaultSinkAbortAlgorithm(stream,
    305    // reason).
    306 
    307    // Inlining TransformStreamDefaultSinkAbortAlgorithm here:
    308    // https://streams.spec.whatwg.org/#transform-stream-default-sink-abort-algorithm
    309 
    310    // Step 1:Perform ! TransformStreamError(stream, reason).
    311    TransformStreamError(
    312        aCx, mStream,
    313        aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv);
    314    if (aRv.Failed()) {
    315      return nullptr;
    316    }
    317 
    318    // Step 2: Return a promise resolved with undefined.
    319    return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
    320                                                aRv);
    321  }
    322 
    323  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CloseCallback(
    324      JSContext* aCx, ErrorResult& aRv) override {
    325    // Step 4. Let closeAlgorithm be the following steps:
    326    // Step 4.1. Return ! TransformStreamDefaultSinkCloseAlgorithm(stream).
    327 
    328    // Inlining TransformStreamDefaultSinkCloseAlgorithm here:
    329    // https://streams.spec.whatwg.org/#transform-stream-default-sink-close-algorithm
    330 
    331    // Step 1: Let readable be stream.[[readable]].
    332    RefPtr<ReadableStream> readable = mStream->Readable();
    333 
    334    // Step 2: Let controller be stream.[[controller]].
    335    RefPtr<TransformStreamDefaultController> controller = mStream->Controller();
    336 
    337    // Step 3: Let flushPromise be the result of performing
    338    // controller.[[flushAlgorithm]].
    339    RefPtr<TransformerAlgorithmsBase> algorithms = controller->Algorithms();
    340    RefPtr<Promise> flushPromise =
    341        algorithms->FlushCallback(aCx, *controller, aRv);
    342    if (aRv.Failed()) {
    343      return nullptr;
    344    }
    345 
    346    // Step 4: Perform !
    347    // TransformStreamDefaultControllerClearAlgorithms(controller).
    348    controller->SetAlgorithms(nullptr);
    349 
    350    // Step 5: Return the result of reacting to flushPromise:
    351    Result<RefPtr<Promise>, nsresult> result =
    352        flushPromise->ThenCatchWithCycleCollectedArgs(
    353            [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    354               const RefPtr<ReadableStream>& aReadable,
    355               const RefPtr<TransformStream>& aStream)
    356                MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
    357            -> already_AddRefed<Promise> {
    358                  // Step 5.1: If flushPromise was fulfilled, then:
    359 
    360                  // Step 5.1.1: If readable.[[state]] is "errored", throw
    361                  // readable.[[storedError]].
    362                  if (aReadable->State() ==
    363                      ReadableStream::ReaderState::Errored) {
    364                    JS::Rooted<JS::Value> storedError(aCx,
    365                                                      aReadable->StoredError());
    366                    aRv.MightThrowJSException();
    367                    aRv.ThrowJSException(aCx, storedError);
    368                    return nullptr;
    369                  }
    370 
    371                  // Step 5.1.2: Perform !
    372                  // ReadableStreamDefaultControllerClose(readable.[[controller]]).
    373                  ReadableStreamDefaultControllerClose(
    374                      aCx, MOZ_KnownLive(aReadable->Controller()->AsDefault()),
    375                      aRv);
    376                  return nullptr;
    377                },
    378            [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    379               const RefPtr<ReadableStream>& aReadable,
    380               const RefPtr<TransformStream>& aStream)
    381                MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
    382            -> already_AddRefed<Promise> {
    383                  // Step 5.2: If flushPromise was rejected with reason r, then:
    384 
    385                  // Step 5.2.1: Perform ! TransformStreamError(stream, r).
    386                  TransformStreamError(aCx, aStream, aValue, aRv);
    387                  if (aRv.Failed()) {
    388                    return nullptr;
    389                  }
    390 
    391                  // Step 5.2.2: Throw readable.[[storedError]].
    392                  JS::Rooted<JS::Value> storedError(aCx,
    393                                                    aReadable->StoredError());
    394                  aRv.MightThrowJSException();
    395                  aRv.ThrowJSException(aCx, storedError);
    396                  return nullptr;
    397                },
    398            readable, mStream);
    399 
    400    if (result.isErr()) {
    401      aRv.Throw(result.unwrapErr());
    402      return nullptr;
    403    }
    404    return result.unwrap().forget();
    405  }
    406 
    407 protected:
    408  ~TransformStreamUnderlyingSinkAlgorithms() override = default;
    409 
    410 private:
    411  RefPtr<Promise> mStartPromise;
    412  // MOZ_KNOWN_LIVE because it won't be reassigned
    413  MOZ_KNOWN_LIVE RefPtr<TransformStream> mStream;
    414 };
    415 
    416 NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSinkAlgorithms,
    417                                   UnderlyingSinkAlgorithmsBase, mStartPromise,
    418                                   mStream)
    419 NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSinkAlgorithms,
    420                         UnderlyingSinkAlgorithmsBase)
    421 NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSinkAlgorithms,
    422                          UnderlyingSinkAlgorithmsBase)
    423 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStreamUnderlyingSinkAlgorithms)
    424 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSinkAlgorithmsBase)
    425 
    426 // https://streams.spec.whatwg.org/#initialize-transform-stream
    427 class TransformStreamUnderlyingSourceAlgorithms final
    428    : public UnderlyingSourceAlgorithmsBase {
    429 public:
    430  NS_DECL_ISUPPORTS_INHERITED
    431  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
    432      TransformStreamUnderlyingSourceAlgorithms, UnderlyingSourceAlgorithmsBase)
    433 
    434  TransformStreamUnderlyingSourceAlgorithms(Promise* aStartPromise,
    435                                            TransformStream* aStream)
    436      : mStartPromise(aStartPromise), mStream(aStream) {}
    437 
    438  void StartCallback(JSContext* aCx, ReadableStreamControllerBase& aController,
    439                     JS::MutableHandle<JS::Value> aRetVal,
    440                     ErrorResult& aRv) override {
    441    // Step 1. Let startAlgorithm be an algorithm that returns startPromise.
    442    // (Same as TransformStreamUnderlyingSinkAlgorithms::StartCallback)
    443    aRetVal.setObject(*mStartPromise->PromiseObj());
    444  }
    445 
    446  already_AddRefed<Promise> PullCallback(
    447      JSContext* aCx, ReadableStreamControllerBase& aController,
    448      ErrorResult& aRv) override {
    449    // Step 6. Let pullAlgorithm be the following steps:
    450    // Step 6.1. Return ! TransformStreamDefaultSourcePullAlgorithm(stream).
    451 
    452    // Inlining TransformStreamDefaultSourcePullAlgorithm here:
    453    // https://streams.spec.whatwg.org/#transform-stream-default-source-pull-algorithm
    454 
    455    // Step 1: Assert: stream.[[backpressure]] is true.
    456    MOZ_ASSERT(mStream->Backpressure());
    457 
    458    // Step 2: Assert: stream.[[backpressureChangePromise]] is not undefined.
    459    MOZ_ASSERT(mStream->BackpressureChangePromise());
    460 
    461    // Step 3: Perform ! TransformStreamSetBackpressure(stream, false).
    462    mStream->SetBackpressure(false);
    463 
    464    // Step 4: Return stream.[[backpressureChangePromise]].
    465    return do_AddRef(mStream->BackpressureChangePromise());
    466  }
    467 
    468  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CancelCallback(
    469      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
    470      ErrorResult& aRv) override {
    471    // Step 7. Let cancelAlgorithm be the following steps, taking a reason
    472    // argument:
    473    // Step 7.1. Perform ! TransformStreamErrorWritableAndUnblockWrite(stream,
    474    // reason).
    475    TransformStreamErrorWritableAndUnblockWrite(
    476        aCx, mStream,
    477        aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv);
    478    if (aRv.Failed()) {
    479      return nullptr;
    480    }
    481 
    482    // Step 7.2. Return a promise resolved with undefined.
    483    return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
    484                                                aRv);
    485  }
    486 
    487 protected:
    488  ~TransformStreamUnderlyingSourceAlgorithms() override = default;
    489 
    490 private:
    491  RefPtr<Promise> mStartPromise;
    492  // MOZ_KNOWNLIVE because it will never be reassigned
    493  MOZ_KNOWN_LIVE RefPtr<TransformStream> mStream;
    494 };
    495 
    496 NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSourceAlgorithms,
    497                                   UnderlyingSourceAlgorithmsBase,
    498                                   mStartPromise, mStream)
    499 NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSourceAlgorithms,
    500                         UnderlyingSourceAlgorithmsBase)
    501 NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSourceAlgorithms,
    502                          UnderlyingSourceAlgorithmsBase)
    503 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    504    TransformStreamUnderlyingSourceAlgorithms)
    505 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase)
    506 
    507 // https://streams.spec.whatwg.org/#transform-stream-set-backpressure
    508 void TransformStream::SetBackpressure(bool aBackpressure) {
    509  // Step 1. Assert: stream.[[backpressure]] is not backpressure.
    510  MOZ_ASSERT(Backpressure() != aBackpressure);
    511 
    512  // Step 2. If stream.[[backpressureChangePromise]] is not undefined, resolve
    513  // stream.[[backpressureChangePromise]] with undefined.
    514  if (Promise* promise = BackpressureChangePromise()) {
    515    promise->MaybeResolveWithUndefined();
    516  }
    517 
    518  // Step 3. Set stream.[[backpressureChangePromise]] to a new promise.
    519  RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
    520  mBackpressureChangePromise = promise;
    521 
    522  // Step 4. Set stream.[[backpressure]] to backpressure.
    523  mBackpressure = aBackpressure;
    524 }
    525 
    526 // https://streams.spec.whatwg.org/#initialize-transform-stream
    527 void TransformStream::Initialize(JSContext* aCx, Promise* aStartPromise,
    528                                 double aWritableHighWaterMark,
    529                                 QueuingStrategySize* aWritableSizeAlgorithm,
    530                                 double aReadableHighWaterMark,
    531                                 QueuingStrategySize* aReadableSizeAlgorithm,
    532                                 ErrorResult& aRv) {
    533  // Step 1 - 4
    534  auto sinkAlgorithms =
    535      MakeRefPtr<TransformStreamUnderlyingSinkAlgorithms>(aStartPromise, this);
    536 
    537  // Step 5. Set stream.[[writable]] to ! CreateWritableStream(startAlgorithm,
    538  // writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark,
    539  // writableSizeAlgorithm).
    540  mWritable = WritableStream::CreateAbstract(
    541      aCx, MOZ_KnownLive(mGlobal), sinkAlgorithms, aWritableHighWaterMark,
    542      aWritableSizeAlgorithm, aRv);
    543  if (aRv.Failed()) {
    544    return;
    545  }
    546 
    547  // Step 6 - 7
    548  auto sourceAlgorithms = MakeRefPtr<TransformStreamUnderlyingSourceAlgorithms>(
    549      aStartPromise, this);
    550 
    551  // Step 8. Set stream.[[readable]] to ! CreateReadableStream(startAlgorithm,
    552  // pullAlgorithm, cancelAlgorithm, readableHighWaterMark,
    553  // readableSizeAlgorithm).
    554  mReadable = ReadableStream::CreateAbstract(
    555      aCx, MOZ_KnownLive(mGlobal), sourceAlgorithms,
    556      Some(aReadableHighWaterMark), aReadableSizeAlgorithm, aRv);
    557  if (aRv.Failed()) {
    558    return;
    559  }
    560 
    561  // Step 9. Set stream.[[backpressure]] and
    562  // stream.[[backpressureChangePromise]] to undefined.
    563  // Note(krosylight): The spec allows setting [[backpressure]] as undefined,
    564  // but I don't see why it should be. Since the spec also allows strict boolean
    565  // type, and this is only to not trigger assertion inside the setter, we just
    566  // set it as false.
    567  mBackpressure = false;
    568  mBackpressureChangePromise = nullptr;
    569 
    570  // Step 10. Perform ! TransformStreamSetBackpressure(stream, true).
    571  SetBackpressure(true);
    572  if (aRv.Failed()) {
    573    return;
    574  }
    575 
    576  // Step 11. Set stream.[[controller]] to undefined.
    577  mController = nullptr;
    578 }
    579 
    580 // https://streams.spec.whatwg.org/#ts-constructor
    581 already_AddRefed<TransformStream> TransformStream::Constructor(
    582    const GlobalObject& aGlobal,
    583    const Optional<JS::Handle<JSObject*>>& aTransformer,
    584    const QueuingStrategy& aWritableStrategy,
    585    const QueuingStrategy& aReadableStrategy, ErrorResult& aRv) {
    586  // Step 1. If transformer is missing, set it to null.
    587  JS::Rooted<JSObject*> transformerObj(
    588      aGlobal.Context(),
    589      aTransformer.WasPassed() ? aTransformer.Value() : nullptr);
    590 
    591  // Step 2. Let transformerDict be transformer, converted to an IDL value of
    592  // type Transformer.
    593  RootedDictionary<Transformer> transformerDict(aGlobal.Context());
    594  if (transformerObj) {
    595    JS::Rooted<JS::Value> objValue(aGlobal.Context(),
    596                                   JS::ObjectValue(*transformerObj));
    597    dom::BindingCallContext callCx(aGlobal.Context(),
    598                                   "TransformStream.constructor");
    599    aRv.MightThrowJSException();
    600    if (!transformerDict.Init(callCx, objValue)) {
    601      aRv.StealExceptionFromJSContext(aGlobal.Context());
    602      return nullptr;
    603    }
    604  }
    605 
    606  // Step 3. If transformerDict["readableType"] exists, throw a RangeError
    607  // exception.
    608  if (!transformerDict.mReadableType.isUndefined()) {
    609    aRv.ThrowRangeError(
    610        "`readableType` is unsupported and preserved for future use");
    611    return nullptr;
    612  }
    613 
    614  // Step 4. If transformerDict["writableType"] exists, throw a RangeError
    615  // exception.
    616  if (!transformerDict.mWritableType.isUndefined()) {
    617    aRv.ThrowRangeError(
    618        "`writableType` is unsupported and preserved for future use");
    619    return nullptr;
    620  }
    621 
    622  // Step 5. Let readableHighWaterMark be ?
    623  // ExtractHighWaterMark(readableStrategy, 0).
    624  double readableHighWaterMark =
    625      ExtractHighWaterMark(aReadableStrategy, 0, aRv);
    626  if (aRv.Failed()) {
    627    return nullptr;
    628  }
    629 
    630  // Step 6. Let readableSizeAlgorithm be !
    631  // ExtractSizeAlgorithm(readableStrategy).
    632  // Note: Callers should recognize nullptr as a callback that returns 1. See
    633  // also ReadableStream::Constructor for this design decision.
    634  RefPtr<QueuingStrategySize> readableSizeAlgorithm =
    635      aReadableStrategy.mSize.WasPassed() ? &aReadableStrategy.mSize.Value()
    636                                          : nullptr;
    637 
    638  // Step 7. Let writableHighWaterMark be ?
    639  // ExtractHighWaterMark(writableStrategy, 1).
    640  double writableHighWaterMark =
    641      ExtractHighWaterMark(aWritableStrategy, 1, aRv);
    642  if (aRv.Failed()) {
    643    return nullptr;
    644  }
    645 
    646  // Step 8. Let writableSizeAlgorithm be !
    647  // ExtractSizeAlgorithm(writableStrategy).
    648  // Note: Callers should recognize nullptr as a callback that returns 1. See
    649  // also WritableStream::Constructor for this design decision.
    650  RefPtr<QueuingStrategySize> writableSizeAlgorithm =
    651      aWritableStrategy.mSize.WasPassed() ? &aWritableStrategy.mSize.Value()
    652                                          : nullptr;
    653 
    654  // Step 9. Let startPromise be a new promise.
    655  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    656  RefPtr<Promise> startPromise = Promise::CreateInfallible(global);
    657 
    658  // Step 10. Perform ! InitializeTransformStream(this, startPromise,
    659  // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark,
    660  // readableSizeAlgorithm).
    661  RefPtr<TransformStream> transformStream = new TransformStream(global);
    662  transformStream->Initialize(
    663      aGlobal.Context(), startPromise, writableHighWaterMark,
    664      writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm, aRv);
    665  if (aRv.Failed()) {
    666    return nullptr;
    667  }
    668 
    669  // Step 11. Perform ?
    670  // SetUpTransformStreamDefaultControllerFromTransformer(this, transformer,
    671  // transformerDict).
    672  SetUpTransformStreamDefaultControllerFromTransformer(
    673      aGlobal.Context(), *transformStream, transformerObj, transformerDict);
    674 
    675  // Step 12. If transformerDict["start"] exists, then resolve startPromise with
    676  // the result of invoking transformerDict["start"] with argument list «
    677  // this.[[controller]] » and callback this value transformer.
    678  if (transformerDict.mStart.WasPassed()) {
    679    RefPtr<TransformerStartCallback> callback = transformerDict.mStart.Value();
    680    RefPtr<TransformStreamDefaultController> controller =
    681        transformStream->Controller();
    682    JS::Rooted<JS::Value> retVal(aGlobal.Context());
    683    callback->Call(transformerObj, *controller, &retVal, aRv,
    684                   "Transformer.start", CallbackFunction::eRethrowExceptions);
    685    if (aRv.Failed()) {
    686      return nullptr;
    687    }
    688 
    689    startPromise->MaybeResolve(retVal);
    690  } else {
    691    // Step 13. Otherwise, resolve startPromise with undefined.
    692    startPromise->MaybeResolveWithUndefined();
    693  }
    694 
    695  return transformStream.forget();
    696 }
    697 
    698 }  // namespace mozilla::dom