fune/dom/streams/ReadableByteStreamController.cpp

1967 lines
69 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- 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 "js/ArrayBuffer.h"
#include "js/ErrorReport.h"
#include "js/Exception.h"
#include "js/TypeDecls.h"
#include "js/Value.h"
#include "js/ValueArray.h"
#include "js/experimental/TypedData.h"
#include "js/friend/ErrorMessages.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/dom/ByteStreamHelpers.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Promise-inl.h"
#include "mozilla/dom/ReadableByteStreamController.h"
#include "mozilla/dom/ReadableByteStreamControllerBinding.h"
#include "mozilla/dom/ReadIntoRequest.h"
#include "mozilla/dom/ReadableStream.h"
#include "mozilla/dom/ReadableStreamBYOBReader.h"
#include "mozilla/dom/ReadableStreamBYOBRequest.h"
#include "mozilla/dom/ReadableStreamController.h"
#include "mozilla/dom/ReadableStreamDefaultController.h"
#include "mozilla/dom/ReadableStreamDefaultReader.h"
#include "mozilla/dom/ReadableStreamGenericReader.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/UnderlyingSourceCallbackHelpers.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIGlobalObject.h"
#include "nsISupports.h"
#include <algorithm> // std::min
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableByteStreamController)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ReadableByteStreamController,
ReadableStreamController)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mByobRequest, mAlgorithms, mStream)
tmp->ClearPendingPullIntos();
tmp->ClearQueue();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ReadableByteStreamController,
ReadableStreamController)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mByobRequest, mAlgorithms, mStream)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ReadableByteStreamController,
ReadableStreamController)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
// Trace the associated queue + list
for (const auto& queueEntry : tmp->mQueue) {
aCallbacks.Trace(&queueEntry->mBuffer, "mQueue.mBuffer", aClosure);
}
for (const auto& pullInto : tmp->mPendingPullIntos) {
aCallbacks.Trace(&pullInto->mBuffer, "mPendingPullIntos.mBuffer", aClosure);
}
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_ADDREF_INHERITED(ReadableByteStreamController, ReadableStreamController)
NS_IMPL_RELEASE_INHERITED(ReadableByteStreamController,
ReadableStreamController)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableByteStreamController)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END_INHERITING(ReadableStreamController)
ReadableByteStreamController::ReadableByteStreamController(
nsIGlobalObject* aGlobal)
: ReadableStreamController(aGlobal) {
mozilla::HoldJSObjects(this);
}
ReadableByteStreamController::~ReadableByteStreamController() {
ClearPendingPullIntos();
ClearQueue();
mozilla::DropJSObjects(this);
}
void ReadableByteStreamController::ClearQueue() {
// Since the pull intos are traced only by the owning
// ReadableByteStreamController, when clearning the list we also clear JS
// references to avoid dangling JS references.
for (auto* queueEntry : mQueue) {
queueEntry->ClearBuffer();
}
mQueue.clear();
}
void ReadableByteStreamController::ClearPendingPullIntos() {
// Since the pull intos are traced only by the owning
// ReadableByteStreamController, when clearning the list we also clear JS
// references to avoid dangling JS references.
for (auto* pullInto : mPendingPullIntos) {
pullInto->ClearBuffer();
}
mPendingPullIntos.clear();
}
// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollergetbyobrequest
already_AddRefed<ReadableStreamBYOBRequest>
ReadableByteStreamControllerGetBYOBRequest(
JSContext* aCx, ReadableByteStreamController* aController,
ErrorResult& aRv) {
// Step 1.
if (!aController->GetByobRequest() &&
!aController->PendingPullIntos().isEmpty()) {
// Step 1.1:
PullIntoDescriptor* firstDescriptor =
aController->PendingPullIntos().getFirst();
// Step 1.2:
aRv.MightThrowJSException();
JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
JS::Rooted<JSObject*> view(
aCx, JS_NewUint8ArrayWithBuffer(
aCx, buffer,
firstDescriptor->ByteOffset() + firstDescriptor->BytesFilled(),
int64_t(firstDescriptor->ByteLength() -
firstDescriptor->BytesFilled())));
if (!view) {
aRv.StealExceptionFromJSContext(aCx);
return nullptr;
}
// Step 1.3:
RefPtr<ReadableStreamBYOBRequest> byobRequest =
new ReadableStreamBYOBRequest(aController->GetParentObject());
// Step 1.4:
byobRequest->SetController(aController);
// Step 1.5:
byobRequest->SetView(view);
// Step 1.6:
aController->SetByobRequest(byobRequest);
}
// Step 2.
RefPtr<ReadableStreamBYOBRequest> request(aController->GetByobRequest());
return request.forget();
}
already_AddRefed<ReadableStreamBYOBRequest>
ReadableByteStreamController::GetByobRequest(JSContext* aCx, ErrorResult& aRv) {
return ReadableByteStreamControllerGetBYOBRequest(aCx, this, aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-get-desired-size
Nullable<double> ReadableByteStreamControllerGetDesiredSize(
const ReadableByteStreamController* aController) {
// Step 1.
ReadableStream::ReaderState state = aController->Stream()->State();
// Step 2.
if (state == ReadableStream::ReaderState::Errored) {
return nullptr;
}
// Step 3.
if (state == ReadableStream::ReaderState::Closed) {
return 0.0;
}
// Step 4.
return aController->StrategyHWM() - aController->QueueTotalSize();
}
Nullable<double> ReadableByteStreamController::GetDesiredSize() const {
// Step 1.
return ReadableByteStreamControllerGetDesiredSize(this);
}
JSObject* ReadableByteStreamController::WrapObject(
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
return ReadableByteStreamController_Binding::Wrap(aCx, this, aGivenProto);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-invalidate-byob-request
static void ReadableByteStreamControllerInvalidateBYOBRequest(
ReadableByteStreamController* aController) {
// Step 1.
if (!aController->GetByobRequest()) {
return;
}
// Step 2.
aController->GetByobRequest()->SetController(nullptr);
// Step 3.
aController->GetByobRequest()->SetView(nullptr);
// Step 4.
aController->SetByobRequest(nullptr);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-pending-pull-intos
void ReadableByteStreamControllerClearPendingPullIntos(
ReadableByteStreamController* aController) {
// Step 1.
ReadableByteStreamControllerInvalidateBYOBRequest(aController);
// Step 2.
aController->ClearPendingPullIntos();
}
// https://streams.spec.whatwg.org/#reset-queue
void ResetQueue(ReadableByteStreamController* aContainer) {
// Step 1. Implied by type.
// Step 2.
aContainer->ClearQueue();
// Step 3.
aContainer->SetQueueTotalSize(0);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-algorithms
void ReadableByteStreamControllerClearAlgorithms(
ReadableByteStreamController* aController) {
// Step 1. Set controller.[[pullAlgorithm]] to undefined.
// Step 2. Set controller.[[cancelAlgorithm]] to undefined.
aController->SetAlgorithms(nullptr);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-error
void ReadableByteStreamControllerError(
ReadableByteStreamController* aController, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) {
// Step 1. Let stream be controller.[[stream]].
ReadableStream* stream = aController->Stream();
// Step 2. If stream.[[state]] is not "readable", return.
if (stream->State() != ReadableStream::ReaderState::Readable) {
return;
}
// Step 3. Perform
// !ReadableByteStreamControllerClearPendingPullIntos(controller).
ReadableByteStreamControllerClearPendingPullIntos(aController);
// Step 4. Perform !ResetQueue(controller).
ResetQueue(aController);
// Step 5. Perform !ReadableByteStreamControllerClearAlgorithms(controller).
ReadableByteStreamControllerClearAlgorithms(aController);
// Step 6. Perform !ReadableStreamError(stream, e).
AutoJSAPI jsapi;
if (!jsapi.Init(aController->GetParentObject())) {
return;
}
ReadableStreamError(jsapi.cx(), stream, aValue, aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-close
void ReadableByteStreamControllerClose(
JSContext* aCx, ReadableByteStreamController* aController,
ErrorResult& aRv) {
// Step 1.
RefPtr<ReadableStream> stream = aController->Stream();
// Step 2.
if (aController->CloseRequested() ||
stream->State() != ReadableStream::ReaderState::Readable) {
return;
}
// Step 3.
if (aController->QueueTotalSize() > 0) {
// Step 3.1
aController->SetCloseRequested(true);
// Step 3.2
return;
}
// Step 4.
if (!aController->PendingPullIntos().isEmpty()) {
// Step 4.1
PullIntoDescriptor* firstPendingPullInto =
aController->PendingPullIntos().getFirst();
// Step 4.2
if (firstPendingPullInto->BytesFilled() > 0) {
// Step 4.2.1
ErrorResult rv;
rv.ThrowTypeError("Leftover Bytes");
JS::Rooted<JS::Value> exception(aCx);
MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), &exception));
// Step 4.2.2
ReadableByteStreamControllerError(aController, exception, aRv);
if (aRv.Failed()) {
return;
}
aRv.MightThrowJSException();
aRv.ThrowJSException(aCx, exception);
return;
}
}
// Step 5.
ReadableByteStreamControllerClearAlgorithms(aController);
// Step 6.
ReadableStreamClose(aCx, stream, aRv);
}
// https://streams.spec.whatwg.org/#rbs-controller-close
void ReadableByteStreamController::Close(JSContext* aCx, ErrorResult& aRv) {
// Step 1.
if (mCloseRequested) {
aRv.ThrowTypeError("Close already requested");
return;
}
// Step 2.
if (Stream()->State() != ReadableStream::ReaderState::Readable) {
aRv.ThrowTypeError("Closing un-readable stream controller");
return;
}
// Step 3.
ReadableByteStreamControllerClose(aCx, this, aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue-chunk-to-queue
void ReadableByteStreamControllerEnqueueChunkToQueue(
ReadableByteStreamController* aController,
JS::Handle<JSObject*> aTransferredBuffer, size_t aByteOffset,
size_t aByteLength) {
// Step 1.
RefPtr<ReadableByteStreamQueueEntry> queueEntry =
new ReadableByteStreamQueueEntry(aTransferredBuffer, aByteOffset,
aByteLength);
aController->Queue().insertBack(queueEntry);
// Step 2.
aController->AddToQueueTotalSize(double(aByteLength));
}
// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueueclonedchunktoqueue
void ReadableByteStreamControllerEnqueueClonedChunkToQueue(
JSContext* aCx, ReadableByteStreamController* aController,
JS::Handle<JSObject*> aBuffer, size_t aByteOffset, size_t aByteLength,
ErrorResult& aRv) {
// Step 1. Let cloneResult be CloneArrayBuffer(buffer, byteOffset, byteLength,
// %ArrayBuffer%).
aRv.MightThrowJSException();
JS::Rooted<JSObject*> cloneResult(
aCx, JS::ArrayBufferClone(aCx, aBuffer, aByteOffset, aByteLength));
// Step 2. If cloneResult is an abrupt completion,
if (!cloneResult) {
JS::Rooted<JS::Value> exception(aCx);
if (!JS_GetPendingException(aCx, &exception)) {
// Uncatchable exception; we should mark aRv and return.
aRv.StealExceptionFromJSContext(aCx);
return;
}
JS_ClearPendingException(aCx);
// Step 2.1. Perform ! ReadableByteStreamControllerError(controller,
// cloneResult.[[Value]]).
ReadableByteStreamControllerError(aController, exception, aRv);
if (aRv.Failed()) {
return;
}
// Step 2.2. Return cloneResult.
aRv.ThrowJSException(aCx, exception);
return;
}
// Step 3. Perform !
// ReadableByteStreamControllerEnqueueChunkToQueue(controller,
// cloneResult.[[Value]], 0, byteLength).
ReadableByteStreamControllerEnqueueChunkToQueue(aController, cloneResult, 0,
aByteLength);
}
already_AddRefed<PullIntoDescriptor>
ReadableByteStreamControllerShiftPendingPullInto(
ReadableByteStreamController* aController);
// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueuedetachedpullintotoqueue
void ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
JSContext* aCx, ReadableByteStreamController* aController,
PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv) {
// Step 1. Assert: pullIntoDescriptors reader type is "none".
MOZ_ASSERT(aPullIntoDescriptor->GetReaderType() == ReaderType::None);
// Step 2. If pullIntoDescriptors bytes filled > 0,
// perform ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
// pullIntoDescriptors buffer, pullIntoDescriptors byte offset,
// pullIntoDescriptors bytes filled).
if (aPullIntoDescriptor->BytesFilled() > 0) {
JS::Rooted<JSObject*> buffer(aCx, aPullIntoDescriptor->Buffer());
ReadableByteStreamControllerEnqueueClonedChunkToQueue(
aCx, aController, buffer, aPullIntoDescriptor->ByteOffset(),
aPullIntoDescriptor->BytesFilled(), aRv);
if (aRv.Failed()) {
return;
}
}
// Step 3. Perform !
// ReadableByteStreamControllerShiftPendingPullInto(controller).
RefPtr<PullIntoDescriptor> discarded =
ReadableByteStreamControllerShiftPendingPullInto(aController);
(void)discarded;
}
// https://streams.spec.whatwg.org/#readable-stream-get-num-read-into-requests
static size_t ReadableStreamGetNumReadIntoRequests(ReadableStream* aStream) {
// Step 1.
MOZ_ASSERT(ReadableStreamHasBYOBReader(aStream));
// Step 2.
return aStream->GetReader()->AsBYOB()->ReadIntoRequests().length();
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-should-call-pull
bool ReadableByteStreamControllerShouldCallPull(
ReadableByteStreamController* aController) {
// Step 1. Let stream be controller.[[stream]].
ReadableStream* stream = aController->Stream();
// Step 2. If stream.[[state]] is not "readable", return false.
if (stream->State() != ReadableStream::ReaderState::Readable) {
return false;
}
// Step 3. If controller.[[closeRequested]] is true, return false.
if (aController->CloseRequested()) {
return false;
}
// Step 4. If controller.[[started]] is false, return false.
if (!aController->Started()) {
return false;
}
// Step 5. If ! ReadableStreamHasDefaultReader(stream) is true
// and ! ReadableStreamGetNumReadRequests(stream) > 0, return true.
if (ReadableStreamHasDefaultReader(stream) &&
ReadableStreamGetNumReadRequests(stream) > 0) {
return true;
}
// Step 6. If ! ReadableStreamHasBYOBReader(stream) is true
// and ! ReadableStreamGetNumReadIntoRequests(stream) > 0, return true.
if (ReadableStreamHasBYOBReader(stream) &&
ReadableStreamGetNumReadIntoRequests(stream) > 0) {
return true;
}
// Step 7. Let desiredSize be
// ! ReadableByteStreamControllerGetDesiredSize(controller).
Nullable<double> desiredSize =
ReadableByteStreamControllerGetDesiredSize(aController);
// Step 8. Assert: desiredSize is not null.
MOZ_ASSERT(!desiredSize.IsNull());
// Step 9. If desiredSize > 0, return true.
// Step 10. Return false.
return desiredSize.Value() > 0;
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-call-pull-if-needed
void ReadableByteStreamControllerCallPullIfNeeded(
JSContext* aCx, ReadableByteStreamController* aController,
ErrorResult& aRv) {
// Step 1.
bool shouldPull = ReadableByteStreamControllerShouldCallPull(aController);
// Step 2.
if (!shouldPull) {
return;
}
// Step 3.
if (aController->Pulling()) {
aController->SetPullAgain(true);
return;
}
// Step 4.
MOZ_ASSERT(!aController->PullAgain());
// Step 5.
aController->SetPulling(true);
// Step 6.
RefPtr<ReadableStreamController> controller(aController);
RefPtr<UnderlyingSourceAlgorithmsBase> algorithms =
aController->GetAlgorithms();
RefPtr<Promise> pullPromise = algorithms->PullCallback(aCx, *controller, aRv);
if (aRv.Failed()) {
return;
}
// Steps 7+8
pullPromise->AddCallbacksWithCycleCollectedArgs(
[](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
ReadableByteStreamController* aController)
MOZ_CAN_RUN_SCRIPT_BOUNDARY {
// Step 7.1
aController->SetPulling(false);
// Step 7.2
if (aController->PullAgain()) {
// Step 7.2.1
aController->SetPullAgain(false);
// Step 7.2.2
ReadableByteStreamControllerCallPullIfNeeded(
aCx, MOZ_KnownLive(aController), aRv);
}
},
[](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
ReadableByteStreamController* aController) {
// Step 8.1
ReadableByteStreamControllerError(aController, aValue, aRv);
},
RefPtr(aController));
}
bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
JSContext* aCx, ReadableByteStreamController* aController,
PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv);
JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
JSContext* aCx, PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv);
// https://streams.spec.whatwg.org/#readable-stream-fulfill-read-into-request
MOZ_CAN_RUN_SCRIPT
void ReadableStreamFulfillReadIntoRequest(JSContext* aCx,
ReadableStream* aStream,
JS::Handle<JS::Value> aChunk,
bool done, ErrorResult& aRv) {
// Step 1. Assert: !ReadableStreamHasBYOBReader(stream) is true.
MOZ_ASSERT(ReadableStreamHasBYOBReader(aStream));
// Step 2. Let reader be stream.[[reader]].
ReadableStreamBYOBReader* reader = aStream->GetReader()->AsBYOB();
// Step 3. Assert: reader.[[readIntoRequests]] is not empty.
MOZ_ASSERT(!reader->ReadIntoRequests().isEmpty());
// Step 4. Let readIntoRequest be reader.[[readIntoRequests]][0].
// Step 5. Remove readIntoRequest from reader.[[readIntoRequests]].
RefPtr<ReadIntoRequest> readIntoRequest =
reader->ReadIntoRequests().popFirst();
// Step 6. If done is true, perform readIntoRequests close steps, given
// chunk.
if (done) {
readIntoRequest->CloseSteps(aCx, aChunk, aRv);
return;
}
// Step 7. Otherwise, perform readIntoRequests chunk steps, given chunk.
readIntoRequest->ChunkSteps(aCx, aChunk, aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-commit-pull-into-descriptor
MOZ_CAN_RUN_SCRIPT
void ReadableByteStreamControllerCommitPullIntoDescriptor(
JSContext* aCx, ReadableStream* aStream,
PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv) {
// Step 1. Assert: stream.[[state]] is not "errored".
MOZ_ASSERT(aStream->State() != ReadableStream::ReaderState::Errored);
// Step 2. Assert: pullIntoDescriptor.reader type is not "none".
MOZ_ASSERT(pullIntoDescriptor->GetReaderType() != ReaderType::None);
// Step 3. Let done be false.
bool done = false;
// Step 4. If stream.[[state]] is "closed",
if (aStream->State() == ReadableStream::ReaderState::Closed) {
// Step 4.1. Assert: pullIntoDescriptors bytes filled is 0.
MOZ_ASSERT(pullIntoDescriptor->BytesFilled() == 0);
// Step 4.2. Set done to true.
done = true;
}
// Step 5. Let filledView be !
// ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
JS::Rooted<JSObject*> filledView(
aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
aCx, pullIntoDescriptor, aRv));
if (aRv.Failed()) {
return;
}
JS::Rooted<JS::Value> filledViewValue(aCx, JS::ObjectValue(*filledView));
// Step 6. If pullIntoDescriptors reader type is "default",
if (pullIntoDescriptor->GetReaderType() == ReaderType::Default) {
// Step 6.1. Perform !ReadableStreamFulfillReadRequest(stream, filledView,
// done).
ReadableStreamFulfillReadRequest(aCx, aStream, filledViewValue, done, aRv);
return;
}
// Step 7.1. Assert: pullIntoDescriptors reader type is "byob".
MOZ_ASSERT(pullIntoDescriptor->GetReaderType() == ReaderType::BYOB);
// Step 7.2 Perform !ReadableStreamFulfillReadIntoRequest(stream, filledView,
// done).
ReadableStreamFulfillReadIntoRequest(aCx, aStream, filledViewValue, done,
aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-process-pull-into-descriptors-using-queue
MOZ_CAN_RUN_SCRIPT
void ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
JSContext* aCx, ReadableByteStreamController* aController,
ErrorResult& aRv) {
// Step 1. Assert: controller.[[closeRequested]] is false.
MOZ_ASSERT(!aController->CloseRequested());
// Step 2. While controller.[[pendingPullIntos]] is not empty,
while (!aController->PendingPullIntos().isEmpty()) {
// Step 2.1 If controller.[[queueTotalSize]] is 0, return.
if (aController->QueueTotalSize() == 0) {
return;
}
// Step 2.2. Let pullIntoDescriptor be controller.[[pendingPullIntos]][0].
RefPtr<PullIntoDescriptor> pullIntoDescriptor =
aController->PendingPullIntos().getFirst();
// Step 2.3. If
// !ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
// pullIntoDescriptor) is true,
bool ready = ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
aCx, aController, pullIntoDescriptor, aRv);
if (aRv.Failed()) {
return;
}
if (ready) {
// Step 2.3.1. Perform
// !ReadableByteStreamControllerShiftPendingPullInto(controller).
RefPtr<PullIntoDescriptor> discardedPullIntoDescriptor =
ReadableByteStreamControllerShiftPendingPullInto(aController);
// Step 2.3.2. Perform
// !ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
// pullIntoDescriptor).
RefPtr<ReadableStream> stream(aController->Stream());
ReadableByteStreamControllerCommitPullIntoDescriptor(
aCx, stream, pullIntoDescriptor, aRv);
if (aRv.Failed()) {
return;
}
}
}
}
MOZ_CAN_RUN_SCRIPT
void ReadableByteStreamControllerHandleQueueDrain(
JSContext* aCx, ReadableByteStreamController* aController,
ErrorResult& aRv);
// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerfillreadrequestfromqueue
MOZ_CAN_RUN_SCRIPT void ReadableByteStreamControllerFillReadRequestFromQueue(
JSContext* aCx, ReadableByteStreamController* aController,
ReadRequest* aReadRequest, ErrorResult& aRv) {
// Step 1. Assert: controller.[[queueTotalSize]] > 0.
MOZ_ASSERT(aController->QueueTotalSize() > 0);
// Also assert that the queue has a non-zero length;
MOZ_ASSERT(aController->Queue().length() > 0);
// Step 2. Let entry be controller.[[queue]][0].
// Step 3. Remove entry from controller.[[queue]].
RefPtr<ReadableByteStreamQueueEntry> entry = aController->Queue().popFirst();
// Assert that we actually got an entry.
MOZ_ASSERT(entry);
// Step 4. Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]]
// entrys byte length.
aController->SetQueueTotalSize(aController->QueueTotalSize() -
double(entry->ByteLength()));
// Step 5. Perform ! ReadableByteStreamControllerHandleQueueDrain(controller).
ReadableByteStreamControllerHandleQueueDrain(aCx, aController, aRv);
if (aRv.Failed()) {
return;
}
// Step 6. Let view be ! Construct(%Uint8Array%, « entrys buffer, entrys
// byte offset, entrys byte length »).
aRv.MightThrowJSException();
JS::Rooted<JSObject*> buffer(aCx, entry->Buffer());
JS::Rooted<JSObject*> view(
aCx, JS_NewUint8ArrayWithBuffer(aCx, buffer, entry->ByteOffset(),
int64_t(entry->ByteLength())));
if (!view) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
// Step 7. Perform readRequests chunk steps, given view.
JS::Rooted<JS::Value> viewValue(aCx, JS::ObjectValue(*view));
aReadRequest->ChunkSteps(aCx, viewValue, aRv);
}
MOZ_CAN_RUN_SCRIPT void
ReadableByteStreamControllerProcessReadRequestsUsingQueue(
JSContext* aCx, ReadableByteStreamController* aController,
ErrorResult& aRv) {
// Step 1. Let reader be controller.[[stream]].[[reader]].
// Step 2. Assert: reader implements ReadableStreamDefaultReader.
RefPtr<ReadableStreamDefaultReader> reader =
aController->Stream()->GetDefaultReader();
// Step 3. While reader.[[readRequests]] is not empty,
while (!reader->ReadRequests().isEmpty()) {
// Step 3.1. If controller.[[queueTotalSize]] is 0, return.
if (aController->QueueTotalSize() == 0) {
return;
}
// Step 3.2. Let readRequest be reader.[[readRequests]][0].
// Step 3.3. Remove readRequest from reader.[[readRequests]].
RefPtr<ReadRequest> readRequest = reader->ReadRequests().popFirst();
// Step 3.4. Perform !
// ReadableByteStreamControllerFillReadRequestFromQueue(controller,
// readRequest).
ReadableByteStreamControllerFillReadRequestFromQueue(aCx, aController,
readRequest, aRv);
if (aRv.Failed()) {
return;
}
}
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue
void ReadableByteStreamControllerEnqueue(
JSContext* aCx, ReadableByteStreamController* aController,
JS::Handle<JSObject*> aChunk, ErrorResult& aRv) {
aRv.MightThrowJSException();
// Step 1.
RefPtr<ReadableStream> stream = aController->Stream();
// Step 2.
if (aController->CloseRequested() ||
stream->State() != ReadableStream::ReaderState::Readable) {
return;
}
// Step 3.
bool isShared;
JS::Rooted<JSObject*> buffer(
aCx, JS_GetArrayBufferViewBuffer(aCx, aChunk, &isShared));
if (!buffer) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
// Step 4.
size_t byteOffset = JS_GetArrayBufferViewByteOffset(aChunk);
// Step 5.
size_t byteLength = JS_GetArrayBufferViewByteLength(aChunk);
// Step 6.
if (JS::IsDetachedArrayBufferObject(buffer)) {
aRv.ThrowTypeError("Detatched Array Buffer");
return;
}
// Step 7.
JS::Rooted<JSObject*> transferredBuffer(aCx,
TransferArrayBuffer(aCx, buffer));
if (!transferredBuffer) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
// Step 8.
if (!aController->PendingPullIntos().isEmpty()) {
// Step 8.1
RefPtr<PullIntoDescriptor> firstPendingPullInto =
aController->PendingPullIntos().getFirst();
// Step 8.2
JS::Rooted<JSObject*> pendingBuffer(aCx, firstPendingPullInto->Buffer());
if (JS::IsDetachedArrayBufferObject(pendingBuffer)) {
aRv.ThrowTypeError("Pending PullInto has detached buffer");
return;
}
// Step 8.3. Perform !
// ReadableByteStreamControllerInvalidateBYOBRequest(controller).
ReadableByteStreamControllerInvalidateBYOBRequest(aController);
// Step 8.4. Set firstPendingPullIntos buffer to !
// TransferArrayBuffer(firstPendingPullIntos buffer).
pendingBuffer = TransferArrayBuffer(aCx, pendingBuffer);
if (!pendingBuffer) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
firstPendingPullInto->SetBuffer(pendingBuffer);
// Step 8.5. If firstPendingPullIntos reader type is "none", perform ?
// ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller,
// firstPendingPullInto).
if (firstPendingPullInto->GetReaderType() == ReaderType::None) {
ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
aCx, aController, firstPendingPullInto, aRv);
if (aRv.Failed()) {
return;
}
}
}
// Step 9. If ! ReadableStreamHasDefaultReader(stream) is true,
if (ReadableStreamHasDefaultReader(stream)) {
// Step 9.1. Perform !
// ReadableByteStreamControllerProcessReadRequestsUsingQueue(controller).
ReadableByteStreamControllerProcessReadRequestsUsingQueue(aCx, aController,
aRv);
if (aRv.Failed()) {
return;
}
// Step 9.2. If ! ReadableStreamGetNumReadRequests(stream) is 0,
if (ReadableStreamGetNumReadRequests(stream) == 0) {
// Step 9.2.1 Assert: controller.[[pendingPullIntos]] is empty.
MOZ_ASSERT(aController->PendingPullIntos().isEmpty());
// Step 9.2.2. Perform !
// ReadableByteStreamControllerEnqueueChunkToQueue(controller,
// transferredBuffer, byteOffset, byteLength).
ReadableByteStreamControllerEnqueueChunkToQueue(
aController, transferredBuffer, byteOffset, byteLength);
// Step 9.3. Otherwise,
} else {
// Step 9.3.1 Assert: controller.[[queue]] is empty.
MOZ_ASSERT(aController->Queue().isEmpty());
// Step 9.3.2. If controller.[[pendingPullIntos]] is not empty,
if (!aController->PendingPullIntos().isEmpty()) {
// Step 9.3.2.1. Assert: controller.[[pendingPullIntos]][0]'s reader
// type is "default".
MOZ_ASSERT(
aController->PendingPullIntos().getFirst()->GetReaderType() ==
ReaderType::Default);
// Step 9.3.2.2. Perform !
// ReadableByteStreamControllerShiftPendingPullInto(controller).
RefPtr<PullIntoDescriptor> pullIntoDescriptor =
ReadableByteStreamControllerShiftPendingPullInto(aController);
(void)pullIntoDescriptor;
}
// Step 9.3.3. Let transferredView be ! Construct(%Uint8Array%, «
// transferredBuffer, byteOffset, byteLength »).
JS::Rooted<JSObject*> transferredView(
aCx, JS_NewUint8ArrayWithBuffer(aCx, transferredBuffer, byteOffset,
int64_t(byteLength)));
if (!transferredView) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
// Step 9.3.4. Perform ! ReadableStreamFulfillReadRequest(stream,
// transferredView, false).
JS::Rooted<JS::Value> transferredViewValue(
aCx, JS::ObjectValue(*transferredView));
ReadableStreamFulfillReadRequest(aCx, stream, transferredViewValue, false,
aRv);
if (aRv.Failed()) {
return;
}
}
// Step 10. Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true,
} else if (ReadableStreamHasBYOBReader(stream)) {
// Step 10.1. Perform !
// ReadableByteStreamControllerEnqueueChunkToQueue(controller,
// transferredBuffer, byteOffset, byteLength).
ReadableByteStreamControllerEnqueueChunkToQueue(
aController, transferredBuffer, byteOffset, byteLength);
// Step 10.2 Perform !
// ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
aCx, aController, aRv);
if (aRv.Failed()) {
return;
}
// Step 11. Otherwise,
} else {
// Step 11.1. Assert: ! IsReadableStreamLocked(stream) is false.
MOZ_ASSERT(!IsReadableStreamLocked(stream));
// Step 11.2. Perform !
// ReadableByteStreamControllerEnqueueChunkToQueue(controller,
// transferredBuffer, byteOffset, byteLength).
ReadableByteStreamControllerEnqueueChunkToQueue(
aController, transferredBuffer, byteOffset, byteLength);
}
// Step 12. Perform !
// ReadableByteStreamControllerCallPullIfNeeded(controller).
ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
}
// https://streams.spec.whatwg.org/#rbs-controller-enqueue
void ReadableByteStreamController::Enqueue(JSContext* aCx,
const ArrayBufferView& aChunk,
ErrorResult& aRv) {
// Step 1.
JS::Rooted<JSObject*> chunk(aCx, aChunk.Obj());
if (JS_GetArrayBufferViewByteLength(chunk) == 0) {
aRv.ThrowTypeError("Zero Length View");
return;
}
// Step 2.
bool isShared;
JS::Rooted<JSObject*> viewedArrayBuffer(
aCx, JS_GetArrayBufferViewBuffer(aCx, chunk, &isShared));
if (!viewedArrayBuffer) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
if (JS::GetArrayBufferByteLength(viewedArrayBuffer) == 0) {
aRv.ThrowTypeError("Zero Length Buffer");
return;
}
// Step 3.
if (CloseRequested()) {
aRv.ThrowTypeError("close requested");
return;
}
// Step 4.
if (Stream()->State() != ReadableStream::ReaderState::Readable) {
aRv.ThrowTypeError("Not Readable");
return;
}
// Step 5.
ReadableByteStreamControllerEnqueue(aCx, this, chunk, aRv);
}
// https://streams.spec.whatwg.org/#rbs-controller-error
void ReadableByteStreamController::Error(JSContext* aCx,
JS::Handle<JS::Value> aErrorValue,
ErrorResult& aRv) {
// Step 1.
ReadableByteStreamControllerError(this, aErrorValue, aRv);
}
// https://streams.spec.whatwg.org/#rbs-controller-private-cancel
already_AddRefed<Promise> ReadableByteStreamController::CancelSteps(
JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
// Step 1.
ReadableByteStreamControllerClearPendingPullIntos(this);
// Step 2.
ResetQueue(this);
// Step 3.
Optional<JS::Handle<JS::Value>> reason(aCx, aReason);
RefPtr<UnderlyingSourceAlgorithmsBase> algorithms = mAlgorithms;
RefPtr<Promise> result = algorithms->CancelCallback(aCx, reason, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
// Step 4.
ReadableByteStreamControllerClearAlgorithms(this);
// Step 5.
return result.forget();
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-handle-queue-drain
void ReadableByteStreamControllerHandleQueueDrain(
JSContext* aCx, ReadableByteStreamController* aController,
ErrorResult& aRv) {
// Step 1.
MOZ_ASSERT(aController->Stream()->State() ==
ReadableStream::ReaderState::Readable);
// Step 2.
if (aController->QueueTotalSize() == 0 && aController->CloseRequested()) {
// Step 2.1
ReadableByteStreamControllerClearAlgorithms(aController);
// Step 2.2
RefPtr<ReadableStream> stream = aController->Stream();
ReadableStreamClose(aCx, stream, aRv);
return;
}
// Step 3.1
ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
}
// https://streams.spec.whatwg.org/#rbs-controller-private-pull
void ReadableByteStreamController::PullSteps(JSContext* aCx,
ReadRequest* aReadRequest,
ErrorResult& aRv) {
// Step 1.
ReadableStream* stream = Stream();
// Step 2.
MOZ_ASSERT(ReadableStreamHasDefaultReader(stream));
// Step 3.
if (QueueTotalSize() > 0) {
// Step 3.1. Assert: ! ReadableStreamGetNumReadRequests ( stream ) is 0.
MOZ_ASSERT(ReadableStreamGetNumReadRequests(stream) == 0);
// Step 3.2. Perform !
// ReadableByteStreamControllerFillReadRequestFromQueue(this, readRequest).
ReadableByteStreamControllerFillReadRequestFromQueue(aCx, this,
aReadRequest, aRv);
// Step 3.3. Return.
return;
}
// Step 4.
Maybe<uint64_t> autoAllocateChunkSize = AutoAllocateChunkSize();
// Step 5.
if (autoAllocateChunkSize) {
// Step 5.1
aRv.MightThrowJSException();
JS::Rooted<JSObject*> buffer(
aCx, JS::NewArrayBuffer(aCx, *autoAllocateChunkSize));
// Step 5.2
if (!buffer) {
// Step 5.2.1
JS::Rooted<JS::Value> bufferError(aCx);
if (!JS_GetPendingException(aCx, &bufferError)) {
// Uncatchable exception; we should mark aRv and return.
aRv.StealExceptionFromJSContext(aCx);
return;
}
// It's not expliclitly stated, but I assume the intention here is that
// we perform a normal completion here.
JS_ClearPendingException(aCx);
aReadRequest->ErrorSteps(aCx, bufferError, aRv);
// Step 5.2.2.
return;
}
// Step 5.3
RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
buffer, *autoAllocateChunkSize, 0, *autoAllocateChunkSize, 0, 1,
PullIntoDescriptor::Constructor::Uint8, ReaderType::Default);
// Step 5.4
PendingPullIntos().insertBack(pullIntoDescriptor);
}
// Step 6.
ReadableStreamAddReadRequest(stream, aReadRequest);
// Step 7.
ReadableByteStreamControllerCallPullIfNeeded(aCx, this, aRv);
}
// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontroller-releasesteps
void ReadableByteStreamController::ReleaseSteps() {
// Step 1. If this.[[pendingPullIntos]] is not empty,
if (!PendingPullIntos().isEmpty()) {
// Step 1.1. Let firstPendingPullInto be this.[[pendingPullIntos]][0].
RefPtr<PullIntoDescriptor> firstPendingPullInto =
PendingPullIntos().popFirst();
// Step 1.2. Set firstPendingPullIntos reader type to "none".
firstPendingPullInto->SetReaderType(ReaderType::None);
// Step 1.3. Set this.[[pendingPullIntos]] to the list «
// firstPendingPullInto ».
PendingPullIntos().clear();
PendingPullIntos().insertBack(firstPendingPullInto);
}
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-shift-pending-pull-into
already_AddRefed<PullIntoDescriptor>
ReadableByteStreamControllerShiftPendingPullInto(
ReadableByteStreamController* aController) {
// Step 1.
MOZ_ASSERT(!aController->GetByobRequest());
// Step 2 + 3
RefPtr<PullIntoDescriptor> descriptor =
aController->PendingPullIntos().popFirst();
// Step 4.
return descriptor.forget();
}
JSObject* ConstructFromPullIntoConstructor(
JSContext* aCx, PullIntoDescriptor::Constructor constructor,
JS::Handle<JSObject*> buffer, size_t byteOffset, size_t length) {
switch (constructor) {
case PullIntoDescriptor::Constructor::DataView:
return JS_NewDataView(aCx, buffer, byteOffset, length);
break;
#define CONSTRUCT_TYPED_ARRAY_TYPE(ExternalT, NativeT, Name) \
case PullIntoDescriptor::Constructor::Name: \
return JS_New##Name##ArrayWithBuffer(aCx, buffer, byteOffset, \
int64_t(length)); \
break;
JS_FOR_EACH_TYPED_ARRAY(CONSTRUCT_TYPED_ARRAY_TYPE)
#undef CONSTRUCT_TYPED_ARRAY_TYPE
default:
MOZ_ASSERT_UNREACHABLE("Unknown PullIntoDescriptor::Constructor");
return nullptr;
}
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-convert-pull-into-descriptor
JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
JSContext* aCx, PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv) {
// Step 1. Let bytesFilled be pullIntoDescriptors bytes filled.
uint64_t bytesFilled = pullIntoDescriptor->BytesFilled();
// Step 2. Let elementSize be pullIntoDescriptors element size.
uint64_t elementSize = pullIntoDescriptor->ElementSize();
// Step 3. Assert: bytesFilled ≤ pullIntoDescriptors byte length.
MOZ_ASSERT(bytesFilled <= pullIntoDescriptor->ByteLength());
// Step 4. Assert: bytesFilled mod elementSize is 0.
MOZ_ASSERT(bytesFilled % elementSize == 0);
// Step 5. Let buffer be ! TransferArrayBuffer(pullIntoDescriptors buffer).
aRv.MightThrowJSException();
JS::Rooted<JSObject*> srcBuffer(aCx, pullIntoDescriptor->Buffer());
JS::Rooted<JSObject*> buffer(aCx, TransferArrayBuffer(aCx, srcBuffer));
if (!buffer) {
aRv.StealExceptionFromJSContext(aCx);
return nullptr;
}
// Step 6. Return ! Construct(pullIntoDescriptors view constructor,
// « buffer, pullIntoDescriptors byte offset, bytesFilled ÷ elementSize »).
JS::Rooted<JSObject*> res(
aCx, ConstructFromPullIntoConstructor(
aCx, pullIntoDescriptor->ViewConstructor(), buffer,
pullIntoDescriptor->ByteOffset(), bytesFilled / elementSize));
if (!res) {
aRv.StealExceptionFromJSContext(aCx);
return nullptr;
}
return res;
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-closed-state
MOZ_CAN_RUN_SCRIPT
static void ReadableByteStreamControllerRespondInClosedState(
JSContext* aCx, ReadableByteStreamController* aController,
RefPtr<PullIntoDescriptor>& aFirstDescriptor, ErrorResult& aRv) {
// Step 1. Assert: firstDescriptor s bytes filled is 0.
MOZ_ASSERT(aFirstDescriptor->BytesFilled() == 0);
// Step 2. If firstDescriptors reader type is "none",
// perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
if (aFirstDescriptor->GetReaderType() == ReaderType::None) {
RefPtr<PullIntoDescriptor> discarded =
ReadableByteStreamControllerShiftPendingPullInto(aController);
(void)discarded;
}
// Step 3. Let stream be controller.[[stream]].
RefPtr<ReadableStream> stream = aController->Stream();
// Step 4. If ! ReadableStreamHasBYOBReader(stream) is true,
if (ReadableStreamHasBYOBReader(stream)) {
// Step 4.1. While ! ReadableStreamGetNumReadIntoRequests(stream) > 0,
while (ReadableStreamGetNumReadIntoRequests(stream) > 0) {
// Step 4.1.1. Let pullIntoDescriptor be !
// ReadableByteStreamControllerShiftPendingPullInto(controller).
RefPtr<PullIntoDescriptor> pullIntoDescriptor =
ReadableByteStreamControllerShiftPendingPullInto(aController);
// Step 4.1.2. Perform !
// ReadableByteStreamControllerCommitPullIntoDescriptor(stream,
// pullIntoDescriptor).
ReadableByteStreamControllerCommitPullIntoDescriptor(
aCx, stream, pullIntoDescriptor, aRv);
}
}
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-head-pull-into-descriptor
void ReadableByteStreamControllerFillHeadPullIntoDescriptor(
ReadableByteStreamController* aController, size_t aSize,
PullIntoDescriptor* aPullIntoDescriptor) {
// Step 1. Assert: either controller.[[pendingPullIntos]] is empty, or
// controller.[[pendingPullIntos]][0] is pullIntoDescriptor.
MOZ_ASSERT(aController->PendingPullIntos().isEmpty() ||
aController->PendingPullIntos().getFirst() == aPullIntoDescriptor);
// Step 2. Assert: controller.[[byobRequest]] is null.
MOZ_ASSERT(!aController->GetByobRequest());
// Step 3. Set pullIntoDescriptors bytes filled to bytes filled + size.
aPullIntoDescriptor->SetBytesFilled(aPullIntoDescriptor->BytesFilled() +
aSize);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-readable-state
MOZ_CAN_RUN_SCRIPT
static void ReadableByteStreamControllerRespondInReadableState(
JSContext* aCx, ReadableByteStreamController* aController,
uint64_t aBytesWritten, PullIntoDescriptor* aPullIntoDescriptor,
ErrorResult& aRv) {
// Step 1. Assert: pullIntoDescriptors bytes filled + bytesWritten ≤
// pullIntoDescriptors byte length.
MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() + aBytesWritten <=
aPullIntoDescriptor->ByteLength());
// Step 2. Perform
// !ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,
// bytesWritten, pullIntoDescriptor).
ReadableByteStreamControllerFillHeadPullIntoDescriptor(
aController, aBytesWritten, aPullIntoDescriptor);
// Step 3. If pullIntoDescriptors reader type is "none",
if (aPullIntoDescriptor->GetReaderType() == ReaderType::None) {
// Step 3.1. Perform ?
// ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller,
// pullIntoDescriptor).
ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
aCx, aController, aPullIntoDescriptor, aRv);
if (aRv.Failed()) {
return;
}
// Step 3.2. Perform !
// ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
aCx, aController, aRv);
// Step 3.3. Return.
return;
}
// Step 4. If pullIntoDescriptors bytes filled < pullIntoDescriptors element
// size, return.
if (aPullIntoDescriptor->BytesFilled() < aPullIntoDescriptor->ElementSize()) {
return;
}
// Step 5. Perform
// !ReadableByteStreamControllerShiftPendingPullInto(controller).
RefPtr<PullIntoDescriptor> pullIntoDescriptor =
ReadableByteStreamControllerShiftPendingPullInto(aController);
(void)pullIntoDescriptor;
// Step 6. Let remainderSize be pullIntoDescriptors bytes filled mod
// pullIntoDescriptors element size.
size_t remainderSize =
aPullIntoDescriptor->BytesFilled() % aPullIntoDescriptor->ElementSize();
// Step 7. If remainderSize > 0,
if (remainderSize > 0) {
// Step 7.1. Let end be pullIntoDescriptors byte offset +
// pullIntoDescriptors bytes filled.
size_t end =
aPullIntoDescriptor->ByteOffset() + aPullIntoDescriptor->BytesFilled();
// Step 7.2. Perform ?
// ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
// pullIntoDescriptors buffer, end remainderSize, remainderSize).
JS::Rooted<JSObject*> pullIntoBuffer(aCx, aPullIntoDescriptor->Buffer());
ReadableByteStreamControllerEnqueueClonedChunkToQueue(
aCx, aController, pullIntoBuffer, end - remainderSize, remainderSize,
aRv);
if (aRv.Failed()) {
return;
}
}
// Step 8. Set pullIntoDescriptors bytes filled to pullIntoDescriptors bytes
// filled remainderSize.
aPullIntoDescriptor->SetBytesFilled(aPullIntoDescriptor->BytesFilled() -
remainderSize);
// Step 9. Perform
// !ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
// pullIntoDescriptor).
RefPtr<ReadableStream> stream(aController->Stream());
ReadableByteStreamControllerCommitPullIntoDescriptor(
aCx, stream, aPullIntoDescriptor, aRv);
if (aRv.Failed()) {
return;
}
// Step 10. Perform
// !ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
aCx, aController, aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-internal
void ReadableByteStreamControllerRespondInternal(
JSContext* aCx, ReadableByteStreamController* aController,
uint64_t aBytesWritten, ErrorResult& aRv) {
// Step 1.
RefPtr<PullIntoDescriptor> firstDescriptor =
aController->PendingPullIntos().getFirst();
// Step 2.
JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
#ifdef DEBUG
bool canTransferBuffer = CanTransferArrayBuffer(aCx, buffer, aRv);
MOZ_ASSERT(!aRv.Failed());
MOZ_ASSERT(canTransferBuffer);
#endif
// Step 3.
ReadableByteStreamControllerInvalidateBYOBRequest(aController);
// Step 4.
auto state = aController->Stream()->State();
// Step 5.
if (state == ReadableStream::ReaderState::Closed) {
// Step 5.1
MOZ_ASSERT(aBytesWritten == 0);
// Step 5.2
ReadableByteStreamControllerRespondInClosedState(aCx, aController,
firstDescriptor, aRv);
if (aRv.Failed()) {
return;
}
} else {
// Step 6.1
MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);
// Step 6.2.
MOZ_ASSERT(aBytesWritten > 0);
// Step 6.3
ReadableByteStreamControllerRespondInReadableState(
aCx, aController, aBytesWritten, firstDescriptor, aRv);
if (aRv.Failed()) {
return;
}
}
// Step 7.
ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond
void ReadableByteStreamControllerRespond(
JSContext* aCx, ReadableByteStreamController* aController,
uint64_t aBytesWritten, ErrorResult& aRv) {
// Step 1.
MOZ_ASSERT(!aController->PendingPullIntos().isEmpty());
// Step 2.
PullIntoDescriptor* firstDescriptor =
aController->PendingPullIntos().getFirst();
// Step 3.
auto state = aController->Stream()->State();
// Step 4.
if (state == ReadableStream::ReaderState::Closed) {
// Step 4.1
if (aBytesWritten != 0) {
aRv.ThrowTypeError("bytesWritten not zero on closed stream");
return;
}
} else {
// Step 5.1
MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);
// Step 5.2
if (aBytesWritten == 0) {
aRv.ThrowTypeError("bytesWritten 0");
return;
}
// Step 5.3
if (firstDescriptor->BytesFilled() + aBytesWritten >
firstDescriptor->ByteLength()) {
aRv.ThrowRangeError("bytesFilled + bytesWritten > byteLength");
return;
}
}
// Step 6.
aRv.MightThrowJSException();
JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
JS::Rooted<JSObject*> transferredBuffer(aCx,
TransferArrayBuffer(aCx, buffer));
if (!transferredBuffer) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
firstDescriptor->SetBuffer(transferredBuffer);
// Step 7.
ReadableByteStreamControllerRespondInternal(aCx, aController, aBytesWritten,
aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-with-new-view
void ReadableByteStreamControllerRespondWithNewView(
JSContext* aCx, ReadableByteStreamController* aController,
JS::Handle<JSObject*> aView, ErrorResult& aRv) {
aRv.MightThrowJSException();
// Step 1.
MOZ_ASSERT(!aController->PendingPullIntos().isEmpty());
// Step 2.
bool isSharedMemory;
JS::Rooted<JSObject*> viewedArrayBuffer(
aCx, JS_GetArrayBufferViewBuffer(aCx, aView, &isSharedMemory));
if (!viewedArrayBuffer) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
MOZ_ASSERT(!JS::IsDetachedArrayBufferObject(viewedArrayBuffer));
// Step 3.
RefPtr<PullIntoDescriptor> firstDescriptor =
aController->PendingPullIntos().getFirst();
// Step 4.
ReadableStream::ReaderState state = aController->Stream()->State();
// Step 5.
if (state == ReadableStream::ReaderState::Closed) {
// Step 5.1
if (JS_GetArrayBufferViewByteLength(aView) != 0) {
aRv.ThrowTypeError("View has non-zero length in closed stream");
return;
}
} else {
// Step 6.1
MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);
// Step 6.2
if (JS_GetArrayBufferViewByteLength(aView) == 0) {
aRv.ThrowTypeError("View has zero length in readable stream");
return;
}
}
// Step 7.
if (firstDescriptor->ByteOffset() + firstDescriptor->BytesFilled() !=
JS_GetArrayBufferViewByteOffset(aView)) {
aRv.ThrowRangeError("Invalid Offset");
return;
}
// Step 8.
if (firstDescriptor->BufferByteLength() !=
JS::GetArrayBufferByteLength(viewedArrayBuffer)) {
aRv.ThrowRangeError("Mismatched buffer byte lengths");
return;
}
// Step 9.
if (firstDescriptor->BytesFilled() + JS_GetArrayBufferViewByteLength(aView) >
firstDescriptor->ByteLength()) {
aRv.ThrowRangeError("Too many bytes");
return;
}
// Step 10. Let viewByteLength be view.[[ByteLength]].
size_t viewByteLength = JS_GetArrayBufferViewByteLength(aView);
// Step 11. Set firstDescriptors buffer to ?
// TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
JS::Rooted<JSObject*> transferedBuffer(
aCx, TransferArrayBuffer(aCx, viewedArrayBuffer));
if (!transferedBuffer) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
firstDescriptor->SetBuffer(transferedBuffer);
// Step 12. Perform ? ReadableByteStreamControllerRespondInternal(controller,
// viewByteLength).
ReadableByteStreamControllerRespondInternal(aCx, aController, viewByteLength,
aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-pull-into-descriptor-from-queue
bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
JSContext* aCx, ReadableByteStreamController* aController,
PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv) {
// Step 1. Let elementSize be pullIntoDescriptor.[[elementSize]].
size_t elementSize = aPullIntoDescriptor->ElementSize();
// Step 2. Let currentAlignedBytes be pullIntoDescriptors bytes filled
// (pullIntoDescriptors bytes filled mod elementSize).
size_t currentAlignedBytes =
aPullIntoDescriptor->BytesFilled() -
(aPullIntoDescriptor->BytesFilled() % elementSize);
// Step 3. Let maxBytesToCopy be min(controller.[[queueTotalSize]],
// pullIntoDescriptors byte length pullIntoDescriptors bytes filled).
size_t maxBytesToCopy =
std::min(static_cast<size_t>(aController->QueueTotalSize()),
static_cast<size_t>((aPullIntoDescriptor->ByteLength() -
aPullIntoDescriptor->BytesFilled())));
// Step 4. Let maxBytesFilled be pullIntoDescriptors bytes filled +
// maxBytesToCopy.
size_t maxBytesFilled = aPullIntoDescriptor->BytesFilled() + maxBytesToCopy;
// Step 5. Let maxAlignedBytes be maxBytesFilled (maxBytesFilled mod
// elementSize).
size_t maxAlignedBytes = maxBytesFilled - (maxBytesFilled % elementSize);
// Step 6. Let totalBytesToCopyRemaining be maxBytesToCopy.
size_t totalBytesToCopyRemaining = maxBytesToCopy;
// Step 7. Let ready be false.
bool ready = false;
// Step 8. If maxAlignedBytes > currentAlignedBytes,
if (maxAlignedBytes > currentAlignedBytes) {
// Step 8.1. Set totalBytesToCopyRemaining to maxAlignedBytes
// pullIntoDescriptors bytes filled.
totalBytesToCopyRemaining =
maxAlignedBytes - aPullIntoDescriptor->BytesFilled();
// Step 8.2. Set ready to true.
ready = true;
}
// Step 9. Let queue be controller.[[queue]].
LinkedList<RefPtr<ReadableByteStreamQueueEntry>>& queue =
aController->Queue();
// Step 10. While totalBytesToCopyRemaining > 0,
while (totalBytesToCopyRemaining > 0) {
// Step 10.1 Let headOfQueue be queue[0].
ReadableByteStreamQueueEntry* headOfQueue = queue.getFirst();
// Step 10.2. Let bytesToCopy be min(totalBytesToCopyRemaining,
// headOfQueues byte length).
size_t bytesToCopy =
std::min(totalBytesToCopyRemaining, headOfQueue->ByteLength());
// Step 10.3. Let destStart be pullIntoDescriptors byte offset +
// pullIntoDescriptors bytes filled.
size_t destStart =
aPullIntoDescriptor->ByteOffset() + aPullIntoDescriptor->BytesFilled();
// Step 10.4. Perform !CopyDataBlockBytes(pullIntoDescriptors
// buffer.[[ArrayBufferData]], destStart, headOfQueues
// buffer.[[ArrayBufferData]], headOfQueues byte offset,
// bytesToCopy).
JS::Rooted<JSObject*> descriptorBuffer(aCx, aPullIntoDescriptor->Buffer());
JS::Rooted<JSObject*> queueBuffer(aCx, headOfQueue->Buffer());
if (!JS::ArrayBufferCopyData(aCx, descriptorBuffer, destStart, queueBuffer,
headOfQueue->ByteOffset(), bytesToCopy)) {
aRv.StealExceptionFromJSContext(aCx);
return false;
}
// Step 10.5. If headOfQueues byte length is bytesToCopy,
if (headOfQueue->ByteLength() == bytesToCopy) {
// Step 10.5.1. Remove queue[0].
queue.popFirst();
} else {
// Step 10.6. Otherwise,
// Step 10.6.1 Set headOfQueues byte offset to
// headOfQueues byte offset + bytesToCopy.
headOfQueue->SetByteOffset(headOfQueue->ByteOffset() + bytesToCopy);
// Step 10.6.2 Set headOfQueues byte length to
// headOfQueues byte length bytesToCopy.
headOfQueue->SetByteLength(headOfQueue->ByteLength() - bytesToCopy);
}
// Step 10.7. Set controller.[[queueTotalSize]] to
// controller.[[queueTotalSize]] bytesToCopy.
aController->SetQueueTotalSize(aController->QueueTotalSize() -
(double)bytesToCopy);
// Step 10.8, Perform
// !ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,
// bytesToCopy, pullIntoDescriptor).
ReadableByteStreamControllerFillHeadPullIntoDescriptor(
aController, bytesToCopy, aPullIntoDescriptor);
// Step 10.9. Set totalBytesToCopyRemaining to totalBytesToCopyRemaining
// bytesToCopy.
totalBytesToCopyRemaining = totalBytesToCopyRemaining - bytesToCopy;
}
// Step 11. If ready is false,
if (!ready) {
// Step 11.1. Assert: controller.[[queueTotalSize]] is 0.
MOZ_ASSERT(aController->QueueTotalSize() == 0);
// Step 11.2. Assert: pullIntoDescriptors bytes filled > 0.
MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() > 0);
// Step 11.3. Assert: pullIntoDescriptors bytes filled <
// pullIntoDescriptors
// element size.
MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() <
aPullIntoDescriptor->ElementSize());
}
// Step 12. Return ready.
return ready;
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-pull-into
void ReadableByteStreamControllerPullInto(
JSContext* aCx, ReadableByteStreamController* aController,
JS::Handle<JSObject*> aView, ReadIntoRequest* aReadIntoRequest,
ErrorResult& aRv) {
aRv.MightThrowJSException();
// Step 1. Let stream be controller.[[stream]].
ReadableStream* stream = aController->Stream();
// Step 2. Let elementSize be 1.
size_t elementSize = 1;
// Step 3. Let ctor be %DataView%.
PullIntoDescriptor::Constructor ctor =
PullIntoDescriptor::Constructor::DataView;
// Step 4. If view has a [[TypedArrayName]] internal slot (i.e., it is not a
// DataView),
if (JS_IsTypedArrayObject(aView)) {
// Step 4.1. Set elementSize to the element size specified in the typed
// array constructors table for view.[[TypedArrayName]].
JS::Scalar::Type type = JS_GetArrayBufferViewType(aView);
elementSize = JS::Scalar::byteSize(type);
// Step 4.2 Set ctor to the constructor specified in the typed array
// constructors table for view.[[TypedArrayName]].
ctor = PullIntoDescriptor::constructorFromScalar(type);
}
// Step 5. Let byteOffset be view.[[ByteOffset]].
size_t byteOffset = JS_GetArrayBufferViewByteOffset(aView);
// Step 6. Let byteLength be view.[[ByteLength]].
size_t byteLength = JS_GetArrayBufferViewByteLength(aView);
// Step 7. Let bufferResult be
// TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
bool isShared;
JS::Rooted<JSObject*> viewedArrayBuffer(
aCx, JS_GetArrayBufferViewBuffer(aCx, aView, &isShared));
if (!viewedArrayBuffer) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
JS::Rooted<JSObject*> bufferResult(
aCx, TransferArrayBuffer(aCx, viewedArrayBuffer));
// Step 8. If bufferResult is an abrupt completion,
if (!bufferResult) {
JS::Rooted<JS::Value> pendingException(aCx);
if (!JS_GetPendingException(aCx, &pendingException)) {
// This means an un-catchable exception. Use StealExceptionFromJSContext
// to setup aRv properly.
aRv.StealExceptionFromJSContext(aCx);
return;
}
// It's not expliclitly stated, but I assume the intention here is that
// we perform a normal completion here; we also need to clear the
// exception state anyhow to succesfully run ErrorSteps.
JS_ClearPendingException(aCx);
// Step 8.1. Perform readIntoRequests error steps, given
// bufferResult.[[Value]].
aReadIntoRequest->ErrorSteps(aCx, pendingException, aRv);
// Step 8.2. Return.
return;
}
// Step 9. Let buffer be bufferResult.[[Value]].
JS::Rooted<JSObject*> buffer(aCx, bufferResult);
// Step 10. Let pullIntoDescriptor be a new pull-into descriptor with
// buffer: buffer,
// buffer byte length: buffer.[[ArrayBufferByteLength]],
// byte offset: byteOffset,
// byte length: byteLength,
// bytes filled: 0,
// element size: elementSize,
// view constructor: ctor,
// and reader type: "byob".
RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
buffer, JS::GetArrayBufferByteLength(buffer), byteOffset, byteLength, 0,
elementSize, ctor, ReaderType::BYOB);
// Step 11. If controller.[[pendingPullIntos]] is not empty,
if (!aController->PendingPullIntos().isEmpty()) {
// Step 11.1. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
aController->PendingPullIntos().insertBack(pullIntoDescriptor);
// Step 11.2. Perform !ReadableStreamAddReadIntoRequest(stream,
// readIntoRequest).
ReadableStreamAddReadIntoRequest(stream, aReadIntoRequest);
// Step 11.3. Return.
return;
}
// Step 12. If stream.[[state]] is "closed",
if (stream->State() == ReadableStream::ReaderState::Closed) {
// Step 12.1. Let emptyView be !Construct(ctor, « pullIntoDescriptors
// buffer, pullIntoDescriptors byte offset, 0 »).
JS::Rooted<JSObject*> pullIntoBuffer(aCx, pullIntoDescriptor->Buffer());
JS::Rooted<JSObject*> emptyView(
aCx,
ConstructFromPullIntoConstructor(aCx, ctor, pullIntoBuffer,
pullIntoDescriptor->ByteOffset(), 0));
if (!emptyView) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
// Step 12.2. Perform readIntoRequests close steps, given emptyView.
JS::Rooted<JS::Value> emptyViewValue(aCx, JS::ObjectValue(*emptyView));
aReadIntoRequest->CloseSteps(aCx, emptyViewValue, aRv);
// Step 12.3. Return.
return;
}
// Step 13,. If controller.[[queueTotalSize]] > 0,
if (aController->QueueTotalSize() > 0) {
// Step 13.1 If
// !ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
// pullIntoDescriptor) is true,
bool ready = ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
aCx, aController, pullIntoDescriptor, aRv);
if (aRv.Failed()) {
return;
}
if (ready) {
// Step 13.1.1 Let filledView be
// !ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
JS::Rooted<JSObject*> filledView(
aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
aCx, pullIntoDescriptor, aRv));
if (aRv.Failed()) {
return;
}
// Step 13.1.2. Perform
// !ReadableByteStreamControllerHandleQueueDrain(controller).
ReadableByteStreamControllerHandleQueueDrain(aCx, aController, aRv);
if (aRv.Failed()) {
return;
}
// Step 13.1.3. Perform readIntoRequests chunk steps, given filledView.
JS::Rooted<JS::Value> filledViewValue(aCx, JS::ObjectValue(*filledView));
aReadIntoRequest->ChunkSteps(aCx, filledViewValue, aRv);
// Step 13.1.4. Return.
return;
}
// Step 13.2 If controller.[[closeRequested]] is true,
if (aController->CloseRequested()) {
// Step 13.2.1. Let e be a TypeError exception.
ErrorResult typeError;
typeError.ThrowTypeError("Close Requested True during Pull Into");
JS::Rooted<JS::Value> e(aCx);
MOZ_RELEASE_ASSERT(ToJSValue(aCx, std::move(typeError), &e));
// Step 13.2.2. Perform !ReadableByteStreamControllerError(controller, e).
ReadableByteStreamControllerError(aController, e, aRv);
if (aRv.Failed()) {
return;
}
// Step 13.2.3. Perform readIntoRequests error steps, given e.
aReadIntoRequest->ErrorSteps(aCx, e, aRv);
// Step 13.2.4. Return.
return;
}
}
// Step 14. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
aController->PendingPullIntos().insertBack(pullIntoDescriptor);
// Step 15. Perform !ReadableStreamAddReadIntoRequest(stream,
// readIntoRequest).
ReadableStreamAddReadIntoRequest(stream, aReadIntoRequest);
// Step 16, Perform
// !ReadableByteStreamControllerCallPullIfNeeded(controller).
ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
}
// https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller
void SetUpReadableByteStreamController(
JSContext* aCx, ReadableStream* aStream,
ReadableByteStreamController* aController,
UnderlyingSourceAlgorithmsBase* aAlgorithms, double aHighWaterMark,
Maybe<uint64_t> aAutoAllocateChunkSize, ErrorResult& aRv) {
// Step 1. Assert: stream.[[controller]] is undefined.
MOZ_ASSERT(!aStream->Controller());
// Step 2. If autoAllocateChunkSize is not undefined,
// Step 2.1. Assert: ! IsInteger(autoAllocateChunkSize) is true. Implicit
// Step 2.2. Assert: autoAllocateChunkSize is positive. (Implicit by
// type.)
// Step 3. Set controller.[[stream]] to stream.
aController->SetStream(aStream);
// Step 4. Set controller.[[pullAgain]] and controller.[[pulling]] to false.
aController->SetPullAgain(false);
aController->SetPulling(false);
// Step 5. Set controller.[[byobRequest]] to null.
aController->SetByobRequest(nullptr);
// Step 6. Perform !ResetQueue(controller).
ResetQueue(aController);
// Step 7. Set controller.[[closeRequested]] and controller.[[started]] to
// false.
aController->SetCloseRequested(false);
aController->SetStarted(false);
// Step 8. Set controller.[[strategyHWM]] to highWaterMark.
aController->SetStrategyHWM(aHighWaterMark);
// Step 9. Set controller.[[pullAlgorithm]] to pullAlgorithm.
// Step 10. Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
aController->SetAlgorithms(aAlgorithms);
// Step 11. Set controller.[[autoAllocateChunkSize]] to autoAllocateChunkSize.
aController->SetAutoAllocateChunkSize(aAutoAllocateChunkSize);
// Step 12. Set controller.[[pendingPullIntos]] to a new empty list.
aController->PendingPullIntos().clear();
// Step 13. Set stream.[[controller]] to controller.
aStream->SetController(*aController);
// Step 14. Let startResult be the result of performing startAlgorithm.
JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue());
RefPtr<ReadableStreamController> controller = aController;
aAlgorithms->StartCallback(aCx, *controller, &startResult, aRv);
if (aRv.Failed()) {
return;
}
// Let startPromise be a promise resolved with startResult.
RefPtr<Promise> startPromise = Promise::Create(GetIncumbentGlobal(), aRv);
if (aRv.Failed()) {
return;
}
startPromise->MaybeResolve(startResult);
// Step 16+17
startPromise->AddCallbacksWithCycleCollectedArgs(
[](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
ReadableByteStreamController* aController)
MOZ_CAN_RUN_SCRIPT_BOUNDARY {
MOZ_ASSERT(aController);
// Step 16.1
aController->SetStarted(true);
// Step 16.2
aController->SetPulling(false);
// Step 16.3
aController->SetPullAgain(false);
// Step 16.4:
ReadableByteStreamControllerCallPullIfNeeded(
aCx, MOZ_KnownLive(aController), aRv);
},
[](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
ReadableByteStreamController* aController) {
// Step 17.1
ReadableByteStreamControllerError(aController, aValue, aRv);
},
RefPtr(aController));
}
// https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller-from-underlying-source
void SetUpReadableByteStreamControllerFromUnderlyingSource(
JSContext* aCx, ReadableStream* aStream,
JS::Handle<JSObject*> aUnderlyingSource,
UnderlyingSource& aUnderlyingSourceDict, double aHighWaterMark,
ErrorResult& aRv) {
// Step 1. Let controller be a new ReadableByteStreamController.
auto controller =
MakeRefPtr<ReadableByteStreamController>(aStream->GetParentObject());
// Step 2 - 7
auto algorithms = MakeRefPtr<UnderlyingSourceAlgorithms>(
aStream->GetParentObject(), aUnderlyingSource, aUnderlyingSourceDict);
// Step 8. Let autoAllocateChunkSize be
// underlyingSourceDict["autoAllocateChunkSize"], if it exists, or undefined
// otherwise.
Maybe<uint64_t> autoAllocateChunkSize = mozilla::Nothing();
if (aUnderlyingSourceDict.mAutoAllocateChunkSize.WasPassed()) {
uint64_t value = aUnderlyingSourceDict.mAutoAllocateChunkSize.Value();
// Step 9. If autoAllocateChunkSize is 0, then throw a TypeError
// exception.
if (value == 0) {
aRv.ThrowTypeError("autoAllocateChunkSize can not be zero.");
return;
}
autoAllocateChunkSize = mozilla::Some(value);
}
// Step 10. Perform ? SetUpReadableByteStreamController(stream, controller,
// startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark,
// autoAllocateChunkSize).
SetUpReadableByteStreamController(aCx, aStream, controller, algorithms,
aHighWaterMark, autoAllocateChunkSize, aRv);
}
} // namespace mozilla::dom