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 "ReadableStreamTee.h"
9 #include "ReadIntoRequest.h"
11 #include "js/Exception.h"
12 #include "js/TypeDecls.h"
13 #include "js/experimental/TypedData.h"
14 #include "mozilla/dom/ByteStreamHelpers.h"
15 #include "mozilla/dom/Promise-inl.h"
16 #include "mozilla/dom/ReadableStream.h"
17 #include "mozilla/dom/ReadableStreamBYOBReader.h"
18 #include "mozilla/dom/ReadableStreamDefaultController.h"
19 #include "mozilla/dom/ReadableStreamGenericReader.h"
20 #include "mozilla/dom/ReadableStreamDefaultReader.h"
21 #include "mozilla/dom/ReadableByteStreamController.h"
22 #include "mozilla/dom/UnderlyingSourceBinding.h"
23 #include "mozilla/dom/UnderlyingSourceCallbackHelpers.h"
24 #include "nsCycleCollectionParticipant.h"
25 #include "mozilla/CycleCollectedJSContext.h"
27 namespace mozilla::dom
{
29 using namespace streams_abstract
;
31 NS_IMPL_CYCLE_COLLECTION_INHERITED(ReadableStreamDefaultTeeSourceAlgorithms
,
32 UnderlyingSourceAlgorithmsBase
, mTeeState
)
33 NS_IMPL_ADDREF_INHERITED(ReadableStreamDefaultTeeSourceAlgorithms
,
34 UnderlyingSourceAlgorithmsBase
)
35 NS_IMPL_RELEASE_INHERITED(ReadableStreamDefaultTeeSourceAlgorithms
,
36 UnderlyingSourceAlgorithmsBase
)
37 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
38 ReadableStreamDefaultTeeSourceAlgorithms
)
39 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase
)
41 already_AddRefed
<Promise
>
42 ReadableStreamDefaultTeeSourceAlgorithms::PullCallback(
43 JSContext
* aCx
, ReadableStreamController
& aController
, ErrorResult
& aRv
) {
44 nsCOMPtr
<nsIGlobalObject
> global
= aController
.GetParentObject();
45 mTeeState
->PullCallback(aCx
, global
, aRv
);
47 return Promise::CreateResolvedWithUndefined(global
, aRv
);
52 NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableStreamDefaultTeeReadRequest
)
54 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(
55 ReadableStreamDefaultTeeReadRequest
, ReadRequest
)
56 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTeeState
)
57 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
59 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
60 ReadableStreamDefaultTeeReadRequest
, ReadRequest
)
61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTeeState
)
62 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
64 NS_IMPL_ADDREF_INHERITED(ReadableStreamDefaultTeeReadRequest
, ReadRequest
)
65 NS_IMPL_RELEASE_INHERITED(ReadableStreamDefaultTeeReadRequest
, ReadRequest
)
67 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamDefaultTeeReadRequest
)
68 NS_INTERFACE_MAP_END_INHERITING(ReadRequest
)
70 void ReadableStreamDefaultTeeReadRequest::ChunkSteps(
71 JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
, ErrorResult
& aRv
) {
73 class ReadableStreamDefaultTeeReadRequestChunkSteps
74 : public MicroTaskRunnable
{
75 // Virtually const, but is cycle collected
76 MOZ_KNOWN_LIVE RefPtr
<TeeState
> mTeeState
;
77 JS::PersistentRooted
<JS::Value
> mChunk
;
80 ReadableStreamDefaultTeeReadRequestChunkSteps(JSContext
* aCx
,
82 JS::Handle
<JS::Value
> aChunk
)
83 : mTeeState(aTeeState
), mChunk(aCx
, aChunk
) {}
86 void Run(AutoSlowOperation
& aAso
) override
{
88 if (NS_WARN_IF(!jsapi
.Init(mTeeState
->GetStream()->GetParentObject()))) {
91 JSContext
* cx
= jsapi
.cx();
92 // Step Numbering below is relative to Chunk steps Microtask:
95 mTeeState
->SetReadAgain(false);
98 JS::Rooted
<JS::Value
> chunk1(cx
, mChunk
);
99 JS::Rooted
<JS::Value
> chunk2(cx
, mChunk
);
101 // Step 3. Skipped until we implement cloneForBranch2 path.
102 MOZ_RELEASE_ASSERT(!mTeeState
->CloneForBranch2());
105 if (!mTeeState
->Canceled1()) {
106 IgnoredErrorResult rv
;
107 // Since we controlled the creation of the two stream branches, we know
108 // they both have default controllers.
109 RefPtr
<ReadableStreamDefaultController
> controller(
110 mTeeState
->Branch1()->DefaultController());
111 ReadableStreamDefaultControllerEnqueue(cx
, controller
, chunk1
, rv
);
112 (void)NS_WARN_IF(rv
.Failed());
116 if (!mTeeState
->Canceled2()) {
117 IgnoredErrorResult rv
;
118 RefPtr
<ReadableStreamDefaultController
> controller(
119 mTeeState
->Branch2()->DefaultController());
120 ReadableStreamDefaultControllerEnqueue(cx
, controller
, chunk2
, rv
);
121 (void)NS_WARN_IF(rv
.Failed());
125 mTeeState
->SetReading(false);
127 // Step 7. If |readAgain| is true, perform |pullAlgorithm|.
128 if (mTeeState
->ReadAgain()) {
129 IgnoredErrorResult rv
;
130 nsCOMPtr
<nsIGlobalObject
> global(
131 mTeeState
->GetStream()->GetParentObject());
132 mTeeState
->PullCallback(cx
, global
, rv
);
133 (void)NS_WARN_IF(rv
.Failed());
137 bool Suppressed() override
{
138 nsIGlobalObject
* global
= mTeeState
->GetStream()->GetParentObject();
139 return global
&& global
->IsInSyncOperation();
143 RefPtr
<ReadableStreamDefaultTeeReadRequestChunkSteps
> task
=
144 MakeRefPtr
<ReadableStreamDefaultTeeReadRequestChunkSteps
>(aCx
, mTeeState
,
146 CycleCollectedJSContext::Get()->DispatchToMicroTask(task
.forget());
149 void ReadableStreamDefaultTeeReadRequest::CloseSteps(JSContext
* aCx
,
151 // Step Numbering below is relative to 'close steps' of
152 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee
155 mTeeState
->SetReading(false);
158 if (!mTeeState
->Canceled1()) {
159 RefPtr
<ReadableStreamDefaultController
> controller(
160 mTeeState
->Branch1()->DefaultController());
161 ReadableStreamDefaultControllerClose(aCx
, controller
, aRv
);
168 if (!mTeeState
->Canceled2()) {
169 RefPtr
<ReadableStreamDefaultController
> controller(
170 mTeeState
->Branch2()->DefaultController());
171 ReadableStreamDefaultControllerClose(aCx
, controller
, aRv
);
178 if (!mTeeState
->Canceled1() || !mTeeState
->Canceled2()) {
179 mTeeState
->CancelPromise()->MaybeResolveWithUndefined();
183 void ReadableStreamDefaultTeeReadRequest::ErrorSteps(
184 JSContext
* aCx
, JS::Handle
<JS::Value
> aError
, ErrorResult
& aRv
) {
185 mTeeState
->SetReading(false);
188 MOZ_CAN_RUN_SCRIPT
void PullWithDefaultReader(JSContext
* aCx
,
191 MOZ_CAN_RUN_SCRIPT
void PullWithBYOBReader(JSContext
* aCx
, TeeState
* aTeeState
,
192 JS::Handle
<JSObject
*> aView
,
193 TeeBranch aForBranch
,
196 // Algorithm described in
197 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee, Steps
198 // 17 and Steps 18, genericized across branch numbers:
200 // Note: As specified this algorithm always returns a promise resolved with
201 // undefined, however as some places immediately discard said promise, we
202 // provide this version which doesn't return a promise.
204 // NativeByteStreamTeePullAlgorithm, which implements
205 // UnderlyingSourcePullCallbackHelper is the version which provies the return
207 MOZ_CAN_RUN_SCRIPT
void ByteStreamTeePullAlgorithm(JSContext
* aCx
,
208 TeeBranch aForBranch
,
211 // Step {17,18}.1: If reading is true,
212 if (aTeeState
->Reading()) {
213 // Step {17,18}.1.1: Set readAgainForBranch{1,2} to true.
214 aTeeState
->SetReadAgainForBranch(aForBranch
, true);
216 // Step {17,18}.1.1: Return a promise resolved with undefined.
220 // Step {17,18}.2: Set reading to true.
221 aTeeState
->SetReading(true);
223 // Step {17,18}.3: Let byobRequest be
224 // !ReadableByteStreamControllerGetBYOBRequest(branch{1,2}.[[controller]]).
225 RefPtr
<ReadableStreamBYOBRequest
> byobRequest
=
226 ReadableByteStreamControllerGetBYOBRequest(
227 aCx
, aTeeState
->Branch(aForBranch
)->Controller()->AsByte(), aRv
);
232 // Step {17,18}.4: If byobRequest is null, perform pullWithDefaultReader.
234 PullWithDefaultReader(aCx
, aTeeState
, aRv
);
236 // Step {17,18}.5: Otherwise, perform pullWithBYOBReader, given
237 // byobRequest.[[view]] and {false, true}.
238 JS::Rooted
<JSObject
*> view(aCx
, byobRequest
->View());
239 PullWithBYOBReader(aCx
, aTeeState
, view
, aForBranch
, aRv
);
242 // Step {17,18}.6: Return a promise resolved with undefined.
245 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
246 class ByteStreamTeeSourceAlgorithms final
247 : public UnderlyingSourceAlgorithmsBase
{
249 NS_DECL_ISUPPORTS_INHERITED
250 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ByteStreamTeeSourceAlgorithms
,
251 UnderlyingSourceAlgorithmsBase
)
253 ByteStreamTeeSourceAlgorithms(TeeState
* aTeeState
, TeeBranch aBranch
)
254 : mTeeState(aTeeState
), mBranch(aBranch
) {}
256 MOZ_CAN_RUN_SCRIPT
void StartCallback(JSContext
* aCx
,
257 ReadableStreamController
& aController
,
258 JS::MutableHandle
<JS::Value
> aRetVal
,
259 ErrorResult
& aRv
) override
{
260 // Step 21: Let startAlgorithm be an algorithm that returns undefined.
261 aRetVal
.setUndefined();
265 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> PullCallback(
266 JSContext
* aCx
, ReadableStreamController
& aController
,
267 ErrorResult
& aRv
) override
{
269 ByteStreamTeePullAlgorithm(aCx
, mBranch
, MOZ_KnownLive(mTeeState
), aRv
);
271 // Step 6: Return a promise resolved with undefined.
272 return Promise::CreateResolvedWithUndefined(
273 mTeeState
->GetStream()->GetParentObject(), aRv
);
277 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CancelCallback(
278 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
279 ErrorResult
& aRv
) override
{
281 mTeeState
->SetCanceled(mBranch
, true);
284 mTeeState
->SetReason(mBranch
, aReason
.Value());
287 if (mTeeState
->Canceled(otherStream())) {
289 JS::Rooted
<JSObject
*> compositeReason(aCx
, JS::NewArrayObject(aCx
, 2));
290 if (!compositeReason
) {
291 aRv
.StealExceptionFromJSContext(aCx
);
295 JS::Rooted
<JS::Value
> reason1(aCx
, mTeeState
->Reason1());
296 if (!JS_SetElement(aCx
, compositeReason
, 0, reason1
)) {
297 aRv
.StealExceptionFromJSContext(aCx
);
301 JS::Rooted
<JS::Value
> reason2(aCx
, mTeeState
->Reason2());
302 if (!JS_SetElement(aCx
, compositeReason
, 1, reason2
)) {
303 aRv
.StealExceptionFromJSContext(aCx
);
308 JS::Rooted
<JS::Value
> compositeReasonValue(
309 aCx
, JS::ObjectValue(*compositeReason
));
310 RefPtr
<ReadableStream
> stream(mTeeState
->GetStream());
311 RefPtr
<Promise
> cancelResult
=
312 ReadableStreamCancel(aCx
, stream
, compositeReasonValue
, aRv
);
318 mTeeState
->CancelPromise()->MaybeResolve(cancelResult
);
322 return do_AddRef(mTeeState
->CancelPromise());
326 ~ByteStreamTeeSourceAlgorithms() override
= default;
329 TeeBranch
otherStream() { return OtherTeeBranch(mBranch
); }
331 RefPtr
<TeeState
> mTeeState
;
335 NS_IMPL_CYCLE_COLLECTION_INHERITED(ByteStreamTeeSourceAlgorithms
,
336 UnderlyingSourceAlgorithmsBase
, mTeeState
)
337 NS_IMPL_ADDREF_INHERITED(ByteStreamTeeSourceAlgorithms
,
338 UnderlyingSourceAlgorithmsBase
)
339 NS_IMPL_RELEASE_INHERITED(ByteStreamTeeSourceAlgorithms
,
340 UnderlyingSourceAlgorithmsBase
)
341 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ByteStreamTeeSourceAlgorithms
)
342 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase
)
344 struct PullWithDefaultReaderReadRequest final
: public ReadRequest
{
345 RefPtr
<TeeState
> mTeeState
;
348 NS_DECL_ISUPPORTS_INHERITED
349 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PullWithDefaultReaderReadRequest
,
352 explicit PullWithDefaultReaderReadRequest(TeeState
* aTeeState
)
353 : mTeeState(aTeeState
) {}
355 void ChunkSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
356 ErrorResult
& aRv
) override
{
357 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
359 class PullWithDefaultReaderChunkStepMicrotask
: public MicroTaskRunnable
{
360 RefPtr
<TeeState
> mTeeState
;
361 JS::PersistentRooted
<JSObject
*> mChunk
;
364 PullWithDefaultReaderChunkStepMicrotask(JSContext
* aCx
,
366 JS::Handle
<JSObject
*> aChunk
)
367 : mTeeState(aTeeState
), mChunk(aCx
, aChunk
) {}
370 void Run(AutoSlowOperation
& aAso
) override
{
371 // Step Numbering in this function is relative to the Queue a microtask
372 // of the Chunk steps of 15.2.1 of
373 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
376 !jsapi
.Init(mTeeState
->GetStream()->GetParentObject()))) {
379 JSContext
* cx
= jsapi
.cx();
381 // Step 1. Set readAgainForBranch1 to false.
382 mTeeState
->SetReadAgainForBranch1(false);
384 // Step 2. Set readAgainForBranch2 to false.
385 mTeeState
->SetReadAgainForBranch2(false);
387 // Step 3. Let chunk1 and chunk2 be chunk.
388 JS::Rooted
<JSObject
*> chunk1(cx
, mChunk
);
389 JS::Rooted
<JSObject
*> chunk2(cx
, mChunk
);
391 // Step 4. If canceled1 is false and canceled2 is false,
393 if (!mTeeState
->Canceled1() && !mTeeState
->Canceled2()) {
394 // Step 4.1. Let cloneResult be CloneAsUint8Array(chunk).
395 JS::Rooted
<JSObject
*> cloneResult(cx
, CloneAsUint8Array(cx
, mChunk
));
397 // Step 4.2. If cloneResult is an abrupt completion,
399 // Step 4.2.1 Perform
400 // !ReadableByteStreamControllerError(branch1.[[controller]],
401 // cloneResult.[[Value]]).
402 JS::Rooted
<JS::Value
> exceptionValue(cx
);
403 if (!JS_GetPendingException(cx
, &exceptionValue
)) {
404 // Uncatchable exception, simply return.
407 JS_ClearPendingException(cx
);
410 ReadableByteStreamControllerError(
411 mTeeState
->Branch1()->Controller()->AsByte(), exceptionValue
,
413 if (rv
.MaybeSetPendingException(
414 cx
, "Error during ReadableByteStreamControllerError")) {
418 // Step 4.2.2. Perform !
419 // ReadableByteStreamControllerError(branch2.[[controller]],
420 // cloneResult.[[Value]]).
421 ReadableByteStreamControllerError(
422 mTeeState
->Branch2()->Controller()->AsByte(), exceptionValue
,
424 if (rv
.MaybeSetPendingException(
425 cx
, "Error during ReadableByteStreamControllerError")) {
429 // Step 4.2.3. Resolve cancelPromise with !
430 // ReadableStreamCancel(stream, cloneResult.[[Value]]).
431 RefPtr
<ReadableStream
> stream(mTeeState
->GetStream());
432 RefPtr
<Promise
> promise
=
433 ReadableStreamCancel(cx
, stream
, exceptionValue
, rv
);
434 if (rv
.MaybeSetPendingException(
435 cx
, "Error during ReadableByteStreamControllerError")) {
438 mTeeState
->CancelPromise()->MaybeResolve(promise
);
440 // Step 4.2.4. Return.
444 // Step 4.3. Otherwise, set chunk2 to cloneResult.[[Value]].
445 chunk2
= cloneResult
;
448 // Step 5. If canceled1 is false,
449 // perform ! ReadableByteStreamControllerEnqueue(branch1.[[controller]],
451 if (!mTeeState
->Canceled1()) {
453 RefPtr
<ReadableByteStreamController
> controller(
454 mTeeState
->Branch1()->Controller()->AsByte());
455 ReadableByteStreamControllerEnqueue(cx
, controller
, chunk1
, rv
);
456 if (rv
.MaybeSetPendingException(
457 cx
, "Error during ReadableByteStreamControllerEnqueue")) {
462 // Step 6. If canceled2 is false,
463 // perform ! ReadableByteStreamControllerEnqueue(branch2.[[controller]],
465 if (!mTeeState
->Canceled2()) {
467 RefPtr
<ReadableByteStreamController
> controller(
468 mTeeState
->Branch2()->Controller()->AsByte());
469 ReadableByteStreamControllerEnqueue(cx
, controller
, chunk2
, rv
);
470 if (rv
.MaybeSetPendingException(
471 cx
, "Error during ReadableByteStreamControllerEnqueue")) {
476 // Step 7. Set reading to false.
477 mTeeState
->SetReading(false);
479 // Step 8. If readAgainForBranch1 is true, perform pull1Algorithm.
480 if (mTeeState
->ReadAgainForBranch1()) {
481 ByteStreamTeePullAlgorithm(cx
, TeeBranch::Branch1
,
482 MOZ_KnownLive(mTeeState
), rv
);
483 } else if (mTeeState
->ReadAgainForBranch2()) {
484 // Step 9. Otherwise, if readAgainForBranch2 is true, perform
486 ByteStreamTeePullAlgorithm(cx
, TeeBranch::Branch2
,
487 MOZ_KnownLive(mTeeState
), rv
);
491 bool Suppressed() override
{
492 nsIGlobalObject
* global
= mTeeState
->GetStream()->GetParentObject();
493 return global
&& global
->IsInSyncOperation();
497 MOZ_ASSERT(aChunk
.isObjectOrNull());
498 MOZ_ASSERT(aChunk
.toObjectOrNull() != nullptr);
499 JS::Rooted
<JSObject
*> chunk(aCx
, &aChunk
.toObject());
500 RefPtr
<PullWithDefaultReaderChunkStepMicrotask
> task
=
501 MakeRefPtr
<PullWithDefaultReaderChunkStepMicrotask
>(aCx
, mTeeState
,
503 CycleCollectedJSContext::Get()->DispatchToMicroTask(task
.forget());
506 MOZ_CAN_RUN_SCRIPT
void CloseSteps(JSContext
* aCx
,
507 ErrorResult
& aRv
) override
{
508 // Step numbering below is relative to Step 15.2. 'close steps' of
509 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
511 // Step 1. Set reading to false.
512 mTeeState
->SetReading(false);
514 // Step 2. If canceled1 is false, perform !
515 // ReadableByteStreamControllerClose(branch1.[[controller]]).
516 RefPtr
<ReadableByteStreamController
> branch1Controller
=
517 mTeeState
->Branch1()->Controller()->AsByte();
518 if (!mTeeState
->Canceled1()) {
519 ReadableByteStreamControllerClose(aCx
, branch1Controller
, aRv
);
525 // Step 3. If canceled2 is false, perform !
526 // ReadableByteStreamControllerClose(branch2.[[controller]]).
527 RefPtr
<ReadableByteStreamController
> branch2Controller
=
528 mTeeState
->Branch2()->Controller()->AsByte();
529 if (!mTeeState
->Canceled2()) {
530 ReadableByteStreamControllerClose(aCx
, branch2Controller
, aRv
);
536 // Step 4. If branch1.[[controller]].[[pendingPullIntos]] is not empty,
537 // perform ! ReadableByteStreamControllerRespond(branch1.[[controller]], 0).
538 if (!branch1Controller
->PendingPullIntos().isEmpty()) {
539 ReadableByteStreamControllerRespond(aCx
, branch1Controller
, 0, aRv
);
545 // Step 5. If branch2.[[controller]].[[pendingPullIntos]] is not empty,
546 // perform ! ReadableByteStreamControllerRespond(branch2.[[controller]], 0).
547 if (!branch2Controller
->PendingPullIntos().isEmpty()) {
548 ReadableByteStreamControllerRespond(aCx
, branch2Controller
, 0, aRv
);
554 // Step 6. If canceled1 is false or canceled2 is false, resolve
555 // cancelPromise with undefined.
556 if (!mTeeState
->Canceled1() || !mTeeState
->Canceled2()) {
557 mTeeState
->CancelPromise()->MaybeResolveWithUndefined();
561 void ErrorSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> aError
,
562 ErrorResult
& aRv
) override
{
563 mTeeState
->SetReading(false);
567 ~PullWithDefaultReaderReadRequest() override
= default;
570 NS_IMPL_CYCLE_COLLECTION_INHERITED(PullWithDefaultReaderReadRequest
,
571 ReadRequest
, mTeeState
)
572 NS_IMPL_ADDREF_INHERITED(PullWithDefaultReaderReadRequest
, ReadRequest
)
573 NS_IMPL_RELEASE_INHERITED(PullWithDefaultReaderReadRequest
, ReadRequest
)
574 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PullWithDefaultReaderReadRequest
)
575 NS_INTERFACE_MAP_END_INHERITING(ReadRequest
)
577 void ForwardReaderError(TeeState
* aTeeState
,
578 ReadableStreamGenericReader
* aThisReader
);
580 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee:
582 void PullWithDefaultReader(JSContext
* aCx
, TeeState
* aTeeState
,
584 RefPtr
<ReadableStreamGenericReader
> reader
= aTeeState
->GetReader();
586 // Step 15.1. If reader implements ReadableStreamBYOBReader,
587 if (reader
->IsBYOB()) {
588 // Step 15.1.1. Assert: reader.[[readIntoRequests]] is empty.
589 MOZ_ASSERT(reader
->AsBYOB()->ReadIntoRequests().length() == 0);
591 // Step 15.1.2. Perform ! ReadableStreamBYOBReaderRelease(reader).
592 ReadableStreamBYOBReaderRelease(aCx
, reader
->AsBYOB(), aRv
);
597 // Step 15.1.3. Set reader to ! AcquireReadableStreamDefaultReader(stream).
598 reader
= AcquireReadableStreamDefaultReader(aTeeState
->GetStream(), aRv
);
602 aTeeState
->SetReader(reader
);
604 // Step 16.1.4. Perform forwardReaderError, given reader.
605 ForwardReaderError(aTeeState
, reader
);
609 RefPtr
<ReadRequest
> readRequest
=
610 new PullWithDefaultReaderReadRequest(aTeeState
);
613 ReadableStreamDefaultReaderRead(aCx
, reader
, readRequest
, aRv
);
616 class PullWithBYOBReader_ReadIntoRequest final
: public ReadIntoRequest
{
617 RefPtr
<TeeState
> mTeeState
;
618 const TeeBranch mForBranch
;
619 ~PullWithBYOBReader_ReadIntoRequest() override
= default;
622 NS_DECL_ISUPPORTS_INHERITED
623 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PullWithBYOBReader_ReadIntoRequest
,
626 explicit PullWithBYOBReader_ReadIntoRequest(TeeState
* aTeeState
,
627 TeeBranch aForBranch
)
628 : mTeeState(aTeeState
), mForBranch(aForBranch
) {}
630 void ChunkSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
631 ErrorResult
& aRv
) override
{
632 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
633 // Step 16.4 chunk steps, Step 1.
634 class PullWithBYOBReaderChunkMicrotask
: public MicroTaskRunnable
{
635 RefPtr
<TeeState
> mTeeState
;
636 JS::PersistentRooted
<JSObject
*> mChunk
;
637 const TeeBranch mForBranch
;
640 PullWithBYOBReaderChunkMicrotask(JSContext
* aCx
, TeeState
* aTeeState
,
641 JS::Handle
<JSObject
*> aChunk
,
642 TeeBranch aForBranch
)
643 : mTeeState(aTeeState
), mChunk(aCx
, aChunk
), mForBranch(aForBranch
) {}
646 void Run(AutoSlowOperation
& aAso
) override
{
649 !jsapi
.Init(mTeeState
->GetStream()->GetParentObject()))) {
652 JSContext
* cx
= jsapi
.cx();
654 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
656 // Step Numbering below is relative to Chunk steps Microtask at
657 // Step 16.4 chunk steps, Step 1.
660 mTeeState
->SetReadAgainForBranch1(false);
663 mTeeState
->SetReadAgainForBranch2(false);
666 bool byobCanceled
= mTeeState
->Canceled(mForBranch
);
668 bool otherCanceled
= mTeeState
->Canceled(OtherTeeBranch(mForBranch
));
670 // Rather than store byobBranch / otherBranch, we re-derive the pointers
671 // below, as borrowed from steps 16.2/16.3
672 ReadableStream
* byobBranch
= mTeeState
->Branch(mForBranch
);
673 ReadableStream
* otherBranch
=
674 mTeeState
->Branch(OtherTeeBranch(mForBranch
));
677 if (!otherCanceled
) {
678 // Step 5.1 (using the name clonedChunk because we don't want to name
679 // the completion record explicitly)
680 JS::Rooted
<JSObject
*> clonedChunk(cx
, CloneAsUint8Array(cx
, mChunk
));
682 // Step 5.2. If cloneResult is an abrupt completion,
684 JS::Rooted
<JS::Value
> exception(cx
);
685 if (!JS_GetPendingException(cx
, &exception
)) {
686 // Uncatchable exception. Return with pending
687 // exception still on context.
691 // It's not expliclitly stated, but I assume the intention here is
692 // that we perform a normal completion here, so we clear the
694 JS_ClearPendingException(cx
);
698 ReadableByteStreamControllerError(
699 byobBranch
->Controller()->AsByte(), exception
, rv
);
700 if (rv
.MaybeSetPendingException(cx
)) {
705 ReadableByteStreamControllerError(
706 otherBranch
->Controller()->AsByte(), exception
, rv
);
707 if (rv
.MaybeSetPendingException(cx
)) {
712 RefPtr
<ReadableStream
> stream
= mTeeState
->GetStream();
713 RefPtr
<Promise
> cancelPromise
=
714 ReadableStreamCancel(cx
, stream
, exception
, rv
);
715 if (rv
.MaybeSetPendingException(cx
)) {
718 mTeeState
->CancelPromise()->MaybeResolve(cancelPromise
);
724 // Step 5.3 (implicitly handled above by name selection)
727 RefPtr
<ReadableByteStreamController
> controller(
728 byobBranch
->Controller()->AsByte());
729 ReadableByteStreamControllerRespondWithNewView(cx
, controller
,
731 if (rv
.MaybeSetPendingException(cx
)) {
737 RefPtr
<ReadableByteStreamController
> otherController
=
738 otherBranch
->Controller()->AsByte();
739 ReadableByteStreamControllerEnqueue(cx
, otherController
, clonedChunk
,
741 if (rv
.MaybeSetPendingException(cx
)) {
745 } else if (!byobCanceled
) {
746 RefPtr
<ReadableByteStreamController
> byobController
=
747 byobBranch
->Controller()->AsByte();
748 ReadableByteStreamControllerRespondWithNewView(cx
, byobController
,
750 if (rv
.MaybeSetPendingException(cx
)) {
756 mTeeState
->SetReading(false);
759 if (mTeeState
->ReadAgainForBranch1()) {
760 ByteStreamTeePullAlgorithm(cx
, TeeBranch::Branch1
,
761 MOZ_KnownLive(mTeeState
), rv
);
762 if (rv
.MaybeSetPendingException(cx
)) {
765 } else if (mTeeState
->ReadAgainForBranch2()) {
766 ByteStreamTeePullAlgorithm(cx
, TeeBranch::Branch2
,
767 MOZ_KnownLive(mTeeState
), rv
);
768 if (rv
.MaybeSetPendingException(cx
)) {
774 bool Suppressed() override
{
775 nsIGlobalObject
* global
= mTeeState
->GetStream()->GetParentObject();
776 return global
&& global
->IsInSyncOperation();
780 MOZ_ASSERT(aChunk
.isObjectOrNull());
781 MOZ_ASSERT(aChunk
.toObjectOrNull());
782 JS::Rooted
<JSObject
*> chunk(aCx
, aChunk
.toObjectOrNull());
783 RefPtr
<PullWithBYOBReaderChunkMicrotask
> task
=
784 MakeRefPtr
<PullWithBYOBReaderChunkMicrotask
>(aCx
, mTeeState
, chunk
,
786 CycleCollectedJSContext::Get()->DispatchToMicroTask(task
.forget());
790 void CloseSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
791 ErrorResult
& aRv
) override
{
793 mTeeState
->SetReading(false);
796 bool byobCanceled
= mTeeState
->Canceled(mForBranch
);
799 bool otherCanceled
= mTeeState
->Canceled(OtherTeeBranch(mForBranch
));
801 // Rather than store byobBranch / otherBranch, we re-derive the pointers
802 // below, as borrowed from steps 16.2/16.3
803 ReadableStream
* byobBranch
= mTeeState
->Branch(mForBranch
);
804 ReadableStream
* otherBranch
= mTeeState
->Branch(OtherTeeBranch(mForBranch
));
808 RefPtr
<ReadableByteStreamController
> controller
=
809 byobBranch
->Controller()->AsByte();
810 ReadableByteStreamControllerClose(aCx
, controller
, aRv
);
816 if (!otherCanceled
) {
817 RefPtr
<ReadableByteStreamController
> controller
=
818 otherBranch
->Controller()->AsByte();
819 ReadableByteStreamControllerClose(aCx
, controller
, aRv
);
826 if (!aChunk
.isUndefined()) {
827 MOZ_ASSERT(aChunk
.isObject());
828 MOZ_ASSERT(aChunk
.toObjectOrNull());
830 JS::Rooted
<JSObject
*> chunkObject(aCx
, &aChunk
.toObject());
831 MOZ_ASSERT(JS_IsArrayBufferViewObject(chunkObject
));
833 MOZ_ASSERT(JS_GetArrayBufferViewByteLength(chunkObject
) == 0);
837 RefPtr
<ReadableByteStreamController
> byobController(
838 byobBranch
->Controller()->AsByte());
839 ReadableByteStreamControllerRespondWithNewView(aCx
, byobController
,
847 if (!otherCanceled
&&
848 !otherBranch
->Controller()->AsByte()->PendingPullIntos().isEmpty()) {
849 RefPtr
<ReadableByteStreamController
> otherController(
850 otherBranch
->Controller()->AsByte());
851 ReadableByteStreamControllerRespond(aCx
, otherController
, 0, aRv
);
859 if (!byobCanceled
|| !otherCanceled
) {
860 mTeeState
->CancelPromise()->MaybeResolveWithUndefined();
864 void ErrorSteps(JSContext
* aCx
, JS::Handle
<JS::Value
> e
,
865 ErrorResult
& aRv
) override
{
867 mTeeState
->SetReading(false);
871 NS_IMPL_CYCLE_COLLECTION_INHERITED(PullWithBYOBReader_ReadIntoRequest
,
872 ReadIntoRequest
, mTeeState
)
873 NS_IMPL_ADDREF_INHERITED(PullWithBYOBReader_ReadIntoRequest
, ReadIntoRequest
)
874 NS_IMPL_RELEASE_INHERITED(PullWithBYOBReader_ReadIntoRequest
, ReadIntoRequest
)
876 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PullWithBYOBReader_ReadIntoRequest
)
877 NS_INTERFACE_MAP_END_INHERITING(ReadIntoRequest
)
879 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
881 void PullWithBYOBReader(JSContext
* aCx
, TeeState
* aTeeState
,
882 JS::Handle
<JSObject
*> aView
, TeeBranch aForBranch
,
885 if (aTeeState
->GetReader()->IsDefault()) {
887 MOZ_ASSERT(aTeeState
->GetDefaultReader()->ReadRequests().isEmpty());
889 // Step 16.1.2. Perform ! ReadableStreamDefaultReaderRelease(reader).
890 ReadableStreamDefaultReaderRelease(aCx
, aTeeState
->GetDefaultReader(), aRv
);
895 // Step 16.1.3. Set reader to !AcquireReadableStreamBYOBReader(stream).
896 RefPtr
<ReadableStreamBYOBReader
> reader
=
897 AcquireReadableStreamBYOBReader(aTeeState
->GetStream(), aRv
);
901 aTeeState
->SetReader(reader
);
903 // Step 16.1.4. Perform forwardReaderError, given reader.
904 ForwardReaderError(aTeeState
, reader
);
907 // Step 16.2. Unused in this function, moved to consumers.
908 // Step 16.3. Unused in this function, moved to consumers.
911 RefPtr
<ReadIntoRequest
> readIntoRequest
=
912 new PullWithBYOBReader_ReadIntoRequest(aTeeState
, aForBranch
);
915 RefPtr
<ReadableStreamBYOBReader
> byobReader
=
916 aTeeState
->GetReader()->AsBYOB();
917 ReadableStreamBYOBReaderRead(aCx
, byobReader
, aView
, readIntoRequest
, aRv
);
920 // See https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
922 void ForwardReaderError(TeeState
* aTeeState
,
923 ReadableStreamGenericReader
* aThisReader
) {
924 aThisReader
->ClosedPromise()->AddCallbacksWithCycleCollectedArgs(
925 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
926 TeeState
* aTeeState
, ReadableStreamGenericReader
* aThisReader
) {},
927 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
928 TeeState
* aTeeState
, ReadableStreamGenericReader
* aReader
) {
930 if (aTeeState
->GetReader() != aReader
) {
935 // Step 14.1.2: Perform
936 // !ReadableByteStreamControllerError(branch1.[[controller]], r).
937 MOZ_ASSERT(aTeeState
->Branch1()->Controller()->IsByte());
938 ReadableByteStreamControllerError(
939 aTeeState
->Branch1()->Controller()->AsByte(), aValue
, aRv
);
944 // Step 14.1.3: Perform
945 // !ReadableByteStreamControllerError(branch2.[[controller]], r).
946 MOZ_ASSERT(aTeeState
->Branch2()->Controller()->IsByte());
947 ReadableByteStreamControllerError(
948 aTeeState
->Branch2()->Controller()->AsByte(), aValue
, aRv
);
953 // Step 14.1.4: If canceled1 is false or canceled2 is false, resolve
954 // cancelPromise with undefined.
955 if (!aTeeState
->Canceled1() || !aTeeState
->Canceled2()) {
956 aTeeState
->CancelPromise()->MaybeResolveWithUndefined();
959 RefPtr(aTeeState
), RefPtr(aThisReader
));
962 namespace streams_abstract
{
963 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
964 void ReadableByteStreamTee(JSContext
* aCx
, ReadableStream
* aStream
,
965 nsTArray
<RefPtr
<ReadableStream
>>& aResult
,
969 MOZ_ASSERT(aStream
->Controller()->IsByte());
971 // Step 3-13 captured as part of TeeState allocation
972 RefPtr
<TeeState
> teeState
= TeeState::Create(aStream
, false, aRv
);
977 // Step 14: See ForwardReaderError
978 // Step 15. See PullWithDefaultReader
979 // Step 16. See PullWithBYOBReader
980 // Step 17,18. See {Native,}ByteStreamTeePullAlgorithm
981 // Step 19,20. See ReadableByteStreamTeeCancelAlgorithm
982 // Step 21. Elided because consumers know how to handle nullptr correctly.
984 nsCOMPtr
<nsIGlobalObject
> global
= aStream
->GetParentObject();
985 auto branch1Algorithms
=
986 MakeRefPtr
<ByteStreamTeeSourceAlgorithms
>(teeState
, TeeBranch::Branch1
);
987 teeState
->SetBranch1(
988 ReadableStream::CreateByteAbstract(aCx
, global
, branch1Algorithms
, aRv
));
994 auto branch2Algorithms
=
995 MakeRefPtr
<ByteStreamTeeSourceAlgorithms
>(teeState
, TeeBranch::Branch2
);
996 teeState
->SetBranch2(
997 ReadableStream::CreateByteAbstract(aCx
, global
, branch2Algorithms
, aRv
));
1003 ForwardReaderError(teeState
, teeState
->GetReader());
1006 aResult
.AppendElement(teeState
->Branch1());
1007 aResult
.AppendElement(teeState
->Branch2());
1009 } // namespace streams_abstract
1011 } // namespace mozilla::dom