2021-10-06 21:43:01 +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/. */
|
|
|
|
|
|
|
|
#include "mozilla/dom/ReadableStream.h"
|
2021-10-06 21:43:02 +03:00
|
|
|
#include "js/Array.h"
|
|
|
|
#include "js/PropertyAndElement.h"
|
2021-10-06 21:43:01 +03:00
|
|
|
#include "js/TypeDecls.h"
|
|
|
|
#include "js/Value.h"
|
2021-10-06 21:43:02 +03:00
|
|
|
#include "mozilla/AlreadyAddRefed.h"
|
|
|
|
#include "mozilla/Assertions.h"
|
2021-10-06 21:43:01 +03:00
|
|
|
#include "mozilla/Attributes.h"
|
2021-10-06 21:43:02 +03:00
|
|
|
#include "mozilla/CycleCollectedJSContext.h"
|
2021-10-06 21:43:01 +03:00
|
|
|
#include "mozilla/FloatingPoint.h"
|
|
|
|
#include "mozilla/HoldDropJSObjects.h"
|
|
|
|
#include "mozilla/dom/BindingCallContext.h"
|
2021-10-06 21:43:02 +03:00
|
|
|
#include "mozilla/dom/ModuleMapKey.h"
|
|
|
|
#include "mozilla/dom/QueueWithSizes.h"
|
2021-10-06 21:43:01 +03:00
|
|
|
#include "mozilla/dom/QueuingStrategyBinding.h"
|
2021-10-06 21:43:02 +03:00
|
|
|
#include "mozilla/dom/ReadRequest.h"
|
2022-01-05 01:33:22 +03:00
|
|
|
#include "mozilla/dom/ReadableByteStreamController.h"
|
|
|
|
#include "mozilla/dom/ReadableStreamBYOBReader.h"
|
2021-10-06 21:43:01 +03:00
|
|
|
#include "mozilla/dom/ReadableStreamBinding.h"
|
|
|
|
#include "mozilla/dom/ReadableStreamDefaultController.h"
|
|
|
|
#include "mozilla/dom/ReadableStreamDefaultReader.h"
|
2021-11-16 01:01:39 +03:00
|
|
|
#include "mozilla/dom/ReadableStreamTee.h"
|
2021-10-06 21:43:02 +03:00
|
|
|
#include "mozilla/dom/ScriptSettings.h"
|
2021-12-31 14:25:09 +03:00
|
|
|
#include "mozilla/dom/StreamUtils.h"
|
2021-10-06 21:43:02 +03:00
|
|
|
#include "mozilla/dom/TeeState.h"
|
2021-10-06 21:43:01 +03:00
|
|
|
#include "mozilla/dom/UnderlyingSourceBinding.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
|
|
|
|
#include "mozilla/dom/Promise-inl.h"
|
2021-10-06 21:43:02 +03:00
|
|
|
#include "nsIGlobalObject.h"
|
|
|
|
#include "nsISupports.h"
|
|
|
|
|
|
|
|
#include <unistd.h>
|
2021-10-06 21:43:01 +03:00
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
inline void ImplCycleCollectionTraverse(
|
|
|
|
nsCycleCollectionTraversalCallback& aCallback,
|
|
|
|
mozilla::Variant<mozilla::Nothing,
|
|
|
|
RefPtr<mozilla::dom::ReadableStreamDefaultReader>>&
|
|
|
|
aReader,
|
|
|
|
const char* aName, uint32_t aFlags = 0) {
|
|
|
|
if (aReader.is<RefPtr<mozilla::dom::ReadableStreamDefaultReader>>()) {
|
|
|
|
ImplCycleCollectionTraverse(
|
|
|
|
aCallback,
|
|
|
|
aReader.as<RefPtr<mozilla::dom::ReadableStreamDefaultReader>>(), aName,
|
|
|
|
aFlags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void ImplCycleCollectionUnlink(
|
|
|
|
mozilla::Variant<mozilla::Nothing,
|
|
|
|
RefPtr<mozilla::dom::ReadableStreamDefaultReader>>&
|
|
|
|
aReader) {
|
|
|
|
aReader = AsVariant(mozilla::Nothing());
|
|
|
|
}
|
|
|
|
|
2021-10-06 21:43:01 +03:00
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
// Only needed for refcounted objects.
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableStream)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ReadableStream)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mController, mReader)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
|
|
tmp->mStoredError.setNull();
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ReadableStream)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mController, mReader)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ReadableStream)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStoredError)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadableStream)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadableStream)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStream)
|
|
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
ReadableStream::ReadableStream(nsIGlobalObject* aGlobal)
|
|
|
|
: mGlobal(aGlobal), mReader(nullptr) {
|
2021-10-06 21:43:02 +03:00
|
|
|
mozilla::HoldJSObjects(this);
|
|
|
|
}
|
|
|
|
|
2021-10-06 21:43:01 +03:00
|
|
|
ReadableStream::ReadableStream(const GlobalObject& aGlobal)
|
2022-01-05 01:33:22 +03:00
|
|
|
: mGlobal(do_QueryInterface(aGlobal.GetAsSupports())), mReader(nullptr) {
|
2021-10-06 21:43:01 +03:00
|
|
|
mozilla::HoldJSObjects(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadableStream::~ReadableStream() { mozilla::DropJSObjects(this); }
|
|
|
|
|
|
|
|
JSObject* ReadableStream::WrapObject(JSContext* aCx,
|
|
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
|
|
return ReadableStream_Binding::Wrap(aCx, this, aGivenProto);
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
ReadableStreamDefaultReader* ReadableStream::GetDefaultReader() {
|
|
|
|
return mReader->AsDefault();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReadableStream::SetReader(ReadableStreamGenericReader* aReader) {
|
2021-10-06 21:43:01 +03:00
|
|
|
mReader = aReader;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
bool ReadableStreamHasDefaultReader(ReadableStream* aStream) {
|
|
|
|
// Step 1.
|
|
|
|
ReadableStreamGenericReader* reader = aStream->GetReader();
|
|
|
|
|
|
|
|
// Step 2.
|
|
|
|
if (!reader) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3+4:
|
|
|
|
return reader->IsDefault();
|
|
|
|
}
|
|
|
|
|
2021-10-06 21:43:01 +03:00
|
|
|
// Streams Spec: 4.2.4: https://streams.spec.whatwg.org/#rs-prototype
|
|
|
|
/* static */
|
|
|
|
already_AddRefed<ReadableStream> ReadableStream::Constructor(
|
|
|
|
const GlobalObject& aGlobal,
|
|
|
|
const Optional<JS::Handle<JSObject*>>& aUnderlyingSource,
|
|
|
|
const QueuingStrategy& aStrategy, ErrorResult& aRv) {
|
|
|
|
// Step 1.
|
|
|
|
JS::RootedObject underlyingSourceObj(
|
|
|
|
aGlobal.Context(),
|
|
|
|
aUnderlyingSource.WasPassed() ? aUnderlyingSource.Value() : nullptr);
|
|
|
|
|
|
|
|
// Step 2.
|
|
|
|
UnderlyingSource underlyingSourceDict;
|
|
|
|
if (underlyingSourceObj) {
|
|
|
|
JS::RootedValue objValue(aGlobal.Context(),
|
|
|
|
JS::ObjectValue(*underlyingSourceObj));
|
|
|
|
dom::BindingCallContext callCx(aGlobal.Context(),
|
|
|
|
"ReadableStream.constructor");
|
|
|
|
aRv.MightThrowJSException();
|
|
|
|
if (!underlyingSourceDict.Init(callCx, objValue)) {
|
|
|
|
aRv.StealExceptionFromJSContext(aGlobal.Context());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3.
|
|
|
|
RefPtr<ReadableStream> readableStream = new ReadableStream(aGlobal);
|
|
|
|
|
|
|
|
// Step 4.
|
|
|
|
if (underlyingSourceDict.mType.WasPassed()) {
|
|
|
|
// Implicit assertion on above check.
|
|
|
|
MOZ_ASSERT(underlyingSourceDict.mType.Value() == ReadableStreamType::Bytes);
|
|
|
|
|
|
|
|
// Step 4.1
|
|
|
|
if (aStrategy.mSize.WasPassed()) {
|
|
|
|
aRv.ThrowRangeError("Implementation preserved member 'size'");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 4.2
|
|
|
|
double highWaterMark = ExtractHighWaterMark(aStrategy, 0, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 4.3
|
|
|
|
(void)highWaterMark;
|
2022-01-05 01:33:22 +03:00
|
|
|
aRv.ThrowNotSupportedError("BYOB Byte Streams Not Yet Supported");
|
2021-10-06 21:43:01 +03:00
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
return nullptr;
|
2021-10-06 21:43:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Step 5.1 (implicit in above check)
|
|
|
|
// Step 5.2. Extract callback.
|
|
|
|
//
|
|
|
|
// Implementation Note: The specification demands that if the size doesn't
|
|
|
|
// exist, we instead would provide an algorithm that returns 1. Instead, we
|
|
|
|
// will teach callers that a missing callback should simply return 1, rather
|
|
|
|
// than gin up a fake callback here.
|
|
|
|
//
|
|
|
|
// This decision may need to be revisited if the default action ever diverges
|
|
|
|
// within the specification.
|
|
|
|
RefPtr<QueuingStrategySize> sizeAlgorithm =
|
|
|
|
aStrategy.mSize.WasPassed() ? &aStrategy.mSize.Value() : nullptr;
|
|
|
|
|
|
|
|
// Step 5.3
|
|
|
|
double highWaterMark = ExtractHighWaterMark(aStrategy, 1, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 5.4.
|
|
|
|
SetupReadableStreamDefaultControllerFromUnderlyingSource(
|
|
|
|
aGlobal.Context(), readableStream, underlyingSourceObj,
|
|
|
|
underlyingSourceDict, highWaterMark, sizeAlgorithm, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return readableStream.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dealing with const this ptr is a pain, so just re-implement.
|
|
|
|
// https://streams.spec.whatwg.org/#is-readable-stream-locked
|
|
|
|
bool ReadableStream::Locked() const {
|
|
|
|
// Step 1 + 2.
|
|
|
|
return mReader;
|
|
|
|
}
|
|
|
|
|
2021-10-06 21:43:02 +03:00
|
|
|
// https://streams.spec.whatwg.org/#initialize-readable-stream
|
|
|
|
static void InitializeReadableStream(ReadableStream* aStream) {
|
|
|
|
// Step 1.
|
|
|
|
aStream->SetState(ReadableStream::ReaderState::Readable);
|
|
|
|
|
|
|
|
// Step 2.
|
|
|
|
aStream->SetReader(nullptr);
|
|
|
|
aStream->SetStoredError(JS::UndefinedHandleValue);
|
|
|
|
|
|
|
|
// Step 3.
|
|
|
|
aStream->SetDisturbed(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#create-readable-stream
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
|
|
|
already_AddRefed<ReadableStream> CreateReadableStream(
|
|
|
|
JSContext* aCx, nsIGlobalObject* aGlobal,
|
|
|
|
UnderlyingSourceStartCallbackHelper* aStartAlgorithm,
|
|
|
|
UnderlyingSourcePullCallbackHelper* aPullAlgorithm,
|
|
|
|
UnderlyingSourceCancelCallbackHelper* aCancelAlgorithm,
|
|
|
|
mozilla::Maybe<double> aHighWaterMark, QueuingStrategySize* aSizeAlgorithm,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
// Step 1.
|
|
|
|
double highWaterMark = aHighWaterMark.isSome() ? *aHighWaterMark : 1.0;
|
|
|
|
|
|
|
|
// Step 2. consumers of sizeAlgorithm
|
|
|
|
// handle null algorithms correctly.
|
|
|
|
// Step 3.
|
|
|
|
MOZ_ASSERT(IsNonNegativeNumber(highWaterMark));
|
|
|
|
// Step 4.
|
|
|
|
RefPtr<ReadableStream> stream = new ReadableStream(aGlobal);
|
|
|
|
|
|
|
|
// Step 5.
|
|
|
|
InitializeReadableStream(stream);
|
|
|
|
|
|
|
|
// Step 6.
|
|
|
|
RefPtr<ReadableStreamDefaultController> controller =
|
|
|
|
new ReadableStreamDefaultController(aGlobal);
|
|
|
|
|
|
|
|
// Step 7.
|
|
|
|
SetUpReadableStreamDefaultController(aCx, stream, controller, aStartAlgorithm,
|
|
|
|
aPullAlgorithm, aCancelAlgorithm,
|
|
|
|
highWaterMark, aSizeAlgorithm, aRv);
|
|
|
|
|
|
|
|
// Step 8.
|
|
|
|
return stream.forget();
|
|
|
|
}
|
|
|
|
|
2021-10-06 21:43:01 +03:00
|
|
|
// https://streams.spec.whatwg.org/#readable-stream-close
|
|
|
|
void ReadableStreamClose(JSContext* aCx, ReadableStream* aStream,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
// Step 1.
|
|
|
|
MOZ_ASSERT(aStream->State() == ReadableStream::ReaderState::Readable);
|
|
|
|
|
|
|
|
// Step 2.
|
|
|
|
aStream->SetState(ReadableStream::ReaderState::Closed);
|
|
|
|
|
|
|
|
// Step 3.
|
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
ReadableStreamGenericReader* reader = aStream->GetReader();
|
2021-10-06 21:43:01 +03:00
|
|
|
// Step 4.
|
|
|
|
if (!reader) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 5.
|
|
|
|
reader->ClosedPromise()->MaybeResolveWithUndefined();
|
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
// Step 6.
|
|
|
|
if (reader->IsDefault()) {
|
|
|
|
// Step 6.1
|
|
|
|
ReadableStreamDefaultReader* defaultReader = reader->AsDefault();
|
|
|
|
for (ReadRequest* readRequest : defaultReader->ReadRequests()) {
|
|
|
|
// Step 6.1.1.
|
|
|
|
readRequest->CloseSteps(aCx, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-10-06 21:43:01 +03:00
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
// Step 6.2
|
|
|
|
defaultReader->ReadRequests().clear();
|
|
|
|
}
|
2021-10-06 21:43:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#readable-stream-cancel
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
|
|
|
already_AddRefed<Promise> ReadableStreamCancel(JSContext* aCx,
|
|
|
|
ReadableStream* aStream,
|
|
|
|
JS::Handle<JS::Value> aError,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
// Step 1.
|
|
|
|
aStream->SetDisturbed(true);
|
|
|
|
|
|
|
|
// Step 2.
|
|
|
|
if (aStream->State() == ReadableStream::ReaderState::Closed) {
|
|
|
|
RefPtr<Promise> promise = Promise::Create(aStream->GetParentObject(), aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
promise->MaybeResolveWithUndefined();
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3.
|
|
|
|
if (aStream->State() == ReadableStream::ReaderState::Errored) {
|
|
|
|
RefPtr<Promise> promise = Promise::Create(aStream->GetParentObject(), aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
JS::RootedValue storedError(aCx, aStream->StoredError());
|
|
|
|
promise->MaybeReject(storedError);
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 4.
|
|
|
|
ReadableStreamClose(aCx, aStream, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
// Step 5.
|
|
|
|
ReadableStreamGenericReader* reader = aStream->GetReader();
|
|
|
|
|
|
|
|
// Step 6.
|
|
|
|
if (reader && reader->IsBYOB()) {
|
2022-01-05 01:33:22 +03:00
|
|
|
for (auto* readIntoRequest : reader->AsBYOB()->ReadIntoRequests()) {
|
|
|
|
readIntoRequest->CloseSteps(aCx, JS::UndefinedHandleValue, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
2022-01-05 01:33:22 +03:00
|
|
|
}
|
2021-10-06 21:43:01 +03:00
|
|
|
|
|
|
|
// Step 7.
|
2022-01-05 01:33:21 +03:00
|
|
|
RefPtr<ReadableStreamController> controller(aStream->Controller());
|
2021-10-06 21:43:01 +03:00
|
|
|
RefPtr<Promise> sourceCancelPromise =
|
2022-01-05 01:33:21 +03:00
|
|
|
controller->CancelSteps(aCx, aError, aRv);
|
2021-10-06 21:43:01 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 8.
|
|
|
|
RefPtr<Promise> promise =
|
|
|
|
Promise::Create(sourceCancelPromise->GetParentObject(), aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ThenWithCycleCollectedArgs will carry promise, keeping it alive until the
|
|
|
|
// callback executes.
|
|
|
|
Result<RefPtr<Promise>, nsresult> returnResult =
|
|
|
|
sourceCancelPromise->ThenWithCycleCollectedArgs(
|
|
|
|
[](JSContext*, JS::HandleValue, RefPtr<Promise> newPromise) {
|
|
|
|
newPromise->MaybeResolveWithUndefined();
|
|
|
|
return newPromise.forget();
|
|
|
|
},
|
|
|
|
promise);
|
|
|
|
|
|
|
|
if (returnResult.isErr()) {
|
|
|
|
aRv.Throw(returnResult.unwrapErr());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return returnResult.unwrap().forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
|
|
|
already_AddRefed<Promise> ReadableStream::Cancel(JSContext* aCx,
|
|
|
|
JS::Handle<JS::Value> aReason,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
if (Locked()) {
|
|
|
|
RefPtr<Promise> promise = Promise::Create(GetParentObject(), aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
promise->MaybeRejectWithTypeError("Canceled Locked Stream");
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
RefPtr<ReadableStream> thisRefPtr = this;
|
|
|
|
return ReadableStreamCancel(aCx, thisRefPtr, aReason, aRv);
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#acquire-readable-stream-reader
|
|
|
|
already_AddRefed<ReadableStreamDefaultReader>
|
|
|
|
AcquireReadableStreamDefaultReader(JSContext* aCx, ReadableStream* aStream,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
// Step 1.
|
|
|
|
RefPtr<ReadableStreamDefaultReader> reader =
|
|
|
|
new ReadableStreamDefaultReader(aStream->GetParentObject());
|
|
|
|
|
|
|
|
// Step 2.
|
|
|
|
SetUpReadableStreamDefaultReader(aCx, reader, aStream, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3.
|
|
|
|
return reader.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#rs-get-reader
|
2022-01-05 01:33:22 +03:00
|
|
|
void ReadableStream::GetReader(JSContext* aCx,
|
|
|
|
const ReadableStreamGetReaderOptions& aOptions,
|
|
|
|
OwningReadableStreamReader& resultReader,
|
|
|
|
ErrorResult& aRv) {
|
2021-10-06 21:43:01 +03:00
|
|
|
// Step 1.
|
|
|
|
if (!aOptions.mMode.WasPassed()) {
|
|
|
|
RefPtr<ReadableStream> thisRefPtr = this;
|
2022-01-05 01:33:22 +03:00
|
|
|
RefPtr<ReadableStreamDefaultReader> defaultReader =
|
|
|
|
AcquireReadableStreamDefaultReader(aCx, thisRefPtr, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
resultReader.SetAsReadableStreamDefaultReader() = defaultReader;
|
|
|
|
return;
|
2021-10-06 21:43:01 +03:00
|
|
|
}
|
|
|
|
// Step 2.
|
2022-01-05 01:33:22 +03:00
|
|
|
aRv.ThrowTypeError("BYOB STREAMS NOT IMPLEMENTED");
|
2021-10-06 21:43:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#is-readable-stream-locked
|
|
|
|
bool IsReadableStreamLocked(ReadableStream* aStream) {
|
|
|
|
// Step 1 + 2.
|
|
|
|
return aStream->Locked();
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#readable-stream-get-num-read-requests
|
|
|
|
double ReadableStreamGetNumReadRequests(ReadableStream* aStream) {
|
|
|
|
// Step 1.
|
|
|
|
MOZ_ASSERT(ReadableStreamHasDefaultReader(aStream));
|
|
|
|
|
|
|
|
// Step 2.
|
2022-01-05 01:33:22 +03:00
|
|
|
return double(aStream->GetDefaultReader()->ReadRequests().length());
|
2021-10-06 21:43:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#readable-stream-error
|
|
|
|
void ReadableStreamError(JSContext* aCx, ReadableStream* aStream,
|
|
|
|
JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
|
|
|
|
// Step 1.
|
|
|
|
MOZ_ASSERT(aStream->State() == ReadableStream::ReaderState::Readable);
|
|
|
|
|
|
|
|
// Step 2.
|
|
|
|
aStream->SetState(ReadableStream::ReaderState::Errored);
|
|
|
|
|
|
|
|
// Step 3.
|
|
|
|
aStream->SetStoredError(aValue);
|
|
|
|
|
|
|
|
// Step 4.
|
2022-01-05 01:33:22 +03:00
|
|
|
ReadableStreamGenericReader* reader = aStream->GetReader();
|
2021-10-06 21:43:01 +03:00
|
|
|
|
|
|
|
// Step 5.
|
|
|
|
if (!reader) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 6.
|
|
|
|
reader->ClosedPromise()->MaybeReject(aValue);
|
|
|
|
|
|
|
|
// Step 7.
|
|
|
|
reader->ClosedPromise()->SetSettledPromiseIsHandled();
|
|
|
|
|
|
|
|
// Step 8. Implicit in the fact that we don't yet support
|
|
|
|
// ReadableStreamBYOBReader
|
2022-01-05 01:33:22 +03:00
|
|
|
if (reader->IsDefault()) {
|
|
|
|
// Step 8.1:
|
|
|
|
ReadableStreamDefaultReader* defaultReader = reader->AsDefault();
|
|
|
|
for (ReadRequest* readRequest : defaultReader->ReadRequests()) {
|
|
|
|
readRequest->ErrorSteps(aCx, aValue, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-10-06 21:43:01 +03:00
|
|
|
}
|
2022-01-05 01:33:22 +03:00
|
|
|
// Step 8.2
|
|
|
|
defaultReader->ReadRequests().clear();
|
|
|
|
} else {
|
2022-01-05 01:33:22 +03:00
|
|
|
// Step 9.
|
|
|
|
// Step 9.1.
|
2022-01-05 01:33:22 +03:00
|
|
|
MOZ_ASSERT(reader->IsBYOB());
|
2022-01-05 01:33:22 +03:00
|
|
|
ReadableStreamBYOBReader* byobReader = reader->AsBYOB();
|
|
|
|
// Step 9.2.
|
|
|
|
for (auto* readIntoRequest : byobReader->ReadIntoRequests()) {
|
|
|
|
readIntoRequest->ErrorSteps(aCx, aValue, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Step 9.3
|
|
|
|
byobReader->ReadIntoRequests().clear();
|
2021-10-06 21:43:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#rs-default-controller-close
|
|
|
|
void ReadableStreamFulfillReadRequest(JSContext* aCx, ReadableStream* aStream,
|
|
|
|
JS::Handle<JS::Value> aChunk, bool aDone,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
// Step 1.
|
|
|
|
MOZ_ASSERT(ReadableStreamHasDefaultReader(aStream));
|
|
|
|
|
|
|
|
// Step 2.
|
2022-01-05 01:33:22 +03:00
|
|
|
ReadableStreamDefaultReader* reader = aStream->GetDefaultReader();
|
2021-10-06 21:43:01 +03:00
|
|
|
|
|
|
|
// Step 3.
|
|
|
|
MOZ_ASSERT(!reader->ReadRequests().isEmpty());
|
|
|
|
|
|
|
|
// Step 4+5.
|
|
|
|
RefPtr<ReadRequest> readRequest = reader->ReadRequests().popFirst();
|
|
|
|
|
|
|
|
// Step 6.
|
|
|
|
if (aDone) {
|
|
|
|
readRequest->CloseSteps(aCx, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 7.
|
|
|
|
readRequest->ChunkSteps(aCx, aChunk, aRv);
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
// https://streams.spec.whatwg.org/#readable-stream-add-read-request
|
2021-10-06 21:43:01 +03:00
|
|
|
void ReadableStreamAddReadRequest(ReadableStream* aStream,
|
|
|
|
ReadRequest* aReadRequest) {
|
2022-01-05 01:33:22 +03:00
|
|
|
// Step 1.
|
|
|
|
MOZ_ASSERT(aStream->GetReader()->IsDefault());
|
2021-10-06 21:43:01 +03:00
|
|
|
// Step 2.
|
|
|
|
MOZ_ASSERT(aStream->State() == ReadableStream::ReaderState::Readable);
|
|
|
|
// Step 3.
|
2022-01-05 01:33:22 +03:00
|
|
|
aStream->GetDefaultReader()->ReadRequests().insertBack(aReadRequest);
|
2021-10-06 21:43:01 +03:00
|
|
|
}
|
|
|
|
|
2021-10-06 21:43:02 +03:00
|
|
|
class ReadableStreamDefaultTeeCancelAlgorithm final
|
|
|
|
: public UnderlyingSourceCancelCallbackHelper {
|
|
|
|
RefPtr<TeeState> mTeeState;
|
2022-01-05 01:33:22 +03:00
|
|
|
// Since cancel1algorithm and cancel2algorithm only differ in which tee
|
|
|
|
// state members to manipulate, we common up the implementation and select
|
2021-10-06 21:43:02 +03:00
|
|
|
// dynamically.
|
|
|
|
bool mIsCancel1 = true;
|
|
|
|
|
|
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS_INHERITED
|
|
|
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
|
|
|
|
ReadableStreamDefaultTeeCancelAlgorithm,
|
|
|
|
UnderlyingSourceCancelCallbackHelper)
|
|
|
|
|
|
|
|
explicit ReadableStreamDefaultTeeCancelAlgorithm(TeeState* aTeeState,
|
|
|
|
bool aIsCancel1)
|
|
|
|
: mTeeState(aTeeState), mIsCancel1(aIsCancel1) {}
|
|
|
|
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
|
|
|
virtual already_AddRefed<Promise> CancelCallback(
|
|
|
|
JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
|
|
|
|
ErrorResult& aRv) override {
|
|
|
|
// Step 1.
|
|
|
|
if (mIsCancel1) {
|
|
|
|
mTeeState->SetCanceled1(true);
|
|
|
|
} else {
|
|
|
|
mTeeState->SetCanceled2(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 2.
|
|
|
|
if (mIsCancel1) {
|
|
|
|
mTeeState->SetReason1(aReason.Value());
|
|
|
|
} else {
|
|
|
|
mTeeState->SetReason2(aReason.Value());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3.
|
|
|
|
|
|
|
|
if ((mIsCancel1 && mTeeState->Canceled2()) ||
|
|
|
|
(!mIsCancel1 && mTeeState->Canceled1())) {
|
|
|
|
// Step 3.1
|
|
|
|
|
|
|
|
JS::RootedObject compositeReason(aCx, JS::NewArrayObject(aCx, 2));
|
|
|
|
if (!compositeReason) {
|
|
|
|
aRv.StealExceptionFromJSContext(aCx);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS::RootedValue reason1(aCx, mTeeState->Reason1());
|
|
|
|
if (!JS_SetElement(aCx, compositeReason, 0, reason1)) {
|
|
|
|
aRv.StealExceptionFromJSContext(aCx);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS::RootedValue reason2(aCx, mTeeState->Reason2());
|
|
|
|
if (!JS_SetElement(aCx, compositeReason, 1, reason2)) {
|
|
|
|
aRv.StealExceptionFromJSContext(aCx);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3.2
|
|
|
|
JS::RootedValue 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:
|
|
|
|
~ReadableStreamDefaultTeeCancelAlgorithm() = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableStreamDefaultTeeCancelAlgorithm)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(
|
|
|
|
ReadableStreamDefaultTeeCancelAlgorithm,
|
|
|
|
UnderlyingSourceCancelCallbackHelper)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTeeState)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
|
|
|
|
ReadableStreamDefaultTeeCancelAlgorithm,
|
|
|
|
UnderlyingSourceCancelCallbackHelper)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTeeState)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF_INHERITED(ReadableStreamDefaultTeeCancelAlgorithm,
|
|
|
|
UnderlyingSourceCancelCallbackHelper)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(ReadableStreamDefaultTeeCancelAlgorithm,
|
|
|
|
UnderlyingSourceCancelCallbackHelper)
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamDefaultTeeCancelAlgorithm)
|
|
|
|
NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceCancelCallbackHelper)
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 19.
|
2021-10-06 21:43:02 +03:00
|
|
|
class ReadableStreamTeeClosePromiseHandler final : public PromiseNativeHandler {
|
|
|
|
~ReadableStreamTeeClosePromiseHandler() = default;
|
|
|
|
RefPtr<TeeState> mTeeState;
|
|
|
|
|
|
|
|
public:
|
|
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
|
|
NS_DECL_CYCLE_COLLECTION_CLASS(ReadableStreamTeeClosePromiseHandler)
|
|
|
|
|
|
|
|
explicit ReadableStreamTeeClosePromiseHandler(TeeState* aTeeState)
|
|
|
|
: mTeeState(aTeeState) {}
|
|
|
|
|
|
|
|
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
|
|
|
|
}
|
|
|
|
void RejectedCallback(JSContext* aCx,
|
|
|
|
JS::Handle<JS::Value> aReason) override {
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 19.1.
|
2021-10-06 21:43:02 +03:00
|
|
|
ErrorResult rv;
|
|
|
|
ReadableStreamDefaultControllerError(
|
2022-01-05 01:33:21 +03:00
|
|
|
aCx, mTeeState->Branch1()->DefaultController(), aReason, rv);
|
2021-10-06 21:43:02 +03:00
|
|
|
if (rv.MaybeSetPendingException(
|
|
|
|
aCx, "ReadableStreamDefaultTee Error During Promise Rejection")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 19.2
|
2021-10-06 21:43:02 +03:00
|
|
|
ReadableStreamDefaultControllerError(
|
2022-01-05 01:33:21 +03:00
|
|
|
aCx, mTeeState->Branch2()->DefaultController(), aReason, rv);
|
2021-10-06 21:43:02 +03:00
|
|
|
if (rv.MaybeSetPendingException(
|
|
|
|
aCx, "ReadableStreamDefaultTee Error During Promise Rejection")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 19.3
|
2021-10-06 21:43:02 +03:00
|
|
|
if (!mTeeState->Canceled1() || !mTeeState->Canceled2()) {
|
|
|
|
mTeeState->CancelPromise()->MaybeResolveWithUndefined();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Cycle collection methods for promise handler.
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(ReadableStreamTeeClosePromiseHandler, mTeeState)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadableStreamTeeClosePromiseHandler)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadableStreamTeeClosePromiseHandler)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamTeeClosePromiseHandler)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
|
|
|
static void ReadableStreamDefaultTee(JSContext* aCx, ReadableStream* aStream,
|
|
|
|
bool aCloneForBranch2,
|
|
|
|
nsTArray<RefPtr<ReadableStream>>& aResult,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
// Step 1. Implicit.
|
|
|
|
// Step 2. Implicit.
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Steps 3-12 are contained in the construction of Tee State.
|
2021-10-06 21:43:02 +03:00
|
|
|
RefPtr<TeeState> teeState =
|
|
|
|
TeeState::Create(aCx, aStream, aCloneForBranch2, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 13:
|
2021-11-16 01:01:39 +03:00
|
|
|
RefPtr<ReadableStreamDefaultTeePullAlgorithm> pullAlgorithm =
|
2021-10-06 21:43:02 +03:00
|
|
|
new ReadableStreamDefaultTeePullAlgorithm(teeState);
|
|
|
|
|
2021-11-16 01:01:39 +03:00
|
|
|
// Link pull algorithm into tee state for use in readRequest
|
|
|
|
teeState->SetPullAlgorithm(pullAlgorithm);
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 14.
|
2021-10-06 21:43:02 +03:00
|
|
|
RefPtr<UnderlyingSourceCancelCallbackHelper> cancel1Algorithm =
|
|
|
|
new ReadableStreamDefaultTeeCancelAlgorithm(teeState, true);
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 15.
|
2021-10-06 21:43:02 +03:00
|
|
|
RefPtr<UnderlyingSourceCancelCallbackHelper> cancel2Algorithm =
|
|
|
|
new ReadableStreamDefaultTeeCancelAlgorithm(teeState, false);
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 16. Consumers are aware that they should return undefined
|
2021-10-06 21:43:02 +03:00
|
|
|
// in the default case for this algorithm.
|
|
|
|
RefPtr<UnderlyingSourceStartCallbackHelper> startAlgorithm;
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 17.
|
2021-10-06 21:43:02 +03:00
|
|
|
nsCOMPtr<nsIGlobalObject> global(
|
|
|
|
do_AddRef(teeState->GetStream()->GetParentObject()));
|
|
|
|
teeState->SetBranch1(CreateReadableStream(aCx, global, startAlgorithm,
|
|
|
|
pullAlgorithm, cancel1Algorithm,
|
|
|
|
mozilla::Nothing(), nullptr, aRv));
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 18.
|
2021-10-06 21:43:02 +03:00
|
|
|
teeState->SetBranch2(CreateReadableStream(aCx, global, startAlgorithm,
|
|
|
|
pullAlgorithm, cancel2Algorithm,
|
|
|
|
mozilla::Nothing(), nullptr, aRv));
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 19.
|
2021-10-06 21:43:02 +03:00
|
|
|
teeState->GetReader()->ClosedPromise()->AppendNativeHandler(
|
|
|
|
new ReadableStreamTeeClosePromiseHandler(teeState));
|
|
|
|
|
2022-01-05 01:33:21 +03:00
|
|
|
// Step 20.
|
2021-10-06 21:43:02 +03:00
|
|
|
aResult.AppendElement(teeState->Branch1());
|
|
|
|
aResult.AppendElement(teeState->Branch2());
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://streams.spec.whatwg.org/#readable-stream-tee
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
|
|
|
static void ReadableStreamTee(JSContext* aCx, ReadableStream* aStream,
|
|
|
|
bool aCloneForBranch2,
|
|
|
|
nsTArray<RefPtr<ReadableStream>>& aResult,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
// Step 1. Implicit.
|
|
|
|
// Step 2. Implicit.
|
2022-01-05 01:33:22 +03:00
|
|
|
// Step 3.
|
|
|
|
if (aStream->Controller()->IsByte()) {
|
|
|
|
aRv.ThrowTypeError("Cannot yet tee a byte stream controller");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Step 4.
|
2021-10-06 21:43:02 +03:00
|
|
|
ReadableStreamDefaultTee(aCx, aStream, aCloneForBranch2, aResult, aRv);
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
|
|
|
void ReadableStream::Tee(JSContext* aCx,
|
|
|
|
nsTArray<RefPtr<ReadableStream>>& aResult,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
ReadableStreamTee(aCx, this, false, aResult, aRv);
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:33:22 +03:00
|
|
|
// https://streams.spec.whatwg.org/#readable-stream-add-read-into-request
|
|
|
|
void ReadableStreamAddReadIntoRequest(ReadableStream* aStream,
|
|
|
|
ReadIntoRequest* aReadIntoRequest) {
|
|
|
|
// Step 1. Assert: stream.[[reader]] implements ReadableStreamBYOBReader.
|
|
|
|
MOZ_ASSERT(aStream->GetReader()->IsBYOB());
|
|
|
|
|
|
|
|
// Step 2. Assert: stream.[[state]] is "readable" or "closed".
|
|
|
|
MOZ_ASSERT(aStream->State() == ReadableStream::ReaderState::Readable ||
|
|
|
|
aStream->State() == ReadableStream::ReaderState::Closed);
|
|
|
|
|
|
|
|
// Step 3. Append readRequest to stream.[[reader]].[[readIntoRequests]].
|
|
|
|
aStream->GetReader()->AsBYOB()->ReadIntoRequests().insertBack(
|
|
|
|
aReadIntoRequest);
|
|
|
|
}
|
|
|
|
|
2021-10-06 21:43:01 +03:00
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|