/* -*- 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/TransformStreamDefaultController.h" #include "TransformerCallbackHelpers.h" #include "mozilla/Attributes.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ReadableStream.h" #include "mozilla/dom/ReadableStreamDefaultController.h" #include "mozilla/dom/TransformStream.h" #include "mozilla/dom/TransformStreamDefaultControllerBinding.h" #include "nsWrapperCache.h" namespace mozilla::dom { using namespace streams_abstract; NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TransformStreamDefaultController, mGlobal, mStream, mTransformerAlgorithms) NS_IMPL_CYCLE_COLLECTING_ADDREF(TransformStreamDefaultController) NS_IMPL_CYCLE_COLLECTING_RELEASE(TransformStreamDefaultController) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStreamDefaultController) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END TransformStream* TransformStreamDefaultController::Stream() { return mStream; } void TransformStreamDefaultController::SetStream(TransformStream& aStream) { MOZ_ASSERT(!mStream); mStream = &aStream; } TransformerAlgorithmsBase* TransformStreamDefaultController::Algorithms() { return mTransformerAlgorithms; } void TransformStreamDefaultController::SetAlgorithms( TransformerAlgorithmsBase* aTransformerAlgorithms) { mTransformerAlgorithms = aTransformerAlgorithms; } TransformStreamDefaultController::TransformStreamDefaultController( nsIGlobalObject* aGlobal) : mGlobal(aGlobal) { mozilla::HoldJSObjects(this); } TransformStreamDefaultController::~TransformStreamDefaultController() { mozilla::DropJSObjects(this); } JSObject* TransformStreamDefaultController::WrapObject( JSContext* aCx, JS::Handle aGivenProto) { return TransformStreamDefaultController_Binding::Wrap(aCx, this, aGivenProto); } // https://streams.spec.whatwg.org/#ts-default-controller-desired-size Nullable TransformStreamDefaultController::GetDesiredSize() const { // Step 1. Let readableController be // this.[[stream]].[[readable]].[[controller]]. RefPtr readableController = mStream->Readable()->Controller()->AsDefault(); // Step 2. Return ! // ReadableStreamDefaultControllerGetDesiredSize(readableController). return ReadableStreamDefaultControllerGetDesiredSize(readableController); } // https://streams.spec.whatwg.org/#rs-default-controller-has-backpressure // Looks like a readable stream thing but the spec explicitly says this is for // TransformStream. static bool ReadableStreamDefaultControllerHasBackpressure( ReadableStreamDefaultController* aController) { // Step 1: If ! ReadableStreamDefaultControllerShouldCallPull(controller) is // true, return false. // Step 2: Otherwise, return true. return !ReadableStreamDefaultControllerShouldCallPull(aController); } void TransformStreamDefaultController::Enqueue(JSContext* aCx, JS::Handle aChunk, ErrorResult& aRv) { // Step 1: Perform ? TransformStreamDefaultControllerEnqueue(this, chunk). // Inlining TransformStreamDefaultControllerEnqueue here. // https://streams.spec.whatwg.org/#transform-stream-default-controller-enqueue // Step 1: Let stream be controller.[[stream]]. RefPtr stream = mStream; // Step 2: Let readableController be stream.[[readable]].[[controller]]. RefPtr readableController = stream->Readable()->Controller()->AsDefault(); // Step 3: If ! // ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController) is // false, throw a TypeError exception. if (!ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow( readableController, CloseOrEnqueue::Enqueue, aRv)) { return; } // Step 4: Let enqueueResult be // ReadableStreamDefaultControllerEnqueue(readableController, chunk). ErrorResult rv; ReadableStreamDefaultControllerEnqueue(aCx, readableController, aChunk, rv); // Step 5: If enqueueResult is an abrupt completion, if (rv.MaybeSetPendingException(aCx)) { JS::Rooted error(aCx); if (!JS_GetPendingException(aCx, &error)) { // Uncatchable exception; we should mark aRv and return. aRv.StealExceptionFromJSContext(aCx); return; } JS_ClearPendingException(aCx); // Step 5.1: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, // enqueueResult.[[Value]]). TransformStreamErrorWritableAndUnblockWrite(aCx, stream, error, aRv); // Step 5.2: Throw stream.[[readable]].[[storedError]]. JS::Rooted storedError(aCx, stream->Readable()->StoredError()); aRv.MightThrowJSException(); aRv.ThrowJSException(aCx, storedError); return; } // Step 6: Let backpressure be ! // ReadableStreamDefaultControllerHasBackpressure(readableController). bool backpressure = ReadableStreamDefaultControllerHasBackpressure(readableController); // Step 7: If backpressure is not stream.[[backpressure]], if (backpressure != stream->Backpressure()) { // Step 7.1: Assert: backpressure is true. MOZ_ASSERT(backpressure); // Step 7.2: Perform ! TransformStreamSetBackpressure(true). stream->SetBackpressure(true); } } // https://streams.spec.whatwg.org/#ts-default-controller-error void TransformStreamDefaultController::Error(JSContext* aCx, JS::Handle aError, ErrorResult& aRv) { // Step 1: Perform ? TransformStreamDefaultControllerError(this, e). // Inlining TransformStreamDefaultControllerError here. // https://streams.spec.whatwg.org/#transform-stream-default-controller-error // Perform ! TransformStreamError(controller.[[stream]], e). // mStream is set in initialization step and only modified in cycle // collection. // TODO: Move mStream initialization to a method/constructor and make it // MOZ_KNOWN_LIVE again. (See bug 1769854) TransformStreamError(aCx, MOZ_KnownLive(mStream), aError, aRv); } // https://streams.spec.whatwg.org/#ts-default-controller-terminate void TransformStreamDefaultController::Terminate(JSContext* aCx, ErrorResult& aRv) { // Step 1: Perform ? TransformStreamDefaultControllerTerminate(this). // Inlining TransformStreamDefaultControllerTerminate here. // https://streams.spec.whatwg.org/#transform-stream-default-controller-terminate // Step 1: Let stream be controller.[[stream]]. RefPtr stream = mStream; // Step 2: Let readableController be stream.[[readable]].[[controller]]. RefPtr readableController = stream->Readable()->Controller()->AsDefault(); // Step 3: Perform ! ReadableStreamDefaultControllerClose(readableController). ReadableStreamDefaultControllerClose(aCx, readableController, aRv); // Step 4: Let error be a TypeError exception indicating that the stream has // been terminated. ErrorResult rv; rv.ThrowTypeError("Terminating the stream"); JS::Rooted error(aCx); MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), &error)); // Step 5: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, // error). TransformStreamErrorWritableAndUnblockWrite(aCx, stream, error, aRv); } namespace streams_abstract { // https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller void SetUpTransformStreamDefaultController( JSContext* aCx, TransformStream& aStream, TransformStreamDefaultController& aController, TransformerAlgorithmsBase& aTransformerAlgorithms) { // Step 1. Assert: stream implements TransformStream. // Step 2. Assert: stream.[[controller]] is undefined. MOZ_ASSERT(!aStream.Controller()); // Step 3. Set controller.[[stream]] to stream. aController.SetStream(aStream); // Step 4. Set stream.[[controller]] to controller. aStream.SetController(aController); // Step 5. Set controller.[[transformAlgorithm]] to transformAlgorithm. // Step 6. Set controller.[[flushAlgorithm]] to flushAlgorithm. aController.SetAlgorithms(&aTransformerAlgorithms); } // https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer void SetUpTransformStreamDefaultControllerFromTransformer( JSContext* aCx, TransformStream& aStream, JS::Handle aTransformer, Transformer& aTransformerDict) { // Step 1. Let controller be a new TransformStreamDefaultController. auto controller = MakeRefPtr(aStream.GetParentObject()); // Step 2 - 5: auto algorithms = MakeRefPtr( aStream.GetParentObject(), aTransformer, aTransformerDict); // Step 6: Perform ! SetUpTransformStreamDefaultController(stream, controller, // transformAlgorithm, flushAlgorithm). SetUpTransformStreamDefaultController(aCx, aStream, *controller, *algorithms); } } // namespace streams_abstract } // namespace mozilla::dom