Bug 1864406 - Add ReadableStreamBYOBReader.prototype.read(view, { min }). r=saschanaz,webidl,smaug

Implements https://github.com/whatwg/streams/pull/1145

Differential Revision: https://phabricator.services.mozilla.com/D226225
This commit is contained in:
Tom Schuster 2024-10-29 10:15:13 +00:00
parent 319c8cab75
commit a140dbde6f
7 changed files with 187 additions and 325 deletions

View File

@ -115,13 +115,15 @@ struct PullIntoDescriptor final
PullIntoDescriptor(JS::Handle<JSObject*> aBuffer, uint64_t aBufferByteLength,
uint64_t aByteOffset, uint64_t aByteLength,
uint64_t aBytesFilled, uint64_t aElementSize,
Constructor aViewConstructor, ReaderType aReaderType)
uint64_t aBytesFilled, uint64_t aMinimumFill,
uint64_t aElementSize, Constructor aViewConstructor,
ReaderType aReaderType)
: mBuffer(aBuffer),
mBufferByteLength(aBufferByteLength),
mByteOffset(aByteOffset),
mByteLength(aByteLength),
mBytesFilled(aBytesFilled),
mMinimumFill(aMinimumFill),
mElementSize(aElementSize),
mViewConstructor(aViewConstructor),
mReaderType(aReaderType) {
@ -147,6 +149,8 @@ struct PullIntoDescriptor final
mBytesFilled = aBytesFilled;
}
uint64_t MinimumFill() const { return mMinimumFill; }
uint64_t ElementSize() const { return mElementSize; }
void SetElementSize(const uint64_t aElementSize) {
mElementSize = aElementSize;
@ -166,6 +170,7 @@ struct PullIntoDescriptor final
uint64_t mByteOffset = 0;
uint64_t mByteLength = 0;
uint64_t mBytesFilled = 0;
uint64_t mMinimumFill = 0;
uint64_t mElementSize = 0;
Constructor mViewConstructor;
ReaderType mReaderType;
@ -394,11 +399,14 @@ void ReadableByteStreamControllerClose(
// Step 4.
if (!aController->PendingPullIntos().isEmpty()) {
// Step 4.1
// Step 4.1. Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
PullIntoDescriptor* firstPendingPullInto =
aController->PendingPullIntos().getFirst();
// Step 4.2
if (firstPendingPullInto->BytesFilled() > 0) {
// Step 4.2. If the remainder after dividing firstPendingPullIntos bytes
// filled by firstPendingPullIntos element size is not 0,
if ((firstPendingPullInto->BytesFilled() %
firstPendingPullInto->ElementSize()) != 0) {
// Step 4.2.1
ErrorResult rv;
rv.ThrowTypeError("Leftover Bytes");
@ -703,8 +711,10 @@ void ReadableByteStreamControllerCommitPullIntoDescriptor(
// 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.1. Assert: the remainder after dividing pullIntoDescriptors bytes
// filled by pullIntoDescriptors element size is 0.
MOZ_ASSERT((pullIntoDescriptor->BytesFilled() %
pullIntoDescriptor->ElementSize()) == 0);
// Step 4.2. Set done to true.
done = true;
@ -1144,13 +1154,13 @@ void ReadableByteStreamControllerHandleQueueDrain(
void ReadableByteStreamController::PullSteps(JSContext* aCx,
ReadRequest* aReadRequest,
ErrorResult& aRv) {
// Step 1.
// Step 1. Let stream be this.[[stream]].
ReadableStream* stream = Stream();
// Step 2.
// Step 2. Assert: ! ReadableStreamHasDefaultReader(stream) is true.
MOZ_ASSERT(ReadableStreamHasDefaultReader(stream));
// Step 3.
// Step 3. If this.[[queueTotalSize]] > 0,
if (QueueTotalSize() > 0) {
// Step 3.1. Assert: ! ReadableStreamGetNumReadRequests ( stream ) is 0.
MOZ_ASSERT(ReadableStreamGetNumReadRequests(stream) == 0);
@ -1164,18 +1174,20 @@ void ReadableByteStreamController::PullSteps(JSContext* aCx,
return;
}
// Step 4.
// Step 4. Let autoAllocateChunkSize be this.[[autoAllocateChunkSize]].
Maybe<uint64_t> autoAllocateChunkSize = AutoAllocateChunkSize();
// Step 5.
// Step 5. If autoAllocateChunkSize is not undefined,
if (autoAllocateChunkSize) {
// Step 5.1
// Step 5.1. Let buffer be Construct(%ArrayBuffer%, « autoAllocateChunkSize
// »).
aRv.MightThrowJSException();
JS::Rooted<JSObject*> buffer(
aCx, JS::NewArrayBuffer(aCx, *autoAllocateChunkSize));
// Step 5.2
// Step 5.2. If buffer is an abrupt completion,
if (!buffer) {
// Step 5.2.1
// Step 5.2.1. Perform readRequests error steps, given buffer.[[Value]].
JS::Rooted<JS::Value> bufferError(aCx);
if (!JS_GetPendingException(aCx, &bufferError)) {
// Uncatchable exception; we should mark aRv and return.
@ -1183,29 +1195,40 @@ void ReadableByteStreamController::PullSteps(JSContext* aCx,
return;
}
// It's not expliclitly stated, but I assume the intention here is that
// It's not explicitly 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.
// Step 5.2.2. Return.
return;
}
// Step 5.3
// Step 5.3 Let pullIntoDescriptor be a new pull-into descriptor with
// buffer buffer.[[Value]]
// buffer byte length autoAllocateChunkSize
// byte offset 0
// byte length autoAllocateChunkSize
// bytes filled 0
// minimum fill 1
// element size 1
// view constructor %Uint8Array%
// reader type "default"
RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
buffer, *autoAllocateChunkSize, 0, *autoAllocateChunkSize, 0, 1,
buffer, /* aBufferByteLength */ *autoAllocateChunkSize,
/*aByteOffset */ 0, /* aByteLength */ *autoAllocateChunkSize,
/* aBytesFilled */ 0, /* aMinimumFill */ 1, /* aElementSize */ 1,
PullIntoDescriptor::Constructor::Uint8, ReaderType::Default);
// Step 5.4
// Step 5.4. Append pullIntoDescriptor to this.[[pendingPullIntos]].
PendingPullIntos().insertBack(pullIntoDescriptor);
}
// Step 6.
// Step 6. Perform ! ReadableStreamAddReadRequest(stream, readRequest).
ReadableStreamAddReadRequest(stream, aReadRequest);
// Step 7.
// Step 7. Perform ! ReadableByteStreamControllerCallPullIfNeeded(this).
ReadableByteStreamControllerCallPullIfNeeded(aCx, this, aRv);
}
@ -1280,7 +1303,8 @@ JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
// Step 3. Assert: bytesFilled ≤ pullIntoDescriptors byte length.
MOZ_ASSERT(bytesFilled <= pullIntoDescriptor->ByteLength());
// Step 4. Assert: bytesFilled mod elementSize is 0.
// Step 4. Assert: the remainder after dividing bytesFilled by elementSize is
// 0.
MOZ_ASSERT(bytesFilled % elementSize == 0);
// Step 5. Let buffer be ! TransferArrayBuffer(pullIntoDescriptors buffer).
@ -1310,8 +1334,10 @@ 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 1. Assert: the remainder after dividing firstDescriptors bytes filled
// by firstDescriptors element size is 0.
MOZ_ASSERT(
(aFirstDescriptor->BytesFilled() % aFirstDescriptor->ElementSize()) == 0);
// Step 2. If firstDescriptors reader type is "none",
// perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
@ -1396,9 +1422,9 @@ static void ReadableByteStreamControllerRespondInReadableState(
return;
}
// Step 4. If pullIntoDescriptors bytes filled < pullIntoDescriptors element
// size, return.
if (aPullIntoDescriptor->BytesFilled() < aPullIntoDescriptor->ElementSize()) {
// Step 4. If pullIntoDescriptors bytes filled < pullIntoDescriptors minimum
// fill, return.
if (aPullIntoDescriptor->BytesFilled() < aPullIntoDescriptor->MinimumFill()) {
return;
}
@ -1408,8 +1434,8 @@ static void ReadableByteStreamControllerRespondInReadableState(
ReadableByteStreamControllerShiftPendingPullInto(aController);
(void)pullIntoDescriptor;
// Step 6. Let remainderSize be pullIntoDescriptors bytes filled mod
// pullIntoDescriptors element size.
// Step 6. Let remainderSize be the remainder after dividing
// pullIntoDescriptors bytes filled by pullIntoDescriptors element size.
size_t remainderSize =
aPullIntoDescriptor->BytesFilled() % aPullIntoDescriptor->ElementSize();
@ -1647,38 +1673,37 @@ void ReadableByteStreamControllerRespondWithNewView(
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]],
// Step 1. 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 +
// Step 2. 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.
// Step 3. Let totalBytesToCopyRemaining be maxBytesToCopy.
size_t totalBytesToCopyRemaining = maxBytesToCopy;
// Step 7. Let ready be false.
// Step 4. Let ready be false.
bool ready = false;
// Step 8. If maxAlignedBytes > currentAlignedBytes,
if (maxAlignedBytes > currentAlignedBytes) {
// Step 5. Assert: pullIntoDescriptors bytes filled < pullIntoDescriptors
// minimum fill.
MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() <
aPullIntoDescriptor->MinimumFill());
// Step 6. Let remainderBytes be the remainder after dividing maxBytesFilled
// by pullIntoDescriptors element size.
size_t remainderBytes = maxBytesFilled % aPullIntoDescriptor->ElementSize();
// Step 7. Let maxAlignedBytes be maxBytesFilled remainderBytes.
size_t maxAlignedBytes = maxBytesFilled - remainderBytes;
// Step 8. If maxAlignedBytes ≥ pullIntoDescriptors minimum fill,
if (maxAlignedBytes >= aPullIntoDescriptor->MinimumFill()) {
// Step 8.1. Set totalBytesToCopyRemaining to maxAlignedBytes
// pullIntoDescriptors bytes filled.
totalBytesToCopyRemaining =
@ -1758,10 +1783,9 @@ bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() > 0);
// Step 11.3. Assert: pullIntoDescriptors bytes filled <
// pullIntoDescriptors
// element size.
// pullIntoDescriptors minimum fill.
MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() <
aPullIntoDescriptor->ElementSize());
aPullIntoDescriptor->MinimumFill());
}
// Step 12. Return ready.
@ -1771,8 +1795,8 @@ bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
// 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) {
JS::Handle<JSObject*> aView, uint64_t aMin,
ReadIntoRequest* aReadIntoRequest, ErrorResult& aRv) {
aRv.MightThrowJSException();
// Step 1. Let stream be controller.[[stream]].
@ -1798,13 +1822,23 @@ void ReadableByteStreamControllerPullInto(
ctor = PullIntoDescriptor::constructorFromScalar(type);
}
// Step 5. Let byteOffset be view.[[ByteOffset]].
// Step 5. Let minimumFill be min × elementSize.
uint64_t minimumFill = aMin * elementSize;
// Step 6. Assert: minimumFill ≥ 0 and minimumFill ≤ view.[[ByteLength]].
MOZ_ASSERT(minimumFill <= JS_GetArrayBufferViewByteLength(aView));
// Step 7. Assert: the remainder after dividing minimumFill by elementSize is
// 0.
MOZ_ASSERT((minimumFill % elementSize) == 0);
// Step 8. Let byteOffset be view.[[ByteOffset]].
size_t byteOffset = JS_GetArrayBufferViewByteOffset(aView);
// Step 6. Let byteLength be view.[[ByteLength]].
// Step 9. Let byteLength be view.[[ByteLength]].
size_t byteLength = JS_GetArrayBufferViewByteLength(aView);
// Step 7. Let bufferResult be
// Step 10. Let bufferResult be
// TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
bool isShared;
JS::Rooted<JSObject*> viewedArrayBuffer(
@ -1816,7 +1850,7 @@ void ReadableByteStreamControllerPullInto(
JS::Rooted<JSObject*> bufferResult(
aCx, TransferArrayBuffer(aCx, viewedArrayBuffer));
// Step 8. If bufferResult is an abrupt completion,
// Step 11. If bufferResult is an abrupt completion,
if (!bufferResult) {
JS::Rooted<JS::Value> pendingException(aCx);
if (!JS_GetPendingException(aCx, &pendingException)) {
@ -1831,46 +1865,47 @@ void ReadableByteStreamControllerPullInto(
// exception state anyhow to succesfully run ErrorSteps.
JS_ClearPendingException(aCx);
// Step 8.1. Perform readIntoRequests error steps, given
// Step 11.1. Perform readIntoRequests error steps, given
// bufferResult.[[Value]].
aReadIntoRequest->ErrorSteps(aCx, pendingException, aRv);
// Step 8.2. Return.
// Step 11.2. Return.
return;
}
// Step 9. Let buffer be bufferResult.[[Value]].
// Step 12. 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".
// Step 13. 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
// minimum fill: minimumFill
// element size: elementSize
// view constructor: ctor
// reader type: "byob"
RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
buffer, JS::GetArrayBufferByteLength(buffer), byteOffset, byteLength, 0,
elementSize, ctor, ReaderType::BYOB);
minimumFill, elementSize, ctor, ReaderType::BYOB);
// Step 11. If controller.[[pendingPullIntos]] is not empty,
// Step 14. If controller.[[pendingPullIntos]] is not empty,
if (!aController->PendingPullIntos().isEmpty()) {
// Step 11.1. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
// Step 14.1. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
aController->PendingPullIntos().insertBack(pullIntoDescriptor);
// Step 11.2. Perform !ReadableStreamAddReadIntoRequest(stream,
// Step 14.2. Perform !ReadableStreamAddReadIntoRequest(stream,
// readIntoRequest).
ReadableStreamAddReadIntoRequest(stream, aReadIntoRequest);
// Step 11.3. Return.
// Step 14.3. Return.
return;
}
// Step 12. If stream.[[state]] is "closed",
// Step 15. If stream.[[state]] is "closed",
if (stream->State() == ReadableStream::ReaderState::Closed) {
// Step 12.1. Let emptyView be !Construct(ctor, « pullIntoDescriptors
// Step 15.1. Let emptyView be !Construct(ctor, « pullIntoDescriptors
// buffer, pullIntoDescriptors byte offset, 0 »).
JS::Rooted<JSObject*> pullIntoBuffer(aCx, pullIntoDescriptor->Buffer());
JS::Rooted<JSObject*> emptyView(
@ -1882,17 +1917,17 @@ void ReadableByteStreamControllerPullInto(
return;
}
// Step 12.2. Perform readIntoRequests close steps, given emptyView.
// Step 15.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.
// Step 15.3. Return.
return;
}
// Step 13,. If controller.[[queueTotalSize]] > 0,
// Step 16. If controller.[[queueTotalSize]] > 0,
if (aController->QueueTotalSize() > 0) {
// Step 13.1 If
// Step 16.1 If
// !ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
// pullIntoDescriptor) is true,
bool ready = ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
@ -1901,7 +1936,7 @@ void ReadableByteStreamControllerPullInto(
return;
}
if (ready) {
// Step 13.1.1 Let filledView be
// Step 16.1.1 Let filledView be
// !ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
JS::Rooted<JSObject*> filledView(
aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
@ -1909,51 +1944,50 @@ void ReadableByteStreamControllerPullInto(
if (aRv.Failed()) {
return;
}
// Step 13.1.2. Perform
// Step 16.1.2. Perform
// !ReadableByteStreamControllerHandleQueueDrain(controller).
ReadableByteStreamControllerHandleQueueDrain(aCx, aController, aRv);
if (aRv.Failed()) {
return;
}
// Step 13.1.3. Perform readIntoRequests chunk steps, given filledView.
// Step 16.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.
// Step 16.1.4. Return.
return;
}
// Step 13.2 If controller.[[closeRequested]] is true,
// Step 16.2 If controller.[[closeRequested]] is true,
if (aController->CloseRequested()) {
// Step 13.2.1. Let e be a TypeError exception.
// Step 16.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).
// Step 16.2.2. Perform !ReadableByteStreamControllerError(controller, e).
ReadableByteStreamControllerError(aController, e, aRv);
if (aRv.Failed()) {
return;
}
// Step 13.2.3. Perform readIntoRequests error steps, given e.
// Step 16.2.3. Perform readIntoRequests error steps, given e.
aReadIntoRequest->ErrorSteps(aCx, e, aRv);
// Step 13.2.4. Return.
// Step 16.2.4. Return.
return;
}
}
// Step 14. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
// Step 17. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
aController->PendingPullIntos().insertBack(pullIntoDescriptor);
// Step 15. Perform !ReadableStreamAddReadIntoRequest(stream,
// Step 18. Perform !ReadableStreamAddReadIntoRequest(stream,
// readIntoRequest).
ReadableStreamAddReadIntoRequest(stream, aReadIntoRequest);
// Step 16, Perform
// !ReadableByteStreamControllerCallPullIfNeeded(controller).
// Step 19. Perform !ReadableByteStreamControllerCallPullIfNeeded(controller).
ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
}

View File

@ -187,8 +187,8 @@ MOZ_CAN_RUN_SCRIPT void ReadableByteStreamControllerRespondWithNewView(
MOZ_CAN_RUN_SCRIPT void ReadableByteStreamControllerPullInto(
JSContext* aCx, ReadableByteStreamController* aController,
JS::Handle<JSObject*> aView, ReadIntoRequest* aReadIntoRequest,
ErrorResult& aRv);
JS::Handle<JSObject*> aView, uint64_t aMin,
ReadIntoRequest* aReadIntoRequest, ErrorResult& aRv);
void ReadableByteStreamControllerError(
ReadableByteStreamController* aController, JS::Handle<JS::Value> aValue,

View File

@ -173,7 +173,7 @@ namespace streams_abstract {
// https://streams.spec.whatwg.org/#readable-stream-byob-reader-read
void ReadableStreamBYOBReaderRead(JSContext* aCx,
ReadableStreamBYOBReader* aReader,
JS::Handle<JSObject*> aView,
JS::Handle<JSObject*> aView, uint64_t aMin,
ReadIntoRequest* aReadIntoRequest,
ErrorResult& aRv) {
// Step 1.Let stream be reader.[[stream]].
@ -195,19 +195,20 @@ void ReadableStreamBYOBReaderRead(JSContext* aCx,
}
// Step 5. Otherwise, perform
// !ReadableByteStreamControllerPullInto(stream.[[controller]], view,
// !ReadableByteStreamControllerPullInto(stream.[[controller]], view, min,
// readIntoRequest).
MOZ_ASSERT(stream->Controller()->IsByte());
RefPtr<ReadableByteStreamController> controller(
stream->Controller()->AsByte());
ReadableByteStreamControllerPullInto(aCx, controller, aView, aReadIntoRequest,
aRv);
ReadableByteStreamControllerPullInto(aCx, controller, aView, aMin,
aReadIntoRequest, aRv);
}
} // namespace streams_abstract
// https://streams.spec.whatwg.org/#byob-reader-read
already_AddRefed<Promise> ReadableStreamBYOBReader::Read(
const ArrayBufferView& aArray, ErrorResult& aRv) {
const ArrayBufferView& aArray,
const ReadableStreamBYOBReaderReadOptions& aOptions, ErrorResult& aRv) {
AutoJSAPI jsapi;
if (!jsapi.Init(GetParentObject())) {
aRv.ThrowUnknownError("Internal error");
@ -247,28 +248,60 @@ already_AddRefed<Promise> ReadableStreamBYOBReader::Read(
return nullptr;
}
// Step 4. If this.[[stream]] is undefined, return a promise rejected with a
// Step 4. If options["min"] is 0, return a promise rejected with a TypeError
// exception.
if (aOptions.mMin == 0) {
aRv.ThrowTypeError(
"Zero is not a valid value for 'min' member of "
"ReadableStreamBYOBReaderReadOptions.");
return nullptr;
}
// Step 5. If view has a [[TypedArrayName]] internal slot,
if (JS_IsTypedArrayObject(view)) {
// Step 5.1. If options["min"] > view.[[ArrayLength]], return a promise
// rejected with a RangeError exception.
if (aOptions.mMin > JS_GetTypedArrayLength(view)) {
aRv.ThrowRangeError(
"Array length exceeded by 'min' member of "
"ReadableStreamBYOBReaderReadOptions.");
return nullptr;
}
} else {
// Step 6. Otherwise (i.e., it is a DataView),
// Step 6.1. If options["min"] > view.[[ByteLength]], return a promise
// rejected with a RangeError exception.
if (aOptions.mMin > JS_GetArrayBufferViewByteLength(view)) {
aRv.ThrowRangeError(
"byteLength exceeded by 'min' member of "
"ReadableStreamBYOBReaderReadOptions.");
return nullptr;
}
}
// Step 7. If this.[[stream]] is undefined, return a promise rejected with a
// TypeError exception.
if (!GetStream()) {
aRv.ThrowTypeError("Reader has undefined stream");
return nullptr;
}
// Step 5.
// Step 8. Let promise be a new promise.
RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
// Step 6. Let readIntoRequest be a new read-into request with the following
// Step 9. Let readIntoRequest be a new read-into request with the following
// items:
RefPtr<ReadIntoRequest> readIntoRequest = new Read_ReadIntoRequest(promise);
// Step 7. Perform ! ReadableStreamBYOBReaderRead(this, view,
// Step 10. Perform ! ReadableStreamBYOBReaderRead(this, view, options["min"],
// readIntoRequest).
ReadableStreamBYOBReaderRead(cx, this, view, readIntoRequest, aRv);
ReadableStreamBYOBReaderRead(cx, this, view, aOptions.mMin, readIntoRequest,
aRv);
if (aRv.Failed()) {
return nullptr;
}
// Step 8. Return promise.
// Step 11. Return promise.
return promise.forget();
}

View File

@ -22,6 +22,7 @@ namespace mozilla::dom {
class Promise;
struct ReadIntoRequest;
class ReadableStream;
struct ReadableStreamBYOBReaderReadOptions;
} // namespace mozilla::dom
@ -52,7 +53,8 @@ class ReadableStreamBYOBReader final : public ReadableStreamGenericReader,
const GlobalObject& global, ReadableStream& stream, ErrorResult& rv);
MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Read(
const ArrayBufferView& aArray, ErrorResult& rv);
const ArrayBufferView& aArray,
const ReadableStreamBYOBReaderReadOptions& aOptions, ErrorResult& rv);
void ReleaseLock(ErrorResult& rv);
@ -73,8 +75,8 @@ already_AddRefed<ReadableStreamBYOBReader> AcquireReadableStreamBYOBReader(
MOZ_CAN_RUN_SCRIPT void ReadableStreamBYOBReaderRead(
JSContext* aCx, ReadableStreamBYOBReader* aReader,
JS::Handle<JSObject*> aView, ReadIntoRequest* aReadIntoRequest,
ErrorResult& aRv);
JS::Handle<JSObject*> aView, uint64_t aMin,
ReadIntoRequest* aReadIntoRequest, ErrorResult& aRv);
void ReadableStreamBYOBReaderErrorReadIntoRequests(
JSContext* aCx, ReadableStreamBYOBReader* aReader,

View File

@ -911,10 +911,11 @@ void PullWithBYOBReader(JSContext* aCx, TeeState* aTeeState,
RefPtr<ReadIntoRequest> readIntoRequest =
new PullWithBYOBReader_ReadIntoRequest(aTeeState, aForBranch);
// Step 16.5.
// Step 16.5. Perform ! ReadableStreamBYOBReaderRead(reader, view, 1,
// readIntoRequest).
RefPtr<ReadableStreamBYOBReader> byobReader =
aTeeState->GetReader()->AsBYOB();
ReadableStreamBYOBReaderRead(aCx, byobReader, aView, readIntoRequest, aRv);
ReadableStreamBYOBReaderRead(aCx, byobReader, aView, 1, readIntoRequest, aRv);
}
// See https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee

View File

@ -13,9 +13,13 @@ interface ReadableStreamBYOBReader {
constructor(ReadableStream stream);
[NewObject]
Promise<ReadableStreamReadResult> read(ArrayBufferView view);
Promise<ReadableStreamReadResult> read(ArrayBufferView view, optional ReadableStreamBYOBReaderReadOptions options = {});
[Throws]
undefined releaseLock();
};
ReadableStreamBYOBReader includes ReadableStreamGenericReader;
dictionary ReadableStreamBYOBReaderReadOptions {
[EnforceRange] unsigned long long min = 1;
};

View File

@ -1,214 +1,2 @@
[read-min.any.worker.html]
[ReadableStream with byte source: read({ min }) rejects if min is 0]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is negative]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (Uint8Array)]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (Uint16Array)]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (DataView)]
expected: FAIL
[ReadableStream with byte source: read({ min }), then read()]
expected: FAIL
[ReadableStream with byte source: read({ min }) with a DataView]
expected: FAIL
[ReadableStream with byte source: enqueue(), then read({ min })]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 3-byte Uint8Array, then multiple enqueue() up to 3 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 5-byte Uint8Array, then multiple enqueue() up to 3 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 5-byte Uint8Array, then multiple enqueue() up to 4 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min }) when closed before view is filled]
expected: FAIL
[ReadableStream with byte source: read({ min }) when closed immediately after view is filled]
expected: FAIL
[ReadableStream with byte source: cancel() with partially filled pending read({ min }) request]
expected: FAIL
[ReadableStream with byte source: 3 byte enqueue(), then close(), then read({ min }) with 2-element Uint16Array must fail]
expected: FAIL
[ReadableStream with byte source: read({ min }) with 2-element Uint16Array, then 3 byte enqueue(), then close() must fail]
expected: FAIL
[ReadableStream with byte source: tee() with read({ min }) from branch1 and read() from branch2]
expected: FAIL
[read-min.any.html]
[ReadableStream with byte source: read({ min }) rejects if min is 0]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is negative]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (Uint8Array)]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (Uint16Array)]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (DataView)]
expected: FAIL
[ReadableStream with byte source: read({ min }), then read()]
expected: FAIL
[ReadableStream with byte source: read({ min }) with a DataView]
expected: FAIL
[ReadableStream with byte source: enqueue(), then read({ min })]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 3-byte Uint8Array, then multiple enqueue() up to 3 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 5-byte Uint8Array, then multiple enqueue() up to 3 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 5-byte Uint8Array, then multiple enqueue() up to 4 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min }) when closed before view is filled]
expected: FAIL
[ReadableStream with byte source: read({ min }) when closed immediately after view is filled]
expected: FAIL
[ReadableStream with byte source: cancel() with partially filled pending read({ min }) request]
expected: FAIL
[ReadableStream with byte source: 3 byte enqueue(), then close(), then read({ min }) with 2-element Uint16Array must fail]
expected: FAIL
[ReadableStream with byte source: read({ min }) with 2-element Uint16Array, then 3 byte enqueue(), then close() must fail]
expected: FAIL
[ReadableStream with byte source: tee() with read({ min }) from branch1 and read() from branch2]
expected: FAIL
[read-min.any.serviceworker.html]
[ReadableStream with byte source: read({ min }) rejects if min is 0]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is negative]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (Uint8Array)]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (Uint16Array)]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (DataView)]
expected: FAIL
[ReadableStream with byte source: read({ min }), then read()]
expected: FAIL
[ReadableStream with byte source: read({ min }) with a DataView]
expected: FAIL
[ReadableStream with byte source: enqueue(), then read({ min })]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 3-byte Uint8Array, then multiple enqueue() up to 3 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 5-byte Uint8Array, then multiple enqueue() up to 3 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 5-byte Uint8Array, then multiple enqueue() up to 4 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min }) when closed before view is filled]
expected: FAIL
[ReadableStream with byte source: read({ min }) when closed immediately after view is filled]
expected: FAIL
[ReadableStream with byte source: cancel() with partially filled pending read({ min }) request]
expected: FAIL
[ReadableStream with byte source: 3 byte enqueue(), then close(), then read({ min }) with 2-element Uint16Array must fail]
expected: FAIL
[ReadableStream with byte source: read({ min }) with 2-element Uint16Array, then 3 byte enqueue(), then close() must fail]
expected: FAIL
[ReadableStream with byte source: tee() with read({ min }) from branch1 and read() from branch2]
expected: FAIL
[read-min.any.sharedworker.html]
[ReadableStream with byte source: read({ min }) rejects if min is 0]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is negative]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (Uint8Array)]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (Uint16Array)]
expected: FAIL
[ReadableStream with byte source: read({ min }) rejects if min is larger than view's length (DataView)]
expected: FAIL
[ReadableStream with byte source: read({ min }), then read()]
expected: FAIL
[ReadableStream with byte source: read({ min }) with a DataView]
expected: FAIL
[ReadableStream with byte source: enqueue(), then read({ min })]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 3-byte Uint8Array, then multiple enqueue() up to 3 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 5-byte Uint8Array, then multiple enqueue() up to 3 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min: 3 }) on a 5-byte Uint8Array, then multiple enqueue() up to 4 bytes]
expected: FAIL
[ReadableStream with byte source: read({ min }) when closed before view is filled]
expected: FAIL
[ReadableStream with byte source: read({ min }) when closed immediately after view is filled]
expected: FAIL
[ReadableStream with byte source: cancel() with partially filled pending read({ min }) request]
expected: FAIL
[ReadableStream with byte source: 3 byte enqueue(), then close(), then read({ min }) with 2-element Uint16Array must fail]
expected: FAIL
[ReadableStream with byte source: read({ min }) with 2-element Uint16Array, then 3 byte enqueue(), then close() must fail]
expected: FAIL
[ReadableStream with byte source: tee() with read({ min }) from branch1 and read() from branch2]
expected: FAIL
[read-min.any.shadowrealm.html]
expected: ERROR