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/. */
7 #include "mozilla/dom/ReadableStreamBYOBReader.h"
9 #include "ReadIntoRequest.h"
10 #include "js/ArrayBuffer.h"
11 #include "js/experimental/TypedData.h"
12 #include "mozilla/dom/ReadableStreamBYOBReader.h"
13 #include "mozilla/dom/ReadableStream.h"
14 #include "mozilla/dom/ReadableStreamBYOBReaderBinding.h"
15 #include "mozilla/dom/ReadableStreamGenericReader.h"
16 #include "mozilla/dom/RootedDictionary.h"
18 #include "nsISupportsImpl.h"
21 #include "mozilla/dom/ReadableByteStreamController.h"
22 #include "mozilla/dom/ReadableStreamBYOBRequest.h"
24 namespace mozilla::dom
{
26 using namespace streams_abstract
;
28 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(ReadableStreamBYOBReader
,
29 ReadableStreamGenericReader
,
31 NS_IMPL_ADDREF_INHERITED(ReadableStreamBYOBReader
, ReadableStreamGenericReader
)
32 NS_IMPL_RELEASE_INHERITED(ReadableStreamBYOBReader
, ReadableStreamGenericReader
)
34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamBYOBReader
)
35 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
36 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamGenericReader
)
38 ReadableStreamBYOBReader::ReadableStreamBYOBReader(nsISupports
* aGlobal
)
39 : ReadableStreamGenericReader(do_QueryInterface(aGlobal
)) {}
41 JSObject
* ReadableStreamBYOBReader::WrapObject(
42 JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
43 return ReadableStreamBYOBReader_Binding::Wrap(aCx
, this, aGivenProto
);
46 // https://streams.spec.whatwg.org/#set-up-readable-stream-byob-reader
47 void SetUpReadableStreamBYOBReader(ReadableStreamBYOBReader
* reader
,
48 ReadableStream
& stream
, ErrorResult
& rv
) {
49 // Step 1. If !IsReadableStreamLocked(stream) is true, throw a TypeError
51 if (IsReadableStreamLocked(&stream
)) {
52 rv
.ThrowTypeError("Trying to read locked stream");
56 // Step 2. If stream.[[controller]] does not implement
57 // ReadableByteStreamController, throw a TypeError exception.
58 if (!stream
.Controller()->IsByte()) {
59 rv
.ThrowTypeError("Trying to read with incompatible controller");
63 // Step 3. Perform ! ReadableStreamReaderGenericInitialize(reader, stream).
64 ReadableStreamReaderGenericInitialize(reader
, &stream
);
66 // Step 4. Set reader.[[readIntoRequests]] to a new empty list.
67 reader
->ReadIntoRequests().clear();
70 // https://streams.spec.whatwg.org/#byob-reader-constructor
71 /* static */ already_AddRefed
<ReadableStreamBYOBReader
>
72 ReadableStreamBYOBReader::Constructor(const GlobalObject
& global
,
73 ReadableStream
& stream
, ErrorResult
& rv
) {
74 nsCOMPtr
<nsIGlobalObject
> globalObject
=
75 do_QueryInterface(global
.GetAsSupports());
76 RefPtr
<ReadableStreamBYOBReader
> reader
=
77 new ReadableStreamBYOBReader(globalObject
);
80 SetUpReadableStreamBYOBReader(reader
, stream
, rv
);
85 return reader
.forget();
88 struct Read_ReadIntoRequest final
: public ReadIntoRequest
{
89 NS_DECL_ISUPPORTS_INHERITED
90 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Read_ReadIntoRequest
,
93 RefPtr
<Promise
> mPromise
;
95 explicit Read_ReadIntoRequest(Promise
* aPromise
) : mPromise(aPromise
) {}
97 void ChunkSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
98 ErrorResult
& aRv
) override
{
99 MOZ_ASSERT(aChunk
.isObject());
100 // https://streams.spec.whatwg.org/#byob-reader-read Step 6.
102 // chunk steps, given chunk:
103 // Resolve promise with «[ "value" → chunk, "done" → false ]».
105 // We need to wrap this as the chunk could have come from
106 // another compartment.
107 JS::Rooted
<JSObject
*> chunk(aCx
, &aChunk
.toObject());
108 if (!JS_WrapObject(aCx
, &chunk
)) {
109 aRv
.StealExceptionFromJSContext(aCx
);
113 RootedDictionary
<ReadableStreamReadResult
> result(aCx
);
114 result
.mValue
= aChunk
;
115 result
.mDone
.Construct(false);
117 mPromise
->MaybeResolve(result
);
120 void CloseSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
121 ErrorResult
& aRv
) override
{
122 MOZ_ASSERT(aChunk
.isObject() || aChunk
.isUndefined());
123 // https://streams.spec.whatwg.org/#byob-reader-read Step 6.
125 // close steps, given chunk:
126 // Resolve promise with «[ "value" → chunk, "done" → true ]».
127 RootedDictionary
<ReadableStreamReadResult
> result(aCx
);
128 if (aChunk
.isObject()) {
129 // We need to wrap this as the chunk could have come from
130 // another compartment.
131 JS::Rooted
<JSObject
*> chunk(aCx
, &aChunk
.toObject());
132 if (!JS_WrapObject(aCx
, &chunk
)) {
133 aRv
.StealExceptionFromJSContext(aCx
);
137 result
.mValue
= aChunk
;
139 result
.mDone
.Construct(true);
141 mPromise
->MaybeResolve(result
);
144 void ErrorSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> e
,
145 ErrorResult
& aRv
) override
{
146 // https://streams.spec.whatwg.org/#byob-reader-read Step 6.
148 // error steps, given e:
149 // Reject promise with e.
150 mPromise
->MaybeReject(e
);
154 ~Read_ReadIntoRequest() override
= default;
157 NS_IMPL_CYCLE_COLLECTION(ReadIntoRequest
)
158 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadIntoRequest
)
159 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadIntoRequest
)
160 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadIntoRequest
)
161 NS_INTERFACE_MAP_ENTRY(nsISupports
)
164 NS_IMPL_CYCLE_COLLECTION_INHERITED(Read_ReadIntoRequest
, ReadIntoRequest
,
166 NS_IMPL_ADDREF_INHERITED(Read_ReadIntoRequest
, ReadIntoRequest
)
167 NS_IMPL_RELEASE_INHERITED(Read_ReadIntoRequest
, ReadIntoRequest
)
169 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Read_ReadIntoRequest
)
170 NS_INTERFACE_MAP_END_INHERITING(ReadIntoRequest
)
172 namespace streams_abstract
{
173 // https://streams.spec.whatwg.org/#readable-stream-byob-reader-read
174 void ReadableStreamBYOBReaderRead(JSContext
* aCx
,
175 ReadableStreamBYOBReader
* aReader
,
176 JS::Handle
<JSObject
*> aView
,
177 ReadIntoRequest
* aReadIntoRequest
,
179 // Step 1.Let stream be reader.[[stream]].
180 ReadableStream
* stream
= aReader
->GetStream();
182 // Step 2. Assert: stream is not undefined.
185 // Step 3. Set stream.[[disturbed]] to true.
186 stream
->SetDisturbed(true);
188 // Step 4. If stream.[[state]] is "errored", perform readIntoRequest’s error
189 // steps given stream.[[storedError]].
190 if (stream
->State() == ReadableStream::ReaderState::Errored
) {
191 JS::Rooted
<JS::Value
> error(aCx
, stream
->StoredError());
193 aReadIntoRequest
->ErrorSteps(aCx
, error
, aRv
);
197 // Step 5. Otherwise, perform
198 // !ReadableByteStreamControllerPullInto(stream.[[controller]], view,
200 MOZ_ASSERT(stream
->Controller()->IsByte());
201 RefPtr
<ReadableByteStreamController
> controller(
202 stream
->Controller()->AsByte());
203 ReadableByteStreamControllerPullInto(aCx
, controller
, aView
, aReadIntoRequest
,
206 } // namespace streams_abstract
208 // https://streams.spec.whatwg.org/#byob-reader-read
209 already_AddRefed
<Promise
> ReadableStreamBYOBReader::Read(
210 const ArrayBufferView
& aArray
, ErrorResult
& aRv
) {
212 if (!jsapi
.Init(GetParentObject())) {
213 aRv
.ThrowUnknownError("Internal error");
216 JSContext
* cx
= jsapi
.cx();
218 JS::Rooted
<JSObject
*> view(cx
, aArray
.Obj());
220 // Step 1. If view.[[ByteLength]] is 0, return a promise rejected with a
221 // TypeError exception.
222 if (JS_GetArrayBufferViewByteLength(view
) == 0) {
223 // Binding code should convert this thrown value into a rejected promise.
224 aRv
.ThrowTypeError("Zero Length View");
228 // Step 2. If view.[[ViewedArrayBuffer]].[[ArrayBufferByteLength]] is 0,
229 // return a promise rejected with a TypeError exception.
231 JS::Rooted
<JSObject
*> viewedArrayBuffer(
232 cx
, JS_GetArrayBufferViewBuffer(cx
, view
, &isSharedMemory
));
233 if (!viewedArrayBuffer
) {
234 aRv
.StealExceptionFromJSContext(cx
);
238 if (JS::GetArrayBufferByteLength(viewedArrayBuffer
) == 0) {
239 aRv
.ThrowTypeError("zero length viewed buffer");
243 // Step 3. If ! IsDetachedBuffer(view.[[ViewedArrayBuffer]]) is true, return a
244 // promise rejected with a TypeError exception.
245 if (JS::IsDetachedArrayBufferObject(viewedArrayBuffer
)) {
246 aRv
.ThrowTypeError("Detached Buffer");
250 // Step 4. If this.[[stream]] is undefined, return a promise rejected with a
251 // TypeError exception.
253 aRv
.ThrowTypeError("Reader has undefined stream");
258 RefPtr
<Promise
> promise
= Promise::CreateInfallible(GetParentObject());
260 // Step 6. Let readIntoRequest be a new read-into request with the following
262 RefPtr
<ReadIntoRequest
> readIntoRequest
= new Read_ReadIntoRequest(promise
);
264 // Step 7. Perform ! ReadableStreamBYOBReaderRead(this, view,
266 ReadableStreamBYOBReaderRead(cx
, this, view
, readIntoRequest
, aRv
);
271 // Step 8. Return promise.
272 return promise
.forget();
275 namespace streams_abstract
{
277 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreambyobreadererrorreadintorequests
278 void ReadableStreamBYOBReaderErrorReadIntoRequests(
279 JSContext
* aCx
, ReadableStreamBYOBReader
* aReader
,
280 JS::Handle
<JS::Value
> aError
, ErrorResult
& aRv
) {
281 // Step 1. Let readIntoRequests be reader.[[readIntoRequests]].
282 LinkedList
<RefPtr
<ReadIntoRequest
>> readIntoRequests
=
283 std::move(aReader
->ReadIntoRequests());
285 // Step 2. Set reader.[[readIntoRequests]] to a new empty list.
286 // Note: The std::move already cleared this anyway.
287 aReader
->ReadIntoRequests().clear();
289 // Step 3. For each readIntoRequest of readIntoRequests,
290 while (RefPtr
<ReadIntoRequest
> readIntoRequest
=
291 readIntoRequests
.popFirst()) {
292 // Step 3.1. Perform readIntoRequest’s error steps, given e.
293 readIntoRequest
->ErrorSteps(aCx
, aError
, aRv
);
300 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreambyobreaderrelease
301 void ReadableStreamBYOBReaderRelease(JSContext
* aCx
,
302 ReadableStreamBYOBReader
* aReader
,
304 // Step 1. Perform ! ReadableStreamReaderGenericRelease(reader).
305 ReadableStreamReaderGenericRelease(aReader
, aRv
);
310 // Step 2. Let e be a new TypeError exception.
312 rv
.ThrowTypeError("Releasing lock");
313 JS::Rooted
<JS::Value
> error(aCx
);
314 MOZ_ALWAYS_TRUE(ToJSValue(aCx
, std::move(rv
), &error
));
316 // Step 3. Perform ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e).
317 ReadableStreamBYOBReaderErrorReadIntoRequests(aCx
, aReader
, error
, aRv
);
320 } // namespace streams_abstract
322 // https://streams.spec.whatwg.org/#byob-reader-release-lock
323 void ReadableStreamBYOBReader::ReleaseLock(ErrorResult
& aRv
) {
324 // Step 1. If this.[[stream]] is undefined, return.
330 if (!jsapi
.Init(mGlobal
)) {
331 return aRv
.ThrowUnknownError("Internal error");
333 JSContext
* cx
= jsapi
.cx();
335 // Step 2. Perform ! ReadableStreamBYOBReaderRelease(this).
336 RefPtr
<ReadableStreamBYOBReader
> thisRefPtr
= this;
337 ReadableStreamBYOBReaderRelease(cx
, thisRefPtr
, aRv
);
340 namespace streams_abstract
{
341 // https://streams.spec.whatwg.org/#acquire-readable-stream-byob-reader
342 already_AddRefed
<ReadableStreamBYOBReader
> AcquireReadableStreamBYOBReader(
343 ReadableStream
* aStream
, ErrorResult
& aRv
) {
344 // Step 1. Let reader be a new ReadableStreamBYOBReader.
345 RefPtr
<ReadableStreamBYOBReader
> reader
=
346 new ReadableStreamBYOBReader(aStream
->GetParentObject());
348 // Step 2. Perform ? SetUpReadableStreamBYOBReader(reader, stream).
349 SetUpReadableStreamBYOBReader(reader
, *aStream
, aRv
);
354 // Step 3. Return reader.
355 return reader
.forget();
357 } // namespace streams_abstract
359 } // namespace mozilla::dom