Bug 1856663 - Add more chunks for Android mochitest-plain. r=jmaher,taskgraph-reviewe...
[gecko.git] / dom / streams / ReadableStreamTee.cpp
blob9bb30d8859335337a850ce674a4740ff53b1d808
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"
10 #include "TeeState.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);
46 if (!aRv.Failed()) {
47 return Promise::CreateResolvedWithUndefined(global, aRv);
49 return nullptr;
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) {
72 // Step 1.
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;
79 public:
80 ReadableStreamDefaultTeeReadRequestChunkSteps(JSContext* aCx,
81 TeeState* aTeeState,
82 JS::Handle<JS::Value> aChunk)
83 : mTeeState(aTeeState), mChunk(aCx, aChunk) {}
85 MOZ_CAN_RUN_SCRIPT
86 void Run(AutoSlowOperation& aAso) override {
87 AutoJSAPI jsapi;
88 if (NS_WARN_IF(!jsapi.Init(mTeeState->GetStream()->GetParentObject()))) {
89 return;
91 JSContext* cx = jsapi.cx();
92 // Step Numbering below is relative to Chunk steps Microtask:
94 // Step 1.
95 mTeeState->SetReadAgain(false);
97 // Step 2.
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());
104 // Step 4.
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());
115 // Step 5.
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());
124 // Step 6.
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,
145 aChunk);
146 CycleCollectedJSContext::Get()->DispatchToMicroTask(task.forget());
149 void ReadableStreamDefaultTeeReadRequest::CloseSteps(JSContext* aCx,
150 ErrorResult& aRv) {
151 // Step Numbering below is relative to 'close steps' of
152 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee
154 // Step 1.
155 mTeeState->SetReading(false);
157 // Step 2.
158 if (!mTeeState->Canceled1()) {
159 RefPtr<ReadableStreamDefaultController> controller(
160 mTeeState->Branch1()->DefaultController());
161 ReadableStreamDefaultControllerClose(aCx, controller, aRv);
162 if (aRv.Failed()) {
163 return;
167 // Step 3.
168 if (!mTeeState->Canceled2()) {
169 RefPtr<ReadableStreamDefaultController> controller(
170 mTeeState->Branch2()->DefaultController());
171 ReadableStreamDefaultControllerClose(aCx, controller, aRv);
172 if (aRv.Failed()) {
173 return;
177 // Step 4.
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,
189 TeeState* aTeeState,
190 ErrorResult& aRv);
191 MOZ_CAN_RUN_SCRIPT void PullWithBYOBReader(JSContext* aCx, TeeState* aTeeState,
192 JS::Handle<JSObject*> aView,
193 TeeBranch aForBranch,
194 ErrorResult& aRv);
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
206 // promise.
207 MOZ_CAN_RUN_SCRIPT void ByteStreamTeePullAlgorithm(JSContext* aCx,
208 TeeBranch aForBranch,
209 TeeState* aTeeState,
210 ErrorResult& aRv) {
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.
217 return;
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);
228 if (aRv.Failed()) {
229 return;
232 // Step {17,18}.4: If byobRequest is null, perform pullWithDefaultReader.
233 if (!byobRequest) {
234 PullWithDefaultReader(aCx, aTeeState, aRv);
235 } else {
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 {
248 public:
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();
264 // Step 17, 18
265 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PullCallback(
266 JSContext* aCx, ReadableStreamController& aController,
267 ErrorResult& aRv) override {
268 // Step 1 - 5
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);
276 // Step 19, 20
277 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CancelCallback(
278 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
279 ErrorResult& aRv) override {
280 // Step 1.
281 mTeeState->SetCanceled(mBranch, true);
283 // Step 2.
284 mTeeState->SetReason(mBranch, aReason.Value());
286 // Step 3.
287 if (mTeeState->Canceled(otherStream())) {
288 // Step 3.1
289 JS::Rooted<JSObject*> compositeReason(aCx, JS::NewArrayObject(aCx, 2));
290 if (!compositeReason) {
291 aRv.StealExceptionFromJSContext(aCx);
292 return nullptr;
295 JS::Rooted<JS::Value> reason1(aCx, mTeeState->Reason1());
296 if (!JS_SetElement(aCx, compositeReason, 0, reason1)) {
297 aRv.StealExceptionFromJSContext(aCx);
298 return nullptr;
301 JS::Rooted<JS::Value> reason2(aCx, mTeeState->Reason2());
302 if (!JS_SetElement(aCx, compositeReason, 1, reason2)) {
303 aRv.StealExceptionFromJSContext(aCx);
304 return nullptr;
307 // Step 3.2
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);
313 if (aRv.Failed()) {
314 return nullptr;
317 // Step 3.3
318 mTeeState->CancelPromise()->MaybeResolve(cancelResult);
321 // Step 4.
322 return do_AddRef(mTeeState->CancelPromise());
325 protected:
326 ~ByteStreamTeeSourceAlgorithms() override = default;
328 private:
329 TeeBranch otherStream() { return OtherTeeBranch(mBranch); }
331 RefPtr<TeeState> mTeeState;
332 TeeBranch mBranch;
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;
347 public:
348 NS_DECL_ISUPPORTS_INHERITED
349 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PullWithDefaultReaderReadRequest,
350 ReadRequest)
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
358 // Step 15.2.1
359 class PullWithDefaultReaderChunkStepMicrotask : public MicroTaskRunnable {
360 RefPtr<TeeState> mTeeState;
361 JS::PersistentRooted<JSObject*> mChunk;
363 public:
364 PullWithDefaultReaderChunkStepMicrotask(JSContext* aCx,
365 TeeState* aTeeState,
366 JS::Handle<JSObject*> aChunk)
367 : mTeeState(aTeeState), mChunk(aCx, aChunk) {}
369 MOZ_CAN_RUN_SCRIPT
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
374 AutoJSAPI jsapi;
375 if (NS_WARN_IF(
376 !jsapi.Init(mTeeState->GetStream()->GetParentObject()))) {
377 return;
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,
392 ErrorResult rv;
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,
398 if (!cloneResult) {
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.
405 return;
407 JS_ClearPendingException(cx);
409 ErrorResult rv;
410 ReadableByteStreamControllerError(
411 mTeeState->Branch1()->Controller()->AsByte(), exceptionValue,
412 rv);
413 if (rv.MaybeSetPendingException(
414 cx, "Error during ReadableByteStreamControllerError")) {
415 return;
418 // Step 4.2.2. Perform !
419 // ReadableByteStreamControllerError(branch2.[[controller]],
420 // cloneResult.[[Value]]).
421 ReadableByteStreamControllerError(
422 mTeeState->Branch2()->Controller()->AsByte(), exceptionValue,
423 rv);
424 if (rv.MaybeSetPendingException(
425 cx, "Error during ReadableByteStreamControllerError")) {
426 return;
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")) {
436 return;
438 mTeeState->CancelPromise()->MaybeResolve(promise);
440 // Step 4.2.4. Return.
441 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]],
450 // chunk1).
451 if (!mTeeState->Canceled1()) {
452 ErrorResult rv;
453 RefPtr<ReadableByteStreamController> controller(
454 mTeeState->Branch1()->Controller()->AsByte());
455 ReadableByteStreamControllerEnqueue(cx, controller, chunk1, rv);
456 if (rv.MaybeSetPendingException(
457 cx, "Error during ReadableByteStreamControllerEnqueue")) {
458 return;
462 // Step 6. If canceled2 is false,
463 // perform ! ReadableByteStreamControllerEnqueue(branch2.[[controller]],
464 // chunk2).
465 if (!mTeeState->Canceled2()) {
466 ErrorResult rv;
467 RefPtr<ReadableByteStreamController> controller(
468 mTeeState->Branch2()->Controller()->AsByte());
469 ReadableByteStreamControllerEnqueue(cx, controller, chunk2, rv);
470 if (rv.MaybeSetPendingException(
471 cx, "Error during ReadableByteStreamControllerEnqueue")) {
472 return;
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
485 // pull2Algorithm.
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,
502 chunk);
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);
520 if (aRv.Failed()) {
521 return;
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);
531 if (aRv.Failed()) {
532 return;
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);
540 if (aRv.Failed()) {
541 return;
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);
549 if (aRv.Failed()) {
550 return;
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);
566 protected:
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:
581 // Step 15.
582 void PullWithDefaultReader(JSContext* aCx, TeeState* aTeeState,
583 ErrorResult& aRv) {
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);
593 if (aRv.Failed()) {
594 return;
597 // Step 15.1.3. Set reader to ! AcquireReadableStreamDefaultReader(stream).
598 reader = AcquireReadableStreamDefaultReader(aTeeState->GetStream(), aRv);
599 if (aRv.Failed()) {
600 return;
602 aTeeState->SetReader(reader);
604 // Step 16.1.4. Perform forwardReaderError, given reader.
605 ForwardReaderError(aTeeState, reader);
608 // Step 15.2
609 RefPtr<ReadRequest> readRequest =
610 new PullWithDefaultReaderReadRequest(aTeeState);
612 // Step 15.3
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;
621 public:
622 NS_DECL_ISUPPORTS_INHERITED
623 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PullWithBYOBReader_ReadIntoRequest,
624 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;
639 public:
640 PullWithBYOBReaderChunkMicrotask(JSContext* aCx, TeeState* aTeeState,
641 JS::Handle<JSObject*> aChunk,
642 TeeBranch aForBranch)
643 : mTeeState(aTeeState), mChunk(aCx, aChunk), mForBranch(aForBranch) {}
645 MOZ_CAN_RUN_SCRIPT
646 void Run(AutoSlowOperation& aAso) override {
647 AutoJSAPI jsapi;
648 if (NS_WARN_IF(
649 !jsapi.Init(mTeeState->GetStream()->GetParentObject()))) {
650 return;
652 JSContext* cx = jsapi.cx();
653 ErrorResult rv;
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.
659 // Step 1.
660 mTeeState->SetReadAgainForBranch1(false);
662 // Step 2.
663 mTeeState->SetReadAgainForBranch2(false);
665 // Step 3.
666 bool byobCanceled = mTeeState->Canceled(mForBranch);
667 // Step 4.
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));
676 // Step 5.
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,
683 if (!clonedChunk) {
684 JS::Rooted<JS::Value> exception(cx);
685 if (!JS_GetPendingException(cx, &exception)) {
686 // Uncatchable exception. Return with pending
687 // exception still on context.
688 return;
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
693 // exception.
694 JS_ClearPendingException(cx);
696 // Step 5.2.1
698 ReadableByteStreamControllerError(
699 byobBranch->Controller()->AsByte(), exception, rv);
700 if (rv.MaybeSetPendingException(cx)) {
701 return;
704 // Step 5.2.2.
705 ReadableByteStreamControllerError(
706 otherBranch->Controller()->AsByte(), exception, rv);
707 if (rv.MaybeSetPendingException(cx)) {
708 return;
711 // Step 5.2.3.
712 RefPtr<ReadableStream> stream = mTeeState->GetStream();
713 RefPtr<Promise> cancelPromise =
714 ReadableStreamCancel(cx, stream, exception, rv);
715 if (rv.MaybeSetPendingException(cx)) {
716 return;
718 mTeeState->CancelPromise()->MaybeResolve(cancelPromise);
720 // Step 5.2.4.
721 return;
724 // Step 5.3 (implicitly handled above by name selection)
725 // Step 5.4.
726 if (!byobCanceled) {
727 RefPtr<ReadableByteStreamController> controller(
728 byobBranch->Controller()->AsByte());
729 ReadableByteStreamControllerRespondWithNewView(cx, controller,
730 mChunk, rv);
731 if (rv.MaybeSetPendingException(cx)) {
732 return;
736 // Step 5.4.
737 RefPtr<ReadableByteStreamController> otherController =
738 otherBranch->Controller()->AsByte();
739 ReadableByteStreamControllerEnqueue(cx, otherController, clonedChunk,
740 rv);
741 if (rv.MaybeSetPendingException(cx)) {
742 return;
744 // Step 6.
745 } else if (!byobCanceled) {
746 RefPtr<ReadableByteStreamController> byobController =
747 byobBranch->Controller()->AsByte();
748 ReadableByteStreamControllerRespondWithNewView(cx, byobController,
749 mChunk, rv);
750 if (rv.MaybeSetPendingException(cx)) {
751 return;
755 // Step 7.
756 mTeeState->SetReading(false);
758 // Step 8.
759 if (mTeeState->ReadAgainForBranch1()) {
760 ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch1,
761 MOZ_KnownLive(mTeeState), rv);
762 if (rv.MaybeSetPendingException(cx)) {
763 return;
765 } else if (mTeeState->ReadAgainForBranch2()) {
766 ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch2,
767 MOZ_KnownLive(mTeeState), rv);
768 if (rv.MaybeSetPendingException(cx)) {
769 return;
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,
785 mForBranch);
786 CycleCollectedJSContext::Get()->DispatchToMicroTask(task.forget());
789 MOZ_CAN_RUN_SCRIPT
790 void CloseSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
791 ErrorResult& aRv) override {
792 // Step 1.
793 mTeeState->SetReading(false);
795 // Step 2.
796 bool byobCanceled = mTeeState->Canceled(mForBranch);
798 // Step 3.
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));
806 // Step 4.
807 if (!byobCanceled) {
808 RefPtr<ReadableByteStreamController> controller =
809 byobBranch->Controller()->AsByte();
810 ReadableByteStreamControllerClose(aCx, controller, aRv);
811 if (aRv.Failed()) {
812 return;
815 // Step 5.
816 if (!otherCanceled) {
817 RefPtr<ReadableByteStreamController> controller =
818 otherBranch->Controller()->AsByte();
819 ReadableByteStreamControllerClose(aCx, controller, aRv);
820 if (aRv.Failed()) {
821 return;
825 // Step 6.
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));
832 // Step 6.1.
833 MOZ_ASSERT(JS_GetArrayBufferViewByteLength(chunkObject) == 0);
835 // Step 6.2.
836 if (!byobCanceled) {
837 RefPtr<ReadableByteStreamController> byobController(
838 byobBranch->Controller()->AsByte());
839 ReadableByteStreamControllerRespondWithNewView(aCx, byobController,
840 chunkObject, aRv);
841 if (aRv.Failed()) {
842 return;
846 // Step 6.3
847 if (!otherCanceled &&
848 !otherBranch->Controller()->AsByte()->PendingPullIntos().isEmpty()) {
849 RefPtr<ReadableByteStreamController> otherController(
850 otherBranch->Controller()->AsByte());
851 ReadableByteStreamControllerRespond(aCx, otherController, 0, aRv);
852 if (aRv.Failed()) {
853 return;
858 // Step 7.
859 if (!byobCanceled || !otherCanceled) {
860 mTeeState->CancelPromise()->MaybeResolveWithUndefined();
864 void ErrorSteps(JSContext* aCx, JS::Handle<JS::Value> e,
865 ErrorResult& aRv) override {
866 // Step 1.
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
880 // Step 16.
881 void PullWithBYOBReader(JSContext* aCx, TeeState* aTeeState,
882 JS::Handle<JSObject*> aView, TeeBranch aForBranch,
883 ErrorResult& aRv) {
884 // Step 16.1
885 if (aTeeState->GetReader()->IsDefault()) {
886 // Step 16.1.1
887 MOZ_ASSERT(aTeeState->GetDefaultReader()->ReadRequests().isEmpty());
889 // Step 16.1.2. Perform ! ReadableStreamDefaultReaderRelease(reader).
890 ReadableStreamDefaultReaderRelease(aCx, aTeeState->GetDefaultReader(), aRv);
891 if (aRv.Failed()) {
892 return;
895 // Step 16.1.3. Set reader to !AcquireReadableStreamBYOBReader(stream).
896 RefPtr<ReadableStreamBYOBReader> reader =
897 AcquireReadableStreamBYOBReader(aTeeState->GetStream(), aRv);
898 if (aRv.Failed()) {
899 return;
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.
910 // Step 16.4.
911 RefPtr<ReadIntoRequest> readIntoRequest =
912 new PullWithBYOBReader_ReadIntoRequest(aTeeState, aForBranch);
914 // Step 16.5.
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
921 // Step 14.
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) {
929 // Step 14.1.1
930 if (aTeeState->GetReader() != aReader) {
931 return;
934 ErrorResult rv;
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);
940 if (aRv.Failed()) {
941 return;
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);
949 if (aRv.Failed()) {
950 return;
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,
966 ErrorResult& aRv) {
967 // Step 1. Implicit
968 // Step 2.
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);
973 if (aRv.Failed()) {
974 return;
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.
983 // Step 22.
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));
989 if (aRv.Failed()) {
990 return;
993 // Step 23.
994 auto branch2Algorithms =
995 MakeRefPtr<ByteStreamTeeSourceAlgorithms>(teeState, TeeBranch::Branch2);
996 teeState->SetBranch2(
997 ReadableStream::CreateByteAbstract(aCx, global, branch2Algorithms, aRv));
998 if (aRv.Failed()) {
999 return;
1002 // Step 24.
1003 ForwardReaderError(teeState, teeState->GetReader());
1005 // Step 25.
1006 aResult.AppendElement(teeState->Branch1());
1007 aResult.AppendElement(teeState->Branch2());
1009 } // namespace streams_abstract
1011 } // namespace mozilla::dom