Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  TransformStream.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/dom/TransformStream.h"

#include "StreamUtils.h"
#include "TransformerCallbackHelpers.h"
#include "UnderlyingSourceCallbackHelpers.h"
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Promise-inl.h"
#include "mozilla/dom/WritableStream.h"
#include "mozilla/dom/ReadableStream.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/TransformStreamBinding.h"
#include "mozilla/dom/TransformerBinding.h"
#include "nsWrapperCache.h"

namespace mozilla::dom {

using namespace streams_abstract;

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TransformStream, mGlobal,
                                      mBackpressureChangePromise, mController,
                                      mReadable, mWritable)
NS_IMPL_CYCLE_COLLECTING_ADDREF(TransformStream)
NS_IMPL_CYCLE_COLLECTING_RELEASE(TransformStream)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStream)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

// https://streams.spec.whatwg.org/#transformstream-set-up
// (except this instead creates a new TransformStream rather than accepting an
// existing instance)
already_AddRefed<TransformStream> TransformStream::CreateGeneric(
    const GlobalObject& aGlobal, TransformerAlgorithmsWrapper& aAlgorithms,
    ErrorResult& aRv) {
  // Step 1. Let writableHighWaterMark be 1.
  double writableHighWaterMark = 1;

  // Step 2. Let writableSizeAlgorithm be an algorithm that returns 1.
  // Note: Callers should recognize nullptr as a callback that returns 1. See
  // also WritableStream::Constructor for this design decision.
  RefPtr<QueuingStrategySize> writableSizeAlgorithm;

  // Step 3. Let readableHighWaterMark be 0.
  double readableHighWaterMark = 0;

  // Step 4. Let readableSizeAlgorithm be an algorithm that returns 1.
  // Note: Callers should recognize nullptr as a callback that returns 1. See
  // also ReadableStream::Constructor for this design decision.
  RefPtr<QueuingStrategySize> readableSizeAlgorithm;

  // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps
  // given a value chunk:
  // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps:
  // (Done by TransformerAlgorithmsWrapper)

  // Step 7. Let startPromise be a promise resolved with undefined.
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
  RefPtr<Promise> startPromise =
      Promise::CreateResolvedWithUndefined(global, aRv);
  if (!startPromise) {
    return nullptr;
  }

  // Step 8. Perform ! InitializeTransformStream(stream, startPromise,
  // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark,
  // readableSizeAlgorithm).
  RefPtr<TransformStream> stream =
      new TransformStream(global, nullptr, nullptr);
  stream->Initialize(aGlobal.Context(), startPromise, writableHighWaterMark,
                     writableSizeAlgorithm, readableHighWaterMark,
                     readableSizeAlgorithm, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  // Step 9. Let controller be a new TransformStreamDefaultController.
  auto controller = MakeRefPtr<TransformStreamDefaultController>(global);

  // Step 10. Perform ! SetUpTransformStreamDefaultController(stream,
  // controller, transformAlgorithmWrapper, flushAlgorithmWrapper).
  SetUpTransformStreamDefaultController(aGlobal.Context(), *stream, *controller,
                                        aAlgorithms);

  return stream.forget();
}

TransformStream::TransformStream(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) {
  mozilla::HoldJSObjects(this);
}

TransformStream::TransformStream(nsIGlobalObject* aGlobal,
                                 ReadableStream* aReadable,
                                 WritableStream* aWritable)
    : mGlobal(aGlobal), mReadable(aReadable), mWritable(aWritable) {
  mozilla::HoldJSObjects(this);
}

TransformStream::~TransformStream() { mozilla::DropJSObjects(this); }

JSObject* TransformStream::WrapObject(JSContext* aCx,
                                      JS::Handle<JSObject*> aGivenProto) {
  return TransformStream_Binding::Wrap(aCx, this, aGivenProto);
}

namespace streams_abstract {

// https://streams.spec.whatwg.org/#transform-stream-error-writable-and-unblock-write
void TransformStreamErrorWritableAndUnblockWrite(JSContext* aCx,
                                                 TransformStream* aStream,
                                                 JS::Handle<JS::Value> aError,
                                                 ErrorResult& aRv) {
  // Step 1: Perform !
  // TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]).
  aStream->Controller()->SetAlgorithms(nullptr);

  // Step 2: Perform !
  // WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]],
  // e).
  // TODO: Remove MOZ_KnownLive (bug 1761577)
  WritableStreamDefaultControllerErrorIfNeeded(
      aCx, MOZ_KnownLive(aStream->Writable()->Controller()), aError, aRv);
  if (aRv.Failed()) {
    return;
  }

  // Step 3: If stream.[[backpressure]] is true, perform !
  // TransformStreamSetBackpressure(stream, false).
  if (aStream->Backpressure()) {
    aStream->SetBackpressure(false);
  }
}

// https://streams.spec.whatwg.org/#transform-stream-error
void TransformStreamError(JSContext* aCx, TransformStream* aStream,
                          JS::Handle<JS::Value> aError, ErrorResult& aRv) {
  // Step 1: Perform !
  // ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]],
  // e).
  ReadableStreamDefaultControllerError(
      aCx, aStream->Readable()->Controller()->AsDefault(), aError, aRv);
  if (aRv.Failed()) {
    return;
  }

  // Step 2: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, e).
  TransformStreamErrorWritableAndUnblockWrite(aCx, aStream, aError, aRv);
}

}  // namespace streams_abstract

// https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform
MOZ_CAN_RUN_SCRIPT static already_AddRefed<Promise>
TransformStreamDefaultControllerPerformTransform(
    JSContext* aCx, TransformStreamDefaultController* aController,
    JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
  // Step 1: Let transformPromise be the result of performing
  // controller.[[transformAlgorithm]], passing chunk.
  RefPtr<TransformerAlgorithmsBase> algorithms = aController->Algorithms();
  RefPtr<Promise> transformPromise =
      algorithms->TransformCallback(aCx, aChunk, *aController, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  // Step 2: Return the result of reacting to transformPromise with the
  // following rejection steps given the argument r:
  auto result = transformPromise->CatchWithCycleCollectedArgs(
      [](JSContext* aCx, JS::Handle<JS::Value> aError, ErrorResult& aRv,
         const RefPtr<TransformStreamDefaultController>& aController)
          MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA -> already_AddRefed<Promise> {
            // Step 2.1: Perform ! TransformStreamError(controller.[[stream]],
            // r).
            // TODO: Remove MOZ_KnownLive (bug 1761577)
            TransformStreamError(aCx, MOZ_KnownLive(aController->Stream()),
                                 aError, aRv);
            if (aRv.Failed()) {
              return nullptr;
            }

            // Step 2.2: Throw r.
            JS::Rooted<JS::Value> r(aCx, aError);
            aRv.MightThrowJSException();
            aRv.ThrowJSException(aCx, r);
            return nullptr;
          },
      RefPtr(aController));
  if (result.isErr()) {
    aRv.Throw(result.unwrapErr());
    return nullptr;
  }
  return result.unwrap().forget();
}

// https://streams.spec.whatwg.org/#initialize-transform-stream
class TransformStreamUnderlyingSinkAlgorithms final
    : public UnderlyingSinkAlgorithmsBase {
 public:
  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
      TransformStreamUnderlyingSinkAlgorithms, UnderlyingSinkAlgorithmsBase)

  TransformStreamUnderlyingSinkAlgorithms(Promise* aStartPromise,
                                          TransformStream* aStream)
      : mStartPromise(aStartPromise), mStream(aStream) {}

  void StartCallback(JSContext* aCx,
                     WritableStreamDefaultController& aController,
                     JS::MutableHandle<JS::Value> aRetVal,
                     ErrorResult& aRv) override {
    // Step 1. Let startAlgorithm be an algorithm that returns startPromise.
    // (Same as TransformStreamUnderlyingSourceAlgorithms::StartCallback)
    aRetVal.setObject(*mStartPromise->PromiseObj());
  }

  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> WriteCallback(
      JSContext* aCx, JS::Handle<JS::Value> aChunk,
      WritableStreamDefaultController& aController, ErrorResult& aRv) override {
    // Step 2. Let writeAlgorithm be the following steps, taking a chunk
    // argument:
    // Step 2.1. Return ! TransformStreamDefaultSinkWriteAlgorithm(stream,
    // chunk).

    // Inlining TransformStreamDefaultSinkWriteAlgorithm here:
    // https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm

    // Step 1: Assert: stream.[[writable]].[[state]] is "writable".
    MOZ_ASSERT(mStream->Writable()->State() ==
               WritableStream::WriterState::Writable);

    // Step 2: Let controller be stream.[[controller]].
    RefPtr<TransformStreamDefaultController> controller = mStream->Controller();

    // Step 3: If stream.[[backpressure]] is true,
    if (mStream->Backpressure()) {
      // Step 3.1: Let backpressureChangePromise be
      // stream.[[backpressureChangePromise]].
      RefPtr<Promise> backpressureChangePromise =
          mStream->BackpressureChangePromise();

      // Step 3.2: Assert: backpressureChangePromise is not undefined.
      MOZ_ASSERT(backpressureChangePromise);

      // Step 3.3: Return the result of reacting to backpressureChangePromise
      // with the following fulfillment steps:
      auto result = backpressureChangePromise->ThenWithCycleCollectedArgsJS(
          [](JSContext* aCx, JS::Handle<JS::Value>, ErrorResult& aRv,
             const RefPtr<TransformStream>& aStream,
             const RefPtr<TransformStreamDefaultController>& aController,
             JS::Handle<JS::Value> aChunk)
              MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA -> already_AddRefed<Promise> {
                // Step 1: Let writable be stream.[[writable]].
                RefPtr<WritableStream> writable = aStream->Writable();

                // Step 2: Let state be writable.[[state]].
                WritableStream::WriterState state = writable->State();

                // Step 3: If state is "erroring", throw
                // writable.[[storedError]].
                if (state == WritableStream::WriterState::Erroring) {
                  JS::Rooted<JS::Value> storedError(aCx,
                                                    writable->StoredError());
                  aRv.MightThrowJSException();
                  aRv.ThrowJSException(aCx, storedError);
                  return nullptr;
                }

                // Step 4: Assert: state is "writable".
                MOZ_ASSERT(state == WritableStream::WriterState::Writable);

                // Step 5: Return !
                // TransformStreamDefaultControllerPerformTransform(controller,
                // chunk).
                return TransformStreamDefaultControllerPerformTransform(
                    aCx, aController, aChunk, aRv);
              },
          std::make_tuple(mStream, controller), std::make_tuple(aChunk));

      if (result.isErr()) {
        aRv.Throw(result.unwrapErr());
        return nullptr;
      }
      return result.unwrap().forget();
    }

    // Step 4: Return !
    // TransformStreamDefaultControllerPerformTransform(controller, chunk).
    return TransformStreamDefaultControllerPerformTransform(aCx, controller,
                                                            aChunk, aRv);
  }

  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> AbortCallback(
      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
      ErrorResult& aRv) override {
    // Step 3. Let abortAlgorithm be the following steps, taking a reason
    // argument:
    // Step 3.1. Return ! TransformStreamDefaultSinkAbortAlgorithm(stream,
    // reason).

    // Inlining TransformStreamDefaultSinkAbortAlgorithm here:
    // https://streams.spec.whatwg.org/#transform-stream-default-sink-abort-algorithm

    // Step 1:Perform ! TransformStreamError(stream, reason).
    TransformStreamError(
        aCx, mStream,
        aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv);
    if (aRv.Failed()) {
      return nullptr;
    }

    // Step 2: Return a promise resolved with undefined.
    return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
                                                aRv);
  }

  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CloseCallback(
      JSContext* aCx, ErrorResult& aRv) override {
    // Step 4. Let closeAlgorithm be the following steps:
    // Step 4.1. Return ! TransformStreamDefaultSinkCloseAlgorithm(stream).

    // Inlining TransformStreamDefaultSinkCloseAlgorithm here:
    // https://streams.spec.whatwg.org/#transform-stream-default-sink-close-algorithm

    // Step 1: Let readable be stream.[[readable]].
    RefPtr<ReadableStream> readable = mStream->Readable();

    // Step 2: Let controller be stream.[[controller]].
    RefPtr<TransformStreamDefaultController> controller = mStream->Controller();

    // Step 3: Let flushPromise be the result of performing
    // controller.[[flushAlgorithm]].
    RefPtr<TransformerAlgorithmsBase> algorithms = controller->Algorithms();
    RefPtr<Promise> flushPromise =
        algorithms->FlushCallback(aCx, *controller, aRv);
    if (aRv.Failed()) {
      return nullptr;
    }

    // Step 4: Perform !
    // TransformStreamDefaultControllerClearAlgorithms(controller).
    controller->SetAlgorithms(nullptr);

    // Step 5: Return the result of reacting to flushPromise:
    Result<RefPtr<Promise>, nsresult> result =
        flushPromise->ThenCatchWithCycleCollectedArgs(
            [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
               const RefPtr<ReadableStream>& aReadable,
               const RefPtr<TransformStream>& aStream)
                MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
            -> already_AddRefed<Promise> {
                  // Step 5.1: If flushPromise was fulfilled, then:

                  // Step 5.1.1: If readable.[[state]] is "errored", throw
                  // readable.[[storedError]].
                  if (aReadable->State() ==
                      ReadableStream::ReaderState::Errored) {
                    JS::Rooted<JS::Value> storedError(aCx,
                                                      aReadable->StoredError());
                    aRv.MightThrowJSException();
                    aRv.ThrowJSException(aCx, storedError);
                    return nullptr;
                  }

                  // Step 5.1.2: Perform !
                  // ReadableStreamDefaultControllerClose(readable.[[controller]]).
                  ReadableStreamDefaultControllerClose(
                      aCx, MOZ_KnownLive(aReadable->Controller()->AsDefault()),
                      aRv);
                  return nullptr;
                },
            [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
               const RefPtr<ReadableStream>& aReadable,
               const RefPtr<TransformStream>& aStream)
                MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
            -> already_AddRefed<Promise> {
                  // Step 5.2: If flushPromise was rejected with reason r, then:

                  // Step 5.2.1: Perform ! TransformStreamError(stream, r).
                  TransformStreamError(aCx, aStream, aValue, aRv);
                  if (aRv.Failed()) {
                    return nullptr;
                  }

                  // Step 5.2.2: Throw readable.[[storedError]].
                  JS::Rooted<JS::Value> storedError(aCx,
                                                    aReadable->StoredError());
                  aRv.MightThrowJSException();
                  aRv.ThrowJSException(aCx, storedError);
                  return nullptr;
                },
            readable, mStream);

    if (result.isErr()) {
      aRv.Throw(result.unwrapErr());
      return nullptr;
    }
    return result.unwrap().forget();
  }

 protected:
  ~TransformStreamUnderlyingSinkAlgorithms() override = default;

 private:
  RefPtr<Promise> mStartPromise;
  // MOZ_KNOWN_LIVE because it won't be reassigned
  MOZ_KNOWN_LIVE RefPtr<TransformStream> mStream;
};

NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSinkAlgorithms,
                                   UnderlyingSinkAlgorithmsBase, mStartPromise,
                                   mStream)
NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSinkAlgorithms,
                         UnderlyingSinkAlgorithmsBase)
NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSinkAlgorithms,
                          UnderlyingSinkAlgorithmsBase)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStreamUnderlyingSinkAlgorithms)
NS_INTERFACE_MAP_END_INHERITING(UnderlyingSinkAlgorithmsBase)

// https://streams.spec.whatwg.org/#initialize-transform-stream
class TransformStreamUnderlyingSourceAlgorithms final
    : public UnderlyingSourceAlgorithmsBase {
 public:
  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
      TransformStreamUnderlyingSourceAlgorithms, UnderlyingSourceAlgorithmsBase)

  TransformStreamUnderlyingSourceAlgorithms(Promise* aStartPromise,
                                            TransformStream* aStream)
      : mStartPromise(aStartPromise), mStream(aStream) {}

  void StartCallback(JSContext* aCx, ReadableStreamController& aController,
                     JS::MutableHandle<JS::Value> aRetVal,
                     ErrorResult& aRv) override {
    // Step 1. Let startAlgorithm be an algorithm that returns startPromise.
    // (Same as TransformStreamUnderlyingSinkAlgorithms::StartCallback)
    aRetVal.setObject(*mStartPromise->PromiseObj());
  }

  already_AddRefed<Promise> PullCallback(JSContext* aCx,
                                         ReadableStreamController& aController,
                                         ErrorResult& aRv) override {
    // Step 6. Let pullAlgorithm be the following steps:
    // Step 6.1. Return ! TransformStreamDefaultSourcePullAlgorithm(stream).

    // Inlining TransformStreamDefaultSourcePullAlgorithm here:
    // https://streams.spec.whatwg.org/#transform-stream-default-source-pull-algorithm

    // Step 1: Assert: stream.[[backpressure]] is true.
    MOZ_ASSERT(mStream->Backpressure());

    // Step 2: Assert: stream.[[backpressureChangePromise]] is not undefined.
    MOZ_ASSERT(mStream->BackpressureChangePromise());

    // Step 3: Perform ! TransformStreamSetBackpressure(stream, false).
    mStream->SetBackpressure(false);

    // Step 4: Return stream.[[backpressureChangePromise]].
    return do_AddRef(mStream->BackpressureChangePromise());
  }

  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CancelCallback(
      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
      ErrorResult& aRv) override {
    // Step 7. Let cancelAlgorithm be the following steps, taking a reason
    // argument:
    // Step 7.1. Perform ! TransformStreamErrorWritableAndUnblockWrite(stream,
    // reason).
    TransformStreamErrorWritableAndUnblockWrite(
        aCx, mStream,
        aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv);
    if (aRv.Failed()) {
      return nullptr;
    }

    // Step 7.2. Return a promise resolved with undefined.
    return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
                                                aRv);
  }

 protected:
  ~TransformStreamUnderlyingSourceAlgorithms() override = default;

 private:
  RefPtr<Promise> mStartPromise;
  // MOZ_KNOWNLIVE because it will never be reassigned
  MOZ_KNOWN_LIVE RefPtr<TransformStream> mStream;
};

NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSourceAlgorithms,
                                   UnderlyingSourceAlgorithmsBase,
                                   mStartPromise, mStream)
NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSourceAlgorithms,
                         UnderlyingSourceAlgorithmsBase)
NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSourceAlgorithms,
                          UnderlyingSourceAlgorithmsBase)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    TransformStreamUnderlyingSourceAlgorithms)
NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase)

// https://streams.spec.whatwg.org/#transform-stream-set-backpressure
void TransformStream::SetBackpressure(bool aBackpressure) {
  // Step 1. Assert: stream.[[backpressure]] is not backpressure.
  MOZ_ASSERT(Backpressure() != aBackpressure);

  // Step 2. If stream.[[backpressureChangePromise]] is not undefined, resolve
  // stream.[[backpressureChangePromise]] with undefined.
  if (Promise* promise = BackpressureChangePromise()) {
    promise->MaybeResolveWithUndefined();
  }

  // Step 3. Set stream.[[backpressureChangePromise]] to a new promise.
  RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
  mBackpressureChangePromise = promise;

  // Step 4. Set stream.[[backpressure]] to backpressure.
  mBackpressure = aBackpressure;
}

// https://streams.spec.whatwg.org/#initialize-transform-stream
void TransformStream::Initialize(JSContext* aCx, Promise* aStartPromise,
                                 double aWritableHighWaterMark,
                                 QueuingStrategySize* aWritableSizeAlgorithm,
                                 double aReadableHighWaterMark,
                                 QueuingStrategySize* aReadableSizeAlgorithm,
                                 ErrorResult& aRv) {
  // Step 1 - 4
  auto sinkAlgorithms =
      MakeRefPtr<TransformStreamUnderlyingSinkAlgorithms>(aStartPromise, this);

  // Step 5. Set stream.[[writable]] to ! CreateWritableStream(startAlgorithm,
  // writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark,
  // writableSizeAlgorithm).
  mWritable = WritableStream::CreateAbstract(
      aCx, MOZ_KnownLive(mGlobal), sinkAlgorithms, aWritableHighWaterMark,
      aWritableSizeAlgorithm, aRv);
  if (aRv.Failed()) {
    return;
  }

  // Step 6 - 7
  auto sourceAlgorithms = MakeRefPtr<TransformStreamUnderlyingSourceAlgorithms>(
      aStartPromise, this);

  // Step 8. Set stream.[[readable]] to ! CreateReadableStream(startAlgorithm,
  // pullAlgorithm, cancelAlgorithm, readableHighWaterMark,
  // readableSizeAlgorithm).
  mReadable = ReadableStream::CreateAbstract(
      aCx, MOZ_KnownLive(mGlobal), sourceAlgorithms,
      Some(aReadableHighWaterMark), aReadableSizeAlgorithm, aRv);
  if (aRv.Failed()) {
    return;
  }

  // Step 9. Set stream.[[backpressure]] and
  // stream.[[backpressureChangePromise]] to undefined.
  // Note(krosylight): The spec allows setting [[backpressure]] as undefined,
  // but I don't see why it should be. Since the spec also allows strict boolean
  // type, and this is only to not trigger assertion inside the setter, we just
  // set it as false.
  mBackpressure = false;
  mBackpressureChangePromise = nullptr;

  // Step 10. Perform ! TransformStreamSetBackpressure(stream, true).
  SetBackpressure(true);
  if (aRv.Failed()) {
    return;
  }

  // Step 11. Set stream.[[controller]] to undefined.
  mController = nullptr;
}

// https://streams.spec.whatwg.org/#ts-constructor
already_AddRefed<TransformStream> TransformStream::Constructor(
    const GlobalObject& aGlobal,
    const Optional<JS::Handle<JSObject*>>& aTransformer,
    const QueuingStrategy& aWritableStrategy,
    const QueuingStrategy& aReadableStrategy, ErrorResult& aRv) {
  // Step 1. If transformer is missing, set it to null.
  JS::Rooted<JSObject*> transformerObj(
      aGlobal.Context(),
      aTransformer.WasPassed() ? aTransformer.Value() : nullptr);

  // Step 2. Let transformerDict be transformer, converted to an IDL value of
  // type Transformer.
  RootedDictionary<Transformer> transformerDict(aGlobal.Context());
  if (transformerObj) {
    JS::Rooted<JS::Value> objValue(aGlobal.Context(),
                                   JS::ObjectValue(*transformerObj));
    dom::BindingCallContext callCx(aGlobal.Context(),
                                   "TransformStream.constructor");
    aRv.MightThrowJSException();
    if (!transformerDict.Init(callCx, objValue)) {
      aRv.StealExceptionFromJSContext(aGlobal.Context());
      return nullptr;
    }
  }

  // Step 3. If transformerDict["readableType"] exists, throw a RangeError
  // exception.
  if (!transformerDict.mReadableType.isUndefined()) {
    aRv.ThrowRangeError(
        "`readableType` is unsupported and preserved for future use");
    return nullptr;
  }

  // Step 4. If transformerDict["writableType"] exists, throw a RangeError
  // exception.
  if (!transformerDict.mWritableType.isUndefined()) {
    aRv.ThrowRangeError(
        "`writableType` is unsupported and preserved for future use");
    return nullptr;
  }

  // Step 5. Let readableHighWaterMark be ?
  // ExtractHighWaterMark(readableStrategy, 0).
  double readableHighWaterMark =
      ExtractHighWaterMark(aReadableStrategy, 0, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  // Step 6. Let readableSizeAlgorithm be !
  // ExtractSizeAlgorithm(readableStrategy).
  // Note: Callers should recognize nullptr as a callback that returns 1. See
  // also ReadableStream::Constructor for this design decision.
  RefPtr<QueuingStrategySize> readableSizeAlgorithm =
      aReadableStrategy.mSize.WasPassed() ? &aReadableStrategy.mSize.Value()
                                          : nullptr;

  // Step 7. Let writableHighWaterMark be ?
  // ExtractHighWaterMark(writableStrategy, 1).
  double writableHighWaterMark =
      ExtractHighWaterMark(aWritableStrategy, 1, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  // Step 8. Let writableSizeAlgorithm be !
  // ExtractSizeAlgorithm(writableStrategy).
  // Note: Callers should recognize nullptr as a callback that returns 1. See
  // also WritableStream::Constructor for this design decision.
  RefPtr<QueuingStrategySize> writableSizeAlgorithm =
      aWritableStrategy.mSize.WasPassed() ? &aWritableStrategy.mSize.Value()
                                          : nullptr;

  // Step 9. Let startPromise be a new promise.
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
  RefPtr<Promise> startPromise = Promise::CreateInfallible(global);

  // Step 10. Perform ! InitializeTransformStream(this, startPromise,
  // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark,
  // readableSizeAlgorithm).
  RefPtr<TransformStream> transformStream = new TransformStream(global);
  transformStream->Initialize(
      aGlobal.Context(), startPromise, writableHighWaterMark,
      writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  // Step 11. Perform ?
  // SetUpTransformStreamDefaultControllerFromTransformer(this, transformer,
  // transformerDict).
  SetUpTransformStreamDefaultControllerFromTransformer(
      aGlobal.Context(), *transformStream, transformerObj, transformerDict);

  // Step 12. If transformerDict["start"] exists, then resolve startPromise with
  // the result of invoking transformerDict["start"] with argument list «
  // this.[[controller]] » and callback this value transformer.
  if (transformerDict.mStart.WasPassed()) {
    RefPtr<TransformerStartCallback> callback = transformerDict.mStart.Value();
    RefPtr<TransformStreamDefaultController> controller =
        transformStream->Controller();
    JS::Rooted<JS::Value> retVal(aGlobal.Context());
    callback->Call(transformerObj, *controller, &retVal, aRv,
                   "Transformer.start", CallbackFunction::eRethrowExceptions);
    if (aRv.Failed()) {
      return nullptr;
    }

    startPromise->MaybeResolve(retVal);
  } else {
    // Step 13. Otherwise, resolve startPromise with undefined.
    startPromise->MaybeResolveWithUndefined();
  }

  return transformStream.forget();
}

}  // namespace mozilla::dom

100%


¤ Dauer der Verarbeitung: 0.41 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge