Bug 1741941 - Implement ReadableStreamBYOBReader r=smaug

Notably, this doesn't yet provide any mechanism for users to create user defined BYOB Readers

Differential Revision: https://phabricator.services.mozilla.com/D131545
This commit is contained in:
Matthew Gaudet 2022-01-04 22:33:22 +00:00
Родитель b2addbf3da
Коммит e3f4641378
14 изменённых файлов: 1148 добавлений и 148 удалений

Просмотреть файл

@ -0,0 +1,47 @@
/* -*- 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/. */
#ifndef mozilla_dom_ReadIntoRequest_h
#define mozilla_dom_ReadIntoRequest_h
#include "js/TypeDecls.h"
#include "js/Value.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/LinkedList.h"
#include "mozilla/RefPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupportsImpl.h"
namespace mozilla::dom {
// This list element needs to be cycle collected by owning structures.
struct ReadIntoRequest : public nsISupports,
public LinkedListElement<RefPtr<ReadIntoRequest>> {
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(ReadIntoRequest)
// An algorithm taking a chunk, called when a chunk is available for reading
virtual void ChunkSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
ErrorResult& errorResult) = 0;
// An algorithm taking a chunk or undefined, called when no chunks are
// available because the stream is closed
virtual void CloseSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
ErrorResult& errorResult) = 0;
// An algorithm taking a JavaScript value, called when no chunks are available
// because the stream is errored
virtual void ErrorSteps(JSContext* aCx, JS::Handle<JS::Value> e,
ErrorResult& errorResult) = 0;
protected:
virtual ~ReadIntoRequest() = default;
};
} // namespace mozilla::dom
#endif

Просмотреть файл

@ -19,9 +19,14 @@
#include "mozilla/dom/PromiseNativeHandler.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/ReadableStreamGenericReader.h"
#include "mozilla/dom/ToJSValue.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIGlobalObject.h"
#include "nsISupports.h"
@ -476,6 +481,134 @@ already_AddRefed<PullIntoDescriptor>
ReadableByteStreamControllerShiftPendingPullInto(
ReadableByteStreamController* 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
void ReadableStreamFulfillReadIntoRequest(JSContext* aCx,
ReadableStream* aStream,
JS::HandleValue 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
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. Let done be false.
bool done = false;
// Step 3. If stream.[[state]] is "closed",
if (aStream->State() == ReadableStream::ReaderState::Closed) {
// Step 3.1. Assert: pullIntoDescriptors bytes filled is 0.
MOZ_ASSERT(pullIntoDescriptor->BytesFilled() == 0);
// Step 3.2. Set done to true.
done = true;
}
// Step 4. Let filledView be !
// ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
JS::RootedObject filledView(
aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
aCx, pullIntoDescriptor, aRv));
if (aRv.Failed()) {
return;
}
JS::RootedValue filledViewValue(aCx, JS::ObjectValue(*filledView));
// Step 5. If pullIntoDescriptors reader type is "default",
if (pullIntoDescriptor->GetReaderType() == ReaderType::Default) {
// Step 5.1. Perform !ReadableStreamFulfillReadRequest(stream, filledView,
// done).
ReadableStreamFulfillReadRequest(aCx, aStream, filledViewValue, done, aRv);
return;
}
// Step 6.1. Assert: pullIntoDescriptors reader type is "byob".
MOZ_ASSERT(pullIntoDescriptor->GetReaderType() == ReaderType::BYOB);
// Step 6.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
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].
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).
ReadableByteStreamControllerCommitPullIntoDescriptor(
aCx, aController->Stream(), pullIntoDescriptor, aRv);
if (aRv.Failed()) {
return;
}
}
}
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue
MOZ_CAN_RUN_SCRIPT
void ReadableByteStreamControllerEnqueue(
@ -585,7 +718,16 @@ void ReadableByteStreamControllerEnqueue(
}
// Step 11
} else if (ReadableStreamHasBYOBReader(stream)) {
MOZ_CRASH("MG:XXX:NYI -- BYOBReaders");
// Step 11.1
ReadableByteStreamControllerEnqueueChunkToQueue(
aController, transferredBuffer, byteOffset, byteLength);
// Step 11.2
ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
aCx, aController, aRv);
if (aRv.Failed()) {
return;
}
} else {
// Step 12.1
MOZ_ASSERT(IsReadableStreamLocked(stream));
@ -789,6 +931,15 @@ void ReadableByteStreamController::PullSteps(JSContext* aCx,
ReadableByteStreamControllerCallPullIfNeeded(aCx, this, aRv);
}
// 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-shift-pending-pull-into
already_AddRefed<PullIntoDescriptor>
ReadableByteStreamControllerShiftPendingPullInto(
@ -804,10 +955,29 @@ ReadableByteStreamControllerShiftPendingPullInto(
return descriptor.forget();
}
JSObject* ConstructFromPullIntoConstructor(
JSContext* aCx, PullIntoDescriptor::Constructor constructor,
JS::HandleObject 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
}
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-convert-pull-into-descriptor
JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
JSContext* aCx, RefPtr<PullIntoDescriptor>& pullIntoDescriptor,
ErrorResult& aRv) {
JSContext* aCx, PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv) {
// Step 1.
uint64_t bytesFilled = pullIntoDescriptor->BytesFilled();
@ -829,26 +999,10 @@ JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
}
// Step 6.
JS::Rooted<JSObject*> res(aCx);
switch (pullIntoDescriptor->ViewConstructor()) {
case PullIntoDescriptor::Constructor::DataView:
res = JS_NewDataView(aCx, buffer, pullIntoDescriptor->ByteOffset(),
bytesFilled % elementSize);
break;
#define CONSTRUCT_TYPED_ARRAY_TYPE(ExternalT, NativeT, Name) \
case PullIntoDescriptor::Constructor::Name: \
res = JS_New##Name##ArrayWithBuffer(aCx, buffer, \
pullIntoDescriptor->ByteOffset(), \
int64_t(bytesFilled % elementSize)); \
break;
JS_FOR_EACH_TYPED_ARRAY(CONSTRUCT_TYPED_ARRAY_TYPE)
#undef CONSTRUCT_TYPED_ARRAY_TYPE
}
JS::Rooted<JSObject*> res(
aCx, ConstructFromPullIntoConstructor(
aCx, pullIntoDescriptor->ViewConstructor(), buffer,
pullIntoDescriptor->ByteOffset(), bytesFilled % elementSize));
if (!res) {
aRv.StealExceptionFromJSContext(aCx);
return nullptr;
@ -857,76 +1011,6 @@ JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
return res;
}
#if 0
// https://streams.spec.whatwg.org/#readable-stream-fulfill-read-into-request
void ReadableStreamFulfillReadIntoRequest(JSContext* aCx,
RefPtr<ReadableStream>& aStream,
<JS:: JS::>HandleValue aChunk, bool done,
ErrorResult& aRv) {
// Step 1.
MOZ_ASSERT(ReadableStreamHasBYOBReader(aStream));
// Step 2.
RefPtr<ReadableStreamBYOBReader> reader =
aStream->mReader.as<RefPtr<ReadableStreamBYOBReader>>();
// Step 3.
MOZ_ASSERT(!reader->mReadIntoRequests.isEmpty());
// Step 4.
RefPtr<ReadIntoRequest> readIntoRequest =
reader->mReadIntoRequests.popFirst();
// Step 5.
if (done) {
readIntoRequest->closeSteps(aCx, aChunk, aRv);
return;
}
// Step 6.
readIntoRequest->chunkSteps(aCx, aChunk, aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-commit-pull-into-descriptor
void ReadableByteStreamControllerCommitPullIntoDescriptor(
JSContext* aCx, RefPtr<ReadableStream>& aStream,
RefPtr<PullIntoDescriptor>& pullIntoDescriptor, ErrorResult& aRv) {
// Step 1.
MOZ_ASSERT(aStream->state() != ReadableStream::ReaderState::Errored);
// Step 2.
bool done = false;
// Step 3.
if (aStream->state() == ReadableStream::ReaderState::Closed) {
// Step 3.1
MOZ_ASSERT(pullIntoDescriptor->bytesFilled() == 0);
done = true;
}
// Step 4.
JS::Rooted<JSObject*> filledView(
aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
aCx, pullIntoDescriptor, aRv));
if (aRv.Failed()) {
return;
}
JS::RootedValue filledViewValue(aCx, JS::ObjectValue(*filledView));
// Step 5.
if (pullIntoDescriptor->readerType() == ReaderType::Default) {
ReadableStreamFulfillReadRequest(aCx, aStream, filledViewValue, done,
aRv);
return;
}
// Step 6.1
MOZ_ASSERT(pullIntoDescriptor->readerType() == ReaderType::BYOB);
ReadableStreamFulfillReadIntoRequest(aCx, aStream, filledViewValue, done,
aRv);
}
#endif
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-closed-state
static void ReadableByteStreamControllerRespondInClosedState(
JSContext* aCx, ReadableByteStreamController* aController,
@ -939,8 +1023,6 @@ static void ReadableByteStreamControllerRespondInClosedState(
// Step 3.
if (ReadableStreamHasBYOBReader(stream)) {
MOZ_CRASH("MG:XXX:NYI -- BYOBReader");
#if 0
// Step 3.1
while (ReadableStreamGetNumReadIntoRequests(stream) > 0) {
// Step 3.1.1
@ -950,18 +1032,103 @@ static void ReadableByteStreamControllerRespondInClosedState(
// Step 3.1.2.
ReadableByteStreamControllerCommitPullIntoDescriptor(
aCx, stream, pullIntoDescriptor, aRv);
MOZ_CRASH("MG:XXX:VERITY NOT MORE STEPS?");
}
#endif
}
}
// 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) {
// MG:XXX: This needs to be finished when
// ReadableByteStreamControllerRespondInClosedState is finished.
// 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 bytes filled < pullIntoDescriptors element
// size, return.
if (aPullIntoDescriptor->BytesFilled() < aPullIntoDescriptor->ElementSize()) {
return;
}
// Step 4. Perform
// !ReadableByteStreamControllerShiftPendingPullInto(controller).
ReadableByteStreamControllerShiftPendingPullInto(aController);
// Step 5. Let remainderSize be pullIntoDescriptors bytes filled mod
// pullIntoDescriptors element size.
size_t remainderSize =
aPullIntoDescriptor->BytesFilled() % aPullIntoDescriptor->ElementSize();
// Step 6. If remainderSize > 0,
if (remainderSize > 0) {
// Step 6.1. Let end be pullIntoDescriptors byte offset +
// pullIntoDescriptors bytes filled.
size_t end =
aPullIntoDescriptor->ByteOffset() + aPullIntoDescriptor->BytesFilled();
// Step 6.2. Let remainder be ? CloneArrayBuffer(pullIntoDescriptors
// buffer, end remainderSize, remainderSize, %ArrayBuffer%).
JS::Rooted<JSObject*> pullIntoBuffer(aCx, aPullIntoDescriptor->Buffer());
JS::Rooted<JSObject*> remainder(
aCx, JS::ArrayBufferClone(aCx, pullIntoBuffer, end - remainderSize,
remainderSize));
if (!remainder) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
// Step 6.3 Perform
// !ReadableByteStreamControllerEnqueueChunkToQueue(controller, remainder,
// 0, remainder.[[ByteLength]]).
ReadableByteStreamControllerEnqueueChunkToQueue(
aController, remainder, 0, JS::GetArrayBufferByteLength(remainder));
}
// Step 7. Set pullIntoDescriptors bytes filled to pullIntoDescriptors bytes
// filled remainderSize.
aPullIntoDescriptor->SetBytesFilled(aPullIntoDescriptor->BytesFilled() -
remainderSize);
// Step 8. Perform
// !ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
// pullIntoDescriptor).
RefPtr<ReadableStream> stream(aController->Stream());
ReadableByteStreamControllerCommitPullIntoDescriptor(
aCx, stream, aPullIntoDescriptor, aRv);
if (aRv.Failed()) {
return;
}
// Step 9. Perform
// !ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
aCx, aController, aRv);
}
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-internal
@ -1148,5 +1315,311 @@ void ReadableByteStreamControllerRespondWithNewView(
aCx, aController, JS_GetArrayBufferViewByteLength(aView), 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::RootedObject descriptorBuffer(aCx, aPullIntoDescriptor->Buffer());
JS::RootedObject 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
MOZ_CAN_RUN_SCRIPT
void ReadableByteStreamControllerPullInto(
JSContext* aCx, ReadableByteStreamController* aController,
JS::HandleObject aView, ReadIntoRequest* aReadIntoRequest,
ErrorResult& aRv) {
// 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::RootedObject viewedArrayBuffer(
aCx, JS_GetArrayBufferViewBuffer(aCx, aView, &isShared));
if (!viewedArrayBuffer) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
JS::RootedObject bufferResult(aCx,
TransferArrayBuffer(aCx, viewedArrayBuffer));
// Step 8. If bufferResult is an abrupt completion,
if (!bufferResult) {
JS::RootedValue pendingException(aCx);
if (!JS_GetPendingException(aCx, &pendingException)) {
// This means an un-catchable exception. Use StealExceptionFromJSContext
// to setup aRv properly.
aRv.StealExceptionFromJSContext(aCx);
return;
}
// 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::RootedObject 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::RootedObject pullIntoBuffer(aCx, pullIntoDescriptor->Buffer());
JS::RootedObject 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::RootedValue 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::RootedObject 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::RootedValue 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::RootedValue 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);
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -7,6 +7,7 @@
#ifndef mozilla_dom_ReadableByteStreamController_h
#define mozilla_dom_ReadableByteStreamController_h
#include <cstddef>
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
@ -32,6 +33,7 @@ enum ReaderType { Default, BYOB };
struct PullIntoDescriptor;
struct ReadableByteStreamQueueEntry;
struct ReadIntoRequest;
class ReadableByteStreamController final : public ReadableStreamController,
public nsWrapperCache {
@ -200,7 +202,7 @@ struct ReadableByteStreamQueueEntry
friend class ReadableByteStreamController::cycleCollection;
ReadableByteStreamQueueEntry(JS::Handle<JSObject*> aBuffer,
uint64_t aByteOffset, uint64_t aByteLength)
size_t aByteOffset, size_t aByteLength)
: LinkedListElement<RefPtr<ReadableByteStreamQueueEntry>>(),
mBuffer(aBuffer),
mByteOffset(aByteOffset),
@ -209,11 +211,11 @@ struct ReadableByteStreamQueueEntry
JSObject* Buffer() const { return mBuffer; }
void SetBuffer(JS::Handle<JSObject*> aBuffer) { mBuffer = aBuffer; }
uint64_t ByteOffset() const { return mByteOffset; }
void SetByteOffset(uint64_t aByteOffset) { mByteOffset = aByteOffset; }
size_t ByteOffset() const { return mByteOffset; }
void SetByteOffset(size_t aByteOffset) { mByteOffset = aByteOffset; }
uint64_t ByteLength() const { return mByteLength; }
void SetByteLength(uint64_t aByteLength) { mByteLength = aByteLength; }
size_t ByteLength() const { return mByteLength; }
void SetByteLength(size_t aByteLength) { mByteLength = aByteLength; }
void ClearBuffer() { mBuffer = nullptr; }
@ -227,11 +229,11 @@ struct ReadableByteStreamQueueEntry
// A nonnegative integer number giving the byte offset derived from the view
// originally supplied by the underlying byte source
uint64_t mByteOffset = 0;
size_t mByteOffset = 0;
// A nonnegative integer number giving the byte length derived from the view
// originally supplied by the underlying byte source
uint64_t mByteLength = 0;
size_t mByteLength = 0;
~ReadableByteStreamQueueEntry() = default;
};
@ -248,6 +250,22 @@ struct PullIntoDescriptor final
#undef DEFINE_TYPED_CONSTRUCTOR_ENUM_NAMES
};
static Constructor constructorFromScalar(JS::Scalar::Type type) {
switch (type) {
#define REMAP_PULL_INTO_DESCRIPTOR_TYPE(ExternalT, NativeT, Name) \
case JS::Scalar::Name: \
return Constructor::Name;
JS_FOR_EACH_TYPED_ARRAY(REMAP_PULL_INTO_DESCRIPTOR_TYPE)
#undef REMAP
case JS::Scalar::Int64:
case JS::Scalar::Simd128:
case JS::Scalar::MaxTypedArrayViewType:
break;
}
MOZ_CRASH("Unexpected Scalar::Type");
}
friend class ReadableByteStreamController::cycleCollection;
PullIntoDescriptor(JS::Handle<JSObject*> aBuffer, uint64_t aBufferByteLength,
@ -325,6 +343,11 @@ extern void ReadableByteStreamControllerRespondWithNewView(
JSContext* aCx, ReadableByteStreamController* aController,
JS::Handle<JSObject*> aView, ErrorResult& aRv);
extern void ReadableByteStreamControllerPullInto(
JSContext* aCx, ReadableByteStreamController* aController,
JS::HandleObject aView, ReadIntoRequest* aReadIntoRequest,
ErrorResult& aRv);
} // namespace mozilla::dom
#endif

Просмотреть файл

@ -20,6 +20,8 @@
#include "mozilla/dom/QueueWithSizes.h"
#include "mozilla/dom/QueuingStrategyBinding.h"
#include "mozilla/dom/ReadRequest.h"
#include "mozilla/dom/ReadableByteStreamController.h"
#include "mozilla/dom/ReadableStreamBYOBReader.h"
#include "mozilla/dom/ReadableStreamBinding.h"
#include "mozilla/dom/ReadableStreamDefaultController.h"
#include "mozilla/dom/ReadableStreamDefaultReader.h"
@ -36,6 +38,27 @@
#include <unistd.h>
inline void ImplCycleCollectionTraverse(
nsCycleCollectionTraversalCallback& aCallback,
mozilla::Variant<mozilla::Nothing,
RefPtr<mozilla::dom::ReadableStreamDefaultReader>>&
aReader,
const char* aName, uint32_t aFlags = 0) {
if (aReader.is<RefPtr<mozilla::dom::ReadableStreamDefaultReader>>()) {
ImplCycleCollectionTraverse(
aCallback,
aReader.as<RefPtr<mozilla::dom::ReadableStreamDefaultReader>>(), aName,
aFlags);
}
}
inline void ImplCycleCollectionUnlink(
mozilla::Variant<mozilla::Nothing,
RefPtr<mozilla::dom::ReadableStreamDefaultReader>>&
aReader) {
aReader = AsVariant(mozilla::Nothing());
}
namespace mozilla {
namespace dom {
@ -62,12 +85,13 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStream)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
ReadableStream::ReadableStream(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) {
ReadableStream::ReadableStream(nsIGlobalObject* aGlobal)
: mGlobal(aGlobal), mReader(nullptr) {
mozilla::HoldJSObjects(this);
}
ReadableStream::ReadableStream(const GlobalObject& aGlobal)
: mGlobal(do_QueryInterface(aGlobal.GetAsSupports())) {
: mGlobal(do_QueryInterface(aGlobal.GetAsSupports())), mReader(nullptr) {
mozilla::HoldJSObjects(this);
}
@ -146,9 +170,9 @@ already_AddRefed<ReadableStream> ReadableStream::Constructor(
// Step 4.3
(void)highWaterMark;
MOZ_CRASH("Byte Streams Not Yet Implemented");
aRv.ThrowNotSupportedError("BYOB Byte Streams Not Yet Supported");
return readableStream.forget();
return nullptr;
}
// Step 5.1 (implicit in above check)
@ -315,8 +339,12 @@ already_AddRefed<Promise> ReadableStreamCancel(JSContext* aCx,
// Step 6.
if (reader && reader->IsBYOB()) {
MOZ_CRASH(
"NYI: Implement read into request clearing when BYOB readers exist");
for (auto* readIntoRequest : reader->AsBYOB()->ReadIntoRequests()) {
readIntoRequest->CloseSteps(aCx, JS::UndefinedHandleValue, aRv);
if (aRv.Failed()) {
return nullptr;
}
}
}
// Step 7.
@ -387,18 +415,23 @@ AcquireReadableStreamDefaultReader(JSContext* aCx, ReadableStream* aStream,
}
// https://streams.spec.whatwg.org/#rs-get-reader
// MG:XXX: This will need tweaking when we implement BYOBreaders
already_AddRefed<ReadableStreamDefaultReader> ReadableStream::GetReader(
JSContext* aCx, const ReadableStreamGetReaderOptions& aOptions,
ErrorResult& aRv) {
void ReadableStream::GetReader(JSContext* aCx,
const ReadableStreamGetReaderOptions& aOptions,
OwningReadableStreamReader& resultReader,
ErrorResult& aRv) {
// Step 1.
if (!aOptions.mMode.WasPassed()) {
RefPtr<ReadableStream> thisRefPtr = this;
return AcquireReadableStreamDefaultReader(aCx, thisRefPtr, aRv);
RefPtr<ReadableStreamDefaultReader> defaultReader =
AcquireReadableStreamDefaultReader(aCx, thisRefPtr, aRv);
if (aRv.Failed()) {
return;
}
resultReader.SetAsReadableStreamDefaultReader() = defaultReader;
return;
}
// Step 2.
MOZ_CRASH("BYOB STREAMS NOT IMPLEMENTED");
return nullptr;
aRv.ThrowTypeError("BYOB STREAMS NOT IMPLEMENTED");
}
// https://streams.spec.whatwg.org/#is-readable-stream-locked
@ -456,9 +489,19 @@ void ReadableStreamError(JSContext* aCx, ReadableStream* aStream,
// Step 8.2
defaultReader->ReadRequests().clear();
} else {
// Step 9.
// Step 9.1.
MOZ_ASSERT(reader->IsBYOB());
// Step 9: Unreachable until we implement ReadableStreamBYOBReader.
MOZ_CRASH("BYOBReader not yet implemented");
ReadableStreamBYOBReader* byobReader = reader->AsBYOB();
// Step 9.2.
for (auto* readIntoRequest : byobReader->ReadIntoRequests()) {
readIntoRequest->ErrorSteps(aCx, aValue, aRv);
if (aRv.Failed()) {
return;
}
}
// Step 9.3
byobReader->ReadIntoRequests().clear();
}
}
@ -724,8 +767,12 @@ static void ReadableStreamTee(JSContext* aCx, ReadableStream* aStream,
ErrorResult& aRv) {
// Step 1. Implicit.
// Step 2. Implicit.
// Step 3. Implicitly false, until we implement
// ReadableByteStreamController. Step 4.
// Step 3.
if (aStream->Controller()->IsByte()) {
aRv.ThrowTypeError("Cannot yet tee a byte stream controller");
return;
}
// Step 4.
ReadableStreamDefaultTee(aCx, aStream, aCloneForBranch2, aResult, aRv);
}
@ -736,5 +783,20 @@ void ReadableStream::Tee(JSContext* aCx,
ReadableStreamTee(aCx, this, false, aResult, aRv);
}
// https://streams.spec.whatwg.org/#readable-stream-add-read-into-request
void ReadableStreamAddReadIntoRequest(ReadableStream* aStream,
ReadIntoRequest* aReadIntoRequest) {
// Step 1. Assert: stream.[[reader]] implements ReadableStreamBYOBReader.
MOZ_ASSERT(aStream->GetReader()->IsBYOB());
// Step 2. Assert: stream.[[state]] is "readable" or "closed".
MOZ_ASSERT(aStream->State() == ReadableStream::ReaderState::Readable ||
aStream->State() == ReadableStream::ReaderState::Closed);
// Step 3. Append readRequest to stream.[[reader]].[[readIntoRequests]].
aStream->GetReader()->AsBYOB()->ReadIntoRequests().insertBack(
aReadIntoRequest);
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -25,9 +25,16 @@ namespace mozilla {
namespace dom {
class Promise;
class ReadableStreamGenericReader;
class ReadableStreamDefaultReader;
class ReadableStreamGenericReader;
struct ReadableStreamGetReaderOptions;
struct ReadIntoRequest;
using ReadableStreamReader =
ReadableStreamDefaultReaderOrReadableStreamBYOBReader;
using OwningReadableStreamReader =
OwningReadableStreamDefaultReaderOrReadableStreamBYOBReader;
class ReadableStream final : public nsISupports, public nsWrapperCache {
public:
@ -89,9 +96,8 @@ class ReadableStream final : public nsISupports, public nsWrapperCache {
already_AddRefed<Promise> Cancel(JSContext* cx, JS::Handle<JS::Value> aReason,
ErrorResult& aRv);
already_AddRefed<ReadableStreamDefaultReader> GetReader(
JSContext* aCx, const ReadableStreamGetReaderOptions& aOptions,
ErrorResult& aRv);
void GetReader(JSContext* aCx, const ReadableStreamGetReaderOptions& aOptions,
OwningReadableStreamReader& resultReader, ErrorResult& aRv);
void Tee(JSContext* aCx, nsTArray<RefPtr<ReadableStream>>& aResult,
ErrorResult& aRv);
@ -121,6 +127,8 @@ extern void ReadableStreamFulfillReadRequest(JSContext* aCx,
extern void ReadableStreamAddReadRequest(ReadableStream* aStream,
ReadRequest* aReadRequest);
extern void ReadableStreamAddReadIntoRequest(ReadableStream* aStream,
ReadIntoRequest* aReadIntoRequest);
extern already_AddRefed<Promise> ReadableStreamCancel(
JSContext* aCx, ReadableStream* aStream, JS::Handle<JS::Value> aError,

Просмотреть файл

@ -0,0 +1,279 @@
/* -*- 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/experimental/TypedData.h"
#include "mozilla/dom/ReadableStreamBYOBReader.h"
#include "mozilla/dom/ReadableStream.h"
#include "mozilla/dom/ReadableStreamBYOBReaderBinding.h"
#include "mozilla/dom/ReadableStreamGenericReader.h"
#include "mozilla/dom/ReadIntoRequest.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
// Temporary Includes
#include "mozilla/dom/ReadableByteStreamController.h"
#include "mozilla/dom/ReadableStreamBYOBRequest.h"
#include "mozilla/dom/ReadableStreamBYOBReader.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(ReadableStreamBYOBReader,
ReadableStreamGenericReader,
mReadIntoRequests)
NS_IMPL_ADDREF_INHERITED(ReadableStreamBYOBReader, ReadableStreamGenericReader)
NS_IMPL_RELEASE_INHERITED(ReadableStreamBYOBReader, ReadableStreamGenericReader)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamBYOBReader)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END_INHERITING(ReadableStreamGenericReader)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ReadableStreamBYOBReader,
ReadableStreamGenericReader)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
JSObject* ReadableStreamBYOBReader::WrapObject(
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
return ReadableStreamBYOBReader_Binding::Wrap(aCx, this, aGivenProto);
}
// https://streams.spec.whatwg.org/#set-up-readable-stream-byob-reader
void SetUpReadableStreamBYOBReader(JSContext* aCx,
ReadableStreamBYOBReader* reader,
ReadableStream& stream, ErrorResult& rv) {
// Step 1. If !IsReadableStreamLocked(stream) is true, throw a TypeError
// exception.
if (IsReadableStreamLocked(&stream)) {
rv.ThrowTypeError("Trying to read locked stream");
return;
}
// Step 2. If stream.[[controller]] does not implement
// ReadableByteStreamController, throw a TypeError exception.
if (!stream.Controller()->IsByte()) {
rv.ThrowTypeError("Trying to read with incompatible controller");
return;
}
// Step 3. Perform ! ReadableStreamReaderGenericInitialize(reader, stream).
ReadableStreamReaderGenericInitialize(aCx, reader, &stream, rv);
if (rv.Failed()) {
return;
}
// Step 4. Set reader.[[readIntoRequests]] to a new empty list.
reader->ReadIntoRequests().clear();
}
// https://streams.spec.whatwg.org/#byob-reader-constructor
/* static */ already_AddRefed<ReadableStreamBYOBReader>
ReadableStreamBYOBReader::Constructor(const GlobalObject& global,
ReadableStream& stream, ErrorResult& rv) {
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(global.GetAsSupports());
RefPtr<ReadableStreamBYOBReader> reader =
new ReadableStreamBYOBReader(globalObject);
// Step 1.
SetUpReadableStreamBYOBReader(global.Context(), reader, stream, rv);
if (rv.Failed()) {
return nullptr;
}
return reader.forget();
}
struct Read_ReadIntoRequest final : public ReadIntoRequest {
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Read_ReadIntoRequest,
ReadIntoRequest)
RefPtr<Promise> mPromise;
explicit Read_ReadIntoRequest(Promise* aPromise) : mPromise(aPromise) {}
void ChunkSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
ErrorResult& errorResult) override {
MOZ_ASSERT(aChunk.isObject());
// https://streams.spec.whatwg.org/#byob-reader-read Step 6.
//
// chunk steps, given chunk:
// Resolve promise with «[ "value" → chunk, "done" → false ]».
ReadableStreamBYOBReadResult result;
result.mValue.Construct();
result.mValue.Value().Init(&aChunk.toObject());
result.mDone.Construct(false);
mPromise->MaybeResolve(result);
}
void CloseSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
ErrorResult& errorResult) override {
MOZ_ASSERT(aChunk.isObject());
// https://streams.spec.whatwg.org/#byob-reader-read Step 6.
//
// close steps, given chunk:
// Resolve promise with «[ "value" → chunk, "done" → true ]».
ReadableStreamBYOBReadResult result;
result.mValue.Construct();
result.mValue.Value().Init(&aChunk.toObject());
result.mDone.Construct(true);
mPromise->MaybeResolve(result);
}
void ErrorSteps(JSContext* aCx, JS::Handle<JS::Value> e,
ErrorResult& errorResult) override {
// https://streams.spec.whatwg.org/#byob-reader-read Step 6.
//
// error steps, given e:
// Reject promise with e.
mPromise->MaybeReject(e);
}
protected:
virtual ~Read_ReadIntoRequest() = default;
};
NS_IMPL_CYCLE_COLLECTION(ReadIntoRequest)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadIntoRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadIntoRequest)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadIntoRequest)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_INHERITED(Read_ReadIntoRequest, ReadIntoRequest,
mPromise)
NS_IMPL_ADDREF_INHERITED(Read_ReadIntoRequest, ReadIntoRequest)
NS_IMPL_RELEASE_INHERITED(Read_ReadIntoRequest, ReadIntoRequest)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Read_ReadIntoRequest)
NS_INTERFACE_MAP_END_INHERITING(ReadIntoRequest)
// https://streams.spec.whatwg.org/#readable-stream-byob-reader-read
MOZ_CAN_RUN_SCRIPT
void ReadableStreamBYOBReaderRead(JSContext* aCx,
ReadableStreamBYOBReader* aReader,
JS::HandleObject aView,
ReadIntoRequest* aReadIntoRequest,
ErrorResult& aRv) {
// Step 1.Let stream be reader.[[stream]].
ReadableStream* stream = aReader->GetStream();
// Step 2. Assert: stream is not undefined.
MOZ_ASSERT(stream);
// Step 3. Set stream.[[disturbed]] to true.
stream->SetDisturbed(true);
// Step 4. If stream.[[state]] is "errored", perform readIntoRequests error
// steps given stream.[[storedError]].
if (stream->State() == ReadableStream::ReaderState::Errored) {
JS::RootedValue error(aCx, stream->StoredError());
aReadIntoRequest->ErrorSteps(aCx, error, aRv);
return;
}
// Step 5. Otherwise, perform
// !ReadableByteStreamControllerPullInto(stream.[[controller]], view,
// readIntoRequest).
MOZ_ASSERT(stream->Controller()->IsByte());
RefPtr<ReadableByteStreamController> controller(
stream->Controller()->AsByte());
ReadableByteStreamControllerPullInto(aCx, controller, aView, aReadIntoRequest,
aRv);
}
// https://streams.spec.whatwg.org/#byob-reader-read
MOZ_CAN_RUN_SCRIPT
already_AddRefed<Promise> ReadableStreamBYOBReader::Read(
const ArrayBufferView& aArray, ErrorResult& aRv) {
AutoJSAPI jsapi;
if (!jsapi.Init(GetParentObject())) {
// MG:XXX: Do I need to have reported this error to aRv?
return nullptr;
}
JSContext* cx = jsapi.cx();
JS::RootedObject view(cx, aArray.Obj());
// Step 1. If view.[[ByteLength]] is 0, return a promise rejected with a
// TypeError exception.
if (JS_GetArrayBufferViewByteLength(view) == 0) {
// Binding code should convert this thrown value into a rejected promise.
aRv.ThrowTypeError("Zero Length View");
return nullptr;
}
// Step 2. If view.[[ViewedArrayBuffer]].[[ArrayBufferByteLength]] is 0,
// return a promise rejected with a TypeError exception.
bool isSharedMemory;
JS::RootedObject viewedArrayBuffer(
cx, JS_GetArrayBufferViewBuffer(cx, view, &isSharedMemory));
if (!viewedArrayBuffer) {
aRv.StealExceptionFromJSContext(cx);
return nullptr;
}
if (JS::GetArrayBufferByteLength(viewedArrayBuffer) == 0) {
aRv.ThrowTypeError("zero length viewed buffer");
return nullptr;
}
// Step 3. If ! IsDetachedBuffer(view.[[ViewedArrayBuffer]]) is true, return a
// promise rejected with a TypeError exception.
if (JS::IsDetachedArrayBufferObject(viewedArrayBuffer)) {
aRv.ThrowTypeError("Detatched Buffer");
return nullptr;
}
// Step 4. 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.
RefPtr<Promise> promise = Promise::Create(GetParentObject(), aRv);
if (aRv.Failed()) {
return nullptr;
}
// Step 6. 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,
// readIntoRequest).
ReadableStreamBYOBReaderRead(cx, this, view, readIntoRequest, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Step 8. Return promise.
return promise.forget();
}
// https://streams.spec.whatwg.org/#byob-reader-release-lock
void ReadableStreamBYOBReader::ReleaseLock(ErrorResult& aRv) {
if (!GetStream()) {
return;
}
if (!ReadIntoRequests().isEmpty()) {
aRv.ThrowTypeError("ReadIntoRequests not empty");
return;
}
ReadableStreamReaderGenericRelease(this, aRv);
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,78 @@
/* -*- 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/. */
#ifndef mozilla_dom_ReadableStreamBYOBReader_h
#define mozilla_dom_ReadableStreamBYOBReader_h
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/ReadableStreamGenericReader.h"
#include "mozilla/dom/ReadIntoRequest.h"
#include "mozilla/dom/TypedArray.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "mozilla/LinkedList.h"
namespace mozilla {
namespace dom {
class Promise;
class ReadableStream;
} // namespace dom
} // namespace mozilla
namespace mozilla {
namespace dom {
class ReadableStreamBYOBReader final : public ReadableStreamGenericReader,
public nsWrapperCache {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
ReadableStreamBYOBReader, ReadableStreamGenericReader)
public:
explicit ReadableStreamBYOBReader(nsISupports* aGlobal)
: ReadableStreamGenericReader(do_QueryInterface(aGlobal)) {}
bool IsDefault() override { return false; };
bool IsBYOB() override { return true; }
ReadableStreamDefaultReader* AsDefault() override {
MOZ_CRASH("Should have verified IsDefaultFirst");
return nullptr;
}
ReadableStreamBYOBReader* AsBYOB() override { return this; }
nsIGlobalObject* GetParentObject() const { return nullptr; }
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
static already_AddRefed<ReadableStreamBYOBReader> Constructor(
const GlobalObject& global, ReadableStream& stream, ErrorResult& rv);
already_AddRefed<Promise> Read(const ArrayBufferView& aArray,
ErrorResult& rv);
void ReleaseLock(ErrorResult& rv);
LinkedList<RefPtr<ReadIntoRequest>>& ReadIntoRequests() {
return mReadIntoRequests;
}
private:
virtual ~ReadableStreamBYOBReader() = default;
LinkedList<RefPtr<ReadIntoRequest>> mReadIntoRequests = {};
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ReadableStreamBYOBReader_h

Просмотреть файл

@ -57,9 +57,10 @@ JSObject* ReadableStreamDefaultReader::WrapObject(
}
// https://streams.spec.whatwg.org/#readable-stream-reader-generic-initialize
static bool ReadableStreamReaderGenericInitialize(
JSContext* aCx, ReadableStreamDefaultReader* aReader,
ReadableStream* aStream, ErrorResult& aRv) {
bool ReadableStreamReaderGenericInitialize(JSContext* aCx,
ReadableStreamGenericReader* aReader,
ReadableStream* aStream,
ErrorResult& aRv) {
// Step 1.
aReader->SetStream(aStream);
@ -267,8 +268,8 @@ already_AddRefed<Promise> ReadableStreamDefaultReader::Read(JSContext* aCx,
}
// https://streams.spec.whatwg.org/#readable-stream-reader-generic-release
static void ReadableStreamGenericRelease(ReadableStreamDefaultReader* aReader,
ErrorResult& aRv) {
void ReadableStreamReaderGenericRelease(ReadableStreamGenericReader* aReader,
ErrorResult& aRv) {
// Step 1.
MOZ_ASSERT(aReader->GetStream());
// Step 2.
@ -311,11 +312,11 @@ void ReadableStreamDefaultReader::ReleaseLock(ErrorResult& aRv) {
}
// Step 3.
ReadableStreamGenericRelease(this, aRv);
ReadableStreamReaderGenericRelease(this, aRv);
}
// https://streams.spec.whatwg.org/#generic-reader-closed
already_AddRefed<Promise> ReadableStreamDefaultReader::Closed() const {
already_AddRefed<Promise> ReadableStreamGenericReader::Closed() const {
// Step 1.
return do_AddRef(mClosedPromise);
}
@ -323,7 +324,7 @@ already_AddRefed<Promise> ReadableStreamDefaultReader::Closed() const {
// https://streams.spec.whatwg.org/#readable-stream-reader-generic-cancel
MOZ_CAN_RUN_SCRIPT
static already_AddRefed<Promise> ReadableStreamGenericReaderCancel(
JSContext* aCx, ReadableStreamDefaultReader* aReader,
JSContext* aCx, ReadableStreamGenericReader* aReader,
JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
// Step 1 (Strong ref for below call).
RefPtr<ReadableStream> stream = aReader->GetStream();
@ -335,10 +336,8 @@ static already_AddRefed<Promise> ReadableStreamGenericReaderCancel(
return ReadableStreamCancel(aCx, stream, aReason, aRv);
}
// Return a raw pointer here to avoid refcounting, but make sure it's safe
// (the object should be kept alive by the callee).
MOZ_CAN_RUN_SCRIPT
already_AddRefed<Promise> ReadableStreamDefaultReader::Cancel(
already_AddRefed<Promise> ReadableStreamGenericReader::Cancel(
JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
// Step 1.
if (!mStream) {

Просмотреть файл

@ -58,12 +58,6 @@ class ReadableStreamDefaultReader final : public ReadableStreamGenericReader,
void ReleaseLock(ErrorResult& aRv);
already_AddRefed<Promise> Closed() const;
already_AddRefed<Promise> Cancel(JSContext* aCx,
JS::Handle<JS::Value> aReason,
ErrorResult& aRv);
LinkedList<RefPtr<ReadRequest>>& ReadRequests() { return mReadRequests; }
private:

Просмотреть файл

@ -8,11 +8,15 @@
#define mozilla_dom_ReadableStreamGenericReader_h
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ReadableStream.h"
#include "mozilla/dom/ReadableStreamDefaultReaderBinding.h"
#include "nsISupports.h"
#include "nsCycleCollectionParticipant.h"
namespace mozilla {
namespace dom {
class ReadableStream;
class ReadableStreamDefaultReader;
class ReadableStreamBYOBReader;
// Base class for internal slots of readable stream readers
@ -45,6 +49,13 @@ class ReadableStreamGenericReader : public nsISupports {
SetStream(stream.forget());
}
// IDL Methods
already_AddRefed<Promise> Closed() const;
already_AddRefed<Promise> Cancel(JSContext* aCx,
JS::Handle<JS::Value> aReason,
ErrorResult& aRv);
protected:
virtual ~ReadableStreamGenericReader() = default;
@ -53,6 +64,14 @@ class ReadableStreamGenericReader : public nsISupports {
RefPtr<ReadableStream> mStream;
};
bool ReadableStreamReaderGenericInitialize(JSContext* aCx,
ReadableStreamGenericReader* aReader,
ReadableStream* aStream,
ErrorResult& aRv);
void ReadableStreamReaderGenericRelease(ReadableStreamGenericReader* aReader,
ErrorResult& aRv);
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -13,12 +13,14 @@ EXPORTS.mozilla.dom += [
"QueueWithSizes.h",
"ReadableByteStreamController.h",
"ReadableStream.h",
"ReadableStreamBYOBReader.h",
"ReadableStreamBYOBRequest.h",
"ReadableStreamController.h",
"ReadableStreamDefaultController.h",
"ReadableStreamDefaultReader.h",
"ReadableStreamGenericReader.h",
"ReadableStreamTee.h",
"ReadIntoRequest.h",
"ReadRequest.h",
"StreamUtils.h",
"TeeState.h",
@ -34,6 +36,7 @@ UNIFIED_SOURCES += [
"CountQueuingStrategy.cpp",
"ReadableByteStreamController.cpp",
"ReadableStream.cpp",
"ReadableStreamBYOBReader.cpp",
"ReadableStreamBYOBRequest.cpp",
"ReadableStreamDefaultController.cpp",
"ReadableStreamDefaultReader.cpp",

Просмотреть файл

@ -0,0 +1,17 @@
[Exposed=(Window,Worker,Worklet)]
interface ReadableStreamBYOBReader {
[Throws]
constructor(ReadableStream stream);
[Throws]
Promise<ReadableStreamBYOBReadResult> read(ArrayBufferView view);
[Throws]
void releaseLock();
};
ReadableStreamBYOBReader includes ReadableStreamGenericReader;
dictionary ReadableStreamBYOBReadResult {
ArrayBufferView value;
boolean done;
};

Просмотреть файл

@ -1,8 +1,5 @@
// This typedef should actually be the union of ReadableStreamDefaultReader
// and ReadableStreamBYOBReader. However, we've not implmented the latter
// yet, and so for now the typedef is subset.
typedef ReadableStreamDefaultReader ReadableStreamReader;
typedef (ReadableStreamDefaultReader or ReadableStreamBYOBReader) ReadableStreamReader;
enum ReadableStreamType { "bytes" };

Просмотреть файл

@ -1007,6 +1007,7 @@ if CONFIG["MOZ_DOM_STREAMS"]:
"QueuingStrategy.webidl",
"ReadableByteStreamController.webidl",
"ReadableStream.webidl",
"ReadableStreamBYOBReader.webidl",
"ReadableStreamBYOBRequest.webidl",
"ReadableStreamDefaultController.webidl",
"ReadableStreamDefaultReader.webidl",