Bug 1946787 - Avoid creating redundant GradientCache::OnMaxEntriesBreached tasks...
[gecko.git] / dom / streams / ReadableStream.cpp
blob5331573166a60a8e4cda3b111705fc17b32cb236
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"
13 #include "TeeState.h"
14 #include "js/Array.h"
15 #include "js/Exception.h"
16 #include "js/PropertyAndElement.h"
17 #include "js/TypeDecls.h"
18 #include "js/Value.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"
44 #include "nsCOMPtr.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>>&
54 aReader,
55 const char* aName, uint32_t aFlags = 0) {
56 if (aReader.is<RefPtr<mozilla::dom::ReadableStreamDefaultReader>>()) {
57 ImplCycleCollectionTraverse(
58 aCallback,
59 aReader.as<RefPtr<mozilla::dom::ReadableStreamDefaultReader>>(), aName,
60 aFlags);
64 inline void ImplCycleCollectionUnlink(
65 mozilla::Variant<mozilla::Nothing,
66 RefPtr<mozilla::dom::ReadableStreamDefaultReader>>&
67 aReader) {
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)
84 NS_INTERFACE_MAP_END
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())),
97 mReader(nullptr),
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) {
120 mReader = 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.
131 if (!reader) {
132 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.
146 if (!reader) {
147 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
158 /* static */
159 already_AddRefed<ReadableStream> ReadableStream::Constructor(
160 const GlobalObject& aGlobal,
161 const Optional<JS::Handle<JSObject*>>& aUnderlyingSource,
162 const QueuingStrategy& aStrategy, ErrorResult& aRv) {
163 // Step 1.
164 JS::Rooted<JSObject*> underlyingSourceObj(
165 aGlobal.Context(),
166 aUnderlyingSource.WasPassed() ? aUnderlyingSource.Value() : nullptr);
168 // Step 2.
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());
178 return nullptr;
182 // Step 3.
183 RefPtr<ReadableStream> readableStream =
184 new ReadableStream(aGlobal, HoldDropJSObjectsCaller::Implicit);
186 // Step 4.
187 if (underlyingSourceDict.mType.WasPassed()) {
188 // Implicit assertion on above check.
189 MOZ_ASSERT(underlyingSourceDict.mType.Value() == ReadableStreamType::Bytes);
191 // Step 4.1
192 if (aStrategy.mSize.WasPassed()) {
193 aRv.ThrowRangeError("Implementation preserved member 'size'");
194 return nullptr;
197 // Step 4.2
198 double highWaterMark = ExtractHighWaterMark(aStrategy, 0, aRv);
199 if (aRv.Failed()) {
200 return nullptr;
203 // Step 4.3
204 SetUpReadableByteStreamControllerFromUnderlyingSource(
205 aGlobal.Context(), readableStream, underlyingSourceObj,
206 underlyingSourceDict, highWaterMark, aRv);
207 if (aRv.Failed()) {
208 return nullptr;
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;
227 // Step 5.3
228 double highWaterMark = ExtractHighWaterMark(aStrategy, 1, aRv);
229 if (aRv.Failed()) {
230 return nullptr;
233 // Step 5.4.
234 SetupReadableStreamDefaultControllerFromUnderlyingSource(
235 aGlobal.Context(), readableStream, underlyingSourceObj,
236 underlyingSourceDict, highWaterMark, sizeAlgorithm, aRv);
237 if (aRv.Failed()) {
238 return nullptr;
241 return readableStream.forget();
244 // https://streams.spec.whatwg.org/#readable-stream-from-iterable
245 class ReadableStreamFromAlgorithms final
246 : public UnderlyingSourceAlgorithmsWrapper {
247 public:
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);
276 return nullptr;
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");
294 return nullptr;
297 JS::Rooted<JSObject*> iterResult(aCx, &aIterResult.toObject());
299 // Step 4.2. Let done be ? IteratorComplete(iterResult).
300 bool done = false;
301 if (!JS::IteratorComplete(aCx, iterResult, &done)) {
302 aRv.StealExceptionFromJSContext(aCx);
303 return nullptr;
306 // Step 4.3. If done is true:
307 if (done) {
308 // Step 4.3.1. Perform !
309 // ReadableStreamDefaultControllerClose(stream.[[controller]]).
310 ReadableStreamDefaultControllerClose(aCx, aController, aRv);
311 } else {
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);
317 return nullptr;
320 // Step 4.4.2. Perform !
321 // ReadableStreamDefaultControllerEnqueue(stream.[[controller]],
322 // value).
323 ReadableStreamDefaultControllerEnqueue(aCx, aController, value,
324 aRv);
327 return nullptr;
329 RefPtr(aController.AsDefault()));
330 if (result.isErr()) {
331 aRv.Throw(result.unwrapErr());
332 return nullptr;
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);
350 return nullptr;
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);
359 return nullptr;
362 // Step 4. If returnMethod.[[Value]] is undefined, return a promise resolved
363 // with undefined.
364 if (returnMethod.isUndefined()) {
365 return Promise::CreateResolvedWithUndefined(mGlobal, aRv);
368 // Step 5. Let returnResult be Call(returnMethod.[[Value]], iterator, «
369 // reason »).
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);
374 return nullptr;
377 JS::Rooted<JS::Value> returnResult(aCx);
378 if (!JS::Call(aCx, iterator, returnMethod, JS::HandleValueArray(reason),
379 &returnResult)) {
380 // Step 6. If returnResult is an abrupt completion, return a promise
381 // rejected with returnResult.[[Value]].
382 aRv.StealExceptionFromJSContext(aCx);
383 return nullptr;
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");
399 return nullptr;
402 // Step 8.2. Return undefined.
403 return nullptr;
405 if (result.isErr()) {
406 aRv.Throw(result.unwrapErr());
407 return nullptr;
409 return result.unwrap().forget();
412 protected:
413 ~ReadableStreamFromAlgorithms() override { mozilla::DropJSObjects(this); };
415 private:
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,
435 ErrorResult& aRv) {
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);
444 return nullptr;
447 // Steps 3-5. are in ReadableStreamFromAlgorithms.
448 auto algorithms =
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);
458 /* static */
459 already_AddRefed<ReadableStream> ReadableStream::From(
460 const GlobalObject& aGlobal, JS::Handle<JS::Value> aAsyncIterable,
461 ErrorResult& aRv) {
462 // Step 1. Return ? ReadableStreamFromIterable(asyncIterable).
463 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
464 return ReadableStreamFromIterable(aGlobal.Context(), global, aAsyncIterable,
465 aRv);
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 {
471 // Step 1 + 2.
472 return mReader;
475 namespace streams_abstract {
476 // https://streams.spec.whatwg.org/#initialize-readable-stream
477 static void InitializeReadableStream(ReadableStream* aStream) {
478 // Step 1.
479 aStream->SetState(ReadableStream::ReaderState::Readable);
481 // Step 2.
482 aStream->SetReader(nullptr);
483 aStream->SetStoredError(JS::UndefinedHandleValue);
485 // Step 3.
486 aStream->SetDisturbed(false);
488 } // namespace streams_abstract
490 // https://streams.spec.whatwg.org/#create-readable-stream
491 MOZ_CAN_RUN_SCRIPT
492 already_AddRefed<ReadableStream> ReadableStream::CreateAbstract(
493 JSContext* aCx, nsIGlobalObject* aGlobal,
494 UnderlyingSourceAlgorithmsBase* aAlgorithms,
495 mozilla::Maybe<double> aHighWaterMark, QueuingStrategySize* aSizeAlgorithm,
496 ErrorResult& aRv) {
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.
502 // Step 3.
503 MOZ_ASSERT(IsNonNegativeNumber(highWaterMark));
504 // Step 4.
505 RefPtr<ReadableStream> stream =
506 new ReadableStream(aGlobal, HoldDropJSObjectsCaller::Implicit);
508 // Step 5.
509 InitializeReadableStream(stream);
511 // Step 6.
512 RefPtr<ReadableStreamDefaultController> controller =
513 new ReadableStreamDefaultController(aGlobal);
515 // Step 7.
516 SetUpReadableStreamDefaultController(aCx, stream, controller, aAlgorithms,
517 highWaterMark, aSizeAlgorithm, aRv);
519 // Step 8.
520 return stream.forget();
523 namespace streams_abstract {
524 // https://streams.spec.whatwg.org/#readable-stream-close
525 void ReadableStreamClose(JSContext* aCx, ReadableStream* aStream,
526 ErrorResult& aRv) {
527 // Step 1.
528 MOZ_ASSERT(aStream->State() == ReadableStream::ReaderState::Readable);
530 // Step 2.
531 aStream->SetState(ReadableStream::ReaderState::Closed);
533 // Step 3.
534 ReadableStreamGenericReader* reader = aStream->GetReader();
536 // Step 4.
537 if (!reader) {
538 return;
541 // Step 5.
542 reader->ClosedPromise()->MaybeResolveWithUndefined();
544 // Step 6.
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);
564 if (aRv.Failed()) {
565 return;
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,
575 ErrorResult& aRv) {
576 // Step 1.
577 aStream->SetDisturbed(true);
579 // Step 2.
580 if (aStream->State() == ReadableStream::ReaderState::Closed) {
581 RefPtr<Promise> promise =
582 Promise::CreateInfallible(aStream->GetParentObject());
583 promise->MaybeResolveWithUndefined();
584 return promise.forget();
587 // Step 3.
588 if (aStream->State() == ReadableStream::ReaderState::Errored) {
589 JS::Rooted<JS::Value> storedError(aCx, aStream->StoredError());
590 return Promise::CreateRejected(aStream->GetParentObject(), storedError,
591 aRv);
594 // Step 4.
595 ReadableStreamClose(aCx, aStream, aRv);
596 if (aRv.Failed()) {
597 return nullptr;
600 // Step 5.
601 ReadableStreamGenericReader* reader = aStream->GetReader();
603 // Step 6.
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);
618 if (aRv.Failed()) {
619 return nullptr;
624 // Step 7.
625 RefPtr<ReadableStreamController> controller(aStream->Controller());
626 RefPtr<Promise> sourceCancelPromise =
627 controller->CancelSteps(aCx, aError, aRv);
628 if (aRv.Failed()) {
629 return nullptr;
632 // Step 8.
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();
645 promise);
647 if (returnResult.isErr()) {
648 aRv.Throw(returnResult.unwrapErr());
649 return nullptr;
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,
660 ErrorResult& aRv) {
661 // Step 1. If ! IsReadableStreamLocked(this) is true,
662 // return a promise rejected with a TypeError exception.
663 if (Locked()) {
664 aRv.ThrowTypeError("Cannot cancel a stream locked by a reader.");
665 return nullptr;
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) {
677 // Step 1.
678 RefPtr<ReadableStreamDefaultReader> reader =
679 new ReadableStreamDefaultReader(aStream->GetParentObject());
681 // Step 2.
682 SetUpReadableStreamDefaultReader(reader, aStream, aRv);
683 if (aRv.Failed()) {
684 return nullptr;
687 // Step 3.
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,
695 ErrorResult& aRv) {
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);
701 if (aRv.Failed()) {
702 return;
704 resultReader.SetAsReadableStreamDefaultReader() = defaultReader;
705 return;
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);
714 if (aRv.Failed()) {
715 return;
717 resultReader.SetAsReadableStreamBYOBReader() = byobReader;
720 namespace streams_abstract {
721 // https://streams.spec.whatwg.org/#is-readable-stream-locked
722 bool IsReadableStreamLocked(ReadableStream* aStream) {
723 // Step 1 + 2.
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,
731 ErrorResult& aRv) {
732 // Step 1: If ! IsReadableStreamLocked(this) is true, throw a TypeError
733 // exception.
734 if (IsReadableStreamLocked(this)) {
735 aRv.ThrowTypeError("Cannot pipe from a locked stream.");
736 return nullptr;
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.");
743 return nullptr;
746 // Step 3: Let signal be options["signal"] if it exists, or undefined
747 // otherwise.
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"],
753 // signal).
754 RefPtr<WritableStream> writable = aTransform.mWritable;
755 RefPtr<Promise> promise = ReadableStreamPipeTo(
756 this, writable, aOptions.mPreventClose, aOptions.mPreventAbort,
757 aOptions.mPreventCancel, signal, aRv);
758 if (aRv.Failed()) {
759 return nullptr;
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) {
773 // Step 1.
774 MOZ_ASSERT(ReadableStreamHasDefaultReader(aStream));
776 // Step 2.
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) {
783 // Step 1.
784 MOZ_ASSERT(aStream->State() == ReadableStream::ReaderState::Readable);
786 // Step 2.
787 aStream->SetState(ReadableStream::ReaderState::Errored);
789 // Step 3.
790 aStream->SetStoredError(aValue);
792 // Step 4.
793 ReadableStreamGenericReader* reader = aStream->GetReader();
795 // Step 5.
796 if (!reader) {
797 return;
800 // Step 6.
801 reader->ClosedPromise()->MaybeReject(aValue);
803 // Step 7.
804 reader->ClosedPromise()->SetSettledPromiseIsHandled();
806 // Step 8.
807 if (reader->IsDefault()) {
808 // Step 8.1. Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader,
809 // e).
810 RefPtr<ReadableStreamDefaultReader> defaultReader = reader->AsDefault();
811 ReadableStreamDefaultReaderErrorReadRequests(aCx, defaultReader, aValue,
812 aRv);
813 if (aRv.Failed()) {
814 return;
816 } else {
817 // Step 9. Otherwise,
818 // Step 9.1. Assert: reader implements ReadableStreamBYOBReader.
819 MOZ_ASSERT(reader->IsBYOB());
821 // Step 9.2. Perform ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader,
822 // e).
823 RefPtr<ReadableStreamBYOBReader> byobReader = reader->AsBYOB();
824 ReadableStreamBYOBReaderErrorReadIntoRequests(aCx, byobReader, aValue, aRv);
825 if (aRv.Failed()) {
826 return;
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,
834 ErrorResult& aRv) {
835 // Step 1.
836 MOZ_ASSERT(ReadableStreamHasDefaultReader(aStream));
838 // Step 2.
839 ReadableStreamDefaultReader* reader = aStream->GetDefaultReader();
841 // Step 3.
842 MOZ_ASSERT(!reader->ReadRequests().isEmpty());
844 // Step 4+5.
845 RefPtr<ReadRequest> readRequest = reader->ReadRequests().popFirst();
847 // Step 6.
848 if (aDone) {
849 readRequest->CloseSteps(aCx, aRv);
850 if (aRv.Failed()) {
851 return;
855 // Step 7.
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) {
862 // Step 1.
863 MOZ_ASSERT(aStream->GetReader()->IsDefault());
864 // Step 2.
865 MOZ_ASSERT(aStream->State() == ReadableStream::ReaderState::Readable);
866 // Step 3.
867 aStream->GetDefaultReader()->ReadRequests().insertBack(aReadRequest);
870 } // namespace streams_abstract
872 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee
873 // Step 14, 15
874 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise>
875 ReadableStreamDefaultTeeSourceAlgorithms::CancelCallback(
876 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
877 ErrorResult& aRv) {
878 // Step 1.
879 mTeeState->SetCanceled(mBranch, true);
881 // Step 2.
882 mTeeState->SetReason(mBranch, aReason.Value());
884 // Step 3.
886 if (mTeeState->Canceled(OtherTeeBranch(mBranch))) {
887 // Step 3.1
889 JS::Rooted<JSObject*> compositeReason(aCx, JS::NewArrayObject(aCx, 2));
890 if (!compositeReason) {
891 aRv.StealExceptionFromJSContext(aCx);
892 return nullptr;
895 JS::Rooted<JS::Value> reason1(aCx, mTeeState->Reason1());
896 if (!JS_SetElement(aCx, compositeReason, 0, reason1)) {
897 aRv.StealExceptionFromJSContext(aCx);
898 return nullptr;
901 JS::Rooted<JS::Value> reason2(aCx, mTeeState->Reason2());
902 if (!JS_SetElement(aCx, compositeReason, 1, reason2)) {
903 aRv.StealExceptionFromJSContext(aCx);
904 return nullptr;
907 // Step 3.2
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);
913 if (aRv.Failed()) {
914 return nullptr;
917 // Step 3.3
918 mTeeState->CancelPromise()->MaybeResolve(cancelResult);
921 // Step 4.
922 return do_AddRef(mTeeState->CancelPromise());
925 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee
926 MOZ_CAN_RUN_SCRIPT
927 static void ReadableStreamDefaultTee(JSContext* aCx, ReadableStream* aStream,
928 bool aCloneForBranch2,
929 nsTArray<RefPtr<ReadableStream>>& aResult,
930 ErrorResult& aRv) {
931 // Step 1. Implicit.
932 // Step 2. Implicit.
934 // Steps 3-12 are contained in the construction of Tee State.
935 RefPtr<TeeState> teeState = TeeState::Create(aStream, aCloneForBranch2, aRv);
936 if (aRv.Failed()) {
937 return;
940 // Step 13 - 16
941 auto branch1Algorithms = MakeRefPtr<ReadableStreamDefaultTeeSourceAlgorithms>(
942 teeState, TeeBranch::Branch1);
943 auto branch2Algorithms = MakeRefPtr<ReadableStreamDefaultTeeSourceAlgorithms>(
944 teeState, TeeBranch::Branch2);
946 // Step 17.
947 nsCOMPtr<nsIGlobalObject> global(
948 do_AddRef(teeState->GetStream()->GetParentObject()));
949 teeState->SetBranch1(ReadableStream::CreateAbstract(
950 aCx, global, branch1Algorithms, mozilla::Nothing(), nullptr, aRv));
951 if (aRv.Failed()) {
952 return;
955 // Step 18.
956 teeState->SetBranch2(ReadableStream::CreateAbstract(
957 aCx, global, branch2Algorithms, mozilla::Nothing(), nullptr, aRv));
958 if (aRv.Failed()) {
959 return;
962 // Step 19.
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) {
968 // Step 19.1.
969 ReadableStreamDefaultControllerError(
970 aCx, aTeeState->Branch1()->DefaultController(), aReason, aRv);
971 if (aRv.Failed()) {
972 return;
975 // Step 19.2
976 ReadableStreamDefaultControllerError(
977 aCx, aTeeState->Branch2()->DefaultController(), aReason, aRv);
978 if (aRv.Failed()) {
979 return;
982 // Step 19.3
983 if (!aTeeState->Canceled1() || !aTeeState->Canceled2()) {
984 aTeeState->CancelPromise()->MaybeResolveWithUndefined();
987 RefPtr(teeState));
989 // Step 20.
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,
997 ErrorResult& aRv) {
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.");
1002 return nullptr;
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.");
1009 return nullptr;
1012 // Step 3. Let signal be options["signal"] if it exists, or undefined
1013 // otherwise.
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"],
1019 // signal).
1020 return ReadableStreamPipeTo(this, &aDestination, aOptions.mPreventClose,
1021 aOptions.mPreventAbort, aOptions.mPreventCancel,
1022 signal, aRv);
1025 // https://streams.spec.whatwg.org/#readable-stream-tee
1026 MOZ_CAN_RUN_SCRIPT
1027 static void ReadableStreamTee(JSContext* aCx, ReadableStream* aStream,
1028 bool aCloneForBranch2,
1029 nsTArray<RefPtr<ReadableStream>>& aResult,
1030 ErrorResult& aRv) {
1031 // Step 1. Implicit.
1032 // Step 2. Implicit.
1033 // Step 3.
1034 if (aStream->Controller()->IsByte()) {
1035 ReadableByteStreamTee(aCx, aStream, aResult, aRv);
1036 return;
1038 // Step 4.
1039 ReadableStreamDefaultTee(aCx, aStream, aCloneForBranch2, aResult, aRv);
1042 void ReadableStream::Tee(JSContext* aCx,
1043 nsTArray<RefPtr<ReadableStream>>& aResult,
1044 ErrorResult& aRv) {
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);
1065 if (aRv.Failed()) {
1066 return;
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
1078 // Step 4.
1079 struct IteratorReadRequest : public ReadRequest {
1080 public:
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);
1098 // close steps
1099 void CloseSteps(JSContext* aCx, ErrorResult& aRv) override {
1100 // Step 1. Perform ! ReadableStreamDefaultReaderRelease(reader).
1101 ReadableStreamDefaultReaderRelease(aCx, mReader, aRv);
1102 if (aRv.Failed()) {
1103 mPromise->MaybeRejectWithUndefined();
1104 return;
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);
1116 if (aRv.Failed()) {
1117 mPromise->MaybeRejectWithUndefined();
1118 return;
1121 // Step 2. Reject promise with e.
1122 mPromise->MaybeReject(aError);
1125 protected:
1126 virtual ~IteratorReadRequest() = default;
1129 NS_IMPL_CYCLE_COLLECTION_INHERITED(IteratorReadRequest, ReadRequest, mPromise,
1130 mReader)
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).
1154 AutoJSAPI jsapi;
1155 if (!jsapi.Init(mGlobal)) {
1156 aRv.ThrowUnknownError("Internal error");
1157 return nullptr;
1160 ReadableStreamDefaultReaderRead(jsapi.cx(), reader, request, aRv);
1161 if (aRv.Failed()) {
1162 return nullptr;
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,
1172 ErrorResult& aRv) {
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
1181 // this is called.
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())) {
1190 return nullptr;
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);
1198 return nullptr;
1201 // Step 4.2. Perform ! ReadableStreamDefaultReaderRelease(reader).
1202 ReadableStreamDefaultReaderRelease(aCx, reader, aRv);
1203 if (NS_WARN_IF(aRv.Failed())) {
1204 return nullptr;
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())) {
1214 return nullptr;
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(
1234 aReadIntoRequest);
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);
1257 if (aRv.Failed()) {
1258 return nullptr;
1261 // Return stream.
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
1270 // in this case.
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,
1276 ErrorResult& aRv) {
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);
1303 if (aRv.Failed()) {
1304 return nullptr;
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
1312 // this case.
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);
1346 if (aRv.Failed()) {
1347 return nullptr;
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);
1363 if (aRv.Failed()) {
1364 return;
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);
1372 return;
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,
1383 ErrorResult& aRv) {
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);
1388 return;
1390 // Step 2: Otherwise, perform !
1391 // ReadableStreamDefaultControllerError(stream.[[controller]], e).
1392 ReadableStreamDefaultControllerError(aCx, mController->AsDefault(), aError,
1393 aRv);
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,
1400 ErrorResult& aRv) {
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.
1409 if (!byobRequest) {
1410 aRetVal.set(nullptr);
1411 return;
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) {
1419 bool isShared;
1420 JS::Rooted<JSObject*> viewedBufferX(
1421 aCx, JS_GetArrayBufferViewBuffer(aCx, aX, &isShared));
1422 if (!viewedBufferX) {
1423 aRv.StealExceptionFromJSContext(aCx);
1424 return false;
1427 JS::Rooted<JSObject*> viewedBufferY(
1428 aCx, JS_GetArrayBufferViewBuffer(aCx, aY, &isShared));
1429 if (!viewedBufferY) {
1430 aRv.StealExceptionFromJSContext(aCx);
1431 return false;
1434 return viewedBufferX == viewedBufferY;
1437 // https://streams.spec.whatwg.org/#readablestream-enqueue
1438 void ReadableStream::EnqueueNative(JSContext* aCx, JS::Handle<JS::Value> aChunk,
1439 ErrorResult& aRv) {
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);
1450 return;
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);
1466 if (aRv.Failed()) {
1467 return;
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);
1484 return;
1487 if (aRv.Failed()) {
1488 return;
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) {
1499 aView.set(nullptr);
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()) {
1512 return;
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