summaryrefslogtreecommitdiffstats
path: root/dom/streams/ReadableStreamBYOBReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/streams/ReadableStreamBYOBReader.cpp')
-rw-r--r--dom/streams/ReadableStreamBYOBReader.cpp359
1 files changed, 359 insertions, 0 deletions
diff --git a/dom/streams/ReadableStreamBYOBReader.cpp b/dom/streams/ReadableStreamBYOBReader.cpp
new file mode 100644
index 0000000000..f40d98bb93
--- /dev/null
+++ b/dom/streams/ReadableStreamBYOBReader.cpp
@@ -0,0 +1,359 @@
+/* -*- 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/ReadableStreamBYOBReader.h"
+
+#include "ReadIntoRequest.h"
+#include "js/ArrayBuffer.h"
+#include "js/experimental/TypedData.h"
+#include "mozilla/dom/ReadableStreamBYOBReader.h"
+#include "mozilla/dom/ReadableStream.h"
+#include "mozilla/dom/ReadableStreamBYOBReaderBinding.h"
+#include "mozilla/dom/ReadableStreamGenericReader.h"
+#include "mozilla/dom/RootedDictionary.h"
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+
+// Temporary Includes
+#include "mozilla/dom/ReadableByteStreamController.h"
+#include "mozilla/dom/ReadableStreamBYOBRequest.h"
+
+namespace mozilla::dom {
+
+using namespace streams_abstract;
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(ReadableStreamBYOBReader,
+ ReadableStreamGenericReader,
+ mReadIntoRequests)
+NS_IMPL_ADDREF_INHERITED(ReadableStreamBYOBReader, ReadableStreamGenericReader)
+NS_IMPL_RELEASE_INHERITED(ReadableStreamBYOBReader, ReadableStreamGenericReader)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamBYOBReader)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_END_INHERITING(ReadableStreamGenericReader)
+
+ReadableStreamBYOBReader::ReadableStreamBYOBReader(nsISupports* aGlobal)
+ : ReadableStreamGenericReader(do_QueryInterface(aGlobal)) {}
+
+JSObject* ReadableStreamBYOBReader::WrapObject(
+ JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+ return ReadableStreamBYOBReader_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+// https://streams.spec.whatwg.org/#set-up-readable-stream-byob-reader
+void SetUpReadableStreamBYOBReader(ReadableStreamBYOBReader* reader,
+ ReadableStream& stream, ErrorResult& rv) {
+ // Step 1. If !IsReadableStreamLocked(stream) is true, throw a TypeError
+ // exception.
+ if (IsReadableStreamLocked(&stream)) {
+ rv.ThrowTypeError("Trying to read locked stream");
+ return;
+ }
+
+ // Step 2. If stream.[[controller]] does not implement
+ // ReadableByteStreamController, throw a TypeError exception.
+ if (!stream.Controller()->IsByte()) {
+ rv.ThrowTypeError("Trying to read with incompatible controller");
+ return;
+ }
+
+ // Step 3. Perform ! ReadableStreamReaderGenericInitialize(reader, stream).
+ ReadableStreamReaderGenericInitialize(reader, &stream);
+
+ // Step 4. Set reader.[[readIntoRequests]] to a new empty list.
+ reader->ReadIntoRequests().clear();
+}
+
+// https://streams.spec.whatwg.org/#byob-reader-constructor
+/* static */ already_AddRefed<ReadableStreamBYOBReader>
+ReadableStreamBYOBReader::Constructor(const GlobalObject& global,
+ ReadableStream& stream, ErrorResult& rv) {
+ nsCOMPtr<nsIGlobalObject> globalObject =
+ do_QueryInterface(global.GetAsSupports());
+ RefPtr<ReadableStreamBYOBReader> reader =
+ new ReadableStreamBYOBReader(globalObject);
+
+ // Step 1.
+ SetUpReadableStreamBYOBReader(reader, stream, rv);
+ if (rv.Failed()) {
+ return nullptr;
+ }
+
+ return reader.forget();
+}
+
+struct Read_ReadIntoRequest final : public ReadIntoRequest {
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Read_ReadIntoRequest,
+ ReadIntoRequest)
+
+ RefPtr<Promise> mPromise;
+
+ explicit Read_ReadIntoRequest(Promise* aPromise) : mPromise(aPromise) {}
+
+ void ChunkSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
+ ErrorResult& aRv) override {
+ MOZ_ASSERT(aChunk.isObject());
+ // https://streams.spec.whatwg.org/#byob-reader-read Step 6.
+ //
+ // chunk steps, given chunk:
+ // Resolve promise with «[ "value" → chunk, "done" → false ]».
+
+ // We need to wrap this as the chunk could have come from
+ // another compartment.
+ JS::Rooted<JSObject*> chunk(aCx, &aChunk.toObject());
+ if (!JS_WrapObject(aCx, &chunk)) {
+ aRv.StealExceptionFromJSContext(aCx);
+ return;
+ }
+
+ RootedDictionary<ReadableStreamReadResult> result(aCx);
+ result.mValue = aChunk;
+ result.mDone.Construct(false);
+
+ mPromise->MaybeResolve(result);
+ }
+
+ void CloseSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
+ ErrorResult& aRv) override {
+ MOZ_ASSERT(aChunk.isObject() || aChunk.isUndefined());
+ // https://streams.spec.whatwg.org/#byob-reader-read Step 6.
+ //
+ // close steps, given chunk:
+ // Resolve promise with «[ "value" → chunk, "done" → true ]».
+ RootedDictionary<ReadableStreamReadResult> result(aCx);
+ if (aChunk.isObject()) {
+ // We need to wrap this as the chunk could have come from
+ // another compartment.
+ JS::Rooted<JSObject*> chunk(aCx, &aChunk.toObject());
+ if (!JS_WrapObject(aCx, &chunk)) {
+ aRv.StealExceptionFromJSContext(aCx);
+ return;
+ }
+
+ result.mValue = aChunk;
+ }
+ result.mDone.Construct(true);
+
+ mPromise->MaybeResolve(result);
+ }
+
+ void ErrorSteps(JSContext* aCx, JS::Handle<JS::Value> e,
+ ErrorResult& aRv) override {
+ // https://streams.spec.whatwg.org/#byob-reader-read Step 6.
+ //
+ // error steps, given e:
+ // Reject promise with e.
+ mPromise->MaybeReject(e);
+ }
+
+ protected:
+ ~Read_ReadIntoRequest() override = default;
+};
+
+NS_IMPL_CYCLE_COLLECTION(ReadIntoRequest)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadIntoRequest)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadIntoRequest)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadIntoRequest)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(Read_ReadIntoRequest, ReadIntoRequest,
+ mPromise)
+NS_IMPL_ADDREF_INHERITED(Read_ReadIntoRequest, ReadIntoRequest)
+NS_IMPL_RELEASE_INHERITED(Read_ReadIntoRequest, ReadIntoRequest)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Read_ReadIntoRequest)
+NS_INTERFACE_MAP_END_INHERITING(ReadIntoRequest)
+
+namespace streams_abstract {
+// https://streams.spec.whatwg.org/#readable-stream-byob-reader-read
+void ReadableStreamBYOBReaderRead(JSContext* aCx,
+ ReadableStreamBYOBReader* aReader,
+ JS::Handle<JSObject*> aView,
+ ReadIntoRequest* aReadIntoRequest,
+ ErrorResult& aRv) {
+ // Step 1.Let stream be reader.[[stream]].
+ ReadableStream* stream = aReader->GetStream();
+
+ // Step 2. Assert: stream is not undefined.
+ MOZ_ASSERT(stream);
+
+ // Step 3. Set stream.[[disturbed]] to true.
+ stream->SetDisturbed(true);
+
+ // Step 4. If stream.[[state]] is "errored", perform readIntoRequest’s error
+ // steps given stream.[[storedError]].
+ if (stream->State() == ReadableStream::ReaderState::Errored) {
+ JS::Rooted<JS::Value> error(aCx, stream->StoredError());
+
+ aReadIntoRequest->ErrorSteps(aCx, error, aRv);
+ return;
+ }
+
+ // Step 5. Otherwise, perform
+ // !ReadableByteStreamControllerPullInto(stream.[[controller]], view,
+ // readIntoRequest).
+ MOZ_ASSERT(stream->Controller()->IsByte());
+ RefPtr<ReadableByteStreamController> controller(
+ stream->Controller()->AsByte());
+ ReadableByteStreamControllerPullInto(aCx, controller, aView, aReadIntoRequest,
+ aRv);
+}
+} // namespace streams_abstract
+
+// https://streams.spec.whatwg.org/#byob-reader-read
+already_AddRefed<Promise> ReadableStreamBYOBReader::Read(
+ const ArrayBufferView& aArray, ErrorResult& aRv) {
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(GetParentObject())) {
+ aRv.ThrowUnknownError("Internal error");
+ return nullptr;
+ }
+ JSContext* cx = jsapi.cx();
+
+ JS::Rooted<JSObject*> view(cx, aArray.Obj());
+
+ // Step 1. If view.[[ByteLength]] is 0, return a promise rejected with a
+ // TypeError exception.
+ if (JS_GetArrayBufferViewByteLength(view) == 0) {
+ // Binding code should convert this thrown value into a rejected promise.
+ aRv.ThrowTypeError("Zero Length View");
+ return nullptr;
+ }
+
+ // Step 2. If view.[[ViewedArrayBuffer]].[[ArrayBufferByteLength]] is 0,
+ // return a promise rejected with a TypeError exception.
+ bool isSharedMemory;
+ JS::Rooted<JSObject*> viewedArrayBuffer(
+ cx, JS_GetArrayBufferViewBuffer(cx, view, &isSharedMemory));
+ if (!viewedArrayBuffer) {
+ aRv.StealExceptionFromJSContext(cx);
+ return nullptr;
+ }
+
+ if (JS::GetArrayBufferByteLength(viewedArrayBuffer) == 0) {
+ aRv.ThrowTypeError("zero length viewed buffer");
+ return nullptr;
+ }
+
+ // Step 3. If ! IsDetachedBuffer(view.[[ViewedArrayBuffer]]) is true, return a
+ // promise rejected with a TypeError exception.
+ if (JS::IsDetachedArrayBufferObject(viewedArrayBuffer)) {
+ aRv.ThrowTypeError("Detached Buffer");
+ return nullptr;
+ }
+
+ // Step 4. If this.[[stream]] is undefined, return a promise rejected with a
+ // TypeError exception.
+ if (!GetStream()) {
+ aRv.ThrowTypeError("Reader has undefined stream");
+ return nullptr;
+ }
+
+ // Step 5.
+ RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
+
+ // Step 6. Let readIntoRequest be a new read-into request with the following
+ // items:
+ RefPtr<ReadIntoRequest> readIntoRequest = new Read_ReadIntoRequest(promise);
+
+ // Step 7. Perform ! ReadableStreamBYOBReaderRead(this, view,
+ // readIntoRequest).
+ ReadableStreamBYOBReaderRead(cx, this, view, readIntoRequest, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ // Step 8. Return promise.
+ return promise.forget();
+}
+
+namespace streams_abstract {
+
+// https://streams.spec.whatwg.org/#abstract-opdef-readablestreambyobreadererrorreadintorequests
+void ReadableStreamBYOBReaderErrorReadIntoRequests(
+ JSContext* aCx, ReadableStreamBYOBReader* aReader,
+ JS::Handle<JS::Value> aError, ErrorResult& aRv) {
+ // Step 1. Let readIntoRequests be reader.[[readIntoRequests]].
+ LinkedList<RefPtr<ReadIntoRequest>> readIntoRequests =
+ std::move(aReader->ReadIntoRequests());
+
+ // Step 2. Set reader.[[readIntoRequests]] to a new empty list.
+ // Note: The std::move already cleared this anyway.
+ aReader->ReadIntoRequests().clear();
+
+ // Step 3. For each readIntoRequest of readIntoRequests,
+ while (RefPtr<ReadIntoRequest> readIntoRequest =
+ readIntoRequests.popFirst()) {
+ // Step 3.1. Perform readIntoRequest’s error steps, given e.
+ readIntoRequest->ErrorSteps(aCx, aError, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+ }
+}
+
+// https://streams.spec.whatwg.org/#abstract-opdef-readablestreambyobreaderrelease
+void ReadableStreamBYOBReaderRelease(JSContext* aCx,
+ ReadableStreamBYOBReader* aReader,
+ ErrorResult& aRv) {
+ // Step 1. Perform ! ReadableStreamReaderGenericRelease(reader).
+ ReadableStreamReaderGenericRelease(aReader, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ // Step 2. Let e be a new TypeError exception.
+ ErrorResult rv;
+ rv.ThrowTypeError("Releasing lock");
+ JS::Rooted<JS::Value> error(aCx);
+ MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), &error));
+
+ // Step 3. Perform ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e).
+ ReadableStreamBYOBReaderErrorReadIntoRequests(aCx, aReader, error, aRv);
+}
+
+} // namespace streams_abstract
+
+// https://streams.spec.whatwg.org/#byob-reader-release-lock
+void ReadableStreamBYOBReader::ReleaseLock(ErrorResult& aRv) {
+ // Step 1. If this.[[stream]] is undefined, return.
+ if (!mStream) {
+ return;
+ }
+
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(mGlobal)) {
+ return aRv.ThrowUnknownError("Internal error");
+ }
+ JSContext* cx = jsapi.cx();
+
+ // Step 2. Perform ! ReadableStreamBYOBReaderRelease(this).
+ RefPtr<ReadableStreamBYOBReader> thisRefPtr = this;
+ ReadableStreamBYOBReaderRelease(cx, thisRefPtr, aRv);
+}
+
+namespace streams_abstract {
+// https://streams.spec.whatwg.org/#acquire-readable-stream-byob-reader
+already_AddRefed<ReadableStreamBYOBReader> AcquireReadableStreamBYOBReader(
+ ReadableStream* aStream, ErrorResult& aRv) {
+ // Step 1. Let reader be a new ReadableStreamBYOBReader.
+ RefPtr<ReadableStreamBYOBReader> reader =
+ new ReadableStreamBYOBReader(aStream->GetParentObject());
+
+ // Step 2. Perform ? SetUpReadableStreamBYOBReader(reader, stream).
+ SetUpReadableStreamBYOBReader(reader, *aStream, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ // Step 3. Return reader.
+ return reader.forget();
+}
+} // namespace streams_abstract
+
+} // namespace mozilla::dom