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/ReadableStreamDefaultReader.h"
9 #include "mozilla/dom/AutoEntryScript.h"
10 #include "mozilla/dom/ReadableStream.h"
11 #include "mozilla/dom/RootedDictionary.h"
12 #include "js/PropertyAndElement.h"
13 #include "js/TypeDecls.h"
16 #include "mozilla/dom/ReadableStreamDefaultReaderBinding.h"
17 #include "mozilla/dom/UnderlyingSourceBinding.h"
19 #include "nsCycleCollectionParticipant.h"
20 #include "nsISupports.h"
21 #include "nsWrapperCache.h"
23 namespace mozilla::dom
{
25 using namespace streams_abstract
;
27 NS_IMPL_CYCLE_COLLECTION(ReadableStreamGenericReader
, mClosedPromise
, mStream
,
29 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadableStreamGenericReader
)
30 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadableStreamGenericReader
)
31 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ReadableStreamGenericReader
)
32 NS_IMPL_CYCLE_COLLECTION_TRACE_END
34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamGenericReader
)
35 NS_INTERFACE_MAP_ENTRY(nsISupports
)
38 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(ReadableStreamDefaultReader
,
39 ReadableStreamGenericReader
,
41 NS_IMPL_ADDREF_INHERITED(ReadableStreamDefaultReader
,
42 ReadableStreamGenericReader
)
43 NS_IMPL_RELEASE_INHERITED(ReadableStreamDefaultReader
,
44 ReadableStreamGenericReader
)
46 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamDefaultReader
)
47 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
48 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamGenericReader
)
50 ReadableStreamDefaultReader::ReadableStreamDefaultReader(nsISupports
* aGlobal
)
51 : ReadableStreamGenericReader(do_QueryInterface(aGlobal
)) {}
53 ReadableStreamDefaultReader::~ReadableStreamDefaultReader() {
54 mReadRequests
.clear();
57 JSObject
* ReadableStreamDefaultReader::WrapObject(
58 JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
59 return ReadableStreamDefaultReader_Binding::Wrap(aCx
, this, aGivenProto
);
62 namespace streams_abstract
{
63 // https://streams.spec.whatwg.org/#readable-stream-reader-generic-initialize
64 bool ReadableStreamReaderGenericInitialize(ReadableStreamGenericReader
* aReader
,
65 ReadableStream
* aStream
) {
67 aReader
->SetStream(aStream
);
70 aStream
->SetReader(aReader
);
72 aReader
->SetClosedPromise(
73 Promise::CreateInfallible(aReader
->GetParentObject()));
75 switch (aStream
->State()) {
77 case ReadableStream::ReaderState::Readable
:
79 // Promise created above.
82 case ReadableStream::ReaderState::Closed
:
84 aReader
->ClosedPromise()->MaybeResolve(JS::UndefinedHandleValue
);
88 case ReadableStream::ReaderState::Errored
: {
91 JS::RootingContext
* rcx
= RootingCx();
92 JS::Rooted
<JS::Value
> rootedError(rcx
, aStream
->StoredError());
93 aReader
->ClosedPromise()->MaybeReject(rootedError
);
96 aReader
->ClosedPromise()->SetSettledPromiseIsHandled();
100 MOZ_ASSERT_UNREACHABLE("Unknown ReaderState");
104 } // namespace streams_abstract
106 // https://streams.spec.whatwg.org/#default-reader-constructor &&
107 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-reader
109 already_AddRefed
<ReadableStreamDefaultReader
>
110 ReadableStreamDefaultReader::Constructor(const GlobalObject
& aGlobal
,
111 ReadableStream
& aStream
,
113 RefPtr
<ReadableStreamDefaultReader
> reader
=
114 new ReadableStreamDefaultReader(aGlobal
.GetAsSupports());
116 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-reader
118 if (aStream
.Locked()) {
120 "Cannot create a new reader for a readable stream already locked by "
126 RefPtr
<ReadableStream
> streamPtr
= &aStream
;
127 if (!ReadableStreamReaderGenericInitialize(reader
, streamPtr
)) {
132 reader
->mReadRequests
.clear();
134 return reader
.forget();
137 void Read_ReadRequest::ChunkSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
139 // https://streams.spec.whatwg.org/#default-reader-read Step 3.
140 // chunk steps, given chunk:
141 // Step 1. Resolve promise with «[ "value" → chunk, "done" → false ]».
143 // Value may need to be wrapped if stream and reader are in different
145 JS::Rooted
<JS::Value
> chunk(aCx
, aChunk
);
146 if (!JS_WrapValue(aCx
, &chunk
)) {
147 aRv
.StealExceptionFromJSContext(aCx
);
151 RootedDictionary
<ReadableStreamReadResult
> result(aCx
);
152 result
.mValue
= chunk
;
153 result
.mDone
.Construct(false);
155 // Ensure that the object is created with the current global.
156 JS::Rooted
<JS::Value
> value(aCx
);
157 if (!ToJSValue(aCx
, std::move(result
), &value
)) {
158 aRv
.StealExceptionFromJSContext(aCx
);
162 mPromise
->MaybeResolve(value
);
165 void Read_ReadRequest::CloseSteps(JSContext
* aCx
, ErrorResult
& aRv
) {
166 // https://streams.spec.whatwg.org/#default-reader-read Step 3.
168 // Step 1. Resolve promise with «[ "value" → undefined, "done" → true ]».
169 RootedDictionary
<ReadableStreamReadResult
> result(aCx
);
170 result
.mValue
.setUndefined();
171 result
.mDone
.Construct(true);
173 JS::Rooted
<JS::Value
> value(aCx
);
174 if (!ToJSValue(aCx
, std::move(result
), &value
)) {
175 aRv
.StealExceptionFromJSContext(aCx
);
179 mPromise
->MaybeResolve(value
);
182 void Read_ReadRequest::ErrorSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> e
,
184 // https://streams.spec.whatwg.org/#default-reader-read Step 3.
186 // Step 1. Reject promise with e.
187 mPromise
->MaybeReject(e
);
190 NS_IMPL_CYCLE_COLLECTION(ReadRequest
)
191 NS_IMPL_CYCLE_COLLECTION_INHERITED(Read_ReadRequest
, ReadRequest
, mPromise
)
192 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadRequest
)
193 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadRequest
)
195 NS_IMPL_ADDREF_INHERITED(Read_ReadRequest
, ReadRequest
)
196 NS_IMPL_RELEASE_INHERITED(Read_ReadRequest
, ReadRequest
)
198 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadRequest
)
199 NS_INTERFACE_MAP_ENTRY(nsISupports
)
202 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Read_ReadRequest
)
203 NS_INTERFACE_MAP_END_INHERITING(ReadRequest
)
205 namespace streams_abstract
{
206 // https://streams.spec.whatwg.org/#readable-stream-default-reader-read
207 void ReadableStreamDefaultReaderRead(JSContext
* aCx
,
208 ReadableStreamGenericReader
* aReader
,
209 ReadRequest
* aRequest
, ErrorResult
& aRv
) {
211 ReadableStream
* stream
= aReader
->GetStream();
217 stream
->SetDisturbed(true);
219 switch (stream
->State()) {
221 case ReadableStream::ReaderState::Closed
: {
222 aRequest
->CloseSteps(aCx
, aRv
);
226 case ReadableStream::ReaderState::Errored
: {
227 JS::Rooted
<JS::Value
> storedError(aCx
, stream
->StoredError());
228 aRequest
->ErrorSteps(aCx
, storedError
, aRv
);
232 case ReadableStream::ReaderState::Readable
: {
233 RefPtr
<ReadableStreamController
> controller(stream
->Controller());
234 MOZ_ASSERT(controller
);
235 controller
->PullSteps(aCx
, aRequest
, aRv
);
240 } // namespace streams_abstract
242 // Return a raw pointer here to avoid refcounting, but make sure it's safe
243 // (the object should be kept alive by the callee).
244 // https://streams.spec.whatwg.org/#default-reader-read
245 already_AddRefed
<Promise
> ReadableStreamDefaultReader::Read(ErrorResult
& aRv
) {
248 aRv
.ThrowTypeError("Reading is not possible after calling releaseLock.");
253 RefPtr
<Promise
> promise
= Promise::CreateInfallible(GetParentObject());
256 RefPtr
<ReadRequest
> request
= new Read_ReadRequest(promise
);
259 AutoEntryScript
aes(mGlobal
, "ReadableStreamDefaultReader::Read");
260 JSContext
* cx
= aes
.cx();
262 ReadableStreamDefaultReaderRead(cx
, this, request
, aRv
);
268 return promise
.forget();
271 namespace streams_abstract
{
273 // https://streams.spec.whatwg.org/#readable-stream-reader-generic-release
274 void ReadableStreamReaderGenericRelease(ReadableStreamGenericReader
* aReader
,
276 // Step 1. Let stream be reader.[[stream]].
277 RefPtr
<ReadableStream
> stream
= aReader
->GetStream();
279 // Step 2. Assert: stream is not undefined.
282 // Step 3. Assert: stream.[[reader]] is reader.
283 MOZ_ASSERT(stream
->GetReader() == aReader
);
285 // Step 4. If stream.[[state]] is "readable", reject reader.[[closedPromise]]
286 // with a TypeError exception.
287 if (stream
->State() == ReadableStream::ReaderState::Readable
) {
288 aReader
->ClosedPromise()->MaybeRejectWithTypeError(
289 "Releasing lock on readable stream");
291 // Step 5. Otherwise, set reader.[[closedPromise]] to a promise rejected
292 // with a TypeError exception.
293 RefPtr
<Promise
> promise
= Promise::CreateRejectedWithTypeError(
294 aReader
->GetParentObject(), "Lock Released"_ns
, aRv
);
295 aReader
->SetClosedPromise(promise
.forget());
298 // Step 6. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
299 aReader
->ClosedPromise()->SetSettledPromiseIsHandled();
301 // Step 7. Perform ! stream.[[controller]].[[ReleaseSteps]]().
302 stream
->Controller()->ReleaseSteps();
304 // Step 8. Set stream.[[reader]] to undefined.
305 stream
->SetReader(nullptr);
307 // Step 9. Set reader.[[stream]] to undefined.
308 aReader
->SetStream(nullptr);
311 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreadererrorreadrequests
312 void ReadableStreamDefaultReaderErrorReadRequests(
313 JSContext
* aCx
, ReadableStreamDefaultReader
* aReader
,
314 JS::Handle
<JS::Value
> aError
, ErrorResult
& aRv
) {
315 // Step 1. Let readRequests be reader.[[readRequests]].
316 LinkedList
<RefPtr
<ReadRequest
>> readRequests
=
317 std::move(aReader
->ReadRequests());
319 // Step 2. Set reader.[[readRequests]] to a new empty list.
320 // Note: The std::move already cleared this anyway.
321 aReader
->ReadRequests().clear();
323 // Step 3. For each readRequest of readRequests,
324 while (RefPtr
<ReadRequest
> readRequest
= readRequests
.popFirst()) {
325 // Step 3.1. Perform readRequest’s error steps, given e.
326 readRequest
->ErrorSteps(aCx
, aError
, aRv
);
333 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreaderrelease
334 void ReadableStreamDefaultReaderRelease(JSContext
* aCx
,
335 ReadableStreamDefaultReader
* aReader
,
337 // Step 1. Perform ! ReadableStreamReaderGenericRelease(reader).
338 ReadableStreamReaderGenericRelease(aReader
, aRv
);
343 // Step 2. Let e be a new TypeError exception.
345 rv
.ThrowTypeError("Releasing lock");
346 JS::Rooted
<JS::Value
> error(aCx
);
347 MOZ_ALWAYS_TRUE(ToJSValue(aCx
, std::move(rv
), &error
));
349 // Step 3. Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e).
350 ReadableStreamDefaultReaderErrorReadRequests(aCx
, aReader
, error
, aRv
);
353 } // namespace streams_abstract
355 // https://streams.spec.whatwg.org/#default-reader-release-lock
356 void ReadableStreamDefaultReader::ReleaseLock(ErrorResult
& aRv
) {
357 // Step 1. If this.[[stream]] is undefined, return.
363 if (!jsapi
.Init(mGlobal
)) {
364 return aRv
.ThrowUnknownError("Internal error");
366 JSContext
* cx
= jsapi
.cx();
368 // Step 2. Perform ! ReadableStreamDefaultReaderRelease(this).
369 RefPtr
<ReadableStreamDefaultReader
> thisRefPtr
= this;
370 ReadableStreamDefaultReaderRelease(cx
, thisRefPtr
, aRv
);
373 // https://streams.spec.whatwg.org/#generic-reader-closed
374 already_AddRefed
<Promise
> ReadableStreamGenericReader::Closed() const {
376 return do_AddRef(mClosedPromise
);
379 // https://streams.spec.whatwg.org/#readable-stream-reader-generic-cancel
381 static already_AddRefed
<Promise
> ReadableStreamGenericReaderCancel(
382 JSContext
* aCx
, ReadableStreamGenericReader
* aReader
,
383 JS::Handle
<JS::Value
> aReason
, ErrorResult
& aRv
) {
384 // Step 1 (Strong ref for below call).
385 RefPtr
<ReadableStream
> stream
= aReader
->GetStream();
391 return ReadableStreamCancel(aCx
, stream
, aReason
, aRv
);
394 already_AddRefed
<Promise
> ReadableStreamGenericReader::Cancel(
395 JSContext
* aCx
, JS::Handle
<JS::Value
> aReason
, ErrorResult
& aRv
) {
396 // Step 1. If this.[[stream]] is undefined,
397 // return a promise rejected with a TypeError exception.
399 aRv
.ThrowTypeError("Canceling is not possible after calling releaseLock.");
403 // Step 2. Return ! ReadableStreamReaderGenericCancel(this, reason).
404 return ReadableStreamGenericReaderCancel(aCx
, this, aReason
, aRv
);
407 namespace streams_abstract
{
408 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-reader
409 void SetUpReadableStreamDefaultReader(ReadableStreamDefaultReader
* aReader
,
410 ReadableStream
* aStream
,
413 if (IsReadableStreamLocked(aStream
)) {
414 return aRv
.ThrowTypeError(
415 "Cannot get a new reader for a readable stream already locked by "
420 if (!ReadableStreamReaderGenericInitialize(aReader
, aStream
)) {
425 aReader
->ReadRequests().clear();
427 } // namespace streams_abstract
429 // https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-a-chunk
430 // To read a chunk from a ReadableStreamDefaultReader reader, given a read
431 // request readRequest, perform ! ReadableStreamDefaultReaderRead(reader,
433 void ReadableStreamDefaultReader::ReadChunk(JSContext
* aCx
,
434 ReadRequest
& aRequest
,
436 ReadableStreamDefaultReaderRead(aCx
, this, &aRequest
, aRv
);
439 } // namespace mozilla::dom