summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/streams/StreamAPI.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/builtin/streams/StreamAPI.cpp
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/builtin/streams/StreamAPI.cpp')
-rw-r--r--js/src/builtin/streams/StreamAPI.cpp612
1 files changed, 612 insertions, 0 deletions
diff --git a/js/src/builtin/streams/StreamAPI.cpp b/js/src/builtin/streams/StreamAPI.cpp
new file mode 100644
index 0000000000..0a9abd168c
--- /dev/null
+++ b/js/src/builtin/streams/StreamAPI.cpp
@@ -0,0 +1,612 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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/. */
+
+/* Public and friend stream APIs for external use. */
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF}
+#include "mozilla/Attributes.h" // MOZ_MUST_USE
+
+#include <stdint.h> // uint32_t, uintptr_t
+
+#include "jsapi.h" // js::AssertHeapIsIdle, JS_ReportErrorNumberASCII
+#include "jsfriendapi.h" // js::IsObjectInContextCompartment
+#include "jstypes.h" // JS_{FRIEND,PUBLIC}_API
+
+#include "builtin/Stream.h" // js::ReadableByteStreamController{,Close}, js::ReadableStreamDefaultController{,Close}, js::StreamController
+#include "builtin/streams/ReadableStream.h" // js::ReadableStream
+#include "builtin/streams/ReadableStreamController.h" // js::CheckReadableStreamControllerCanCloseOrEnqueue
+#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamController{Error,GetDesiredSizeUnchecked}, js::SetUpReadableStreamDefaultControllerFromUnderlyingSource
+#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{Cancel,FulfillReadOrReadIntoRequest,GetNumReadRequests,HasDefaultReader}
+#include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStreamTee
+#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader, js::ForAuthorCodeBool
+#include "builtin/streams/StreamController.h" // js::StreamController
+#include "gc/Zone.h" // JS::Zone
+#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_NewUint8Array
+#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
+#include "js/GCAPI.h" // JS::AutoCheckCannotGC, JS::AutoSuppressGCAnalysis
+#include "js/Object.h" // JS::SetPrivate
+#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted
+#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource
+#include "js/Value.h" // JS::{,Object,Undefined}Value
+#include "vm/ArrayBufferViewObject.h" // js::ArrayBufferViewObject
+#include "vm/JSContext.h" // JSContext, CHECK_THREAD
+#include "vm/JSObject.h" // JSObject
+#include "vm/PlainObject.h" // js::PlainObject
+#include "vm/PromiseObject.h" // js::PromiseObject
+
+#include "builtin/streams/ReadableStreamReader-inl.h" // js::UnwrapStreamFromReader
+#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapAndDowncastObject
+#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance
+#include "vm/Realm-inl.h" // js::AutoRealm
+
+using js::ArrayBufferViewObject;
+using js::AssertHeapIsIdle;
+using js::AutoRealm;
+using js::CheckReadableStreamControllerCanCloseOrEnqueue;
+using js::ForAuthorCodeBool;
+using js::GetErrorMessage;
+using js::IsObjectInContextCompartment;
+using js::NewBuiltinClassInstance;
+using js::PlainObject;
+using js::ReadableByteStreamController;
+using js::ReadableByteStreamControllerClose;
+using js::ReadableStream;
+using js::ReadableStreamController;
+using js::ReadableStreamControllerError;
+using js::ReadableStreamControllerGetDesiredSizeUnchecked;
+using js::ReadableStreamDefaultController;
+using js::ReadableStreamDefaultControllerClose;
+using js::ReadableStreamDefaultReader;
+using js::ReadableStreamFulfillReadOrReadIntoRequest;
+using js::ReadableStreamGetNumReadRequests;
+using js::ReadableStreamHasDefaultReader;
+using js::ReadableStreamReader;
+using js::ReadableStreamTee;
+using js::SetUpReadableStreamDefaultControllerFromUnderlyingSource;
+using js::StreamController;
+using js::UnwrapAndDowncastObject;
+using js::UnwrapStreamFromReader;
+
+JS_FRIEND_API JSObject* js::UnwrapReadableStream(JSObject* obj) {
+ return obj->maybeUnwrapIf<ReadableStream>();
+}
+
+JS_PUBLIC_API JSObject* JS::NewReadableDefaultStreamObject(
+ JSContext* cx, JS::Handle<JSObject*> underlyingSource /* = nullptr */,
+ JS::Handle<JSFunction*> size /* = nullptr */,
+ double highWaterMark /* = 1 */,
+ JS::Handle<JSObject*> proto /* = nullptr */) {
+ MOZ_ASSERT(!cx->zone()->isAtomsZone());
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+ cx->check(underlyingSource, size, proto);
+ MOZ_ASSERT(highWaterMark >= 0);
+
+ // A copy of ReadableStream::constructor, with most of the
+ // argument-checking done implicitly by C++ type checking.
+ Rooted<ReadableStream*> stream(cx, ReadableStream::create(cx));
+ if (!stream) {
+ return nullptr;
+ }
+ Rooted<Value> sourceVal(cx);
+ if (underlyingSource) {
+ sourceVal.setObject(*underlyingSource);
+ } else {
+ JSObject* source = NewBuiltinClassInstance<PlainObject>(cx);
+ if (!source) {
+ return nullptr;
+ }
+ sourceVal.setObject(*source);
+ }
+ Rooted<Value> sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue());
+
+ if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource(
+ cx, stream, sourceVal, highWaterMark, sizeVal)) {
+ return nullptr;
+ }
+
+ return stream;
+}
+
+JS_PUBLIC_API JSObject* JS::NewReadableExternalSourceStreamObject(
+ JSContext* cx, JS::ReadableStreamUnderlyingSource* underlyingSource,
+ void* nsISupportsObject_alreadyAddreffed /* = nullptr */,
+ Handle<JSObject*> proto /* = nullptr */) {
+ MOZ_ASSERT(!cx->zone()->isAtomsZone());
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+ MOZ_ASSERT(underlyingSource);
+ MOZ_ASSERT((uintptr_t(underlyingSource) & 1) == 0,
+ "external underlying source pointers must be aligned");
+ cx->check(proto);
+
+ return ReadableStream::createExternalSourceStream(
+ cx, underlyingSource, nsISupportsObject_alreadyAddreffed, proto);
+}
+
+JS_PUBLIC_API bool JS::IsReadableStream(JSObject* obj) {
+ return obj->canUnwrapAs<ReadableStream>();
+}
+
+JS_PUBLIC_API bool JS::IsReadableStreamReader(JSObject* obj) {
+ return obj->canUnwrapAs<ReadableStreamDefaultReader>();
+}
+
+JS_PUBLIC_API bool JS::IsReadableStreamDefaultReader(JSObject* obj) {
+ return obj->canUnwrapAs<ReadableStreamDefaultReader>();
+}
+
+template <class T>
+static MOZ_MUST_USE T* APIUnwrapAndDowncast(JSContext* cx, JSObject* obj) {
+ cx->check(obj);
+ return UnwrapAndDowncastObject<T>(cx, obj);
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamIsReadable(JSContext* cx,
+ Handle<JSObject*> streamObj,
+ bool* result) {
+ ReadableStream* unwrappedStream =
+ APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ *result = unwrappedStream->readable();
+ return true;
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamIsLocked(JSContext* cx,
+ Handle<JSObject*> streamObj,
+ bool* result) {
+ ReadableStream* unwrappedStream =
+ APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ *result = unwrappedStream->locked();
+ return true;
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamIsDisturbed(JSContext* cx,
+ Handle<JSObject*> streamObj,
+ bool* result) {
+ ReadableStream* unwrappedStream =
+ APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ *result = unwrappedStream->disturbed();
+ return true;
+}
+
+JS_PUBLIC_API JSObject* JS::ReadableStreamCancel(JSContext* cx,
+ Handle<JSObject*> streamObj,
+ Handle<Value> reason) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+ cx->check(reason);
+
+ Rooted<ReadableStream*> unwrappedStream(
+ cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
+ if (!unwrappedStream) {
+ return nullptr;
+ }
+
+ return js::ReadableStreamCancel(cx, unwrappedStream, reason);
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamGetMode(JSContext* cx,
+ Handle<JSObject*> streamObj,
+ JS::ReadableStreamMode* mode) {
+ ReadableStream* unwrappedStream =
+ APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ *mode = unwrappedStream->mode();
+ return true;
+}
+
+JS_PUBLIC_API JSObject* JS::ReadableStreamGetReader(
+ JSContext* cx, Handle<JSObject*> streamObj, ReadableStreamReaderMode mode) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+
+ Rooted<ReadableStream*> unwrappedStream(
+ cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
+ if (!unwrappedStream) {
+ return nullptr;
+ }
+
+ JSObject* result = CreateReadableStreamDefaultReader(cx, unwrappedStream,
+ ForAuthorCodeBool::No);
+ MOZ_ASSERT_IF(result, IsObjectInContextCompartment(result, cx));
+ return result;
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamGetExternalUnderlyingSource(
+ JSContext* cx, Handle<JSObject*> streamObj,
+ JS::ReadableStreamUnderlyingSource** source) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+
+ Rooted<ReadableStream*> unwrappedStream(
+ cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ MOZ_ASSERT(unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource);
+ if (unwrappedStream->locked()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_READABLESTREAM_LOCKED);
+ return false;
+ }
+ if (!unwrappedStream->readable()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE,
+ "ReadableStreamGetExternalUnderlyingSource");
+ return false;
+ }
+
+ auto unwrappedController =
+ &unwrappedStream->controller()->as<ReadableByteStreamController>();
+ unwrappedController->setSourceLocked();
+ *source = unwrappedController->externalSource();
+ return true;
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamReleaseExternalUnderlyingSource(
+ JSContext* cx, Handle<JSObject*> streamObj) {
+ ReadableStream* unwrappedStream =
+ APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ MOZ_ASSERT(unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource);
+ MOZ_ASSERT(unwrappedStream->locked());
+ MOZ_ASSERT(unwrappedStream->controller()->sourceLocked());
+ unwrappedStream->controller()->clearSourceLocked();
+ return true;
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamUpdateDataAvailableFromSource(
+ JSContext* cx, JS::Handle<JSObject*> streamObj, uint32_t availableData) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+
+ Rooted<ReadableStream*> unwrappedStream(
+ cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ // This is based on Streams spec 3.11.4.4. enqueue(chunk) steps 1-3 and
+ // 3.13.9. ReadableByteStreamControllerEnqueue(controller, chunk) steps
+ // 8-9.
+ //
+ // Adapted to handling updates signaled by the embedding for streams with
+ // external underlying sources.
+ //
+ // The remaining steps of those two functions perform checks and asserts
+ // that don't apply to streams with external underlying sources.
+
+ Rooted<ReadableByteStreamController*> unwrappedController(
+ cx, &unwrappedStream->controller()->as<ReadableByteStreamController>());
+
+ // Step 2: If this.[[closeRequested]] is true, throw a TypeError exception.
+ if (unwrappedController->closeRequested()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_READABLESTREAMCONTROLLER_CLOSED, "enqueue");
+ return false;
+ }
+
+ // Step 3: If this.[[controlledReadableStream]].[[state]] is not "readable",
+ // throw a TypeError exception.
+ if (!unwrappedController->stream()->readable()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE,
+ "enqueue");
+ return false;
+ }
+
+ unwrappedController->clearPullFlags();
+
+#if DEBUG
+ uint32_t oldAvailableData =
+ unwrappedController->getFixedSlot(StreamController::Slot_TotalSize)
+ .toInt32();
+#endif // DEBUG
+ unwrappedController->setQueueTotalSize(availableData);
+
+ // 3.139. ReadableByteStreamControllerEnqueue
+ // Step 8.a: If ! ReadableStreamGetNumReadRequests(stream) is 0,
+ // Reordered because for externally-sourced streams it applies regardless
+ // of reader type.
+ if (ReadableStreamGetNumReadRequests(unwrappedStream) == 0) {
+ return true;
+ }
+
+ // Step 8: If ! ReadableStreamHasDefaultReader(stream) is true
+ bool hasDefaultReader;
+ if (!ReadableStreamHasDefaultReader(cx, unwrappedStream, &hasDefaultReader)) {
+ return false;
+ }
+ if (hasDefaultReader) {
+ // Step b: Otherwise,
+ // Step i: Assert: controller.[[queue]] is empty.
+ MOZ_ASSERT(oldAvailableData == 0);
+
+ // Step ii: Let transferredView be
+ // ! Construct(%Uint8Array%, transferredBuffer,
+ // byteOffset, byteLength).
+ JSObject* viewObj = JS_NewUint8Array(cx, availableData);
+ if (!viewObj) {
+ return false;
+ }
+ Rooted<ArrayBufferViewObject*> transferredView(
+ cx, &viewObj->as<ArrayBufferViewObject>());
+ if (!transferredView) {
+ return false;
+ }
+
+ JS::ReadableStreamUnderlyingSource* source =
+ unwrappedController->externalSource();
+
+ size_t bytesWritten;
+ {
+ AutoRealm ar(cx, unwrappedStream);
+ JS::AutoSuppressGCAnalysis suppressGC(cx);
+ JS::AutoCheckCannotGC noGC;
+ bool dummy;
+ void* buffer = JS_GetArrayBufferViewData(transferredView, &dummy, noGC);
+ source->writeIntoReadRequestBuffer(cx, unwrappedStream, buffer,
+ availableData, &bytesWritten);
+ }
+
+ // Step iii: Perform ! ReadableStreamFulfillReadRequest(stream,
+ // transferredView,
+ // false).
+ Rooted<Value> chunk(cx, ObjectValue(*transferredView));
+ if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, unwrappedStream, chunk,
+ false)) {
+ return false;
+ }
+
+ unwrappedController->setQueueTotalSize(availableData - bytesWritten);
+ } else {
+ // Step 9: Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true,
+ // [...]
+ // (Omitted. BYOB readers are not implemented.)
+
+ // Step 10: Otherwise,
+ // Step a: Assert: ! IsReadableStreamLocked(stream) is false.
+ MOZ_ASSERT(!unwrappedStream->locked());
+
+ // Step b: Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(
+ // controller, transferredBuffer, byteOffset, byteLength).
+ // (Not needed for external underlying sources.)
+ }
+
+ return true;
+}
+
+JS_PUBLIC_API void JS::ReadableStreamReleaseCCObject(JSObject* streamObj) {
+ MOZ_ASSERT(JS::IsReadableStream(streamObj));
+ JS::SetPrivate(streamObj, nullptr);
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamTee(JSContext* cx,
+ Handle<JSObject*> streamObj,
+ MutableHandle<JSObject*> branch1Obj,
+ MutableHandle<JSObject*> branch2Obj) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+
+ Rooted<ReadableStream*> unwrappedStream(
+ cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ Rooted<ReadableStream*> branch1Stream(cx);
+ Rooted<ReadableStream*> branch2Stream(cx);
+ if (!ReadableStreamTee(cx, unwrappedStream, false, &branch1Stream,
+ &branch2Stream)) {
+ return false;
+ }
+
+ branch1Obj.set(branch1Stream);
+ branch2Obj.set(branch2Stream);
+ return true;
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamGetDesiredSize(JSContext* cx,
+ JSObject* streamObj,
+ bool* hasValue,
+ double* value) {
+ ReadableStream* unwrappedStream =
+ APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ if (unwrappedStream->errored()) {
+ *hasValue = false;
+ return true;
+ }
+
+ *hasValue = true;
+
+ if (unwrappedStream->closed()) {
+ *value = 0;
+ return true;
+ }
+
+ *value = ReadableStreamControllerGetDesiredSizeUnchecked(
+ unwrappedStream->controller());
+ return true;
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamClose(JSContext* cx,
+ Handle<JSObject*> streamObj) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+
+ Rooted<ReadableStream*> unwrappedStream(
+ cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ Rooted<ReadableStreamController*> unwrappedControllerObj(
+ cx, unwrappedStream->controller());
+ if (!CheckReadableStreamControllerCanCloseOrEnqueue(
+ cx, unwrappedControllerObj, "close")) {
+ return false;
+ }
+
+ if (unwrappedControllerObj->is<ReadableStreamDefaultController>()) {
+ Rooted<ReadableStreamDefaultController*> unwrappedController(cx);
+ unwrappedController =
+ &unwrappedControllerObj->as<ReadableStreamDefaultController>();
+ return ReadableStreamDefaultControllerClose(cx, unwrappedController);
+ }
+
+ Rooted<ReadableByteStreamController*> unwrappedController(cx);
+ unwrappedController =
+ &unwrappedControllerObj->as<ReadableByteStreamController>();
+ return ReadableByteStreamControllerClose(cx, unwrappedController);
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamEnqueue(JSContext* cx,
+ Handle<JSObject*> streamObj,
+ Handle<Value> chunk) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+ cx->check(chunk);
+
+ Rooted<ReadableStream*> unwrappedStream(
+ cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ if (unwrappedStream->mode() != JS::ReadableStreamMode::Default) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER,
+ "JS::ReadableStreamEnqueue");
+ return false;
+ }
+
+ Rooted<ReadableStreamDefaultController*> unwrappedController(cx);
+ unwrappedController =
+ &unwrappedStream->controller()->as<ReadableStreamDefaultController>();
+
+ MOZ_ASSERT(!unwrappedController->closeRequested());
+ MOZ_ASSERT(unwrappedStream->readable());
+
+ return ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, chunk);
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamError(JSContext* cx,
+ Handle<JSObject*> streamObj,
+ Handle<Value> error) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+ cx->check(error);
+
+ Rooted<ReadableStream*> unwrappedStream(
+ cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
+ if (!unwrappedStream) {
+ return false;
+ }
+
+ Rooted<ReadableStreamController*> unwrappedController(
+ cx, unwrappedStream->controller());
+ return ReadableStreamControllerError(cx, unwrappedController, error);
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamReaderIsClosed(JSContext* cx,
+ Handle<JSObject*> readerObj,
+ bool* result) {
+ Rooted<ReadableStreamReader*> unwrappedReader(
+ cx, APIUnwrapAndDowncast<ReadableStreamReader>(cx, readerObj));
+ if (!unwrappedReader) {
+ return false;
+ }
+
+ *result = unwrappedReader->isClosed();
+ return true;
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamReaderCancel(JSContext* cx,
+ Handle<JSObject*> readerObj,
+ Handle<Value> reason) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+ cx->check(reason);
+
+ Rooted<ReadableStreamReader*> unwrappedReader(
+ cx, APIUnwrapAndDowncast<ReadableStreamReader>(cx, readerObj));
+ if (!unwrappedReader) {
+ return false;
+ }
+ MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
+ "C++ code should not touch readers created by scripts");
+
+ return ReadableStreamReaderGenericCancel(cx, unwrappedReader, reason);
+}
+
+JS_PUBLIC_API bool JS::ReadableStreamReaderReleaseLock(
+ JSContext* cx, Handle<JSObject*> readerObj) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+
+ Rooted<ReadableStreamReader*> unwrappedReader(
+ cx, APIUnwrapAndDowncast<ReadableStreamReader>(cx, readerObj));
+ if (!unwrappedReader) {
+ return false;
+ }
+ MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
+ "C++ code should not touch readers created by scripts");
+
+#ifdef DEBUG
+ Rooted<ReadableStream*> unwrappedStream(
+ cx, UnwrapStreamFromReader(cx, unwrappedReader));
+ if (!unwrappedStream) {
+ return false;
+ }
+ MOZ_ASSERT(ReadableStreamGetNumReadRequests(unwrappedStream) == 0);
+#endif // DEBUG
+
+ return ReadableStreamReaderGenericRelease(cx, unwrappedReader);
+}
+
+JS_PUBLIC_API JSObject* JS::ReadableStreamDefaultReaderRead(
+ JSContext* cx, Handle<JSObject*> readerObj) {
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+
+ Rooted<ReadableStreamDefaultReader*> unwrappedReader(
+ cx, APIUnwrapAndDowncast<ReadableStreamDefaultReader>(cx, readerObj));
+ if (!unwrappedReader) {
+ return nullptr;
+ }
+ MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
+ "C++ code should not touch readers created by scripts");
+
+ return js::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
+}
+
+void JS::InitPipeToHandling(const JSClass* abortSignalClass,
+ AbortSignalIsAborted isAborted, JSContext* cx) {
+ cx->runtime()->initPipeToHandling(abortSignalClass, isAborted);
+}