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/ReadableStream.h"
9 #include "ReadIntoRequest.h"
10 #include "ReadableStreamPipeTo.h"
11 #include "ReadableStreamTee.h"
12 #include "StreamUtils.h"
15 #include "js/Exception.h"
16 #include "js/PropertyAndElement.h"
17 #include "js/TypeDecls.h"
19 #include "js/Iterator.h"
20 #include "mozilla/AlreadyAddRefed.h"
21 #include "mozilla/Assertions.h"
22 #include "mozilla/Attributes.h"
23 #include "mozilla/CycleCollectedJSContext.h"
24 #include "mozilla/HoldDropJSObjects.h"
25 #include "mozilla/StaticPrefs_dom.h"
26 #include "mozilla/dom/BindingCallContext.h"
27 #include "mozilla/dom/ByteStreamHelpers.h"
28 #include "mozilla/dom/QueueWithSizes.h"
29 #include "mozilla/dom/QueuingStrategyBinding.h"
30 #include "mozilla/dom/ReadRequest.h"
31 #include "mozilla/dom/ReadableByteStreamController.h"
32 #include "mozilla/dom/ReadableStreamBYOBReader.h"
33 #include "mozilla/dom/ReadableStreamBYOBRequest.h"
34 #include "mozilla/dom/ReadableStreamBinding.h"
35 #include "mozilla/dom/ReadableStreamController.h"
36 #include "mozilla/dom/ReadableStreamDefaultController.h"
37 #include "mozilla/dom/ReadableStreamDefaultReader.h"
38 #include "mozilla/dom/RootedDictionary.h"
39 #include "mozilla/dom/ScriptSettings.h"
40 #include "mozilla/dom/UnderlyingSourceBinding.h"
41 #include "mozilla/dom/UnderlyingSourceCallbackHelpers.h"
42 #include "mozilla/dom/WritableStream.h"
43 #include "mozilla/dom/WritableStreamDefaultWriter.h"
46 #include "mozilla/dom/Promise-inl.h"
47 #include "nsIGlobalObject.h"
48 #include "nsISupports.h"
50 inline void ImplCycleCollectionTraverse(
51 nsCycleCollectionTraversalCallback
& aCallback
,
52 mozilla::Variant
<mozilla::Nothing
,
53 RefPtr
<mozilla::dom::ReadableStreamDefaultReader
>>&
55 const char* aName
, uint32_t aFlags
= 0) {
56 if (aReader
.is
<RefPtr
<mozilla::dom::ReadableStreamDefaultReader
>>()) {
57 ImplCycleCollectionTraverse(
59 aReader
.as
<RefPtr
<mozilla::dom::ReadableStreamDefaultReader
>>(), aName
,
64 inline void ImplCycleCollectionUnlink(
65 mozilla::Variant
<mozilla::Nothing
,
66 RefPtr
<mozilla::dom::ReadableStreamDefaultReader
>>&
68 aReader
= AsVariant(mozilla::Nothing());
71 namespace mozilla::dom
{
73 using namespace streams_abstract
;
75 // Only needed for refcounted objects.
76 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(
77 ReadableStream
, (mGlobal
, mController
, mReader
), (mStoredError
))
79 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadableStream
)
80 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadableStream
)
81 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStream
)
82 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
83 NS_INTERFACE_MAP_ENTRY(nsISupports
)
86 ReadableStream::ReadableStream(nsIGlobalObject
* aGlobal
,
87 HoldDropJSObjectsCaller aHoldDropCaller
)
88 : mGlobal(aGlobal
), mReader(nullptr), mHoldDropCaller(aHoldDropCaller
) {
89 if (mHoldDropCaller
== HoldDropJSObjectsCaller::Implicit
) {
90 mozilla::HoldJSObjects(this);
94 ReadableStream::ReadableStream(const GlobalObject
& aGlobal
,
95 HoldDropJSObjectsCaller aHoldDropCaller
)
96 : mGlobal(do_QueryInterface(aGlobal
.GetAsSupports())),
98 mHoldDropCaller(aHoldDropCaller
) {
99 if (mHoldDropCaller
== HoldDropJSObjectsCaller::Implicit
) {
100 mozilla::HoldJSObjects(this);
104 ReadableStream::~ReadableStream() {
105 if (mHoldDropCaller
== HoldDropJSObjectsCaller::Implicit
) {
106 mozilla::DropJSObjects(this);
110 JSObject
* ReadableStream::WrapObject(JSContext
* aCx
,
111 JS::Handle
<JSObject
*> aGivenProto
) {
112 return ReadableStream_Binding::Wrap(aCx
, this, aGivenProto
);
115 ReadableStreamDefaultReader
* ReadableStream::GetDefaultReader() {
116 return mReader
->AsDefault();
119 void ReadableStream::SetReader(ReadableStreamGenericReader
* aReader
) {
123 namespace streams_abstract
{
125 // https://streams.spec.whatwg.org/#readable-stream-has-byob-reader
126 bool ReadableStreamHasBYOBReader(ReadableStream
* aStream
) {
127 // Step 1. Let reader be stream.[[reader]].
128 ReadableStreamGenericReader
* reader
= aStream
->GetReader();
130 // Step 2. If reader is undefined, return false.
135 // Step 3. If reader implements ReadableStreamBYOBReader, return true.
136 // Step 4. Return false.
137 return reader
->IsBYOB();
140 // https://streams.spec.whatwg.org/#readable-stream-has-default-reader
141 bool ReadableStreamHasDefaultReader(ReadableStream
* aStream
) {
142 // Step 1. Let reader be stream.[[reader]].
143 ReadableStreamGenericReader
* reader
= aStream
->GetReader();
145 // Step 2. If reader is undefined, return false.
150 // Step 3. If reader implements ReadableStreamDefaultReader, return true.
151 // Step 4. Return false.
152 return reader
->IsDefault();
155 } // namespace streams_abstract
157 // Streams Spec: 4.2.4: https://streams.spec.whatwg.org/#rs-prototype
159 already_AddRefed
<ReadableStream
> ReadableStream::Constructor(
160 const GlobalObject
& aGlobal
,
161 const Optional
<JS::Handle
<JSObject
*>>& aUnderlyingSource
,
162 const QueuingStrategy
& aStrategy
, ErrorResult
& aRv
) {
164 JS::Rooted
<JSObject
*> underlyingSourceObj(
166 aUnderlyingSource
.WasPassed() ? aUnderlyingSource
.Value() : nullptr);
169 RootedDictionary
<UnderlyingSource
> underlyingSourceDict(aGlobal
.Context());
170 if (underlyingSourceObj
) {
171 JS::Rooted
<JS::Value
> objValue(aGlobal
.Context(),
172 JS::ObjectValue(*underlyingSourceObj
));
173 dom::BindingCallContext
callCx(aGlobal
.Context(),
174 "ReadableStream.constructor");
175 aRv
.MightThrowJSException();
176 if (!underlyingSourceDict
.Init(callCx
, objValue
)) {
177 aRv
.StealExceptionFromJSContext(aGlobal
.Context());
183 RefPtr
<ReadableStream
> readableStream
=
184 new ReadableStream(aGlobal
, HoldDropJSObjectsCaller::Implicit
);
187 if (underlyingSourceDict
.mType
.WasPassed()) {
188 // Implicit assertion on above check.
189 MOZ_ASSERT(underlyingSourceDict
.mType
.Value() == ReadableStreamType::Bytes
);
192 if (aStrategy
.mSize
.WasPassed()) {
193 aRv
.ThrowRangeError("Implementation preserved member 'size'");
198 double highWaterMark
= ExtractHighWaterMark(aStrategy
, 0, aRv
);
204 SetUpReadableByteStreamControllerFromUnderlyingSource(
205 aGlobal
.Context(), readableStream
, underlyingSourceObj
,
206 underlyingSourceDict
, highWaterMark
, aRv
);
211 return readableStream
.forget();
214 // Step 5.1 (implicit in above check)
215 // Step 5.2. Extract callback.
217 // Implementation Note: The specification demands that if the size doesn't
218 // exist, we instead would provide an algorithm that returns 1. Instead, we
219 // will teach callers that a missing callback should simply return 1, rather
220 // than gin up a fake callback here.
222 // This decision may need to be revisited if the default action ever diverges
223 // within the specification.
224 RefPtr
<QueuingStrategySize
> sizeAlgorithm
=
225 aStrategy
.mSize
.WasPassed() ? &aStrategy
.mSize
.Value() : nullptr;
228 double highWaterMark
= ExtractHighWaterMark(aStrategy
, 1, aRv
);
234 SetupReadableStreamDefaultControllerFromUnderlyingSource(
235 aGlobal
.Context(), readableStream
, underlyingSourceObj
,
236 underlyingSourceDict
, highWaterMark
, sizeAlgorithm
, aRv
);
241 return readableStream
.forget();
244 // https://streams.spec.whatwg.org/#readable-stream-from-iterable
245 class ReadableStreamFromAlgorithms final
246 : public UnderlyingSourceAlgorithmsWrapper
{
248 NS_DECL_ISUPPORTS_INHERITED
249 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
250 ReadableStreamFromAlgorithms
, UnderlyingSourceAlgorithmsWrapper
)
252 ReadableStreamFromAlgorithms(nsIGlobalObject
* aGlobal
,
253 JS::Handle
<JSObject
*> aIteratorRecord
)
254 : mGlobal(aGlobal
), mIteratorRecordMaybeCrossRealm(aIteratorRecord
) {
255 mozilla::HoldJSObjects(this);
258 // Step 3. Let startAlgorithm be an algorithm that returns undefined.
259 // Note: Provided by UnderlyingSourceAlgorithmsWrapper::StartCallback.
261 // Step 4. Let pullAlgorithm be the following steps:
262 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> PullCallbackImpl(
263 JSContext
* aCx
, ReadableStreamController
& aController
,
264 ErrorResult
& aRv
) override
{
265 aRv
.MightThrowJSException();
267 JS::Rooted
<JSObject
*> iteratorRecord(aCx
, mIteratorRecordMaybeCrossRealm
);
268 JSAutoRealm
ar(aCx
, iteratorRecord
);
270 // Step 1. Let nextResult be IteratorNext(iteratorRecord).
271 JS::Rooted
<JS::Value
> nextResult(aCx
);
272 if (!JS::IteratorNext(aCx
, iteratorRecord
, &nextResult
)) {
273 // Step 2. If nextResult is an abrupt completion, return a promise
274 // rejected with nextResult.[[Value]].
275 aRv
.StealExceptionFromJSContext(aCx
);
279 // Step 3. Let nextPromise be a promise resolved with nextResult.[[Value]].
280 RefPtr
<Promise
> nextPromise
= Promise::CreateInfallible(mGlobal
);
281 nextPromise
->MaybeResolve(nextResult
);
283 // Step 4. Return the result of reacting to nextPromise with the following
284 // fulfillment steps, given iterResult:
285 auto result
= nextPromise
->ThenWithCycleCollectedArgs(
286 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aIterResult
, ErrorResult
& aRv
,
287 const RefPtr
<ReadableStreamDefaultController
>& aController
)
288 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
-> already_AddRefed
<Promise
> {
289 aRv
.MightThrowJSException();
291 // Step 4.1. If Type(iterResult) is not Object, throw a TypeError.
292 if (!aIterResult
.isObject()) {
293 aRv
.ThrowTypeError("next() returned a non-object value");
297 JS::Rooted
<JSObject
*> iterResult(aCx
, &aIterResult
.toObject());
299 // Step 4.2. Let done be ? IteratorComplete(iterResult).
301 if (!JS::IteratorComplete(aCx
, iterResult
, &done
)) {
302 aRv
.StealExceptionFromJSContext(aCx
);
306 // Step 4.3. If done is true:
308 // Step 4.3.1. Perform !
309 // ReadableStreamDefaultControllerClose(stream.[[controller]]).
310 ReadableStreamDefaultControllerClose(aCx
, aController
, aRv
);
312 // Step 4.4. Otherwise:
313 // Step 4.4.1. Let value be ? IteratorValue(iterResult).
314 JS::Rooted
<JS::Value
> value(aCx
);
315 if (!JS::IteratorValue(aCx
, iterResult
, &value
)) {
316 aRv
.StealExceptionFromJSContext(aCx
);
320 // Step 4.4.2. Perform !
321 // ReadableStreamDefaultControllerEnqueue(stream.[[controller]],
323 ReadableStreamDefaultControllerEnqueue(aCx
, aController
, value
,
329 RefPtr(aController
.AsDefault()));
330 if (result
.isErr()) {
331 aRv
.Throw(result
.unwrapErr());
334 return result
.unwrap().forget();
337 // Step 5. Let cancelAlgorithm be the following steps, given reason:
338 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CancelCallbackImpl(
339 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
340 ErrorResult
& aRv
) override
{
341 aRv
.MightThrowJSException();
343 JS::Rooted
<JSObject
*> iteratorRecord(aCx
, mIteratorRecordMaybeCrossRealm
);
344 JSAutoRealm
ar(aCx
, iteratorRecord
);
346 // Step 1. Let iterator be iteratorRecord.[[Iterator]].
347 JS::Rooted
<JS::Value
> iterator(aCx
);
348 if (!JS::GetIteratorRecordIterator(aCx
, iteratorRecord
, &iterator
)) {
349 aRv
.StealExceptionFromJSContext(aCx
);
353 // Step 2. Let returnMethod be GetMethod(iterator, "return").
354 JS::Rooted
<JS::Value
> returnMethod(aCx
);
355 if (!JS::GetReturnMethod(aCx
, iterator
, &returnMethod
)) {
356 // Step 3. If returnMethod is an abrupt completion, return a promise
357 // rejected with returnMethod.[[Value]].
358 aRv
.StealExceptionFromJSContext(aCx
);
362 // Step 4. If returnMethod.[[Value]] is undefined, return a promise resolved
364 if (returnMethod
.isUndefined()) {
365 return Promise::CreateResolvedWithUndefined(mGlobal
, aRv
);
368 // Step 5. Let returnResult be Call(returnMethod.[[Value]], iterator, «
370 JS::Rooted
<JS::Value
> reason(aCx
, aReason
.Value());
371 if (!JS_WrapValue(aCx
, &reason
)) {
372 JS_ClearPendingException(aCx
);
373 aRv
.Throw(NS_ERROR_UNEXPECTED
);
377 JS::Rooted
<JS::Value
> returnResult(aCx
);
378 if (!JS::Call(aCx
, iterator
, returnMethod
, JS::HandleValueArray(reason
),
380 // Step 6. If returnResult is an abrupt completion, return a promise
381 // rejected with returnResult.[[Value]].
382 aRv
.StealExceptionFromJSContext(aCx
);
386 // Step 7. Let returnPromise be a promise resolved with
387 // returnResult.[[Value]].
388 RefPtr
<Promise
> returnPromise
= Promise::CreateInfallible(mGlobal
);
389 returnPromise
->MaybeResolve(returnResult
);
391 // Step 8. Return the result of reacting to returnPromise with the following
392 // fulfillment steps, given iterResult:
393 auto result
= returnPromise
->ThenWithCycleCollectedArgs(
394 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aIterResult
, ErrorResult
& aRv
)
395 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
-> already_AddRefed
<Promise
> {
396 // Step 8.1. If Type(iterResult) is not Object, throw a TypeError.
397 if (!aIterResult
.isObject()) {
398 aRv
.ThrowTypeError("return() returned a non-object value");
402 // Step 8.2. Return undefined.
405 if (result
.isErr()) {
406 aRv
.Throw(result
.unwrapErr());
409 return result
.unwrap().forget();
413 ~ReadableStreamFromAlgorithms() override
{ mozilla::DropJSObjects(this); };
416 // Virtually const, but are cycle collected
417 nsCOMPtr
<nsIGlobalObject
> mGlobal
;
418 JS::Heap
<JSObject
*> mIteratorRecordMaybeCrossRealm
;
421 NS_IMPL_CYCLE_COLLECTION_INHERITED_WITH_JS_MEMBERS(
422 ReadableStreamFromAlgorithms
, UnderlyingSourceAlgorithmsWrapper
, (mGlobal
),
423 (mIteratorRecordMaybeCrossRealm
))
424 NS_IMPL_ADDREF_INHERITED(ReadableStreamFromAlgorithms
,
425 UnderlyingSourceAlgorithmsWrapper
)
426 NS_IMPL_RELEASE_INHERITED(ReadableStreamFromAlgorithms
,
427 UnderlyingSourceAlgorithmsWrapper
)
428 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamFromAlgorithms
)
429 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsWrapper
)
431 // https://streams.spec.whatwg.org/#readable-stream-from-iterable
432 static already_AddRefed
<ReadableStream
> MOZ_CAN_RUN_SCRIPT
433 ReadableStreamFromIterable(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
434 JS::Handle
<JS::Value
> aAsyncIterable
,
436 aRv
.MightThrowJSException();
438 // Step 1. Let stream be undefined. (not required)
439 // Step 2. Let iteratorRecord be ? GetIterator(asyncIterable, async).
440 JS::Rooted
<JSObject
*> iteratorRecord(
441 aCx
, JS::GetIteratorObject(aCx
, aAsyncIterable
, true));
442 if (!iteratorRecord
) {
443 aRv
.StealExceptionFromJSContext(aCx
);
447 // Steps 3-5. are in ReadableStreamFromAlgorithms.
449 MakeRefPtr
<ReadableStreamFromAlgorithms
>(aGlobal
, iteratorRecord
);
451 // Step 6. Set stream to ! CreateReadableStream(startAlgorithm, pullAlgorithm,
452 // cancelAlgorithm, 0).
453 // Step 7. Return stream.
454 return ReadableStream::CreateAbstract(aCx
, aGlobal
, algorithms
,
455 mozilla::Some(0.0), nullptr, aRv
);
459 already_AddRefed
<ReadableStream
> ReadableStream::From(
460 const GlobalObject
& aGlobal
, JS::Handle
<JS::Value
> aAsyncIterable
,
462 // Step 1. Return ? ReadableStreamFromIterable(asyncIterable).
463 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
464 return ReadableStreamFromIterable(aGlobal
.Context(), global
, aAsyncIterable
,
468 // Dealing with const this ptr is a pain, so just re-implement.
469 // https://streams.spec.whatwg.org/#is-readable-stream-locked
470 bool ReadableStream::Locked() const {
475 namespace streams_abstract
{
476 // https://streams.spec.whatwg.org/#initialize-readable-stream
477 static void InitializeReadableStream(ReadableStream
* aStream
) {
479 aStream
->SetState(ReadableStream::ReaderState::Readable
);
482 aStream
->SetReader(nullptr);
483 aStream
->SetStoredError(JS::UndefinedHandleValue
);
486 aStream
->SetDisturbed(false);
488 } // namespace streams_abstract
490 // https://streams.spec.whatwg.org/#create-readable-stream
492 already_AddRefed
<ReadableStream
> ReadableStream::CreateAbstract(
493 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
494 UnderlyingSourceAlgorithmsBase
* aAlgorithms
,
495 mozilla::Maybe
<double> aHighWaterMark
, QueuingStrategySize
* aSizeAlgorithm
,
497 // Step 1. If highWaterMark was not passed, set it to 1.
498 double highWaterMark
= aHighWaterMark
.valueOr(1.0);
500 // Step 2. consumers of sizeAlgorithm
501 // handle null algorithms correctly.
503 MOZ_ASSERT(IsNonNegativeNumber(highWaterMark
));
505 RefPtr
<ReadableStream
> stream
=
506 new ReadableStream(aGlobal
, HoldDropJSObjectsCaller::Implicit
);
509 InitializeReadableStream(stream
);
512 RefPtr
<ReadableStreamDefaultController
> controller
=
513 new ReadableStreamDefaultController(aGlobal
);
516 SetUpReadableStreamDefaultController(aCx
, stream
, controller
, aAlgorithms
,
517 highWaterMark
, aSizeAlgorithm
, aRv
);
520 return stream
.forget();
523 namespace streams_abstract
{
524 // https://streams.spec.whatwg.org/#readable-stream-close
525 void ReadableStreamClose(JSContext
* aCx
, ReadableStream
* aStream
,
528 MOZ_ASSERT(aStream
->State() == ReadableStream::ReaderState::Readable
);
531 aStream
->SetState(ReadableStream::ReaderState::Closed
);
534 ReadableStreamGenericReader
* reader
= aStream
->GetReader();
542 reader
->ClosedPromise()->MaybeResolveWithUndefined();
545 if (reader
->IsDefault()) {
546 // Step 6.1. Let readRequests be reader.[[readRequests]].
547 // Move LinkedList out of DefaultReader onto stack to avoid the potential
548 // for concurrent modification, which could invalidate the iterator.
550 // See https://bugs.chromium.org/p/chromium/issues/detail?id=1045874 as an
551 // example of the kind of issue that could occur.
552 LinkedList
<RefPtr
<ReadRequest
>> readRequests
=
553 std::move(reader
->AsDefault()->ReadRequests());
555 // Step 6.2. Set reader.[[readRequests]] to an empty list.
556 // Note: The std::move already cleared this anyway.
557 reader
->AsDefault()->ReadRequests().clear();
559 // Step 6.3. For each readRequest of readRequests,
560 // Drain the local list and destroy elements along the way.
561 while (RefPtr
<ReadRequest
> readRequest
= readRequests
.popFirst()) {
562 // Step 6.3.1. Perform readRequest’s close steps.
563 readRequest
->CloseSteps(aCx
, aRv
);
571 // https://streams.spec.whatwg.org/#readable-stream-cancel
572 already_AddRefed
<Promise
> ReadableStreamCancel(JSContext
* aCx
,
573 ReadableStream
* aStream
,
574 JS::Handle
<JS::Value
> aError
,
577 aStream
->SetDisturbed(true);
580 if (aStream
->State() == ReadableStream::ReaderState::Closed
) {
581 RefPtr
<Promise
> promise
=
582 Promise::CreateInfallible(aStream
->GetParentObject());
583 promise
->MaybeResolveWithUndefined();
584 return promise
.forget();
588 if (aStream
->State() == ReadableStream::ReaderState::Errored
) {
589 JS::Rooted
<JS::Value
> storedError(aCx
, aStream
->StoredError());
590 return Promise::CreateRejected(aStream
->GetParentObject(), storedError
,
595 ReadableStreamClose(aCx
, aStream
, aRv
);
601 ReadableStreamGenericReader
* reader
= aStream
->GetReader();
604 if (reader
&& reader
->IsBYOB()) {
605 // Step 6.1. Let readIntoRequests be reader.[[readIntoRequests]].
606 LinkedList
<RefPtr
<ReadIntoRequest
>> readIntoRequests
=
607 std::move(reader
->AsBYOB()->ReadIntoRequests());
609 // Step 6.2. Set reader.[[readIntoRequests]] to an empty list.
610 // Note: The std::move already cleared this anyway.
611 reader
->AsBYOB()->ReadIntoRequests().clear();
613 // Step 6.3. For each readIntoRequest of readIntoRequests,
614 while (RefPtr
<ReadIntoRequest
> readIntoRequest
=
615 readIntoRequests
.popFirst()) {
616 // Step 6.3.1.Perform readIntoRequest’s close steps, given undefined.
617 readIntoRequest
->CloseSteps(aCx
, JS::UndefinedHandleValue
, aRv
);
625 RefPtr
<ReadableStreamController
> controller(aStream
->Controller());
626 RefPtr
<Promise
> sourceCancelPromise
=
627 controller
->CancelSteps(aCx
, aError
, aRv
);
633 RefPtr
<Promise
> promise
=
634 Promise::CreateInfallible(sourceCancelPromise
->GetParentObject());
636 // ThenWithCycleCollectedArgs will carry promise, keeping it alive until the
637 // callback executes.
638 Result
<RefPtr
<Promise
>, nsresult
> returnResult
=
639 sourceCancelPromise
->ThenWithCycleCollectedArgs(
640 [](JSContext
*, JS::Handle
<JS::Value
>, ErrorResult
&,
641 RefPtr
<Promise
> newPromise
) {
642 newPromise
->MaybeResolveWithUndefined();
643 return newPromise
.forget();
647 if (returnResult
.isErr()) {
648 aRv
.Throw(returnResult
.unwrapErr());
652 return returnResult
.unwrap().forget();
655 } // namespace streams_abstract
657 // https://streams.spec.whatwg.org/#rs-cancel
658 already_AddRefed
<Promise
> ReadableStream::Cancel(JSContext
* aCx
,
659 JS::Handle
<JS::Value
> aReason
,
661 // Step 1. If ! IsReadableStreamLocked(this) is true,
662 // return a promise rejected with a TypeError exception.
664 aRv
.ThrowTypeError("Cannot cancel a stream locked by a reader.");
668 // Step 2. Return ! ReadableStreamCancel(this, reason).
669 RefPtr
<ReadableStream
> thisRefPtr
= this;
670 return ReadableStreamCancel(aCx
, thisRefPtr
, aReason
, aRv
);
673 namespace streams_abstract
{
674 // https://streams.spec.whatwg.org/#acquire-readable-stream-reader
675 already_AddRefed
<ReadableStreamDefaultReader
>
676 AcquireReadableStreamDefaultReader(ReadableStream
* aStream
, ErrorResult
& aRv
) {
678 RefPtr
<ReadableStreamDefaultReader
> reader
=
679 new ReadableStreamDefaultReader(aStream
->GetParentObject());
682 SetUpReadableStreamDefaultReader(reader
, aStream
, aRv
);
688 return reader
.forget();
690 } // namespace streams_abstract
692 // https://streams.spec.whatwg.org/#rs-get-reader
693 void ReadableStream::GetReader(const ReadableStreamGetReaderOptions
& aOptions
,
694 OwningReadableStreamReader
& resultReader
,
696 // Step 1. If options["mode"] does not exist,
697 // return ? AcquireReadableStreamDefaultReader(this).
698 if (!aOptions
.mMode
.WasPassed()) {
699 RefPtr
<ReadableStreamDefaultReader
> defaultReader
=
700 AcquireReadableStreamDefaultReader(this, aRv
);
704 resultReader
.SetAsReadableStreamDefaultReader() = defaultReader
;
708 // Step 2. Assert: options["mode"] is "byob".
709 MOZ_ASSERT(aOptions
.mMode
.Value() == ReadableStreamReaderMode::Byob
);
711 // Step 3. Return ? AcquireReadableStreamBYOBReader(this).
712 RefPtr
<ReadableStreamBYOBReader
> byobReader
=
713 AcquireReadableStreamBYOBReader(this, aRv
);
717 resultReader
.SetAsReadableStreamBYOBReader() = byobReader
;
720 namespace streams_abstract
{
721 // https://streams.spec.whatwg.org/#is-readable-stream-locked
722 bool IsReadableStreamLocked(ReadableStream
* aStream
) {
724 return aStream
->Locked();
726 } // namespace streams_abstract
728 // https://streams.spec.whatwg.org/#rs-pipe-through
729 MOZ_CAN_RUN_SCRIPT already_AddRefed
<ReadableStream
> ReadableStream::PipeThrough(
730 const ReadableWritablePair
& aTransform
, const StreamPipeOptions
& aOptions
,
732 // Step 1: If ! IsReadableStreamLocked(this) is true, throw a TypeError
734 if (IsReadableStreamLocked(this)) {
735 aRv
.ThrowTypeError("Cannot pipe from a locked stream.");
739 // Step 2: If ! IsWritableStreamLocked(transform["writable"]) is true, throw a
740 // TypeError exception.
741 if (IsWritableStreamLocked(aTransform
.mWritable
)) {
742 aRv
.ThrowTypeError("Cannot pipe to a locked stream.");
746 // Step 3: Let signal be options["signal"] if it exists, or undefined
748 RefPtr
<AbortSignal
> signal
=
749 aOptions
.mSignal
.WasPassed() ? &aOptions
.mSignal
.Value() : nullptr;
751 // Step 4: Let promise be ! ReadableStreamPipeTo(this, transform["writable"],
752 // options["preventClose"], options["preventAbort"], options["preventCancel"],
754 RefPtr
<WritableStream
> writable
= aTransform
.mWritable
;
755 RefPtr
<Promise
> promise
= ReadableStreamPipeTo(
756 this, writable
, aOptions
.mPreventClose
, aOptions
.mPreventAbort
,
757 aOptions
.mPreventCancel
, signal
, aRv
);
762 // Step 5: Set promise.[[PromiseIsHandled]] to true.
763 MOZ_ALWAYS_TRUE(promise
->SetAnyPromiseIsHandled());
765 // Step 6: Return transform["readable"].
766 return do_AddRef(aTransform
.mReadable
.get());
769 namespace streams_abstract
{
771 // https://streams.spec.whatwg.org/#readable-stream-get-num-read-requests
772 double ReadableStreamGetNumReadRequests(ReadableStream
* aStream
) {
774 MOZ_ASSERT(ReadableStreamHasDefaultReader(aStream
));
777 return double(aStream
->GetDefaultReader()->ReadRequests().length());
780 // https://streams.spec.whatwg.org/#readable-stream-error
781 void ReadableStreamError(JSContext
* aCx
, ReadableStream
* aStream
,
782 JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
) {
784 MOZ_ASSERT(aStream
->State() == ReadableStream::ReaderState::Readable
);
787 aStream
->SetState(ReadableStream::ReaderState::Errored
);
790 aStream
->SetStoredError(aValue
);
793 ReadableStreamGenericReader
* reader
= aStream
->GetReader();
801 reader
->ClosedPromise()->MaybeReject(aValue
);
804 reader
->ClosedPromise()->SetSettledPromiseIsHandled();
807 if (reader
->IsDefault()) {
808 // Step 8.1. Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader,
810 RefPtr
<ReadableStreamDefaultReader
> defaultReader
= reader
->AsDefault();
811 ReadableStreamDefaultReaderErrorReadRequests(aCx
, defaultReader
, aValue
,
817 // Step 9. Otherwise,
818 // Step 9.1. Assert: reader implements ReadableStreamBYOBReader.
819 MOZ_ASSERT(reader
->IsBYOB());
821 // Step 9.2. Perform ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader,
823 RefPtr
<ReadableStreamBYOBReader
> byobReader
= reader
->AsBYOB();
824 ReadableStreamBYOBReaderErrorReadIntoRequests(aCx
, byobReader
, aValue
, aRv
);
831 // https://streams.spec.whatwg.org/#rs-default-controller-close
832 void ReadableStreamFulfillReadRequest(JSContext
* aCx
, ReadableStream
* aStream
,
833 JS::Handle
<JS::Value
> aChunk
, bool aDone
,
836 MOZ_ASSERT(ReadableStreamHasDefaultReader(aStream
));
839 ReadableStreamDefaultReader
* reader
= aStream
->GetDefaultReader();
842 MOZ_ASSERT(!reader
->ReadRequests().isEmpty());
845 RefPtr
<ReadRequest
> readRequest
= reader
->ReadRequests().popFirst();
849 readRequest
->CloseSteps(aCx
, aRv
);
856 readRequest
->ChunkSteps(aCx
, aChunk
, aRv
);
859 // https://streams.spec.whatwg.org/#readable-stream-add-read-request
860 void ReadableStreamAddReadRequest(ReadableStream
* aStream
,
861 ReadRequest
* aReadRequest
) {
863 MOZ_ASSERT(aStream
->GetReader()->IsDefault());
865 MOZ_ASSERT(aStream
->State() == ReadableStream::ReaderState::Readable
);
867 aStream
->GetDefaultReader()->ReadRequests().insertBack(aReadRequest
);
870 } // namespace streams_abstract
872 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee
874 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
>
875 ReadableStreamDefaultTeeSourceAlgorithms::CancelCallback(
876 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
879 mTeeState
->SetCanceled(mBranch
, true);
882 mTeeState
->SetReason(mBranch
, aReason
.Value());
886 if (mTeeState
->Canceled(OtherTeeBranch(mBranch
))) {
889 JS::Rooted
<JSObject
*> compositeReason(aCx
, JS::NewArrayObject(aCx
, 2));
890 if (!compositeReason
) {
891 aRv
.StealExceptionFromJSContext(aCx
);
895 JS::Rooted
<JS::Value
> reason1(aCx
, mTeeState
->Reason1());
896 if (!JS_SetElement(aCx
, compositeReason
, 0, reason1
)) {
897 aRv
.StealExceptionFromJSContext(aCx
);
901 JS::Rooted
<JS::Value
> reason2(aCx
, mTeeState
->Reason2());
902 if (!JS_SetElement(aCx
, compositeReason
, 1, reason2
)) {
903 aRv
.StealExceptionFromJSContext(aCx
);
908 JS::Rooted
<JS::Value
> compositeReasonValue(
909 aCx
, JS::ObjectValue(*compositeReason
));
910 RefPtr
<ReadableStream
> stream(mTeeState
->GetStream());
911 RefPtr
<Promise
> cancelResult
=
912 ReadableStreamCancel(aCx
, stream
, compositeReasonValue
, aRv
);
918 mTeeState
->CancelPromise()->MaybeResolve(cancelResult
);
922 return do_AddRef(mTeeState
->CancelPromise());
925 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee
927 static void ReadableStreamDefaultTee(JSContext
* aCx
, ReadableStream
* aStream
,
928 bool aCloneForBranch2
,
929 nsTArray
<RefPtr
<ReadableStream
>>& aResult
,
934 // Steps 3-12 are contained in the construction of Tee State.
935 RefPtr
<TeeState
> teeState
= TeeState::Create(aStream
, aCloneForBranch2
, aRv
);
941 auto branch1Algorithms
= MakeRefPtr
<ReadableStreamDefaultTeeSourceAlgorithms
>(
942 teeState
, TeeBranch::Branch1
);
943 auto branch2Algorithms
= MakeRefPtr
<ReadableStreamDefaultTeeSourceAlgorithms
>(
944 teeState
, TeeBranch::Branch2
);
947 nsCOMPtr
<nsIGlobalObject
> global(
948 do_AddRef(teeState
->GetStream()->GetParentObject()));
949 teeState
->SetBranch1(ReadableStream::CreateAbstract(
950 aCx
, global
, branch1Algorithms
, mozilla::Nothing(), nullptr, aRv
));
956 teeState
->SetBranch2(ReadableStream::CreateAbstract(
957 aCx
, global
, branch2Algorithms
, mozilla::Nothing(), nullptr, aRv
));
963 teeState
->GetReader()->ClosedPromise()->AddCallbacksWithCycleCollectedArgs(
964 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
965 TeeState
* aTeeState
) {},
966 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aReason
, ErrorResult
& aRv
,
967 TeeState
* aTeeState
) {
969 ReadableStreamDefaultControllerError(
970 aCx
, aTeeState
->Branch1()->DefaultController(), aReason
, aRv
);
976 ReadableStreamDefaultControllerError(
977 aCx
, aTeeState
->Branch2()->DefaultController(), aReason
, aRv
);
983 if (!aTeeState
->Canceled1() || !aTeeState
->Canceled2()) {
984 aTeeState
->CancelPromise()->MaybeResolveWithUndefined();
990 aResult
.AppendElement(teeState
->Branch1());
991 aResult
.AppendElement(teeState
->Branch2());
994 // https://streams.spec.whatwg.org/#rs-pipe-to
995 already_AddRefed
<Promise
> ReadableStream::PipeTo(
996 WritableStream
& aDestination
, const StreamPipeOptions
& aOptions
,
998 // Step 1. If !IsReadableStreamLocked(this) is true, return a promise rejected
999 // with a TypeError exception.
1000 if (IsReadableStreamLocked(this)) {
1001 aRv
.ThrowTypeError("Cannot pipe from a locked stream.");
1005 // Step 2. If !IsWritableStreamLocked(destination) is true, return a promise
1006 // rejected with a TypeError exception.
1007 if (IsWritableStreamLocked(&aDestination
)) {
1008 aRv
.ThrowTypeError("Cannot pipe to a locked stream.");
1012 // Step 3. Let signal be options["signal"] if it exists, or undefined
1014 RefPtr
<AbortSignal
> signal
=
1015 aOptions
.mSignal
.WasPassed() ? &aOptions
.mSignal
.Value() : nullptr;
1017 // Step 4. Return ! ReadableStreamPipeTo(this, destination,
1018 // options["preventClose"], options["preventAbort"], options["preventCancel"],
1020 return ReadableStreamPipeTo(this, &aDestination
, aOptions
.mPreventClose
,
1021 aOptions
.mPreventAbort
, aOptions
.mPreventCancel
,
1025 // https://streams.spec.whatwg.org/#readable-stream-tee
1027 static void ReadableStreamTee(JSContext
* aCx
, ReadableStream
* aStream
,
1028 bool aCloneForBranch2
,
1029 nsTArray
<RefPtr
<ReadableStream
>>& aResult
,
1031 // Step 1. Implicit.
1032 // Step 2. Implicit.
1034 if (aStream
->Controller()->IsByte()) {
1035 ReadableByteStreamTee(aCx
, aStream
, aResult
, aRv
);
1039 ReadableStreamDefaultTee(aCx
, aStream
, aCloneForBranch2
, aResult
, aRv
);
1042 void ReadableStream::Tee(JSContext
* aCx
,
1043 nsTArray
<RefPtr
<ReadableStream
>>& aResult
,
1045 ReadableStreamTee(aCx
, this, false, aResult
, aRv
);
1048 void ReadableStream::IteratorData::Traverse(
1049 nsCycleCollectionTraversalCallback
& cb
) {
1050 ReadableStream::IteratorData
* tmp
= this;
1051 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReader
);
1053 void ReadableStream::IteratorData::Unlink() {
1054 ReadableStream::IteratorData
* tmp
= this;
1055 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReader
);
1058 // https://streams.spec.whatwg.org/#rs-get-iterator
1059 void ReadableStream::InitAsyncIteratorData(
1060 IteratorData
& aData
, Iterator::IteratorType aType
,
1061 const ReadableStreamIteratorOptions
& aOptions
, ErrorResult
& aRv
) {
1062 // Step 1. Let reader be ? AcquireReadableStreamDefaultReader(stream).
1063 RefPtr
<ReadableStreamDefaultReader
> reader
=
1064 AcquireReadableStreamDefaultReader(this, aRv
);
1069 // Step 2. Set iterator’s reader to reader.
1070 aData
.mReader
= reader
;
1072 // Step 3. Let preventCancel be args[0]["preventCancel"].
1073 // Step 4. Set iterator’s prevent cancel to preventCancel.
1074 aData
.mPreventCancel
= aOptions
.mPreventCancel
;
1077 // https://streams.spec.whatwg.org/#rs-asynciterator-prototype-next
1079 struct IteratorReadRequest
: public ReadRequest
{
1081 NS_DECL_ISUPPORTS_INHERITED
1082 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IteratorReadRequest
, ReadRequest
)
1084 RefPtr
<Promise
> mPromise
;
1085 RefPtr
<ReadableStreamDefaultReader
> mReader
;
1087 explicit IteratorReadRequest(Promise
* aPromise
,
1088 ReadableStreamDefaultReader
* aReader
)
1089 : mPromise(aPromise
), mReader(aReader
) {}
1091 // chunk steps, given chunk
1092 void ChunkSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
1093 ErrorResult
& aRv
) override
{
1094 // Step 1. Resolve promise with chunk.
1095 mPromise
->MaybeResolve(aChunk
);
1099 void CloseSteps(JSContext
* aCx
, ErrorResult
& aRv
) override
{
1100 // Step 1. Perform ! ReadableStreamDefaultReaderRelease(reader).
1101 ReadableStreamDefaultReaderRelease(aCx
, mReader
, aRv
);
1103 mPromise
->MaybeRejectWithUndefined();
1107 // Step 2. Resolve promise with end of iteration.
1108 iterator_utils::ResolvePromiseForFinished(mPromise
);
1111 // error steps, given e
1112 void ErrorSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> aError
,
1113 ErrorResult
& aRv
) override
{
1114 // Step 1. Perform ! ReadableStreamDefaultReaderRelease(reader).
1115 ReadableStreamDefaultReaderRelease(aCx
, mReader
, aRv
);
1117 mPromise
->MaybeRejectWithUndefined();
1121 // Step 2. Reject promise with e.
1122 mPromise
->MaybeReject(aError
);
1126 virtual ~IteratorReadRequest() = default;
1129 NS_IMPL_CYCLE_COLLECTION_INHERITED(IteratorReadRequest
, ReadRequest
, mPromise
,
1132 NS_IMPL_ADDREF_INHERITED(IteratorReadRequest
, ReadRequest
)
1133 NS_IMPL_RELEASE_INHERITED(IteratorReadRequest
, ReadRequest
)
1135 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IteratorReadRequest
)
1136 NS_INTERFACE_MAP_END_INHERITING(ReadRequest
)
1138 // https://streams.spec.whatwg.org/#rs-asynciterator-prototype-next
1139 already_AddRefed
<Promise
> ReadableStream::GetNextIterationResult(
1140 Iterator
* aIterator
, ErrorResult
& aRv
) {
1141 // Step 1. Let reader be iterator’s reader.
1142 RefPtr
<ReadableStreamDefaultReader
> reader
= aIterator
->Data().mReader
;
1144 // Step 2. Assert: reader.[[stream]] is not undefined.
1145 MOZ_ASSERT(reader
->GetStream());
1147 // Step 3. Let promise be a new promise.
1148 RefPtr
<Promise
> promise
= Promise::CreateInfallible(GetParentObject());
1150 // Step 4. Let readRequest be a new read request with the following items:
1151 RefPtr
<ReadRequest
> request
= new IteratorReadRequest(promise
, reader
);
1153 // Step 5. Perform ! ReadableStreamDefaultReaderRead(this, readRequest).
1155 if (!jsapi
.Init(mGlobal
)) {
1156 aRv
.ThrowUnknownError("Internal error");
1160 ReadableStreamDefaultReaderRead(jsapi
.cx(), reader
, request
, aRv
);
1165 // Step 6. Return promise.
1166 return promise
.forget();
1169 // https://streams.spec.whatwg.org/#rs-asynciterator-prototype-return
1170 already_AddRefed
<Promise
> ReadableStream::IteratorReturn(
1171 JSContext
* aCx
, Iterator
* aIterator
, JS::Handle
<JS::Value
> aValue
,
1173 // Step 1. Let reader be iterator’s reader.
1174 RefPtr
<ReadableStreamDefaultReader
> reader
= aIterator
->Data().mReader
;
1176 // Step 2. Assert: reader.[[stream]] is not undefined.
1177 MOZ_ASSERT(reader
->GetStream());
1179 // Step 3. Assert: reader.[[readRequests]] is empty, as the async iterator
1180 // machinery guarantees that any previous calls to next() have settled before
1182 MOZ_ASSERT(reader
->ReadRequests().isEmpty());
1184 // Step 4. If iterator’s prevent cancel is false:
1185 if (!aIterator
->Data().mPreventCancel
) {
1186 // Step 4.1. Let result be ! ReadableStreamReaderGenericCancel(reader, arg).
1187 RefPtr
<ReadableStream
> stream(reader
->GetStream());
1188 RefPtr
<Promise
> result
= ReadableStreamCancel(aCx
, stream
, aValue
, aRv
);
1189 if (NS_WARN_IF(aRv
.Failed())) {
1193 MOZ_DIAGNOSTIC_ASSERT(
1194 reader
->GetStream(),
1195 "We shouldn't have a null stream here (bug 1821169).");
1196 if (!reader
->GetStream()) {
1197 aRv
.Throw(NS_ERROR_FAILURE
);
1201 // Step 4.2. Perform ! ReadableStreamDefaultReaderRelease(reader).
1202 ReadableStreamDefaultReaderRelease(aCx
, reader
, aRv
);
1203 if (NS_WARN_IF(aRv
.Failed())) {
1207 // Step 4.3. Return result.
1208 return result
.forget();
1211 // Step 5. Perform ! ReadableStreamDefaultReaderRelease(reader).
1212 ReadableStreamDefaultReaderRelease(aCx
, reader
, aRv
);
1213 if (NS_WARN_IF(aRv
.Failed())) {
1217 // Step 6. Return a promise resolved with undefined.
1218 return Promise::CreateResolvedWithUndefined(GetParentObject(), aRv
);
1221 namespace streams_abstract
{
1222 // https://streams.spec.whatwg.org/#readable-stream-add-read-into-request
1223 void ReadableStreamAddReadIntoRequest(ReadableStream
* aStream
,
1224 ReadIntoRequest
* aReadIntoRequest
) {
1225 // Step 1. Assert: stream.[[reader]] implements ReadableStreamBYOBReader.
1226 MOZ_ASSERT(aStream
->GetReader()->IsBYOB());
1228 // Step 2. Assert: stream.[[state]] is "readable" or "closed".
1229 MOZ_ASSERT(aStream
->State() == ReadableStream::ReaderState::Readable
||
1230 aStream
->State() == ReadableStream::ReaderState::Closed
);
1232 // Step 3. Append readRequest to stream.[[reader]].[[readIntoRequests]].
1233 aStream
->GetReader()->AsBYOB()->ReadIntoRequests().insertBack(
1236 } // namespace streams_abstract
1238 // https://streams.spec.whatwg.org/#abstract-opdef-createreadablebytestream
1239 already_AddRefed
<ReadableStream
> ReadableStream::CreateByteAbstract(
1240 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
1241 UnderlyingSourceAlgorithmsBase
* aAlgorithms
, ErrorResult
& aRv
) {
1242 // Step 1. Let stream be a new ReadableStream.
1243 RefPtr
<ReadableStream
> stream
=
1244 new ReadableStream(aGlobal
, HoldDropJSObjectsCaller::Implicit
);
1246 // Step 2. Perform ! InitializeReadableStream(stream).
1247 InitializeReadableStream(stream
);
1249 // Step 3. Let controller be a new ReadableByteStreamController.
1250 RefPtr
<ReadableByteStreamController
> controller
=
1251 new ReadableByteStreamController(aGlobal
);
1253 // Step 4. Perform ? SetUpReadableByteStreamController(stream, controller,
1254 // startAlgorithm, pullAlgorithm, cancelAlgorithm, 0, undefined).
1255 SetUpReadableByteStreamController(aCx
, stream
, controller
, aAlgorithms
, 0,
1256 mozilla::Nothing(), aRv
);
1262 return stream
.forget();
1265 // https://streams.spec.whatwg.org/#readablestream-set-up
1266 // (except this instead creates a new ReadableStream rather than accepting an
1267 // existing instance)
1268 // _BOUNDARY because `aAlgorithms->StartCallback` (called by
1269 // SetUpReadableStreamDefaultController below) should not be able to run script
1271 MOZ_CAN_RUN_SCRIPT_BOUNDARY already_AddRefed
<ReadableStream
>
1272 ReadableStream::CreateNative(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
1273 UnderlyingSourceAlgorithmsWrapper
& aAlgorithms
,
1274 mozilla::Maybe
<double> aHighWaterMark
,
1275 QueuingStrategySize
* aSizeAlgorithm
,
1277 // an optional number highWaterMark (default 1)
1278 double highWaterMark
= aHighWaterMark
.valueOr(1);
1279 // and if given, highWaterMark must be a non-negative, non-NaN number.
1280 MOZ_ASSERT(IsNonNegativeNumber(highWaterMark
));
1282 // Step 1: Let startAlgorithm be an algorithm that returns undefined.
1283 // Step 2: Let pullAlgorithmWrapper be an algorithm that runs these steps:
1284 // Step 3: Let cancelAlgorithmWrapper be an algorithm that runs these steps:
1285 // (Done by UnderlyingSourceAlgorithmsWrapper)
1287 // Step 4: If sizeAlgorithm was not given, then set it to an algorithm that
1288 // returns 1. (Callers will treat nullptr as such, see
1289 // ReadableStream::Constructor for details)
1291 // Step 5: Perform ! InitializeReadableStream(stream).
1292 RefPtr
<ReadableStream
> stream
=
1293 new ReadableStream(aGlobal
, HoldDropJSObjectsCaller::Implicit
);
1295 // Step 6: Let controller be a new ReadableStreamDefaultController.
1296 auto controller
= MakeRefPtr
<ReadableStreamDefaultController
>(aGlobal
);
1298 // Step 7: Perform ! SetUpReadableStreamDefaultController(stream, controller,
1299 // startAlgorithm, pullAlgorithmWrapper, cancelAlgorithmWrapper,
1300 // highWaterMark, sizeAlgorithm).
1301 SetUpReadableStreamDefaultController(aCx
, stream
, controller
, &aAlgorithms
,
1302 highWaterMark
, aSizeAlgorithm
, aRv
);
1306 return stream
.forget();
1309 // https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support
1310 // _BOUNDARY because `aAlgorithms->StartCallback` (called by
1311 // SetUpReadableByteStreamController below) should not be able to run script in
1313 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void ReadableStream::SetUpByteNative(
1314 JSContext
* aCx
, UnderlyingSourceAlgorithmsWrapper
& aAlgorithms
,
1315 mozilla::Maybe
<double> aHighWaterMark
, ErrorResult
& aRv
) {
1316 // an optional number highWaterMark (default 0)
1317 double highWaterMark
= aHighWaterMark
.valueOr(0);
1318 // and if given, highWaterMark must be a non-negative, non-NaN number.
1319 MOZ_ASSERT(IsNonNegativeNumber(highWaterMark
));
1321 // Step 1: Let startAlgorithm be an algorithm that returns undefined.
1322 // Step 2: Let pullAlgorithmWrapper be an algorithm that runs these steps:
1323 // Step 3: Let cancelAlgorithmWrapper be an algorithm that runs these steps:
1324 // (Done by UnderlyingSourceAlgorithmsWrapper)
1326 // Step 4: Perform ! InitializeReadableStream(stream).
1327 // (Covered by constructor)
1329 // Step 5: Let controller be a new ReadableByteStreamController.
1330 auto controller
= MakeRefPtr
<ReadableByteStreamController
>(GetParentObject());
1332 // Step 6: Perform ! SetUpReadableByteStreamController(stream, controller,
1333 // startAlgorithm, pullAlgorithmWrapper, cancelAlgorithmWrapper,
1334 // highWaterMark, undefined).
1335 SetUpReadableByteStreamController(aCx
, this, controller
, &aAlgorithms
,
1336 highWaterMark
, Nothing(), aRv
);
1339 already_AddRefed
<ReadableStream
> ReadableStream::CreateByteNative(
1340 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
1341 UnderlyingSourceAlgorithmsWrapper
& aAlgorithms
,
1342 mozilla::Maybe
<double> aHighWaterMark
, ErrorResult
& aRv
) {
1343 RefPtr
<ReadableStream
> stream
=
1344 new ReadableStream(aGlobal
, HoldDropJSObjectsCaller::Implicit
);
1345 stream
->SetUpByteNative(aCx
, aAlgorithms
, aHighWaterMark
, aRv
);
1349 return stream
.forget();
1352 // https://streams.spec.whatwg.org/#readablestream-close
1353 void ReadableStream::CloseNative(JSContext
* aCx
, ErrorResult
& aRv
) {
1354 MOZ_ASSERT_IF(mController
->GetAlgorithms(),
1355 mController
->GetAlgorithms()->IsNative());
1356 // Step 1: If stream.[[controller]] implements ReadableByteStreamController,
1357 if (mController
->IsByte()) {
1358 RefPtr
<ReadableByteStreamController
> controller
= mController
->AsByte();
1360 // Step 1.1: Perform !
1361 // ReadableByteStreamControllerClose(stream.[[controller]]).
1362 ReadableByteStreamControllerClose(aCx
, controller
, aRv
);
1367 // Step 1.2: If stream.[[controller]].[[pendingPullIntos]] is not empty,
1368 // perform ! ReadableByteStreamControllerRespond(stream.[[controller]], 0).
1369 if (!controller
->PendingPullIntos().isEmpty()) {
1370 ReadableByteStreamControllerRespond(aCx
, controller
, 0, aRv
);
1375 // Step 2: Otherwise, perform !
1376 // ReadableStreamDefaultControllerClose(stream.[[controller]]).
1377 RefPtr
<ReadableStreamDefaultController
> controller
= mController
->AsDefault();
1378 ReadableStreamDefaultControllerClose(aCx
, controller
, aRv
);
1381 // https://streams.spec.whatwg.org/#readablestream-error
1382 void ReadableStream::ErrorNative(JSContext
* aCx
, JS::Handle
<JS::Value
> aError
,
1384 // Step 1: If stream.[[controller]] implements ReadableByteStreamController,
1385 // then perform ! ReadableByteStreamControllerError(stream.[[controller]], e).
1386 if (mController
->IsByte()) {
1387 ReadableByteStreamControllerError(mController
->AsByte(), aError
, aRv
);
1390 // Step 2: Otherwise, perform !
1391 // ReadableStreamDefaultControllerError(stream.[[controller]], e).
1392 ReadableStreamDefaultControllerError(aCx
, mController
->AsDefault(), aError
,
1396 // https://streams.spec.whatwg.org/#readablestream-current-byob-request-view
1397 static void CurrentBYOBRequestView(JSContext
* aCx
,
1398 ReadableByteStreamController
& aController
,
1399 JS::MutableHandle
<JSObject
*> aRetVal
,
1401 // Step 1. Assert: stream.[[controller]] implements
1402 // ReadableByteStreamController. (implicit)
1404 // Step 2: Let byobRequest be !
1405 // ReadableByteStreamControllerGetBYOBRequest(stream.[[controller]]).
1406 RefPtr
<ReadableStreamBYOBRequest
> byobRequest
=
1407 ReadableByteStreamControllerGetBYOBRequest(aCx
, &aController
, aRv
);
1408 // Step 3: If byobRequest is null, then return null.
1410 aRetVal
.set(nullptr);
1413 // Step 4: Return byobRequest.[[view]].
1414 byobRequest
->GetView(aCx
, aRetVal
);
1417 static bool HasSameBufferView(JSContext
* aCx
, JS::Handle
<JSObject
*> aX
,
1418 JS::Handle
<JSObject
*> aY
, ErrorResult
& aRv
) {
1420 JS::Rooted
<JSObject
*> viewedBufferX(
1421 aCx
, JS_GetArrayBufferViewBuffer(aCx
, aX
, &isShared
));
1422 if (!viewedBufferX
) {
1423 aRv
.StealExceptionFromJSContext(aCx
);
1427 JS::Rooted
<JSObject
*> viewedBufferY(
1428 aCx
, JS_GetArrayBufferViewBuffer(aCx
, aY
, &isShared
));
1429 if (!viewedBufferY
) {
1430 aRv
.StealExceptionFromJSContext(aCx
);
1434 return viewedBufferX
== viewedBufferY
;
1437 // https://streams.spec.whatwg.org/#readablestream-enqueue
1438 void ReadableStream::EnqueueNative(JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
1440 MOZ_ASSERT(mController
->GetAlgorithms()->IsNative());
1442 // Step 1: If stream.[[controller]] implements
1443 // ReadableStreamDefaultController,
1444 if (mController
->IsDefault()) {
1445 // Step 1.1: Perform !
1446 // ReadableStreamDefaultControllerEnqueue(stream.[[controller]], chunk).
1447 RefPtr
<ReadableStreamDefaultController
> controller
=
1448 mController
->AsDefault();
1449 ReadableStreamDefaultControllerEnqueue(aCx
, controller
, aChunk
, aRv
);
1453 // Step 2.1: Assert: stream.[[controller]] implements
1454 // ReadableByteStreamController.
1455 MOZ_ASSERT(mController
->IsByte());
1456 RefPtr
<ReadableByteStreamController
> controller
= mController
->AsByte();
1458 // Step 2.2: Assert: chunk is an ArrayBufferView.
1459 MOZ_ASSERT(aChunk
.isObject() &&
1460 JS_IsArrayBufferViewObject(&aChunk
.toObject()));
1461 JS::Rooted
<JSObject
*> chunk(aCx
, &aChunk
.toObject());
1463 // Step 3: Let byobView be the current BYOB request view for stream.
1464 JS::Rooted
<JSObject
*> byobView(aCx
);
1465 CurrentBYOBRequestView(aCx
, *controller
, &byobView
, aRv
);
1470 // Step 4: If byobView is non-null, and chunk.[[ViewedArrayBuffer]] is
1471 // byobView.[[ViewedArrayBuffer]], then:
1472 if (byobView
&& HasSameBufferView(aCx
, chunk
, byobView
, aRv
)) {
1473 // Step 4.1: Assert: chunk.[[ByteOffset]] is byobView.[[ByteOffset]].
1474 MOZ_ASSERT(JS_GetArrayBufferViewByteOffset(chunk
) ==
1475 JS_GetArrayBufferViewByteOffset(byobView
));
1476 // Step 4.2: Assert: chunk.[[ByteLength]] ≤ byobView.[[ByteLength]].
1477 MOZ_ASSERT(JS_GetArrayBufferViewByteLength(chunk
) <=
1478 JS_GetArrayBufferViewByteLength(byobView
));
1479 // Step 4.3: Perform ?
1480 // ReadableByteStreamControllerRespond(stream.[[controller]],
1481 // chunk.[[ByteLength]]).
1482 ReadableByteStreamControllerRespond(
1483 aCx
, controller
, JS_GetArrayBufferViewByteLength(chunk
), aRv
);
1491 // Step 5: Otherwise, perform ?
1492 // ReadableByteStreamControllerEnqueue(stream.[[controller]], chunk).
1493 ReadableByteStreamControllerEnqueue(aCx
, controller
, chunk
, aRv
);
1496 // https://streams.spec.whatwg.org/#readablestream-current-byob-request-view
1497 void ReadableStream::GetCurrentBYOBRequestView(
1498 JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aView
, ErrorResult
& aRv
) {
1501 // Step 1: Assert: stream.[[controller]] implements
1502 // ReadableByteStreamController.
1503 MOZ_ASSERT(mController
->IsByte());
1505 // Step 2: Let byobRequest be !
1506 // ReadableByteStreamControllerGetBYOBRequest(stream.[[controller]]).
1507 RefPtr
<ReadableStreamBYOBRequest
> byobRequest
=
1508 mController
->AsByte()->GetByobRequest(aCx
, aRv
);
1510 // Step 3: If byobRequest is null, then return null.
1511 if (!byobRequest
|| aRv
.Failed()) {
1515 // Step 4: Return byobRequest.[[view]].
1516 byobRequest
->GetView(aCx
, aView
);
1519 // https://streams.spec.whatwg.org/#readablestream-get-a-reader
1520 // To get a reader for a ReadableStream stream, return ?
1521 // AcquireReadableStreamDefaultReader(stream). The result will be a
1522 // ReadableStreamDefaultReader.
1523 already_AddRefed
<mozilla::dom::ReadableStreamDefaultReader
>
1524 ReadableStream::GetReader(ErrorResult
& aRv
) {
1525 return AcquireReadableStreamDefaultReader(this, aRv
);
1528 } // namespace mozilla::dom