mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-01 00:38:50 +02:00 
			
		
		
		
	 e6535142a8
			
		
	
	
		e6535142a8
		
	
	
	
	
		
			
			The name ReadableStreamController collides with the same-named Web IDL union type. Differential Revision: https://phabricator.services.mozilla.com/D244385
		
			
				
	
	
		
			1012 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1012 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim:set ts=2 sw=2 sts=2 et cindent: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "ReadableStreamTee.h"
 | |
| 
 | |
| #include "ReadIntoRequest.h"
 | |
| #include "TeeState.h"
 | |
| #include "js/Exception.h"
 | |
| #include "js/TypeDecls.h"
 | |
| #include "js/experimental/TypedData.h"
 | |
| #include "mozilla/dom/ByteStreamHelpers.h"
 | |
| #include "mozilla/dom/Promise-inl.h"
 | |
| #include "mozilla/dom/ReadableStream.h"
 | |
| #include "mozilla/dom/ReadableStreamBYOBReader.h"
 | |
| #include "mozilla/dom/ReadableStreamDefaultController.h"
 | |
| #include "mozilla/dom/ReadableStreamGenericReader.h"
 | |
| #include "mozilla/dom/ReadableStreamDefaultReader.h"
 | |
| #include "mozilla/dom/ReadableByteStreamController.h"
 | |
| #include "mozilla/dom/UnderlyingSourceBinding.h"
 | |
| #include "mozilla/dom/UnderlyingSourceCallbackHelpers.h"
 | |
| #include "nsCycleCollectionParticipant.h"
 | |
| #include "mozilla/CycleCollectedJSContext.h"
 | |
| 
 | |
| namespace mozilla::dom {
 | |
| 
 | |
| using namespace streams_abstract;
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_INHERITED(ReadableStreamDefaultTeeSourceAlgorithms,
 | |
|                                    UnderlyingSourceAlgorithmsBase, mTeeState)
 | |
| NS_IMPL_ADDREF_INHERITED(ReadableStreamDefaultTeeSourceAlgorithms,
 | |
|                          UnderlyingSourceAlgorithmsBase)
 | |
| NS_IMPL_RELEASE_INHERITED(ReadableStreamDefaultTeeSourceAlgorithms,
 | |
|                           UnderlyingSourceAlgorithmsBase)
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 | |
|     ReadableStreamDefaultTeeSourceAlgorithms)
 | |
| NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase)
 | |
| 
 | |
| already_AddRefed<Promise>
 | |
| ReadableStreamDefaultTeeSourceAlgorithms::PullCallback(
 | |
|     JSContext* aCx, ReadableStreamControllerBase& aController,
 | |
|     ErrorResult& aRv) {
 | |
|   nsCOMPtr<nsIGlobalObject> global = aController.GetParentObject();
 | |
|   mTeeState->PullCallback(aCx, global, aRv);
 | |
|   if (!aRv.Failed()) {
 | |
|     return Promise::CreateResolvedWithUndefined(global, aRv);
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableStreamDefaultTeeReadRequest)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(
 | |
|     ReadableStreamDefaultTeeReadRequest, ReadRequest)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTeeState)
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
 | |
|     ReadableStreamDefaultTeeReadRequest, ReadRequest)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTeeState)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| NS_IMPL_ADDREF_INHERITED(ReadableStreamDefaultTeeReadRequest, ReadRequest)
 | |
| NS_IMPL_RELEASE_INHERITED(ReadableStreamDefaultTeeReadRequest, ReadRequest)
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamDefaultTeeReadRequest)
 | |
| NS_INTERFACE_MAP_END_INHERITING(ReadRequest)
 | |
| 
 | |
| void ReadableStreamDefaultTeeReadRequest::ChunkSteps(
 | |
|     JSContext* aCx, JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
 | |
|   // Step 1.
 | |
|   class ReadableStreamDefaultTeeReadRequestChunkSteps
 | |
|       : public MicroTaskRunnable {
 | |
|     // Virtually const, but is cycle collected
 | |
|     MOZ_KNOWN_LIVE RefPtr<TeeState> mTeeState;
 | |
|     JS::PersistentRooted<JS::Value> mChunk;
 | |
| 
 | |
|    public:
 | |
|     ReadableStreamDefaultTeeReadRequestChunkSteps(JSContext* aCx,
 | |
|                                                   TeeState* aTeeState,
 | |
|                                                   JS::Handle<JS::Value> aChunk)
 | |
|         : mTeeState(aTeeState), mChunk(aCx, aChunk) {}
 | |
| 
 | |
|     MOZ_CAN_RUN_SCRIPT
 | |
|     void Run(AutoSlowOperation& aAso) override {
 | |
|       AutoJSAPI jsapi;
 | |
|       if (NS_WARN_IF(!jsapi.Init(mTeeState->GetStream()->GetParentObject()))) {
 | |
|         return;
 | |
|       }
 | |
|       JSContext* cx = jsapi.cx();
 | |
|       // Step Numbering below is relative to Chunk steps Microtask:
 | |
|       //
 | |
|       // Step 1.
 | |
|       mTeeState->SetReadAgain(false);
 | |
| 
 | |
|       // Step 2.
 | |
|       JS::Rooted<JS::Value> chunk1(cx, mChunk);
 | |
|       JS::Rooted<JS::Value> chunk2(cx, mChunk);
 | |
| 
 | |
|       // Step 3. Skipped until we implement cloneForBranch2 path.
 | |
|       MOZ_RELEASE_ASSERT(!mTeeState->CloneForBranch2());
 | |
| 
 | |
|       // Step 4.
 | |
|       if (!mTeeState->Canceled1()) {
 | |
|         IgnoredErrorResult rv;
 | |
|         // Since we controlled the creation of the two stream branches, we know
 | |
|         // they both have default controllers.
 | |
|         RefPtr<ReadableStreamDefaultController> controller(
 | |
|             mTeeState->Branch1()->DefaultController());
 | |
|         ReadableStreamDefaultControllerEnqueue(cx, controller, chunk1, rv);
 | |
|         (void)NS_WARN_IF(rv.Failed());
 | |
|       }
 | |
| 
 | |
|       // Step 5.
 | |
|       if (!mTeeState->Canceled2()) {
 | |
|         IgnoredErrorResult rv;
 | |
|         RefPtr<ReadableStreamDefaultController> controller(
 | |
|             mTeeState->Branch2()->DefaultController());
 | |
|         ReadableStreamDefaultControllerEnqueue(cx, controller, chunk2, rv);
 | |
|         (void)NS_WARN_IF(rv.Failed());
 | |
|       }
 | |
| 
 | |
|       // Step 6.
 | |
|       mTeeState->SetReading(false);
 | |
| 
 | |
|       // Step 7. If |readAgain| is true, perform |pullAlgorithm|.
 | |
|       if (mTeeState->ReadAgain()) {
 | |
|         IgnoredErrorResult rv;
 | |
|         nsCOMPtr<nsIGlobalObject> global(
 | |
|             mTeeState->GetStream()->GetParentObject());
 | |
|         mTeeState->PullCallback(cx, global, rv);
 | |
|         (void)NS_WARN_IF(rv.Failed());
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     bool Suppressed() override {
 | |
|       nsIGlobalObject* global = mTeeState->GetStream()->GetParentObject();
 | |
|       return global && global->IsInSyncOperation();
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   RefPtr<ReadableStreamDefaultTeeReadRequestChunkSteps> task =
 | |
|       MakeRefPtr<ReadableStreamDefaultTeeReadRequestChunkSteps>(aCx, mTeeState,
 | |
|                                                                 aChunk);
 | |
|   CycleCollectedJSContext::Get()->DispatchToMicroTask(task.forget());
 | |
| }
 | |
| 
 | |
| void ReadableStreamDefaultTeeReadRequest::CloseSteps(JSContext* aCx,
 | |
|                                                      ErrorResult& aRv) {
 | |
|   // Step Numbering below is relative to 'close steps' of
 | |
|   // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee
 | |
|   //
 | |
|   // Step 1.
 | |
|   mTeeState->SetReading(false);
 | |
| 
 | |
|   // Step 2.
 | |
|   if (!mTeeState->Canceled1()) {
 | |
|     RefPtr<ReadableStreamDefaultController> controller(
 | |
|         mTeeState->Branch1()->DefaultController());
 | |
|     ReadableStreamDefaultControllerClose(aCx, controller, aRv);
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Step 3.
 | |
|   if (!mTeeState->Canceled2()) {
 | |
|     RefPtr<ReadableStreamDefaultController> controller(
 | |
|         mTeeState->Branch2()->DefaultController());
 | |
|     ReadableStreamDefaultControllerClose(aCx, controller, aRv);
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Step 4.
 | |
|   if (!mTeeState->Canceled1() || !mTeeState->Canceled2()) {
 | |
|     mTeeState->CancelPromise()->MaybeResolveWithUndefined();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ReadableStreamDefaultTeeReadRequest::ErrorSteps(
 | |
|     JSContext* aCx, JS::Handle<JS::Value> aError, ErrorResult& aRv) {
 | |
|   mTeeState->SetReading(false);
 | |
| }
 | |
| 
 | |
| MOZ_CAN_RUN_SCRIPT void PullWithDefaultReader(JSContext* aCx,
 | |
|                                               TeeState* aTeeState,
 | |
|                                               ErrorResult& aRv);
 | |
| MOZ_CAN_RUN_SCRIPT void PullWithBYOBReader(JSContext* aCx, TeeState* aTeeState,
 | |
|                                            JS::Handle<JSObject*> aView,
 | |
|                                            TeeBranch aForBranch,
 | |
|                                            ErrorResult& aRv);
 | |
| 
 | |
| // Algorithm described in
 | |
| // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee, Steps
 | |
| // 17 and Steps 18, genericized across branch numbers:
 | |
| //
 | |
| // Note: As specified this algorithm always returns a promise resolved with
 | |
| // undefined, however as some places immediately discard said promise, we
 | |
| // provide this version which doesn't return a promise.
 | |
| //
 | |
| // NativeByteStreamTeePullAlgorithm, which implements
 | |
| // UnderlyingSourcePullCallbackHelper is the version which provies the return
 | |
| // promise.
 | |
| MOZ_CAN_RUN_SCRIPT void ByteStreamTeePullAlgorithm(JSContext* aCx,
 | |
|                                                    TeeBranch aForBranch,
 | |
|                                                    TeeState* aTeeState,
 | |
|                                                    ErrorResult& aRv) {
 | |
|   // Step {17,18}.1: If reading is true,
 | |
|   if (aTeeState->Reading()) {
 | |
|     // Step {17,18}.1.1: Set readAgainForBranch{1,2} to true.
 | |
|     aTeeState->SetReadAgainForBranch(aForBranch, true);
 | |
| 
 | |
|     // Step {17,18}.1.1: Return a promise resolved with undefined.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Step {17,18}.2: Set reading to true.
 | |
|   aTeeState->SetReading(true);
 | |
| 
 | |
|   // Step {17,18}.3: Let byobRequest be
 | |
|   // !ReadableByteStreamControllerGetBYOBRequest(branch{1,2}.[[controller]]).
 | |
|   RefPtr<ReadableStreamBYOBRequest> byobRequest =
 | |
|       ReadableByteStreamControllerGetBYOBRequest(
 | |
|           aCx, aTeeState->Branch(aForBranch)->Controller()->AsByte(), aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Step {17,18}.4: If byobRequest is null, perform pullWithDefaultReader.
 | |
|   if (!byobRequest) {
 | |
|     PullWithDefaultReader(aCx, aTeeState, aRv);
 | |
|   } else {
 | |
|     // Step {17,18}.5: Otherwise, perform pullWithBYOBReader, given
 | |
|     // byobRequest.[[view]] and {false, true}.
 | |
|     JS::Rooted<JSObject*> view(aCx, byobRequest->View());
 | |
|     PullWithBYOBReader(aCx, aTeeState, view, aForBranch, aRv);
 | |
|   }
 | |
| 
 | |
|   // Step {17,18}.6: Return a promise resolved with undefined.
 | |
| }
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
 | |
| class ByteStreamTeeSourceAlgorithms final
 | |
|     : public UnderlyingSourceAlgorithmsBase {
 | |
|  public:
 | |
|   NS_DECL_ISUPPORTS_INHERITED
 | |
|   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ByteStreamTeeSourceAlgorithms,
 | |
|                                            UnderlyingSourceAlgorithmsBase)
 | |
| 
 | |
|   ByteStreamTeeSourceAlgorithms(TeeState* aTeeState, TeeBranch aBranch)
 | |
|       : mTeeState(aTeeState), mBranch(aBranch) {}
 | |
| 
 | |
|   MOZ_CAN_RUN_SCRIPT void StartCallback(
 | |
|       JSContext* aCx, ReadableStreamControllerBase& aController,
 | |
|       JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv) override {
 | |
|     // Step 21: Let startAlgorithm be an algorithm that returns undefined.
 | |
|     aRetVal.setUndefined();
 | |
|   }
 | |
| 
 | |
|   // Step 17, 18
 | |
|   MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PullCallback(
 | |
|       JSContext* aCx, ReadableStreamControllerBase& aController,
 | |
|       ErrorResult& aRv) override {
 | |
|     // Step 1 - 5
 | |
|     ByteStreamTeePullAlgorithm(aCx, mBranch, MOZ_KnownLive(mTeeState), aRv);
 | |
| 
 | |
|     // Step 6: Return a promise resolved with undefined.
 | |
|     return Promise::CreateResolvedWithUndefined(
 | |
|         mTeeState->GetStream()->GetParentObject(), aRv);
 | |
|   }
 | |
| 
 | |
|   // Step 19, 20
 | |
|   MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CancelCallback(
 | |
|       JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
 | |
|       ErrorResult& aRv) override {
 | |
|     // Step 1.
 | |
|     mTeeState->SetCanceled(mBranch, true);
 | |
| 
 | |
|     // Step 2.
 | |
|     mTeeState->SetReason(mBranch, aReason.Value());
 | |
| 
 | |
|     // Step 3.
 | |
|     if (mTeeState->Canceled(otherStream())) {
 | |
|       // Step 3.1
 | |
|       JS::Rooted<JSObject*> compositeReason(aCx, JS::NewArrayObject(aCx, 2));
 | |
|       if (!compositeReason) {
 | |
|         aRv.StealExceptionFromJSContext(aCx);
 | |
|         return nullptr;
 | |
|       }
 | |
| 
 | |
|       JS::Rooted<JS::Value> reason1(aCx, mTeeState->Reason1());
 | |
|       if (!JS_SetElement(aCx, compositeReason, 0, reason1)) {
 | |
|         aRv.StealExceptionFromJSContext(aCx);
 | |
|         return nullptr;
 | |
|       }
 | |
| 
 | |
|       JS::Rooted<JS::Value> reason2(aCx, mTeeState->Reason2());
 | |
|       if (!JS_SetElement(aCx, compositeReason, 1, reason2)) {
 | |
|         aRv.StealExceptionFromJSContext(aCx);
 | |
|         return nullptr;
 | |
|       }
 | |
| 
 | |
|       // Step 3.2
 | |
|       JS::Rooted<JS::Value> compositeReasonValue(
 | |
|           aCx, JS::ObjectValue(*compositeReason));
 | |
|       RefPtr<ReadableStream> stream(mTeeState->GetStream());
 | |
|       RefPtr<Promise> cancelResult =
 | |
|           ReadableStreamCancel(aCx, stream, compositeReasonValue, aRv);
 | |
|       if (aRv.Failed()) {
 | |
|         return nullptr;
 | |
|       }
 | |
| 
 | |
|       // Step 3.3
 | |
|       mTeeState->CancelPromise()->MaybeResolve(cancelResult);
 | |
|     }
 | |
| 
 | |
|     // Step 4.
 | |
|     return do_AddRef(mTeeState->CancelPromise());
 | |
|   };
 | |
| 
 | |
|  protected:
 | |
|   ~ByteStreamTeeSourceAlgorithms() override = default;
 | |
| 
 | |
|  private:
 | |
|   TeeBranch otherStream() { return OtherTeeBranch(mBranch); }
 | |
| 
 | |
|   RefPtr<TeeState> mTeeState;
 | |
|   TeeBranch mBranch;
 | |
| };
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_INHERITED(ByteStreamTeeSourceAlgorithms,
 | |
|                                    UnderlyingSourceAlgorithmsBase, mTeeState)
 | |
| NS_IMPL_ADDREF_INHERITED(ByteStreamTeeSourceAlgorithms,
 | |
|                          UnderlyingSourceAlgorithmsBase)
 | |
| NS_IMPL_RELEASE_INHERITED(ByteStreamTeeSourceAlgorithms,
 | |
|                           UnderlyingSourceAlgorithmsBase)
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ByteStreamTeeSourceAlgorithms)
 | |
| NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase)
 | |
| 
 | |
| struct PullWithDefaultReaderReadRequest final : public ReadRequest {
 | |
|   RefPtr<TeeState> mTeeState;
 | |
| 
 | |
|  public:
 | |
|   NS_DECL_ISUPPORTS_INHERITED
 | |
|   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PullWithDefaultReaderReadRequest,
 | |
|                                            ReadRequest)
 | |
| 
 | |
|   explicit PullWithDefaultReaderReadRequest(TeeState* aTeeState)
 | |
|       : mTeeState(aTeeState) {}
 | |
| 
 | |
|   void ChunkSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
 | |
|                   ErrorResult& aRv) override {
 | |
|     // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
 | |
|     // Step 15.2.1
 | |
|     class PullWithDefaultReaderChunkStepMicrotask : public MicroTaskRunnable {
 | |
|       RefPtr<TeeState> mTeeState;
 | |
|       JS::PersistentRooted<JSObject*> mChunk;
 | |
| 
 | |
|      public:
 | |
|       PullWithDefaultReaderChunkStepMicrotask(JSContext* aCx,
 | |
|                                               TeeState* aTeeState,
 | |
|                                               JS::Handle<JSObject*> aChunk)
 | |
|           : mTeeState(aTeeState), mChunk(aCx, aChunk) {}
 | |
| 
 | |
|       MOZ_CAN_RUN_SCRIPT
 | |
|       void Run(AutoSlowOperation& aAso) override {
 | |
|         // Step Numbering in this function is relative to the Queue a microtask
 | |
|         // of the Chunk steps of 15.2.1 of
 | |
|         // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
 | |
|         AutoJSAPI jsapi;
 | |
|         if (NS_WARN_IF(
 | |
|                 !jsapi.Init(mTeeState->GetStream()->GetParentObject()))) {
 | |
|           return;
 | |
|         }
 | |
|         JSContext* cx = jsapi.cx();
 | |
| 
 | |
|         // Step 1. Set readAgainForBranch1 to false.
 | |
|         mTeeState->SetReadAgainForBranch1(false);
 | |
| 
 | |
|         // Step 2. Set readAgainForBranch2 to false.
 | |
|         mTeeState->SetReadAgainForBranch2(false);
 | |
| 
 | |
|         // Step 3. Let chunk1 and chunk2 be chunk.
 | |
|         JS::Rooted<JSObject*> chunk1(cx, mChunk);
 | |
|         JS::Rooted<JSObject*> chunk2(cx, mChunk);
 | |
| 
 | |
|         // Step 4. If canceled1 is false and canceled2 is false,
 | |
|         ErrorResult rv;
 | |
|         if (!mTeeState->Canceled1() && !mTeeState->Canceled2()) {
 | |
|           // Step 4.1. Let cloneResult be CloneAsUint8Array(chunk).
 | |
|           JS::Rooted<JSObject*> cloneResult(cx, CloneAsUint8Array(cx, mChunk));
 | |
| 
 | |
|           // Step 4.2. If cloneResult is an abrupt completion,
 | |
|           if (!cloneResult) {
 | |
|             // Step 4.2.1 Perform
 | |
|             // !ReadableByteStreamControllerError(branch1.[[controller]],
 | |
|             // cloneResult.[[Value]]).
 | |
|             JS::Rooted<JS::Value> exceptionValue(cx);
 | |
|             if (!JS_GetPendingException(cx, &exceptionValue)) {
 | |
|               // Uncatchable exception, simply return.
 | |
|               return;
 | |
|             }
 | |
|             JS_ClearPendingException(cx);
 | |
| 
 | |
|             ErrorResult rv;
 | |
|             ReadableByteStreamControllerError(
 | |
|                 mTeeState->Branch1()->Controller()->AsByte(), exceptionValue,
 | |
|                 rv);
 | |
|             if (rv.MaybeSetPendingException(
 | |
|                     cx, "Error during ReadableByteStreamControllerError")) {
 | |
|               return;
 | |
|             }
 | |
| 
 | |
|             // Step 4.2.2. Perform !
 | |
|             // ReadableByteStreamControllerError(branch2.[[controller]],
 | |
|             // cloneResult.[[Value]]).
 | |
|             ReadableByteStreamControllerError(
 | |
|                 mTeeState->Branch2()->Controller()->AsByte(), exceptionValue,
 | |
|                 rv);
 | |
|             if (rv.MaybeSetPendingException(
 | |
|                     cx, "Error during ReadableByteStreamControllerError")) {
 | |
|               return;
 | |
|             }
 | |
| 
 | |
|             // Step 4.2.3. Resolve cancelPromise with !
 | |
|             // ReadableStreamCancel(stream, cloneResult.[[Value]]).
 | |
|             RefPtr<ReadableStream> stream(mTeeState->GetStream());
 | |
|             RefPtr<Promise> promise =
 | |
|                 ReadableStreamCancel(cx, stream, exceptionValue, rv);
 | |
|             if (rv.MaybeSetPendingException(
 | |
|                     cx, "Error during ReadableByteStreamControllerError")) {
 | |
|               return;
 | |
|             }
 | |
|             mTeeState->CancelPromise()->MaybeResolve(promise);
 | |
| 
 | |
|             // Step 4.2.4. Return.
 | |
|             return;
 | |
|           }
 | |
| 
 | |
|           // Step 4.3. Otherwise, set chunk2 to cloneResult.[[Value]].
 | |
|           chunk2 = cloneResult;
 | |
|         }
 | |
| 
 | |
|         // Step 5. If canceled1 is false,
 | |
|         // perform ! ReadableByteStreamControllerEnqueue(branch1.[[controller]],
 | |
|         // chunk1).
 | |
|         if (!mTeeState->Canceled1()) {
 | |
|           ErrorResult rv;
 | |
|           RefPtr<ReadableByteStreamController> controller(
 | |
|               mTeeState->Branch1()->Controller()->AsByte());
 | |
|           ReadableByteStreamControllerEnqueue(cx, controller, chunk1, rv);
 | |
|           if (rv.MaybeSetPendingException(
 | |
|                   cx, "Error during ReadableByteStreamControllerEnqueue")) {
 | |
|             return;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // Step 6. If canceled2 is false,
 | |
|         // perform ! ReadableByteStreamControllerEnqueue(branch2.[[controller]],
 | |
|         // chunk2).
 | |
|         if (!mTeeState->Canceled2()) {
 | |
|           ErrorResult rv;
 | |
|           RefPtr<ReadableByteStreamController> controller(
 | |
|               mTeeState->Branch2()->Controller()->AsByte());
 | |
|           ReadableByteStreamControllerEnqueue(cx, controller, chunk2, rv);
 | |
|           if (rv.MaybeSetPendingException(
 | |
|                   cx, "Error during ReadableByteStreamControllerEnqueue")) {
 | |
|             return;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // Step 7. Set reading to false.
 | |
|         mTeeState->SetReading(false);
 | |
| 
 | |
|         // Step 8. If readAgainForBranch1 is true, perform pull1Algorithm.
 | |
|         if (mTeeState->ReadAgainForBranch1()) {
 | |
|           ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch1,
 | |
|                                      MOZ_KnownLive(mTeeState), rv);
 | |
|         } else if (mTeeState->ReadAgainForBranch2()) {
 | |
|           // Step 9. Otherwise, if readAgainForBranch2 is true, perform
 | |
|           // pull2Algorithm.
 | |
|           ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch2,
 | |
|                                      MOZ_KnownLive(mTeeState), rv);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       bool Suppressed() override {
 | |
|         nsIGlobalObject* global = mTeeState->GetStream()->GetParentObject();
 | |
|         return global && global->IsInSyncOperation();
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     MOZ_ASSERT(aChunk.isObjectOrNull());
 | |
|     MOZ_ASSERT(aChunk.toObjectOrNull() != nullptr);
 | |
|     JS::Rooted<JSObject*> chunk(aCx, &aChunk.toObject());
 | |
|     RefPtr<PullWithDefaultReaderChunkStepMicrotask> task =
 | |
|         MakeRefPtr<PullWithDefaultReaderChunkStepMicrotask>(aCx, mTeeState,
 | |
|                                                             chunk);
 | |
|     CycleCollectedJSContext::Get()->DispatchToMicroTask(task.forget());
 | |
|   }
 | |
| 
 | |
|   MOZ_CAN_RUN_SCRIPT void CloseSteps(JSContext* aCx,
 | |
|                                      ErrorResult& aRv) override {
 | |
|     // Step numbering below is relative to Step 15.2. 'close steps' of
 | |
|     // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
 | |
| 
 | |
|     // Step 1. Set reading to false.
 | |
|     mTeeState->SetReading(false);
 | |
| 
 | |
|     // Step 2. If canceled1 is false, perform !
 | |
|     // ReadableByteStreamControllerClose(branch1.[[controller]]).
 | |
|     RefPtr<ReadableByteStreamController> branch1Controller =
 | |
|         mTeeState->Branch1()->Controller()->AsByte();
 | |
|     if (!mTeeState->Canceled1()) {
 | |
|       ReadableByteStreamControllerClose(aCx, branch1Controller, aRv);
 | |
|       if (aRv.Failed()) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Step 3. If canceled2 is false, perform !
 | |
|     // ReadableByteStreamControllerClose(branch2.[[controller]]).
 | |
|     RefPtr<ReadableByteStreamController> branch2Controller =
 | |
|         mTeeState->Branch2()->Controller()->AsByte();
 | |
|     if (!mTeeState->Canceled2()) {
 | |
|       ReadableByteStreamControllerClose(aCx, branch2Controller, aRv);
 | |
|       if (aRv.Failed()) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Step 4. If branch1.[[controller]].[[pendingPullIntos]] is not empty,
 | |
|     // perform ! ReadableByteStreamControllerRespond(branch1.[[controller]], 0).
 | |
|     if (!branch1Controller->PendingPullIntos().isEmpty()) {
 | |
|       ReadableByteStreamControllerRespond(aCx, branch1Controller, 0, aRv);
 | |
|       if (aRv.Failed()) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Step 5. If branch2.[[controller]].[[pendingPullIntos]] is not empty,
 | |
|     // perform ! ReadableByteStreamControllerRespond(branch2.[[controller]], 0).
 | |
|     if (!branch2Controller->PendingPullIntos().isEmpty()) {
 | |
|       ReadableByteStreamControllerRespond(aCx, branch2Controller, 0, aRv);
 | |
|       if (aRv.Failed()) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Step 6. If canceled1 is false or canceled2 is false, resolve
 | |
|     // cancelPromise with undefined.
 | |
|     if (!mTeeState->Canceled1() || !mTeeState->Canceled2()) {
 | |
|       mTeeState->CancelPromise()->MaybeResolveWithUndefined();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void ErrorSteps(JSContext* aCx, JS::Handle<JS::Value> aError,
 | |
|                   ErrorResult& aRv) override {
 | |
|     mTeeState->SetReading(false);
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   ~PullWithDefaultReaderReadRequest() override = default;
 | |
| };
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_INHERITED(PullWithDefaultReaderReadRequest,
 | |
|                                    ReadRequest, mTeeState)
 | |
| NS_IMPL_ADDREF_INHERITED(PullWithDefaultReaderReadRequest, ReadRequest)
 | |
| NS_IMPL_RELEASE_INHERITED(PullWithDefaultReaderReadRequest, ReadRequest)
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PullWithDefaultReaderReadRequest)
 | |
| NS_INTERFACE_MAP_END_INHERITING(ReadRequest)
 | |
| 
 | |
| void ForwardReaderError(TeeState* aTeeState,
 | |
|                         ReadableStreamGenericReader* aThisReader);
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee:
 | |
| // Step 15.
 | |
| void PullWithDefaultReader(JSContext* aCx, TeeState* aTeeState,
 | |
|                            ErrorResult& aRv) {
 | |
|   RefPtr<ReadableStreamGenericReader> reader = aTeeState->GetReader();
 | |
| 
 | |
|   // Step 15.1. If reader implements ReadableStreamBYOBReader,
 | |
|   if (reader->IsBYOB()) {
 | |
|     // Step 15.1.1. Assert: reader.[[readIntoRequests]] is empty.
 | |
|     MOZ_ASSERT(reader->AsBYOB()->ReadIntoRequests().length() == 0);
 | |
| 
 | |
|     // Step 15.1.2. Perform ! ReadableStreamBYOBReaderRelease(reader).
 | |
|     ReadableStreamBYOBReaderRelease(aCx, reader->AsBYOB(), aRv);
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // Step 15.1.3. Set reader to ! AcquireReadableStreamDefaultReader(stream).
 | |
|     reader = AcquireReadableStreamDefaultReader(aTeeState->GetStream(), aRv);
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
|     aTeeState->SetReader(reader);
 | |
| 
 | |
|     // Step 16.1.4. Perform forwardReaderError, given reader.
 | |
|     ForwardReaderError(aTeeState, reader);
 | |
|   }
 | |
| 
 | |
|   // Step 15.2
 | |
|   RefPtr<ReadRequest> readRequest =
 | |
|       new PullWithDefaultReaderReadRequest(aTeeState);
 | |
| 
 | |
|   // Step 15.3
 | |
|   ReadableStreamDefaultReaderRead(aCx, reader, readRequest, aRv);
 | |
| }
 | |
| 
 | |
| class PullWithBYOBReader_ReadIntoRequest final : public ReadIntoRequest {
 | |
|   RefPtr<TeeState> mTeeState;
 | |
|   const TeeBranch mForBranch;
 | |
|   ~PullWithBYOBReader_ReadIntoRequest() override = default;
 | |
| 
 | |
|  public:
 | |
|   NS_DECL_ISUPPORTS_INHERITED
 | |
|   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PullWithBYOBReader_ReadIntoRequest,
 | |
|                                            ReadIntoRequest)
 | |
| 
 | |
|   explicit PullWithBYOBReader_ReadIntoRequest(TeeState* aTeeState,
 | |
|                                               TeeBranch aForBranch)
 | |
|       : mTeeState(aTeeState), mForBranch(aForBranch) {}
 | |
| 
 | |
|   void ChunkSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
 | |
|                   ErrorResult& aRv) override {
 | |
|     // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
 | |
|     // Step 16.4 chunk steps, Step 1.
 | |
|     class PullWithBYOBReaderChunkMicrotask : public MicroTaskRunnable {
 | |
|       RefPtr<TeeState> mTeeState;
 | |
|       JS::PersistentRooted<JSObject*> mChunk;
 | |
|       const TeeBranch mForBranch;
 | |
| 
 | |
|      public:
 | |
|       PullWithBYOBReaderChunkMicrotask(JSContext* aCx, TeeState* aTeeState,
 | |
|                                        JS::Handle<JSObject*> aChunk,
 | |
|                                        TeeBranch aForBranch)
 | |
|           : mTeeState(aTeeState), mChunk(aCx, aChunk), mForBranch(aForBranch) {}
 | |
| 
 | |
|       MOZ_CAN_RUN_SCRIPT
 | |
|       void Run(AutoSlowOperation& aAso) override {
 | |
|         AutoJSAPI jsapi;
 | |
|         if (NS_WARN_IF(
 | |
|                 !jsapi.Init(mTeeState->GetStream()->GetParentObject()))) {
 | |
|           return;
 | |
|         }
 | |
|         JSContext* cx = jsapi.cx();
 | |
|         ErrorResult rv;
 | |
|         // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
 | |
|         //
 | |
|         // Step Numbering below is relative to Chunk steps Microtask at
 | |
|         // Step 16.4 chunk steps, Step 1.
 | |
| 
 | |
|         // Step 1.
 | |
|         mTeeState->SetReadAgainForBranch1(false);
 | |
| 
 | |
|         // Step 2.
 | |
|         mTeeState->SetReadAgainForBranch2(false);
 | |
| 
 | |
|         // Step 3.
 | |
|         bool byobCanceled = mTeeState->Canceled(mForBranch);
 | |
|         // Step 4.
 | |
|         bool otherCanceled = mTeeState->Canceled(OtherTeeBranch(mForBranch));
 | |
| 
 | |
|         // Rather than store byobBranch / otherBranch, we re-derive the pointers
 | |
|         // below, as borrowed from steps 16.2/16.3
 | |
|         ReadableStream* byobBranch = mTeeState->Branch(mForBranch);
 | |
|         ReadableStream* otherBranch =
 | |
|             mTeeState->Branch(OtherTeeBranch(mForBranch));
 | |
| 
 | |
|         // Step 5.
 | |
|         if (!otherCanceled) {
 | |
|           // Step 5.1 (using the name clonedChunk because we don't want to name
 | |
|           // the completion record explicitly)
 | |
|           JS::Rooted<JSObject*> clonedChunk(cx, CloneAsUint8Array(cx, mChunk));
 | |
| 
 | |
|           // Step 5.2. If cloneResult is an abrupt completion,
 | |
|           if (!clonedChunk) {
 | |
|             JS::Rooted<JS::Value> exception(cx);
 | |
|             if (!JS_GetPendingException(cx, &exception)) {
 | |
|               // Uncatchable exception. Return with pending
 | |
|               // exception still on context.
 | |
|               return;
 | |
|             }
 | |
| 
 | |
|             // It's not expliclitly stated, but I assume the intention here is
 | |
|             // that we perform a normal completion here, so we clear the
 | |
|             // exception.
 | |
|             JS_ClearPendingException(cx);
 | |
| 
 | |
|             // Step 5.2.1
 | |
| 
 | |
|             ReadableByteStreamControllerError(
 | |
|                 byobBranch->Controller()->AsByte(), exception, rv);
 | |
|             if (rv.MaybeSetPendingException(cx)) {
 | |
|               return;
 | |
|             }
 | |
| 
 | |
|             // Step 5.2.2.
 | |
|             ReadableByteStreamControllerError(
 | |
|                 otherBranch->Controller()->AsByte(), exception, rv);
 | |
|             if (rv.MaybeSetPendingException(cx)) {
 | |
|               return;
 | |
|             }
 | |
| 
 | |
|             // Step 5.2.3.
 | |
|             RefPtr<ReadableStream> stream = mTeeState->GetStream();
 | |
|             RefPtr<Promise> cancelPromise =
 | |
|                 ReadableStreamCancel(cx, stream, exception, rv);
 | |
|             if (rv.MaybeSetPendingException(cx)) {
 | |
|               return;
 | |
|             }
 | |
|             mTeeState->CancelPromise()->MaybeResolve(cancelPromise);
 | |
| 
 | |
|             // Step 5.2.4.
 | |
|             return;
 | |
|           }
 | |
| 
 | |
|           // Step 5.3 (implicitly handled above by name selection)
 | |
|           // Step 5.4.
 | |
|           if (!byobCanceled) {
 | |
|             RefPtr<ReadableByteStreamController> controller(
 | |
|                 byobBranch->Controller()->AsByte());
 | |
|             ReadableByteStreamControllerRespondWithNewView(cx, controller,
 | |
|                                                            mChunk, rv);
 | |
|             if (rv.MaybeSetPendingException(cx)) {
 | |
|               return;
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           // Step 5.4.
 | |
|           RefPtr<ReadableByteStreamController> otherController =
 | |
|               otherBranch->Controller()->AsByte();
 | |
|           ReadableByteStreamControllerEnqueue(cx, otherController, clonedChunk,
 | |
|                                               rv);
 | |
|           if (rv.MaybeSetPendingException(cx)) {
 | |
|             return;
 | |
|           }
 | |
|           // Step 6.
 | |
|         } else if (!byobCanceled) {
 | |
|           RefPtr<ReadableByteStreamController> byobController =
 | |
|               byobBranch->Controller()->AsByte();
 | |
|           ReadableByteStreamControllerRespondWithNewView(cx, byobController,
 | |
|                                                          mChunk, rv);
 | |
|           if (rv.MaybeSetPendingException(cx)) {
 | |
|             return;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // Step 7.
 | |
|         mTeeState->SetReading(false);
 | |
| 
 | |
|         // Step 8.
 | |
|         if (mTeeState->ReadAgainForBranch1()) {
 | |
|           ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch1,
 | |
|                                      MOZ_KnownLive(mTeeState), rv);
 | |
|           if (rv.MaybeSetPendingException(cx)) {
 | |
|             return;
 | |
|           }
 | |
|         } else if (mTeeState->ReadAgainForBranch2()) {
 | |
|           ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch2,
 | |
|                                      MOZ_KnownLive(mTeeState), rv);
 | |
|           if (rv.MaybeSetPendingException(cx)) {
 | |
|             return;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       bool Suppressed() override {
 | |
|         nsIGlobalObject* global = mTeeState->GetStream()->GetParentObject();
 | |
|         return global && global->IsInSyncOperation();
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     MOZ_ASSERT(aChunk.isObjectOrNull());
 | |
|     MOZ_ASSERT(aChunk.toObjectOrNull());
 | |
|     JS::Rooted<JSObject*> chunk(aCx, aChunk.toObjectOrNull());
 | |
|     RefPtr<PullWithBYOBReaderChunkMicrotask> task =
 | |
|         MakeRefPtr<PullWithBYOBReaderChunkMicrotask>(aCx, mTeeState, chunk,
 | |
|                                                      mForBranch);
 | |
|     CycleCollectedJSContext::Get()->DispatchToMicroTask(task.forget());
 | |
|   }
 | |
| 
 | |
|   MOZ_CAN_RUN_SCRIPT
 | |
|   void CloseSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
 | |
|                   ErrorResult& aRv) override {
 | |
|     // Step 1.
 | |
|     mTeeState->SetReading(false);
 | |
| 
 | |
|     // Step 2.
 | |
|     bool byobCanceled = mTeeState->Canceled(mForBranch);
 | |
| 
 | |
|     // Step 3.
 | |
|     bool otherCanceled = mTeeState->Canceled(OtherTeeBranch(mForBranch));
 | |
| 
 | |
|     // Rather than store byobBranch / otherBranch, we re-derive the pointers
 | |
|     // below, as borrowed from steps 16.2/16.3
 | |
|     ReadableStream* byobBranch = mTeeState->Branch(mForBranch);
 | |
|     ReadableStream* otherBranch = mTeeState->Branch(OtherTeeBranch(mForBranch));
 | |
| 
 | |
|     // Step 4.
 | |
|     if (!byobCanceled) {
 | |
|       RefPtr<ReadableByteStreamController> controller =
 | |
|           byobBranch->Controller()->AsByte();
 | |
|       ReadableByteStreamControllerClose(aCx, controller, aRv);
 | |
|       if (aRv.Failed()) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|     // Step 5.
 | |
|     if (!otherCanceled) {
 | |
|       RefPtr<ReadableByteStreamController> controller =
 | |
|           otherBranch->Controller()->AsByte();
 | |
|       ReadableByteStreamControllerClose(aCx, controller, aRv);
 | |
|       if (aRv.Failed()) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Step 6.
 | |
|     if (!aChunk.isUndefined()) {
 | |
|       MOZ_ASSERT(aChunk.isObject());
 | |
|       MOZ_ASSERT(aChunk.toObjectOrNull());
 | |
| 
 | |
|       JS::Rooted<JSObject*> chunkObject(aCx, &aChunk.toObject());
 | |
|       MOZ_ASSERT(JS_IsArrayBufferViewObject(chunkObject));
 | |
|       // Step 6.1.
 | |
|       MOZ_ASSERT(JS_GetArrayBufferViewByteLength(chunkObject) == 0);
 | |
| 
 | |
|       // Step 6.2.
 | |
|       if (!byobCanceled) {
 | |
|         RefPtr<ReadableByteStreamController> byobController(
 | |
|             byobBranch->Controller()->AsByte());
 | |
|         ReadableByteStreamControllerRespondWithNewView(aCx, byobController,
 | |
|                                                        chunkObject, aRv);
 | |
|         if (aRv.Failed()) {
 | |
|           return;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Step 6.3
 | |
|       if (!otherCanceled &&
 | |
|           !otherBranch->Controller()->AsByte()->PendingPullIntos().isEmpty()) {
 | |
|         RefPtr<ReadableByteStreamController> otherController(
 | |
|             otherBranch->Controller()->AsByte());
 | |
|         ReadableByteStreamControllerRespond(aCx, otherController, 0, aRv);
 | |
|         if (aRv.Failed()) {
 | |
|           return;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Step 7.
 | |
|     if (!byobCanceled || !otherCanceled) {
 | |
|       mTeeState->CancelPromise()->MaybeResolveWithUndefined();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void ErrorSteps(JSContext* aCx, JS::Handle<JS::Value> e,
 | |
|                   ErrorResult& aRv) override {
 | |
|     // Step 1.
 | |
|     mTeeState->SetReading(false);
 | |
|   }
 | |
| };
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_INHERITED(PullWithBYOBReader_ReadIntoRequest,
 | |
|                                    ReadIntoRequest, mTeeState)
 | |
| NS_IMPL_ADDREF_INHERITED(PullWithBYOBReader_ReadIntoRequest, ReadIntoRequest)
 | |
| NS_IMPL_RELEASE_INHERITED(PullWithBYOBReader_ReadIntoRequest, ReadIntoRequest)
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PullWithBYOBReader_ReadIntoRequest)
 | |
| NS_INTERFACE_MAP_END_INHERITING(ReadIntoRequest)
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
 | |
| // Step 16.
 | |
| void PullWithBYOBReader(JSContext* aCx, TeeState* aTeeState,
 | |
|                         JS::Handle<JSObject*> aView, TeeBranch aForBranch,
 | |
|                         ErrorResult& aRv) {
 | |
|   // Step 16.1
 | |
|   if (aTeeState->GetReader()->IsDefault()) {
 | |
|     // Step 16.1.1
 | |
|     MOZ_ASSERT(aTeeState->GetDefaultReader()->ReadRequests().isEmpty());
 | |
| 
 | |
|     // Step 16.1.2. Perform ! ReadableStreamDefaultReaderRelease(reader).
 | |
|     ReadableStreamDefaultReaderRelease(aCx, aTeeState->GetDefaultReader(), aRv);
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // Step 16.1.3. Set reader to !AcquireReadableStreamBYOBReader(stream).
 | |
|     RefPtr<ReadableStreamBYOBReader> reader =
 | |
|         AcquireReadableStreamBYOBReader(aTeeState->GetStream(), aRv);
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
|     aTeeState->SetReader(reader);
 | |
| 
 | |
|     // Step 16.1.4. Perform forwardReaderError, given reader.
 | |
|     ForwardReaderError(aTeeState, reader);
 | |
|   }
 | |
| 
 | |
|   // Step 16.2. Unused in this function, moved to consumers.
 | |
|   // Step 16.3. Unused in this function, moved to consumers.
 | |
| 
 | |
|   // Step 16.4.
 | |
|   RefPtr<ReadIntoRequest> readIntoRequest =
 | |
|       new PullWithBYOBReader_ReadIntoRequest(aTeeState, aForBranch);
 | |
| 
 | |
|   // Step 16.5. Perform ! ReadableStreamBYOBReaderRead(reader, view, 1,
 | |
|   // readIntoRequest).
 | |
|   RefPtr<ReadableStreamBYOBReader> byobReader =
 | |
|       aTeeState->GetReader()->AsBYOB();
 | |
|   ReadableStreamBYOBReaderRead(aCx, byobReader, aView, 1, readIntoRequest, aRv);
 | |
| }
 | |
| 
 | |
| // See https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
 | |
| // Step 14.
 | |
| void ForwardReaderError(TeeState* aTeeState,
 | |
|                         ReadableStreamGenericReader* aThisReader) {
 | |
|   aThisReader->ClosedPromise()->AddCallbacksWithCycleCollectedArgs(
 | |
|       [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
 | |
|          TeeState* aTeeState, ReadableStreamGenericReader* aThisReader) {},
 | |
|       [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
 | |
|          TeeState* aTeeState, ReadableStreamGenericReader* aReader) {
 | |
|         // Step 14.1.1
 | |
|         if (aTeeState->GetReader() != aReader) {
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         ErrorResult rv;
 | |
|         // Step 14.1.2: Perform
 | |
|         // !ReadableByteStreamControllerError(branch1.[[controller]], r).
 | |
|         MOZ_ASSERT(aTeeState->Branch1()->Controller()->IsByte());
 | |
|         ReadableByteStreamControllerError(
 | |
|             aTeeState->Branch1()->Controller()->AsByte(), aValue, aRv);
 | |
|         if (aRv.Failed()) {
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         // Step 14.1.3: Perform
 | |
|         // !ReadableByteStreamControllerError(branch2.[[controller]], r).
 | |
|         MOZ_ASSERT(aTeeState->Branch2()->Controller()->IsByte());
 | |
|         ReadableByteStreamControllerError(
 | |
|             aTeeState->Branch2()->Controller()->AsByte(), aValue, aRv);
 | |
|         if (aRv.Failed()) {
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         // Step 14.1.4: If canceled1 is false or canceled2 is false, resolve
 | |
|         // cancelPromise with undefined.
 | |
|         if (!aTeeState->Canceled1() || !aTeeState->Canceled2()) {
 | |
|           aTeeState->CancelPromise()->MaybeResolveWithUndefined();
 | |
|         }
 | |
|       },
 | |
|       RefPtr(aTeeState), RefPtr(aThisReader));
 | |
| }
 | |
| 
 | |
| namespace streams_abstract {
 | |
| // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
 | |
| void ReadableByteStreamTee(JSContext* aCx, ReadableStream* aStream,
 | |
|                            nsTArray<RefPtr<ReadableStream>>& aResult,
 | |
|                            ErrorResult& aRv) {
 | |
|   // Step 1. Implicit
 | |
|   // Step 2.
 | |
|   MOZ_ASSERT(aStream->Controller()->IsByte());
 | |
| 
 | |
|   // Step 3-13 captured as part of TeeState allocation
 | |
|   RefPtr<TeeState> teeState = TeeState::Create(aStream, false, aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Step 14: See ForwardReaderError
 | |
|   // Step 15. See PullWithDefaultReader
 | |
|   // Step 16. See PullWithBYOBReader
 | |
|   // Step 17,18. See {Native,}ByteStreamTeePullAlgorithm
 | |
|   // Step 19,20. See ReadableByteStreamTeeCancelAlgorithm
 | |
|   // Step 21. Elided because consumers know how to handle nullptr correctly.
 | |
|   // Step 22.
 | |
|   nsCOMPtr<nsIGlobalObject> global = aStream->GetParentObject();
 | |
|   auto branch1Algorithms =
 | |
|       MakeRefPtr<ByteStreamTeeSourceAlgorithms>(teeState, TeeBranch::Branch1);
 | |
|   teeState->SetBranch1(
 | |
|       ReadableStream::CreateByteAbstract(aCx, global, branch1Algorithms, aRv));
 | |
|   if (aRv.Failed()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Step 23.
 | |
|   auto branch2Algorithms =
 | |
|       MakeRefPtr<ByteStreamTeeSourceAlgorithms>(teeState, TeeBranch::Branch2);
 | |
|   teeState->SetBranch2(
 | |
|       ReadableStream::CreateByteAbstract(aCx, global, branch2Algorithms, aRv));
 | |
|   if (aRv.Failed()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Step 24.
 | |
|   ForwardReaderError(teeState, teeState->GetReader());
 | |
| 
 | |
|   // Step 25.
 | |
|   aResult.AppendElement(teeState->Branch1());
 | |
|   aResult.AppendElement(teeState->Branch2());
 | |
| }
 | |
| }  // namespace streams_abstract
 | |
| 
 | |
| }  // namespace mozilla::dom
 |