2021-11-16 01:01:39 +03:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
2023-01-13 20:05:18 +03:00
|
|
|
#include "ReadableStreamTee.h"
|
|
|
|
|
|
|
|
#include "ReadIntoRequest.h"
|
|
|
|
#include "TeeState.h"
|
2022-01-05 01:33:24 +03:00
|
|
|
#include "js/Exception.h"
|
|
|
|
#include "js/TypeDecls.h"
|
|
|
|
#include "js/experimental/TypedData.h"
|
|
|
|
#include "mozilla/dom/ByteStreamHelpers.h"
|
2022-03-21 19:45:37 +03:00
|
|
|
#include "mozilla/dom/Promise-inl.h"
|
2022-02-19 01:56:33 +03:00
|
|
|
#include "mozilla/dom/ReadableStream.h"
|
2022-01-05 01:33:24 +03:00
|
|
|
#include "mozilla/dom/ReadableStreamBYOBReader.h"
|
|
|
|
#include "mozilla/dom/ReadableStreamDefaultController.h"
|
|
|
|
#include "mozilla/dom/ReadableStreamGenericReader.h"
|
2021-11-16 01:01:39 +03:00
|
|
|
#include "mozilla/dom/ReadableStreamDefaultReader.h"
|
2022-01-05 01:33:24 +03:00
|
|
|
#include "mozilla/dom/ReadableByteStreamController.h"
|
2021-11-16 01:01:39 +03:00
|
|
|
#include "mozilla/dom/UnderlyingSourceBinding.h"
|
2022-01-05 01:33:24 +03:00
|
|
|
#include "mozilla/dom/UnderlyingSourceCallbackHelpers.h"
|
2021-11-16 01:01:39 +03:00
|
|
|
#include "nsCycleCollectionParticipant.h"
|
|
|
|
#include "mozilla/CycleCollectedJSContext.h"
|
|
|
|
|
|
|
|
namespace mozilla::dom {
|
|
|
|
|
2023-02-07 14:56:05 +03:00
|
|
|
using namespace streams_abstract;
|
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
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, ReadableStreamController& aController, ErrorResult& aRv) {
|
|
|
|
nsCOMPtr<nsIGlobalObject> global = aController.GetParentObject();
|
|
|
|
mTeeState->PullCallback(aCx, global, aRv);
|
|
|
|
if (!aRv.Failed()) {
|
|
|
|
return Promise::CreateResolvedWithUndefined(global, aRv);
|
2021-11-16 01:01:39 +03:00
|
|
|
}
|
2022-03-08 22:29:14 +03:00
|
|
|
return nullptr;
|
2021-11-16 01:01:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2022-03-08 22:29:14 +03:00
|
|
|
// Virtually const, but is cycle collected
|
|
|
|
MOZ_KNOWN_LIVE RefPtr<TeeState> mTeeState;
|
2021-11-16 01:01:39 +03:00
|
|
|
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.
|
2021-11-16 01:01:39 +03:00
|
|
|
mTeeState->SetReadAgain(false);
|
2021-11-16 01:01:39 +03:00
|
|
|
|
|
|
|
// Step 2.
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JS::Value> chunk1(cx, mChunk);
|
|
|
|
JS::Rooted<JS::Value> chunk2(cx, mChunk);
|
2021-11-16 01:01:39 +03:00
|
|
|
|
|
|
|
// Step 3. Skipped until we implement cloneForBranch2 path.
|
|
|
|
MOZ_RELEASE_ASSERT(!mTeeState->CloneForBranch2());
|
|
|
|
|
|
|
|
// Step 4.
|
|
|
|
if (!mTeeState->Canceled1()) {
|
|
|
|
IgnoredErrorResult rv;
|
2022-01-05 01:33:21 +03:00
|
|
|
// Since we controlled the creation of the two stream branches, we know
|
|
|
|
// they both have default controllers.
|
2021-11-16 01:01:39 +03:00
|
|
|
RefPtr<ReadableStreamDefaultController> controller(
|
2022-01-05 01:33:21 +03:00
|
|
|
mTeeState->Branch1()->DefaultController());
|
2021-11-16 01:01:39 +03:00
|
|
|
ReadableStreamDefaultControllerEnqueue(cx, controller, chunk1, rv);
|
|
|
|
(void)NS_WARN_IF(rv.Failed());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 5.
|
|
|
|
if (!mTeeState->Canceled2()) {
|
|
|
|
IgnoredErrorResult rv;
|
|
|
|
RefPtr<ReadableStreamDefaultController> controller(
|
2022-01-05 01:33:21 +03:00
|
|
|
mTeeState->Branch2()->DefaultController());
|
2021-11-16 01:01:39 +03:00
|
|
|
ReadableStreamDefaultControllerEnqueue(cx, controller, chunk2, rv);
|
|
|
|
(void)NS_WARN_IF(rv.Failed());
|
|
|
|
}
|
2021-11-16 01:01:39 +03:00
|
|
|
|
|
|
|
// Step 6.
|
|
|
|
mTeeState->SetReading(false);
|
|
|
|
|
|
|
|
// Step 7. If |readAgain| is true, perform |pullAlgorithm|.
|
|
|
|
if (mTeeState->ReadAgain()) {
|
|
|
|
IgnoredErrorResult rv;
|
|
|
|
nsCOMPtr<nsIGlobalObject> global(
|
|
|
|
mTeeState->GetStream()->GetParentObject());
|
2022-03-08 22:29:14 +03:00
|
|
|
mTeeState->PullCallback(cx, global, rv);
|
2021-11-16 01:01:39 +03:00
|
|
|
(void)NS_WARN_IF(rv.Failed());
|
|
|
|
}
|
2021-11-16 01:01:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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()) {
|
2022-02-04 23:35:14 +03:00
|
|
|
RefPtr<ReadableStreamDefaultController> controller(
|
|
|
|
mTeeState->Branch1()->DefaultController());
|
|
|
|
ReadableStreamDefaultControllerClose(aCx, controller, aRv);
|
2021-11-16 01:01:39 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3.
|
|
|
|
if (!mTeeState->Canceled2()) {
|
2022-02-04 23:35:14 +03:00
|
|
|
RefPtr<ReadableStreamDefaultController> controller(
|
|
|
|
mTeeState->Branch2()->DefaultController());
|
|
|
|
ReadableStreamDefaultControllerClose(aCx, controller, aRv);
|
2021-11-16 01:01:39 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-01-07 23:46:19 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT void PullWithDefaultReader(JSContext* aCx,
|
|
|
|
TeeState* aTeeState,
|
|
|
|
ErrorResult& aRv);
|
|
|
|
MOZ_CAN_RUN_SCRIPT void PullWithBYOBReader(JSContext* aCx, TeeState* aTeeState,
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Handle<JSObject*> aView,
|
2022-02-08 19:37:45 +03:00
|
|
|
TeeBranch aForBranch,
|
|
|
|
ErrorResult& aRv);
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// 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.
|
2022-02-08 19:37:45 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT void ByteStreamTeePullAlgorithm(JSContext* aCx,
|
|
|
|
TeeBranch aForBranch,
|
2022-01-07 23:46:19 +03:00
|
|
|
TeeState* aTeeState,
|
|
|
|
ErrorResult& aRv) {
|
2022-01-05 01:33:24 +03:00
|
|
|
// Step {17,18}.1: If reading is true,
|
|
|
|
if (aTeeState->Reading()) {
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step {17,18}.1.1: Set readAgainForBranch{1,2} to true.
|
2022-02-08 19:37:45 +03:00
|
|
|
aTeeState->SetReadAgainForBranch(aForBranch, true);
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// Step {17,18}.1.1: Return a promise resolved with undefined.
|
|
|
|
return;
|
|
|
|
}
|
2022-01-19 12:56:49 +03:00
|
|
|
|
2022-01-05 01:33:24 +03:00
|
|
|
// Step {17,18}.2: Set reading to true.
|
|
|
|
aTeeState->SetReading(true);
|
|
|
|
|
|
|
|
// Step {17,18}.3: Let byobRequest be
|
2022-01-19 12:56:49 +03:00
|
|
|
// !ReadableByteStreamControllerGetBYOBRequest(branch{1,2}.[[controller]]).
|
2022-01-05 01:33:24 +03:00
|
|
|
RefPtr<ReadableStreamBYOBRequest> byobRequest =
|
|
|
|
ReadableByteStreamControllerGetBYOBRequest(
|
2022-02-08 19:37:45 +03:00
|
|
|
aCx, aTeeState->Branch(aForBranch)->Controller()->AsByte(), aRv);
|
2022-01-19 12:56:49 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// 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
|
2022-02-08 19:37:45 +03:00
|
|
|
// byobRequest.[[view]] and {false, true}.
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JSObject*> view(aCx, byobRequest->View());
|
2022-02-08 19:37:45 +03:00
|
|
|
PullWithBYOBReader(aCx, aTeeState, view, aForBranch, aRv);
|
2022-01-05 01:33:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Step {17,18}.6: Return a promise resolved with undefined.
|
|
|
|
}
|
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
|
|
|
|
class ByteStreamTeeSourceAlgorithms final
|
|
|
|
: public UnderlyingSourceAlgorithmsBase {
|
2022-01-05 01:33:24 +03:00
|
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS_INHERITED
|
2022-03-08 22:29:14 +03:00
|
|
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ByteStreamTeeSourceAlgorithms,
|
|
|
|
UnderlyingSourceAlgorithmsBase)
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
ByteStreamTeeSourceAlgorithms(TeeState* aTeeState, TeeBranch aBranch)
|
2022-02-08 19:37:45 +03:00
|
|
|
: mTeeState(aTeeState), mBranch(aBranch) {}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT void StartCallback(JSContext* aCx,
|
|
|
|
ReadableStreamController& aController,
|
|
|
|
JS::MutableHandle<JS::Value> aRetVal,
|
|
|
|
ErrorResult& aRv) override {
|
|
|
|
// Step 21: Let startAlgorithm be an algorithm that returns undefined.
|
|
|
|
aRetVal.setUndefined();
|
|
|
|
}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
// Step 17, 18
|
|
|
|
MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PullCallback(
|
|
|
|
JSContext* aCx, ReadableStreamController& aController,
|
|
|
|
ErrorResult& aRv) override {
|
|
|
|
// Step 1 - 5
|
2022-02-08 19:37:45 +03:00
|
|
|
ByteStreamTeePullAlgorithm(aCx, mBranch, MOZ_KnownLive(mTeeState), aRv);
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
// Step 6: Return a promise resolved with undefined.
|
|
|
|
return Promise::CreateResolvedWithUndefined(
|
|
|
|
mTeeState->GetStream()->GetParentObject(), aRv);
|
2022-01-05 01:33:24 +03:00
|
|
|
}
|
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
// 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);
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
// Step 2.
|
|
|
|
mTeeState->SetReason(mBranch, aReason.Value());
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
// Step 3.
|
|
|
|
if (mTeeState->Canceled(otherStream())) {
|
|
|
|
// Step 3.1
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JSObject*> compositeReason(aCx, JS::NewArrayObject(aCx, 2));
|
2022-03-08 22:29:14 +03:00
|
|
|
if (!compositeReason) {
|
|
|
|
aRv.StealExceptionFromJSContext(aCx);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JS::Value> reason1(aCx, mTeeState->Reason1());
|
2022-03-08 22:29:14 +03:00
|
|
|
if (!JS_SetElement(aCx, compositeReason, 0, reason1)) {
|
|
|
|
aRv.StealExceptionFromJSContext(aCx);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JS::Value> reason2(aCx, mTeeState->Reason2());
|
2022-03-08 22:29:14 +03:00
|
|
|
if (!JS_SetElement(aCx, compositeReason, 1, reason2)) {
|
|
|
|
aRv.StealExceptionFromJSContext(aCx);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3.2
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JS::Value> compositeReasonValue(
|
|
|
|
aCx, JS::ObjectValue(*compositeReason));
|
2022-03-08 22:29:14 +03:00
|
|
|
RefPtr<ReadableStream> stream(mTeeState->GetStream());
|
|
|
|
RefPtr<Promise> cancelResult =
|
|
|
|
ReadableStreamCancel(aCx, stream, compositeReasonValue, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
// Step 3.3
|
|
|
|
mTeeState->CancelPromise()->MaybeResolve(cancelResult);
|
|
|
|
}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-03-08 22:29:14 +03:00
|
|
|
// 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)
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 1. Set readAgainForBranch1 to false.
|
2022-01-05 01:33:24 +03:00
|
|
|
mTeeState->SetReadAgainForBranch1(false);
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 2. Set readAgainForBranch2 to false.
|
2022-01-05 01:33:24 +03:00
|
|
|
mTeeState->SetReadAgainForBranch2(false);
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 3. Let chunk1 and chunk2 be chunk.
|
|
|
|
JS::Rooted<JSObject*> chunk1(cx, mChunk);
|
|
|
|
JS::Rooted<JSObject*> chunk2(cx, mChunk);
|
2022-01-05 01:33:24 +03:00
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 4. If canceled1 is false and canceled2 is false,
|
2022-01-05 01:33:24 +03:00
|
|
|
ErrorResult rv;
|
|
|
|
if (!mTeeState->Canceled1() && !mTeeState->Canceled2()) {
|
2022-01-19 12:56:49 +03:00
|
|
|
// 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,
|
2022-01-05 01:33:24 +03:00
|
|
|
if (!cloneResult) {
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 4.2.1 Perform
|
|
|
|
// !ReadableByteStreamControllerError(branch1.[[controller]],
|
|
|
|
// cloneResult.[[Value]]).
|
|
|
|
JS::Rooted<JS::Value> exceptionValue(cx);
|
2022-01-05 01:33:24 +03:00
|
|
|
if (!JS_GetPendingException(cx, &exceptionValue)) {
|
|
|
|
// Uncatchable exception, simply return.
|
|
|
|
return;
|
|
|
|
}
|
2022-01-19 12:56:49 +03:00
|
|
|
JS_ClearPendingException(cx);
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
ErrorResult rv;
|
|
|
|
ReadableByteStreamControllerError(
|
|
|
|
mTeeState->Branch1()->Controller()->AsByte(), exceptionValue,
|
|
|
|
rv);
|
|
|
|
if (rv.MaybeSetPendingException(
|
|
|
|
cx, "Error during ReadableByteStreamControllerError")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 4.2.2. Perform !
|
|
|
|
// ReadableByteStreamControllerError(branch2.[[controller]],
|
|
|
|
// cloneResult.[[Value]]).
|
2022-01-05 01:33:24 +03:00
|
|
|
ReadableByteStreamControllerError(
|
|
|
|
mTeeState->Branch2()->Controller()->AsByte(), exceptionValue,
|
|
|
|
rv);
|
|
|
|
if (rv.MaybeSetPendingException(
|
|
|
|
cx, "Error during ReadableByteStreamControllerError")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 4.2.3. Resolve cancelPromise with !
|
|
|
|
// ReadableStreamCancel(stream, cloneResult.[[Value]]).
|
2022-01-05 01:33:24 +03:00
|
|
|
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);
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 4.2.4. Return.
|
2022-01-05 01:33:24 +03:00
|
|
|
return;
|
|
|
|
}
|
2022-01-19 12:56:49 +03:00
|
|
|
|
|
|
|
// Step 4.3. Otherwise, set chunk2 to cloneResult.[[Value]].
|
2022-01-05 01:33:24 +03:00
|
|
|
chunk2 = cloneResult;
|
|
|
|
}
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 5. If canceled1 is false,
|
|
|
|
// perform ! ReadableByteStreamControllerEnqueue(branch1.[[controller]],
|
|
|
|
// chunk1).
|
2022-01-05 01:33:24 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2022-01-19 12:56:49 +03:00
|
|
|
|
|
|
|
// Step 6. If canceled2 is false,
|
|
|
|
// perform ! ReadableByteStreamControllerEnqueue(branch2.[[controller]],
|
|
|
|
// chunk2).
|
2022-01-05 01:33:24 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 7. Set reading to false.
|
2022-01-05 01:33:24 +03:00
|
|
|
mTeeState->SetReading(false);
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
// Step 8. If readAgainForBranch1 is true, perform pull1Algorithm.
|
2022-01-05 01:33:24 +03:00
|
|
|
if (mTeeState->ReadAgainForBranch1()) {
|
2022-02-08 19:37:45 +03:00
|
|
|
ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch1,
|
|
|
|
MOZ_KnownLive(mTeeState), rv);
|
2022-01-19 12:56:49 +03:00
|
|
|
} else if (mTeeState->ReadAgainForBranch2()) {
|
|
|
|
// Step 9. Otherwise, if readAgainForBranch2 is true, perform
|
|
|
|
// pull2Algorithm.
|
2022-02-08 19:37:45 +03:00
|
|
|
ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch2,
|
|
|
|
MOZ_KnownLive(mTeeState), rv);
|
2022-01-05 01:33:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Suppressed() override {
|
|
|
|
nsIGlobalObject* global = mTeeState->GetStream()->GetParentObject();
|
|
|
|
return global && global->IsInSyncOperation();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
MOZ_ASSERT(aChunk.isObjectOrNull());
|
|
|
|
MOZ_ASSERT(aChunk.toObjectOrNull() != nullptr);
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JSObject*> chunk(aCx, &aChunk.toObject());
|
2022-01-05 01:33:24 +03:00
|
|
|
RefPtr<PullWithDefaultReaderChunkStepMicrotask> task =
|
|
|
|
MakeRefPtr<PullWithDefaultReaderChunkStepMicrotask>(aCx, mTeeState,
|
|
|
|
chunk);
|
|
|
|
CycleCollectedJSContext::Get()->DispatchToMicroTask(task.forget());
|
|
|
|
}
|
|
|
|
|
2022-02-04 23:35:14 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT void CloseSteps(JSContext* aCx,
|
|
|
|
ErrorResult& aRv) override {
|
2022-01-19 12:56:49 +03:00
|
|
|
// 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);
|
|
|
|
}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
protected:
|
2022-02-19 01:56:33 +03:00
|
|
|
~PullWithDefaultReaderReadRequest() override = default;
|
2022-01-05 01:33:24 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
2022-01-19 12:56:49 +03:00
|
|
|
void ForwardReaderError(TeeState* aTeeState,
|
|
|
|
ReadableStreamGenericReader* aThisReader);
|
|
|
|
|
2022-01-05 01:33:24 +03:00
|
|
|
// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee:
|
|
|
|
// Step 15.
|
|
|
|
void PullWithDefaultReader(JSContext* aCx, TeeState* aTeeState,
|
|
|
|
ErrorResult& aRv) {
|
2022-01-19 12:56:49 +03:00
|
|
|
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).
|
2022-02-06 13:11:53 +03:00
|
|
|
ReadableStreamBYOBReaderRelease(aCx, reader->AsBYOB(), aRv);
|
2022-01-19 12:56:49 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 15.1.3. Set reader to ! AcquireReadableStreamDefaultReader(stream).
|
2022-03-01 01:31:41 +03:00
|
|
|
reader = AcquireReadableStreamDefaultReader(aTeeState->GetStream(), aRv);
|
2022-01-19 12:56:49 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
aTeeState->SetReader(reader);
|
|
|
|
|
|
|
|
// Step 16.1.4. Perform forwardReaderError, given reader.
|
|
|
|
ForwardReaderError(aTeeState, reader);
|
|
|
|
}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// 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;
|
2022-02-08 19:37:45 +03:00
|
|
|
const TeeBranch mForBranch;
|
2022-02-19 01:56:33 +03:00
|
|
|
~PullWithBYOBReader_ReadIntoRequest() override = default;
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS_INHERITED
|
|
|
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PullWithBYOBReader_ReadIntoRequest,
|
|
|
|
ReadIntoRequest)
|
|
|
|
|
|
|
|
explicit PullWithBYOBReader_ReadIntoRequest(TeeState* aTeeState,
|
2022-02-08 19:37:45 +03:00
|
|
|
TeeBranch aForBranch)
|
|
|
|
: mTeeState(aTeeState), mForBranch(aForBranch) {}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
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;
|
2022-02-08 19:37:45 +03:00
|
|
|
const TeeBranch mForBranch;
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
public:
|
|
|
|
PullWithBYOBReaderChunkMicrotask(JSContext* aCx, TeeState* aTeeState,
|
|
|
|
JS::Handle<JSObject*> aChunk,
|
2022-02-08 19:37:45 +03:00
|
|
|
TeeBranch aForBranch)
|
|
|
|
: mTeeState(aTeeState), mChunk(aCx, aChunk), mForBranch(aForBranch) {}
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
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.
|
2022-02-08 19:37:45 +03:00
|
|
|
bool byobCanceled = mTeeState->Canceled(mForBranch);
|
2022-01-05 01:33:24 +03:00
|
|
|
// Step 4.
|
2022-02-08 19:37:45 +03:00
|
|
|
bool otherCanceled = mTeeState->Canceled(OtherTeeBranch(mForBranch));
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// Rather than store byobBranch / otherBranch, we re-derive the pointers
|
|
|
|
// below, as borrowed from steps 16.2/16.3
|
2022-02-08 19:37:45 +03:00
|
|
|
ReadableStream* byobBranch = mTeeState->Branch(mForBranch);
|
2022-01-05 01:33:24 +03:00
|
|
|
ReadableStream* otherBranch =
|
2022-02-08 19:37:45 +03:00
|
|
|
mTeeState->Branch(OtherTeeBranch(mForBranch));
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// Step 5.
|
|
|
|
if (!otherCanceled) {
|
|
|
|
// Step 5.1 (using the name clonedChunk because we don't want to name
|
|
|
|
// the completion record explicitly)
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JSObject*> clonedChunk(cx, CloneAsUint8Array(cx, mChunk));
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// Step 5.2. If cloneResult is an abrupt completion,
|
|
|
|
if (!clonedChunk) {
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JS::Value> exception(cx);
|
2022-01-05 01:33:24 +03:00
|
|
|
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()) {
|
2022-02-08 19:37:45 +03:00
|
|
|
ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch1,
|
|
|
|
MOZ_KnownLive(mTeeState), rv);
|
2022-01-05 01:33:24 +03:00
|
|
|
if (rv.MaybeSetPendingException(cx)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (mTeeState->ReadAgainForBranch2()) {
|
2022-02-08 19:37:45 +03:00
|
|
|
ByteStreamTeePullAlgorithm(cx, TeeBranch::Branch2,
|
|
|
|
MOZ_KnownLive(mTeeState), rv);
|
2022-01-05 01:33:24 +03:00
|
|
|
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());
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JSObject*> chunk(aCx, aChunk.toObjectOrNull());
|
2022-01-05 01:33:24 +03:00
|
|
|
RefPtr<PullWithBYOBReaderChunkMicrotask> task =
|
|
|
|
MakeRefPtr<PullWithBYOBReaderChunkMicrotask>(aCx, mTeeState, chunk,
|
2022-02-08 19:37:45 +03:00
|
|
|
mForBranch);
|
2022-01-05 01:33:24 +03:00
|
|
|
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.
|
2022-02-08 19:37:45 +03:00
|
|
|
bool byobCanceled = mTeeState->Canceled(mForBranch);
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// Step 3.
|
2022-02-08 19:37:45 +03:00
|
|
|
bool otherCanceled = mTeeState->Canceled(OtherTeeBranch(mForBranch));
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// Rather than store byobBranch / otherBranch, we re-derive the pointers
|
|
|
|
// below, as borrowed from steps 16.2/16.3
|
2022-02-08 19:37:45 +03:00
|
|
|
ReadableStream* byobBranch = mTeeState->Branch(mForBranch);
|
|
|
|
ReadableStream* otherBranch = mTeeState->Branch(OtherTeeBranch(mForBranch));
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// Step 4.
|
|
|
|
if (!byobCanceled) {
|
2022-02-04 23:35:14 +03:00
|
|
|
RefPtr<ReadableByteStreamController> controller =
|
|
|
|
byobBranch->Controller()->AsByte();
|
|
|
|
ReadableByteStreamControllerClose(aCx, controller, aRv);
|
2022-01-05 01:33:24 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Step 5.
|
|
|
|
if (!otherCanceled) {
|
2022-02-04 23:35:14 +03:00
|
|
|
RefPtr<ReadableByteStreamController> controller =
|
|
|
|
otherBranch->Controller()->AsByte();
|
|
|
|
ReadableByteStreamControllerClose(aCx, controller, aRv);
|
2022-01-05 01:33:24 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 6.
|
|
|
|
if (!aChunk.isUndefined()) {
|
|
|
|
MOZ_ASSERT(aChunk.isObject());
|
|
|
|
MOZ_ASSERT(aChunk.toObjectOrNull());
|
|
|
|
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Rooted<JSObject*> chunkObject(aCx, &aChunk.toObject());
|
2022-01-05 01:33:24 +03:00
|
|
|
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,
|
2022-04-20 17:34:18 +03:00
|
|
|
ErrorResult& aRv) override {
|
2022-01-05 01:33:24 +03:00
|
|
|
// 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,
|
2022-05-18 11:43:08 +03:00
|
|
|
JS::Handle<JSObject*> aView, TeeBranch aForBranch,
|
2022-01-05 01:33:24 +03:00
|
|
|
ErrorResult& aRv) {
|
|
|
|
// Step 16.1
|
|
|
|
if (aTeeState->GetReader()->IsDefault()) {
|
|
|
|
// Step 16.1.1
|
|
|
|
MOZ_ASSERT(aTeeState->GetDefaultReader()->ReadRequests().isEmpty());
|
|
|
|
|
2022-02-06 13:11:53 +03:00
|
|
|
// Step 16.1.2. Perform ! ReadableStreamDefaultReaderRelease(reader).
|
|
|
|
ReadableStreamDefaultReaderRelease(aCx, aTeeState->GetDefaultReader(), aRv);
|
2022-01-05 01:33:24 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 16.1.3. Set reader to !AcquireReadableStreamBYOBReader(stream).
|
|
|
|
RefPtr<ReadableStreamBYOBReader> reader =
|
2022-03-01 01:31:41 +03:00
|
|
|
AcquireReadableStreamBYOBReader(aTeeState->GetStream(), aRv);
|
2022-01-05 01:33:24 +03:00
|
|
|
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 =
|
2022-02-08 19:37:45 +03:00
|
|
|
new PullWithBYOBReader_ReadIntoRequest(aTeeState, aForBranch);
|
2022-01-05 01:33:24 +03:00
|
|
|
|
|
|
|
// Step 16.5.
|
|
|
|
RefPtr<ReadableStreamBYOBReader> byobReader =
|
|
|
|
aTeeState->GetReader()->AsBYOB();
|
|
|
|
ReadableStreamBYOBReaderRead(aCx, byobReader, aView, readIntoRequest, aRv);
|
|
|
|
}
|
|
|
|
|
|
|
|
// See https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee
|
|
|
|
// Step 14.
|
|
|
|
void ForwardReaderError(TeeState* aTeeState,
|
|
|
|
ReadableStreamGenericReader* aThisReader) {
|
2022-03-21 19:45:37 +03:00
|
|
|
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));
|
2022-01-05 01:33:24 +03:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:56:05 +03:00
|
|
|
namespace streams_abstract {
|
2022-01-05 01:33:24 +03:00
|
|
|
// 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
|
2022-03-01 01:31:42 +03:00
|
|
|
RefPtr<TeeState> teeState = TeeState::Create(aStream, false, aRv);
|
2022-01-05 01:33:24 +03:00
|
|
|
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();
|
2022-03-08 22:29:14 +03:00
|
|
|
auto branch1Algorithms =
|
|
|
|
MakeRefPtr<ByteStreamTeeSourceAlgorithms>(teeState, TeeBranch::Branch1);
|
|
|
|
teeState->SetBranch1(
|
2023-02-13 16:36:34 +03:00
|
|
|
ReadableStream::CreateByteAbstract(aCx, global, branch1Algorithms, aRv));
|
2022-01-05 01:33:24 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 23.
|
2022-03-08 22:29:14 +03:00
|
|
|
auto branch2Algorithms =
|
|
|
|
MakeRefPtr<ByteStreamTeeSourceAlgorithms>(teeState, TeeBranch::Branch2);
|
|
|
|
teeState->SetBranch2(
|
2023-02-13 16:36:34 +03:00
|
|
|
ReadableStream::CreateByteAbstract(aCx, global, branch2Algorithms, aRv));
|
2022-01-05 01:33:24 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 24.
|
|
|
|
ForwardReaderError(teeState, teeState->GetReader());
|
|
|
|
|
|
|
|
// Step 25.
|
|
|
|
aResult.AppendElement(teeState->Branch1());
|
|
|
|
aResult.AppendElement(teeState->Branch2());
|
|
|
|
}
|
2023-02-07 14:56:05 +03:00
|
|
|
} // namespace streams_abstract
|
|
|
|
|
2021-11-16 01:01:39 +03:00
|
|
|
} // namespace mozilla::dom
|