Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / streams / ReadableByteStreamController.cpp
blob1ce2ae4331f699b5ff7d12ddee96d7d600ee8fd3
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/ReadableByteStreamController.h"
9 #include "ReadIntoRequest.h"
10 #include "js/ArrayBuffer.h"
11 #include "js/ErrorReport.h"
12 #include "js/Exception.h"
13 #include "js/TypeDecls.h"
14 #include "js/Value.h"
15 #include "js/ValueArray.h"
16 #include "js/experimental/TypedData.h"
17 #include "js/friend/ErrorMessages.h"
18 #include "mozilla/AlreadyAddRefed.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/ErrorResult.h"
21 #include "mozilla/HoldDropJSObjects.h"
22 #include "mozilla/dom/ByteStreamHelpers.h"
23 #include "mozilla/dom/Promise.h"
24 #include "mozilla/dom/Promise-inl.h"
25 #include "mozilla/dom/ReadableByteStreamControllerBinding.h"
26 #include "mozilla/dom/ReadableStream.h"
27 #include "mozilla/dom/ReadableStreamBYOBReader.h"
28 #include "mozilla/dom/ReadableStreamBYOBRequest.h"
29 #include "mozilla/dom/ReadableStreamController.h"
30 #include "mozilla/dom/ReadableStreamDefaultController.h"
31 #include "mozilla/dom/ReadableStreamDefaultReader.h"
32 #include "mozilla/dom/ReadableStreamGenericReader.h"
33 #include "mozilla/dom/ToJSValue.h"
34 #include "mozilla/dom/ScriptSettings.h"
35 #include "mozilla/dom/UnderlyingSourceCallbackHelpers.h"
36 #include "nsCycleCollectionParticipant.h"
37 #include "nsIGlobalObject.h"
38 #include "nsISupports.h"
40 #include <algorithm> // std::min
42 namespace mozilla::dom {
44 using namespace streams_abstract;
46 // https://streams.spec.whatwg.org/#readable-byte-stream-queue-entry
47 struct ReadableByteStreamQueueEntry
48 : LinkedListElement<RefPtr<ReadableByteStreamQueueEntry>> {
49 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(
50 ReadableByteStreamQueueEntry)
51 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(
52 ReadableByteStreamQueueEntry)
54 ReadableByteStreamQueueEntry(JS::Handle<JSObject*> aBuffer,
55 size_t aByteOffset, size_t aByteLength)
56 : mBuffer(aBuffer), mByteOffset(aByteOffset), mByteLength(aByteLength) {
57 mozilla::HoldJSObjects(this);
60 JSObject* Buffer() const { return mBuffer; }
61 void SetBuffer(JS::Handle<JSObject*> aBuffer) { mBuffer = aBuffer; }
63 size_t ByteOffset() const { return mByteOffset; }
64 void SetByteOffset(size_t aByteOffset) { mByteOffset = aByteOffset; }
66 size_t ByteLength() const { return mByteLength; }
67 void SetByteLength(size_t aByteLength) { mByteLength = aByteLength; }
69 private:
70 // An ArrayBuffer, which will be a transferred version of the one originally
71 // supplied by the underlying byte source.
72 JS::Heap<JSObject*> mBuffer;
74 // A nonnegative integer number giving the byte offset derived from the view
75 // originally supplied by the underlying byte source
76 size_t mByteOffset = 0;
78 // A nonnegative integer number giving the byte length derived from the view
79 // originally supplied by the underlying byte source
80 size_t mByteLength = 0;
82 ~ReadableByteStreamQueueEntry() { mozilla::DropJSObjects(this); }
85 NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(ReadableByteStreamQueueEntry, (),
86 (mBuffer));
88 struct PullIntoDescriptor final
89 : LinkedListElement<RefPtr<PullIntoDescriptor>> {
90 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PullIntoDescriptor)
91 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PullIntoDescriptor)
93 enum Constructor {
94 DataView,
95 #define DEFINE_TYPED_CONSTRUCTOR_ENUM_NAMES(ExternalT, NativeT, Name) Name,
96 JS_FOR_EACH_TYPED_ARRAY(DEFINE_TYPED_CONSTRUCTOR_ENUM_NAMES)
97 #undef DEFINE_TYPED_CONSTRUCTOR_ENUM_NAMES
100 static Constructor constructorFromScalar(JS::Scalar::Type type) {
101 switch (type) {
102 #define REMAP_PULL_INTO_DESCRIPTOR_TYPE(ExternalT, NativeT, Name) \
103 case JS::Scalar::Name: \
104 return Constructor::Name;
105 JS_FOR_EACH_TYPED_ARRAY(REMAP_PULL_INTO_DESCRIPTOR_TYPE)
106 #undef REMAP
108 case JS::Scalar::Int64:
109 case JS::Scalar::Simd128:
110 case JS::Scalar::MaxTypedArrayViewType:
111 break;
113 MOZ_CRASH("Unexpected Scalar::Type");
116 PullIntoDescriptor(JS::Handle<JSObject*> aBuffer, uint64_t aBufferByteLength,
117 uint64_t aByteOffset, uint64_t aByteLength,
118 uint64_t aBytesFilled, uint64_t aElementSize,
119 Constructor aViewConstructor, ReaderType aReaderType)
120 : mBuffer(aBuffer),
121 mBufferByteLength(aBufferByteLength),
122 mByteOffset(aByteOffset),
123 mByteLength(aByteLength),
124 mBytesFilled(aBytesFilled),
125 mElementSize(aElementSize),
126 mViewConstructor(aViewConstructor),
127 mReaderType(aReaderType) {
128 mozilla::HoldJSObjects(this);
131 JSObject* Buffer() const { return mBuffer; }
132 void SetBuffer(JS::Handle<JSObject*> aBuffer) { mBuffer = aBuffer; }
134 uint64_t BufferByteLength() const { return mBufferByteLength; }
135 void SetBufferByteLength(const uint64_t aBufferByteLength) {
136 mBufferByteLength = aBufferByteLength;
139 uint64_t ByteOffset() const { return mByteOffset; }
140 void SetByteOffset(const uint64_t aByteOffset) { mByteOffset = aByteOffset; }
142 uint64_t ByteLength() const { return mByteLength; }
143 void SetByteLength(const uint64_t aByteLength) { mByteLength = aByteLength; }
145 uint64_t BytesFilled() const { return mBytesFilled; }
146 void SetBytesFilled(const uint64_t aBytesFilled) {
147 mBytesFilled = aBytesFilled;
150 uint64_t ElementSize() const { return mElementSize; }
151 void SetElementSize(const uint64_t aElementSize) {
152 mElementSize = aElementSize;
155 Constructor ViewConstructor() const { return mViewConstructor; }
157 // Note: Named GetReaderType to avoid name conflict with type.
158 ReaderType GetReaderType() const { return mReaderType; }
159 void SetReaderType(const ReaderType aReaderType) {
160 mReaderType = aReaderType;
163 private:
164 JS::Heap<JSObject*> mBuffer;
165 uint64_t mBufferByteLength = 0;
166 uint64_t mByteOffset = 0;
167 uint64_t mByteLength = 0;
168 uint64_t mBytesFilled = 0;
169 uint64_t mElementSize = 0;
170 Constructor mViewConstructor;
171 ReaderType mReaderType;
173 ~PullIntoDescriptor() { mozilla::DropJSObjects(this); }
176 NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(PullIntoDescriptor, (), (mBuffer));
178 NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableByteStreamController)
179 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ReadableByteStreamController,
180 ReadableStreamController)
181 NS_IMPL_CYCLE_COLLECTION_UNLINK(mByobRequest, mQueue, mPendingPullIntos)
182 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
183 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
185 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ReadableByteStreamController,
186 ReadableStreamController)
187 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mByobRequest, mQueue, mPendingPullIntos)
188 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
190 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ReadableByteStreamController,
191 ReadableStreamController)
192 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
193 NS_IMPL_CYCLE_COLLECTION_TRACE_END
195 NS_IMPL_ADDREF_INHERITED(ReadableByteStreamController, ReadableStreamController)
196 NS_IMPL_RELEASE_INHERITED(ReadableByteStreamController,
197 ReadableStreamController)
198 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableByteStreamController)
199 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
200 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamController)
202 ReadableByteStreamController::ReadableByteStreamController(
203 nsIGlobalObject* aGlobal)
204 : ReadableStreamController(aGlobal) {}
206 ReadableByteStreamController::~ReadableByteStreamController() = default;
208 void ReadableByteStreamController::ClearQueue() { mQueue.clear(); }
210 void ReadableByteStreamController::ClearPendingPullIntos() {
211 mPendingPullIntos.clear();
214 namespace streams_abstract {
215 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollergetbyobrequest
216 already_AddRefed<ReadableStreamBYOBRequest>
217 ReadableByteStreamControllerGetBYOBRequest(
218 JSContext* aCx, ReadableByteStreamController* aController,
219 ErrorResult& aRv) {
220 // Step 1.
221 if (!aController->GetByobRequest() &&
222 !aController->PendingPullIntos().isEmpty()) {
223 // Step 1.1:
224 PullIntoDescriptor* firstDescriptor =
225 aController->PendingPullIntos().getFirst();
227 // Step 1.2:
228 aRv.MightThrowJSException();
229 JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
230 JS::Rooted<JSObject*> view(
231 aCx, JS_NewUint8ArrayWithBuffer(
232 aCx, buffer,
233 firstDescriptor->ByteOffset() + firstDescriptor->BytesFilled(),
234 int64_t(firstDescriptor->ByteLength() -
235 firstDescriptor->BytesFilled())));
236 if (!view) {
237 aRv.StealExceptionFromJSContext(aCx);
238 return nullptr;
241 // Step 1.3:
242 RefPtr<ReadableStreamBYOBRequest> byobRequest =
243 new ReadableStreamBYOBRequest(aController->GetParentObject());
245 // Step 1.4:
246 byobRequest->SetController(aController);
248 // Step 1.5:
249 byobRequest->SetView(view);
251 // Step 1.6:
252 aController->SetByobRequest(byobRequest);
255 // Step 2.
256 RefPtr<ReadableStreamBYOBRequest> request(aController->GetByobRequest());
257 return request.forget();
259 } // namespace streams_abstract
261 already_AddRefed<ReadableStreamBYOBRequest>
262 ReadableByteStreamController::GetByobRequest(JSContext* aCx, ErrorResult& aRv) {
263 return ReadableByteStreamControllerGetBYOBRequest(aCx, this, aRv);
266 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-get-desired-size
267 Nullable<double> ReadableByteStreamControllerGetDesiredSize(
268 const ReadableByteStreamController* aController) {
269 // Step 1.
270 ReadableStream::ReaderState state = aController->Stream()->State();
272 // Step 2.
273 if (state == ReadableStream::ReaderState::Errored) {
274 return nullptr;
277 // Step 3.
278 if (state == ReadableStream::ReaderState::Closed) {
279 return 0.0;
282 // Step 4.
283 return aController->StrategyHWM() - aController->QueueTotalSize();
286 Nullable<double> ReadableByteStreamController::GetDesiredSize() const {
287 // Step 1.
288 return ReadableByteStreamControllerGetDesiredSize(this);
291 JSObject* ReadableByteStreamController::WrapObject(
292 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
293 return ReadableByteStreamController_Binding::Wrap(aCx, this, aGivenProto);
296 namespace streams_abstract {
298 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-invalidate-byob-request
299 static void ReadableByteStreamControllerInvalidateBYOBRequest(
300 ReadableByteStreamController* aController) {
301 // Step 1.
302 if (!aController->GetByobRequest()) {
303 return;
306 // Step 2.
307 aController->GetByobRequest()->SetController(nullptr);
309 // Step 3.
310 aController->GetByobRequest()->SetView(nullptr);
312 // Step 4.
313 aController->SetByobRequest(nullptr);
316 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-pending-pull-intos
317 void ReadableByteStreamControllerClearPendingPullIntos(
318 ReadableByteStreamController* aController) {
319 // Step 1.
320 ReadableByteStreamControllerInvalidateBYOBRequest(aController);
322 // Step 2.
323 aController->ClearPendingPullIntos();
326 // https://streams.spec.whatwg.org/#reset-queue
327 void ResetQueue(ReadableByteStreamController* aContainer) {
328 // Step 1. Implied by type.
329 // Step 2.
330 aContainer->ClearQueue();
332 // Step 3.
333 aContainer->SetQueueTotalSize(0);
336 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-algorithms
337 void ReadableByteStreamControllerClearAlgorithms(
338 ReadableByteStreamController* aController) {
339 // Step 1. Set controller.[[pullAlgorithm]] to undefined.
340 // Step 2. Set controller.[[cancelAlgorithm]] to undefined.
341 aController->ClearAlgorithms();
344 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-error
345 void ReadableByteStreamControllerError(
346 ReadableByteStreamController* aController, JS::Handle<JS::Value> aValue,
347 ErrorResult& aRv) {
348 // Step 1. Let stream be controller.[[stream]].
349 ReadableStream* stream = aController->Stream();
351 // Step 2. If stream.[[state]] is not "readable", return.
352 if (stream->State() != ReadableStream::ReaderState::Readable) {
353 return;
356 // Step 3. Perform
357 // !ReadableByteStreamControllerClearPendingPullIntos(controller).
358 ReadableByteStreamControllerClearPendingPullIntos(aController);
360 // Step 4. Perform !ResetQueue(controller).
361 ResetQueue(aController);
363 // Step 5. Perform !ReadableByteStreamControllerClearAlgorithms(controller).
364 ReadableByteStreamControllerClearAlgorithms(aController);
366 // Step 6. Perform !ReadableStreamError(stream, e).
367 AutoJSAPI jsapi;
368 if (!jsapi.Init(aController->GetParentObject())) {
369 return;
371 ReadableStreamError(jsapi.cx(), stream, aValue, aRv);
374 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-close
375 void ReadableByteStreamControllerClose(
376 JSContext* aCx, ReadableByteStreamController* aController,
377 ErrorResult& aRv) {
378 // Step 1.
379 RefPtr<ReadableStream> stream = aController->Stream();
381 // Step 2.
382 if (aController->CloseRequested() ||
383 stream->State() != ReadableStream::ReaderState::Readable) {
384 return;
387 // Step 3.
388 if (aController->QueueTotalSize() > 0) {
389 // Step 3.1
390 aController->SetCloseRequested(true);
391 // Step 3.2
392 return;
395 // Step 4.
396 if (!aController->PendingPullIntos().isEmpty()) {
397 // Step 4.1
398 PullIntoDescriptor* firstPendingPullInto =
399 aController->PendingPullIntos().getFirst();
400 // Step 4.2
401 if (firstPendingPullInto->BytesFilled() > 0) {
402 // Step 4.2.1
403 ErrorResult rv;
404 rv.ThrowTypeError("Leftover Bytes");
406 JS::Rooted<JS::Value> exception(aCx);
407 MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), &exception));
409 // Step 4.2.2
410 ReadableByteStreamControllerError(aController, exception, aRv);
411 if (aRv.Failed()) {
412 return;
415 aRv.MightThrowJSException();
416 aRv.ThrowJSException(aCx, exception);
417 return;
421 // Step 5.
422 ReadableByteStreamControllerClearAlgorithms(aController);
424 // Step 6.
425 ReadableStreamClose(aCx, stream, aRv);
428 } // namespace streams_abstract
430 // https://streams.spec.whatwg.org/#rbs-controller-close
431 void ReadableByteStreamController::Close(JSContext* aCx, ErrorResult& aRv) {
432 // Step 1.
433 if (mCloseRequested) {
434 aRv.ThrowTypeError("Close already requested");
435 return;
438 // Step 2.
439 if (Stream()->State() != ReadableStream::ReaderState::Readable) {
440 aRv.ThrowTypeError("Closing un-readable stream controller");
441 return;
444 // Step 3.
445 ReadableByteStreamControllerClose(aCx, this, aRv);
448 namespace streams_abstract {
450 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue-chunk-to-queue
451 void ReadableByteStreamControllerEnqueueChunkToQueue(
452 ReadableByteStreamController* aController,
453 JS::Handle<JSObject*> aTransferredBuffer, size_t aByteOffset,
454 size_t aByteLength) {
455 // Step 1.
456 RefPtr<ReadableByteStreamQueueEntry> queueEntry =
457 new ReadableByteStreamQueueEntry(aTransferredBuffer, aByteOffset,
458 aByteLength);
459 aController->Queue().insertBack(queueEntry);
461 // Step 2.
462 aController->AddToQueueTotalSize(double(aByteLength));
465 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueueclonedchunktoqueue
466 void ReadableByteStreamControllerEnqueueClonedChunkToQueue(
467 JSContext* aCx, ReadableByteStreamController* aController,
468 JS::Handle<JSObject*> aBuffer, size_t aByteOffset, size_t aByteLength,
469 ErrorResult& aRv) {
470 // Step 1. Let cloneResult be CloneArrayBuffer(buffer, byteOffset, byteLength,
471 // %ArrayBuffer%).
472 aRv.MightThrowJSException();
473 JS::Rooted<JSObject*> cloneResult(
474 aCx, JS::ArrayBufferClone(aCx, aBuffer, aByteOffset, aByteLength));
476 // Step 2. If cloneResult is an abrupt completion,
477 if (!cloneResult) {
478 JS::Rooted<JS::Value> exception(aCx);
479 if (!JS_GetPendingException(aCx, &exception)) {
480 // Uncatchable exception; we should mark aRv and return.
481 aRv.StealExceptionFromJSContext(aCx);
482 return;
484 JS_ClearPendingException(aCx);
486 // Step 2.1. Perform ! ReadableByteStreamControllerError(controller,
487 // cloneResult.[[Value]]).
488 ReadableByteStreamControllerError(aController, exception, aRv);
489 if (aRv.Failed()) {
490 return;
493 // Step 2.2. Return cloneResult.
494 aRv.ThrowJSException(aCx, exception);
495 return;
498 // Step 3. Perform !
499 // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
500 // cloneResult.[[Value]], 0, byteLength).
501 ReadableByteStreamControllerEnqueueChunkToQueue(aController, cloneResult, 0,
502 aByteLength);
505 already_AddRefed<PullIntoDescriptor>
506 ReadableByteStreamControllerShiftPendingPullInto(
507 ReadableByteStreamController* aController);
509 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueuedetachedpullintotoqueue
510 void ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
511 JSContext* aCx, ReadableByteStreamController* aController,
512 PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv) {
513 // Step 1. Assert: pullIntoDescriptor’s reader type is "none".
514 MOZ_ASSERT(aPullIntoDescriptor->GetReaderType() == ReaderType::None);
516 // Step 2. If pullIntoDescriptor’s bytes filled > 0,
517 // perform ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
518 // pullIntoDescriptor’s buffer, pullIntoDescriptor’s byte offset,
519 // pullIntoDescriptor’s bytes filled).
520 if (aPullIntoDescriptor->BytesFilled() > 0) {
521 JS::Rooted<JSObject*> buffer(aCx, aPullIntoDescriptor->Buffer());
522 ReadableByteStreamControllerEnqueueClonedChunkToQueue(
523 aCx, aController, buffer, aPullIntoDescriptor->ByteOffset(),
524 aPullIntoDescriptor->BytesFilled(), aRv);
525 if (aRv.Failed()) {
526 return;
530 // Step 3. Perform !
531 // ReadableByteStreamControllerShiftPendingPullInto(controller).
532 RefPtr<PullIntoDescriptor> discarded =
533 ReadableByteStreamControllerShiftPendingPullInto(aController);
534 (void)discarded;
537 // https://streams.spec.whatwg.org/#readable-stream-get-num-read-into-requests
538 static size_t ReadableStreamGetNumReadIntoRequests(ReadableStream* aStream) {
539 // Step 1.
540 MOZ_ASSERT(ReadableStreamHasBYOBReader(aStream));
542 // Step 2.
543 return aStream->GetReader()->AsBYOB()->ReadIntoRequests().length();
546 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-should-call-pull
547 bool ReadableByteStreamControllerShouldCallPull(
548 ReadableByteStreamController* aController) {
549 // Step 1. Let stream be controller.[[stream]].
550 ReadableStream* stream = aController->Stream();
552 // Step 2. If stream.[[state]] is not "readable", return false.
553 if (stream->State() != ReadableStream::ReaderState::Readable) {
554 return false;
557 // Step 3. If controller.[[closeRequested]] is true, return false.
558 if (aController->CloseRequested()) {
559 return false;
562 // Step 4. If controller.[[started]] is false, return false.
563 if (!aController->Started()) {
564 return false;
567 // Step 5. If ! ReadableStreamHasDefaultReader(stream) is true
568 // and ! ReadableStreamGetNumReadRequests(stream) > 0, return true.
569 if (ReadableStreamHasDefaultReader(stream) &&
570 ReadableStreamGetNumReadRequests(stream) > 0) {
571 return true;
574 // Step 6. If ! ReadableStreamHasBYOBReader(stream) is true
575 // and ! ReadableStreamGetNumReadIntoRequests(stream) > 0, return true.
576 if (ReadableStreamHasBYOBReader(stream) &&
577 ReadableStreamGetNumReadIntoRequests(stream) > 0) {
578 return true;
581 // Step 7. Let desiredSize be
582 // ! ReadableByteStreamControllerGetDesiredSize(controller).
583 Nullable<double> desiredSize =
584 ReadableByteStreamControllerGetDesiredSize(aController);
586 // Step 8. Assert: desiredSize is not null.
587 MOZ_ASSERT(!desiredSize.IsNull());
589 // Step 9. If desiredSize > 0, return true.
590 // Step 10. Return false.
591 return desiredSize.Value() > 0;
594 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-call-pull-if-needed
595 void ReadableByteStreamControllerCallPullIfNeeded(
596 JSContext* aCx, ReadableByteStreamController* aController,
597 ErrorResult& aRv) {
598 // Step 1.
599 bool shouldPull = ReadableByteStreamControllerShouldCallPull(aController);
601 // Step 2.
602 if (!shouldPull) {
603 return;
606 // Step 3.
607 if (aController->Pulling()) {
608 aController->SetPullAgain(true);
609 return;
612 // Step 4.
613 MOZ_ASSERT(!aController->PullAgain());
615 // Step 5.
616 aController->SetPulling(true);
618 // Step 6.
619 RefPtr<ReadableStreamController> controller(aController);
620 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms =
621 aController->GetAlgorithms();
622 RefPtr<Promise> pullPromise = algorithms->PullCallback(aCx, *controller, aRv);
623 if (aRv.Failed()) {
624 return;
627 // Steps 7+8
628 pullPromise->AddCallbacksWithCycleCollectedArgs(
629 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
630 ReadableByteStreamController* aController)
631 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
632 // Step 7.1
633 aController->SetPulling(false);
634 // Step 7.2
635 if (aController->PullAgain()) {
636 // Step 7.2.1
637 aController->SetPullAgain(false);
639 // Step 7.2.2
640 ReadableByteStreamControllerCallPullIfNeeded(
641 aCx, MOZ_KnownLive(aController), aRv);
644 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
645 ReadableByteStreamController* aController) {
646 // Step 8.1
647 ReadableByteStreamControllerError(aController, aValue, aRv);
649 RefPtr(aController));
652 bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
653 JSContext* aCx, ReadableByteStreamController* aController,
654 PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv);
656 JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
657 JSContext* aCx, PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv);
659 // https://streams.spec.whatwg.org/#readable-stream-fulfill-read-into-request
660 MOZ_CAN_RUN_SCRIPT
661 void ReadableStreamFulfillReadIntoRequest(JSContext* aCx,
662 ReadableStream* aStream,
663 JS::Handle<JS::Value> aChunk,
664 bool done, ErrorResult& aRv) {
665 // Step 1. Assert: !ReadableStreamHasBYOBReader(stream) is true.
666 MOZ_ASSERT(ReadableStreamHasBYOBReader(aStream));
668 // Step 2. Let reader be stream.[[reader]].
669 ReadableStreamBYOBReader* reader = aStream->GetReader()->AsBYOB();
671 // Step 3. Assert: reader.[[readIntoRequests]] is not empty.
672 MOZ_ASSERT(!reader->ReadIntoRequests().isEmpty());
674 // Step 4. Let readIntoRequest be reader.[[readIntoRequests]][0].
675 // Step 5. Remove readIntoRequest from reader.[[readIntoRequests]].
676 RefPtr<ReadIntoRequest> readIntoRequest =
677 reader->ReadIntoRequests().popFirst();
679 // Step 6. If done is true, perform readIntoRequest’s close steps, given
680 // chunk.
681 if (done) {
682 readIntoRequest->CloseSteps(aCx, aChunk, aRv);
683 return;
686 // Step 7. Otherwise, perform readIntoRequest’s chunk steps, given chunk.
687 readIntoRequest->ChunkSteps(aCx, aChunk, aRv);
690 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-commit-pull-into-descriptor
691 MOZ_CAN_RUN_SCRIPT
692 void ReadableByteStreamControllerCommitPullIntoDescriptor(
693 JSContext* aCx, ReadableStream* aStream,
694 PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv) {
695 // Step 1. Assert: stream.[[state]] is not "errored".
696 MOZ_ASSERT(aStream->State() != ReadableStream::ReaderState::Errored);
698 // Step 2. Assert: pullIntoDescriptor.reader type is not "none".
699 MOZ_ASSERT(pullIntoDescriptor->GetReaderType() != ReaderType::None);
701 // Step 3. Let done be false.
702 bool done = false;
704 // Step 4. If stream.[[state]] is "closed",
705 if (aStream->State() == ReadableStream::ReaderState::Closed) {
706 // Step 4.1. Assert: pullIntoDescriptor’s bytes filled is 0.
707 MOZ_ASSERT(pullIntoDescriptor->BytesFilled() == 0);
709 // Step 4.2. Set done to true.
710 done = true;
713 // Step 5. Let filledView be !
714 // ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
715 JS::Rooted<JSObject*> filledView(
716 aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
717 aCx, pullIntoDescriptor, aRv));
718 if (aRv.Failed()) {
719 return;
721 JS::Rooted<JS::Value> filledViewValue(aCx, JS::ObjectValue(*filledView));
723 // Step 6. If pullIntoDescriptor’s reader type is "default",
724 if (pullIntoDescriptor->GetReaderType() == ReaderType::Default) {
725 // Step 6.1. Perform !ReadableStreamFulfillReadRequest(stream, filledView,
726 // done).
727 ReadableStreamFulfillReadRequest(aCx, aStream, filledViewValue, done, aRv);
728 return;
731 // Step 7.1. Assert: pullIntoDescriptor’s reader type is "byob".
732 MOZ_ASSERT(pullIntoDescriptor->GetReaderType() == ReaderType::BYOB);
734 // Step 7.2 Perform !ReadableStreamFulfillReadIntoRequest(stream, filledView,
735 // done).
736 ReadableStreamFulfillReadIntoRequest(aCx, aStream, filledViewValue, done,
737 aRv);
740 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-process-pull-into-descriptors-using-queue
741 MOZ_CAN_RUN_SCRIPT
742 void ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
743 JSContext* aCx, ReadableByteStreamController* aController,
744 ErrorResult& aRv) {
745 // Step 1. Assert: controller.[[closeRequested]] is false.
746 MOZ_ASSERT(!aController->CloseRequested());
748 // Step 2. While controller.[[pendingPullIntos]] is not empty,
749 while (!aController->PendingPullIntos().isEmpty()) {
750 // Step 2.1 If controller.[[queueTotalSize]] is 0, return.
751 if (aController->QueueTotalSize() == 0) {
752 return;
755 // Step 2.2. Let pullIntoDescriptor be controller.[[pendingPullIntos]][0].
756 RefPtr<PullIntoDescriptor> pullIntoDescriptor =
757 aController->PendingPullIntos().getFirst();
759 // Step 2.3. If
760 // !ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
761 // pullIntoDescriptor) is true,
762 bool ready = ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
763 aCx, aController, pullIntoDescriptor, aRv);
764 if (aRv.Failed()) {
765 return;
768 if (ready) {
769 // Step 2.3.1. Perform
770 // !ReadableByteStreamControllerShiftPendingPullInto(controller).
771 RefPtr<PullIntoDescriptor> discardedPullIntoDescriptor =
772 ReadableByteStreamControllerShiftPendingPullInto(aController);
774 // Step 2.3.2. Perform
775 // !ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
776 // pullIntoDescriptor).
777 RefPtr<ReadableStream> stream(aController->Stream());
778 ReadableByteStreamControllerCommitPullIntoDescriptor(
779 aCx, stream, pullIntoDescriptor, aRv);
780 if (aRv.Failed()) {
781 return;
787 MOZ_CAN_RUN_SCRIPT
788 void ReadableByteStreamControllerHandleQueueDrain(
789 JSContext* aCx, ReadableByteStreamController* aController,
790 ErrorResult& aRv);
792 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerfillreadrequestfromqueue
793 MOZ_CAN_RUN_SCRIPT void ReadableByteStreamControllerFillReadRequestFromQueue(
794 JSContext* aCx, ReadableByteStreamController* aController,
795 ReadRequest* aReadRequest, ErrorResult& aRv) {
796 // Step 1. Assert: controller.[[queueTotalSize]] > 0.
797 MOZ_ASSERT(aController->QueueTotalSize() > 0);
798 // Also assert that the queue has a non-zero length;
799 MOZ_ASSERT(aController->Queue().length() > 0);
801 // Step 2. Let entry be controller.[[queue]][0].
802 // Step 3. Remove entry from controller.[[queue]].
803 RefPtr<ReadableByteStreamQueueEntry> entry = aController->Queue().popFirst();
805 // Assert that we actually got an entry.
806 MOZ_ASSERT(entry);
808 // Step 4. Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]]
809 // − entry’s byte length.
810 aController->SetQueueTotalSize(aController->QueueTotalSize() -
811 double(entry->ByteLength()));
813 // Step 5. Perform ! ReadableByteStreamControllerHandleQueueDrain(controller).
814 ReadableByteStreamControllerHandleQueueDrain(aCx, aController, aRv);
815 if (aRv.Failed()) {
816 return;
819 // Step 6. Let view be ! Construct(%Uint8Array%, « entry’s buffer, entry’s
820 // byte offset, entry’s byte length »).
821 aRv.MightThrowJSException();
822 JS::Rooted<JSObject*> buffer(aCx, entry->Buffer());
823 JS::Rooted<JSObject*> view(
824 aCx, JS_NewUint8ArrayWithBuffer(aCx, buffer, entry->ByteOffset(),
825 int64_t(entry->ByteLength())));
826 if (!view) {
827 aRv.StealExceptionFromJSContext(aCx);
828 return;
831 // Step 7. Perform readRequest’s chunk steps, given view.
832 JS::Rooted<JS::Value> viewValue(aCx, JS::ObjectValue(*view));
833 aReadRequest->ChunkSteps(aCx, viewValue, aRv);
836 MOZ_CAN_RUN_SCRIPT void
837 ReadableByteStreamControllerProcessReadRequestsUsingQueue(
838 JSContext* aCx, ReadableByteStreamController* aController,
839 ErrorResult& aRv) {
840 // Step 1. Let reader be controller.[[stream]].[[reader]].
841 // Step 2. Assert: reader implements ReadableStreamDefaultReader.
842 RefPtr<ReadableStreamDefaultReader> reader =
843 aController->Stream()->GetDefaultReader();
845 // Step 3. While reader.[[readRequests]] is not empty,
846 while (!reader->ReadRequests().isEmpty()) {
847 // Step 3.1. If controller.[[queueTotalSize]] is 0, return.
848 if (aController->QueueTotalSize() == 0) {
849 return;
852 // Step 3.2. Let readRequest be reader.[[readRequests]][0].
853 // Step 3.3. Remove readRequest from reader.[[readRequests]].
854 RefPtr<ReadRequest> readRequest = reader->ReadRequests().popFirst();
856 // Step 3.4. Perform !
857 // ReadableByteStreamControllerFillReadRequestFromQueue(controller,
858 // readRequest).
859 ReadableByteStreamControllerFillReadRequestFromQueue(aCx, aController,
860 readRequest, aRv);
861 if (aRv.Failed()) {
862 return;
867 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue
868 void ReadableByteStreamControllerEnqueue(
869 JSContext* aCx, ReadableByteStreamController* aController,
870 JS::Handle<JSObject*> aChunk, ErrorResult& aRv) {
871 aRv.MightThrowJSException();
873 // Step 1.
874 RefPtr<ReadableStream> stream = aController->Stream();
876 // Step 2.
877 if (aController->CloseRequested() ||
878 stream->State() != ReadableStream::ReaderState::Readable) {
879 return;
882 // Step 3.
883 bool isShared;
884 JS::Rooted<JSObject*> buffer(
885 aCx, JS_GetArrayBufferViewBuffer(aCx, aChunk, &isShared));
886 if (!buffer) {
887 aRv.StealExceptionFromJSContext(aCx);
888 return;
891 // Step 4.
892 size_t byteOffset = JS_GetArrayBufferViewByteOffset(aChunk);
894 // Step 5.
895 size_t byteLength = JS_GetArrayBufferViewByteLength(aChunk);
897 // Step 6.
898 if (JS::IsDetachedArrayBufferObject(buffer)) {
899 aRv.ThrowTypeError("Detached Array Buffer");
900 return;
903 // Step 7.
904 JS::Rooted<JSObject*> transferredBuffer(aCx,
905 TransferArrayBuffer(aCx, buffer));
906 if (!transferredBuffer) {
907 aRv.StealExceptionFromJSContext(aCx);
908 return;
911 // Step 8.
912 if (!aController->PendingPullIntos().isEmpty()) {
913 // Step 8.1
914 RefPtr<PullIntoDescriptor> firstPendingPullInto =
915 aController->PendingPullIntos().getFirst();
917 // Step 8.2
918 JS::Rooted<JSObject*> pendingBuffer(aCx, firstPendingPullInto->Buffer());
919 if (JS::IsDetachedArrayBufferObject(pendingBuffer)) {
920 aRv.ThrowTypeError("Pending PullInto has detached buffer");
921 return;
924 // Step 8.3. Perform !
925 // ReadableByteStreamControllerInvalidateBYOBRequest(controller).
926 ReadableByteStreamControllerInvalidateBYOBRequest(aController);
928 // Step 8.4. Set firstPendingPullInto’s buffer to !
929 // TransferArrayBuffer(firstPendingPullInto’s buffer).
930 pendingBuffer = TransferArrayBuffer(aCx, pendingBuffer);
931 if (!pendingBuffer) {
932 aRv.StealExceptionFromJSContext(aCx);
933 return;
935 firstPendingPullInto->SetBuffer(pendingBuffer);
937 // Step 8.5. If firstPendingPullInto’s reader type is "none", perform ?
938 // ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller,
939 // firstPendingPullInto).
940 if (firstPendingPullInto->GetReaderType() == ReaderType::None) {
941 ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
942 aCx, aController, firstPendingPullInto, aRv);
943 if (aRv.Failed()) {
944 return;
949 // Step 9. If ! ReadableStreamHasDefaultReader(stream) is true,
950 if (ReadableStreamHasDefaultReader(stream)) {
951 // Step 9.1. Perform !
952 // ReadableByteStreamControllerProcessReadRequestsUsingQueue(controller).
953 ReadableByteStreamControllerProcessReadRequestsUsingQueue(aCx, aController,
954 aRv);
955 if (aRv.Failed()) {
956 return;
959 // Step 9.2. If ! ReadableStreamGetNumReadRequests(stream) is 0,
960 if (ReadableStreamGetNumReadRequests(stream) == 0) {
961 // Step 9.2.1 Assert: controller.[[pendingPullIntos]] is empty.
962 MOZ_ASSERT(aController->PendingPullIntos().isEmpty());
964 // Step 9.2.2. Perform !
965 // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
966 // transferredBuffer, byteOffset, byteLength).
967 ReadableByteStreamControllerEnqueueChunkToQueue(
968 aController, transferredBuffer, byteOffset, byteLength);
970 // Step 9.3. Otherwise,
971 } else {
972 // Step 9.3.1 Assert: controller.[[queue]] is empty.
973 MOZ_ASSERT(aController->Queue().isEmpty());
975 // Step 9.3.2. If controller.[[pendingPullIntos]] is not empty,
976 if (!aController->PendingPullIntos().isEmpty()) {
977 // Step 9.3.2.1. Assert: controller.[[pendingPullIntos]][0]'s reader
978 // type is "default".
979 MOZ_ASSERT(
980 aController->PendingPullIntos().getFirst()->GetReaderType() ==
981 ReaderType::Default);
983 // Step 9.3.2.2. Perform !
984 // ReadableByteStreamControllerShiftPendingPullInto(controller).
985 RefPtr<PullIntoDescriptor> pullIntoDescriptor =
986 ReadableByteStreamControllerShiftPendingPullInto(aController);
987 (void)pullIntoDescriptor;
990 // Step 9.3.3. Let transferredView be ! Construct(%Uint8Array%, «
991 // transferredBuffer, byteOffset, byteLength »).
992 JS::Rooted<JSObject*> transferredView(
993 aCx, JS_NewUint8ArrayWithBuffer(aCx, transferredBuffer, byteOffset,
994 int64_t(byteLength)));
995 if (!transferredView) {
996 aRv.StealExceptionFromJSContext(aCx);
997 return;
1000 // Step 9.3.4. Perform ! ReadableStreamFulfillReadRequest(stream,
1001 // transferredView, false).
1002 JS::Rooted<JS::Value> transferredViewValue(
1003 aCx, JS::ObjectValue(*transferredView));
1004 ReadableStreamFulfillReadRequest(aCx, stream, transferredViewValue, false,
1005 aRv);
1006 if (aRv.Failed()) {
1007 return;
1011 // Step 10. Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true,
1012 } else if (ReadableStreamHasBYOBReader(stream)) {
1013 // Step 10.1. Perform !
1014 // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
1015 // transferredBuffer, byteOffset, byteLength).
1016 ReadableByteStreamControllerEnqueueChunkToQueue(
1017 aController, transferredBuffer, byteOffset, byteLength);
1019 // Step 10.2 Perform !
1020 // ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
1021 ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
1022 aCx, aController, aRv);
1023 if (aRv.Failed()) {
1024 return;
1027 // Step 11. Otherwise,
1028 } else {
1029 // Step 11.1. Assert: ! IsReadableStreamLocked(stream) is false.
1030 MOZ_ASSERT(!IsReadableStreamLocked(stream));
1032 // Step 11.2. Perform !
1033 // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
1034 // transferredBuffer, byteOffset, byteLength).
1035 ReadableByteStreamControllerEnqueueChunkToQueue(
1036 aController, transferredBuffer, byteOffset, byteLength);
1039 // Step 12. Perform !
1040 // ReadableByteStreamControllerCallPullIfNeeded(controller).
1041 ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
1044 } // namespace streams_abstract
1046 // https://streams.spec.whatwg.org/#rbs-controller-enqueue
1047 void ReadableByteStreamController::Enqueue(JSContext* aCx,
1048 const ArrayBufferView& aChunk,
1049 ErrorResult& aRv) {
1050 // Step 1.
1051 JS::Rooted<JSObject*> chunk(aCx, aChunk.Obj());
1052 if (JS_GetArrayBufferViewByteLength(chunk) == 0) {
1053 aRv.ThrowTypeError("Zero Length View");
1054 return;
1057 // Step 2.
1058 bool isShared;
1059 JS::Rooted<JSObject*> viewedArrayBuffer(
1060 aCx, JS_GetArrayBufferViewBuffer(aCx, chunk, &isShared));
1061 if (!viewedArrayBuffer) {
1062 aRv.StealExceptionFromJSContext(aCx);
1063 return;
1066 if (JS::GetArrayBufferByteLength(viewedArrayBuffer) == 0) {
1067 aRv.ThrowTypeError("Zero Length Buffer");
1068 return;
1071 // Step 3.
1072 if (CloseRequested()) {
1073 aRv.ThrowTypeError("close requested");
1074 return;
1077 // Step 4.
1078 if (Stream()->State() != ReadableStream::ReaderState::Readable) {
1079 aRv.ThrowTypeError("Not Readable");
1080 return;
1083 // Step 5.
1084 ReadableByteStreamControllerEnqueue(aCx, this, chunk, aRv);
1087 // https://streams.spec.whatwg.org/#rbs-controller-error
1088 void ReadableByteStreamController::Error(JSContext* aCx,
1089 JS::Handle<JS::Value> aErrorValue,
1090 ErrorResult& aRv) {
1091 // Step 1.
1092 ReadableByteStreamControllerError(this, aErrorValue, aRv);
1095 // https://streams.spec.whatwg.org/#rbs-controller-private-cancel
1096 already_AddRefed<Promise> ReadableByteStreamController::CancelSteps(
1097 JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
1098 // Step 1.
1099 ReadableByteStreamControllerClearPendingPullIntos(this);
1101 // Step 2.
1102 ResetQueue(this);
1104 // Step 3.
1105 Optional<JS::Handle<JS::Value>> reason(aCx, aReason);
1106 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms = mAlgorithms;
1107 RefPtr<Promise> result = algorithms->CancelCallback(aCx, reason, aRv);
1108 if (NS_WARN_IF(aRv.Failed())) {
1109 return nullptr;
1111 // Step 4.
1112 ReadableByteStreamControllerClearAlgorithms(this);
1114 // Step 5.
1115 return result.forget();
1118 namespace streams_abstract {
1119 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-handle-queue-drain
1120 void ReadableByteStreamControllerHandleQueueDrain(
1121 JSContext* aCx, ReadableByteStreamController* aController,
1122 ErrorResult& aRv) {
1123 // Step 1.
1124 MOZ_ASSERT(aController->Stream()->State() ==
1125 ReadableStream::ReaderState::Readable);
1127 // Step 2.
1128 if (aController->QueueTotalSize() == 0 && aController->CloseRequested()) {
1129 // Step 2.1
1130 ReadableByteStreamControllerClearAlgorithms(aController);
1132 // Step 2.2
1133 RefPtr<ReadableStream> stream = aController->Stream();
1134 ReadableStreamClose(aCx, stream, aRv);
1135 return;
1138 // Step 3.1
1139 ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
1141 } // namespace streams_abstract
1143 // https://streams.spec.whatwg.org/#rbs-controller-private-pull
1144 void ReadableByteStreamController::PullSteps(JSContext* aCx,
1145 ReadRequest* aReadRequest,
1146 ErrorResult& aRv) {
1147 // Step 1.
1148 ReadableStream* stream = Stream();
1150 // Step 2.
1151 MOZ_ASSERT(ReadableStreamHasDefaultReader(stream));
1153 // Step 3.
1154 if (QueueTotalSize() > 0) {
1155 // Step 3.1. Assert: ! ReadableStreamGetNumReadRequests ( stream ) is 0.
1156 MOZ_ASSERT(ReadableStreamGetNumReadRequests(stream) == 0);
1158 // Step 3.2. Perform !
1159 // ReadableByteStreamControllerFillReadRequestFromQueue(this, readRequest).
1160 ReadableByteStreamControllerFillReadRequestFromQueue(aCx, this,
1161 aReadRequest, aRv);
1163 // Step 3.3. Return.
1164 return;
1167 // Step 4.
1168 Maybe<uint64_t> autoAllocateChunkSize = AutoAllocateChunkSize();
1170 // Step 5.
1171 if (autoAllocateChunkSize) {
1172 // Step 5.1
1173 aRv.MightThrowJSException();
1174 JS::Rooted<JSObject*> buffer(
1175 aCx, JS::NewArrayBuffer(aCx, *autoAllocateChunkSize));
1176 // Step 5.2
1177 if (!buffer) {
1178 // Step 5.2.1
1179 JS::Rooted<JS::Value> bufferError(aCx);
1180 if (!JS_GetPendingException(aCx, &bufferError)) {
1181 // Uncatchable exception; we should mark aRv and return.
1182 aRv.StealExceptionFromJSContext(aCx);
1183 return;
1186 // It's not expliclitly stated, but I assume the intention here is that
1187 // we perform a normal completion here.
1188 JS_ClearPendingException(aCx);
1190 aReadRequest->ErrorSteps(aCx, bufferError, aRv);
1192 // Step 5.2.2.
1193 return;
1196 // Step 5.3
1197 RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
1198 buffer, *autoAllocateChunkSize, 0, *autoAllocateChunkSize, 0, 1,
1199 PullIntoDescriptor::Constructor::Uint8, ReaderType::Default);
1201 // Step 5.4
1202 PendingPullIntos().insertBack(pullIntoDescriptor);
1205 // Step 6.
1206 ReadableStreamAddReadRequest(stream, aReadRequest);
1208 // Step 7.
1209 ReadableByteStreamControllerCallPullIfNeeded(aCx, this, aRv);
1212 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontroller-releasesteps
1213 void ReadableByteStreamController::ReleaseSteps() {
1214 // Step 1. If this.[[pendingPullIntos]] is not empty,
1215 if (!PendingPullIntos().isEmpty()) {
1216 // Step 1.1. Let firstPendingPullInto be this.[[pendingPullIntos]][0].
1217 RefPtr<PullIntoDescriptor> firstPendingPullInto =
1218 PendingPullIntos().popFirst();
1220 // Step 1.2. Set firstPendingPullInto’s reader type to "none".
1221 firstPendingPullInto->SetReaderType(ReaderType::None);
1223 // Step 1.3. Set this.[[pendingPullIntos]] to the list «
1224 // firstPendingPullInto ».
1225 PendingPullIntos().clear();
1226 PendingPullIntos().insertBack(firstPendingPullInto);
1230 namespace streams_abstract {
1232 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-shift-pending-pull-into
1233 already_AddRefed<PullIntoDescriptor>
1234 ReadableByteStreamControllerShiftPendingPullInto(
1235 ReadableByteStreamController* aController) {
1236 // Step 1.
1237 MOZ_ASSERT(!aController->GetByobRequest());
1239 // Step 2 + 3
1240 RefPtr<PullIntoDescriptor> descriptor =
1241 aController->PendingPullIntos().popFirst();
1243 // Step 4.
1244 return descriptor.forget();
1247 JSObject* ConstructFromPullIntoConstructor(
1248 JSContext* aCx, PullIntoDescriptor::Constructor constructor,
1249 JS::Handle<JSObject*> buffer, size_t byteOffset, size_t length) {
1250 switch (constructor) {
1251 case PullIntoDescriptor::Constructor::DataView:
1252 return JS_NewDataView(aCx, buffer, byteOffset, length);
1253 break;
1255 #define CONSTRUCT_TYPED_ARRAY_TYPE(ExternalT, NativeT, Name) \
1256 case PullIntoDescriptor::Constructor::Name: \
1257 return JS_New##Name##ArrayWithBuffer(aCx, buffer, byteOffset, \
1258 int64_t(length)); \
1259 break;
1261 JS_FOR_EACH_TYPED_ARRAY(CONSTRUCT_TYPED_ARRAY_TYPE)
1263 #undef CONSTRUCT_TYPED_ARRAY_TYPE
1265 default:
1266 MOZ_ASSERT_UNREACHABLE("Unknown PullIntoDescriptor::Constructor");
1267 return nullptr;
1271 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-convert-pull-into-descriptor
1272 JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
1273 JSContext* aCx, PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv) {
1274 // Step 1. Let bytesFilled be pullIntoDescriptor’s bytes filled.
1275 uint64_t bytesFilled = pullIntoDescriptor->BytesFilled();
1277 // Step 2. Let elementSize be pullIntoDescriptor’s element size.
1278 uint64_t elementSize = pullIntoDescriptor->ElementSize();
1280 // Step 3. Assert: bytesFilled ≤ pullIntoDescriptor’s byte length.
1281 MOZ_ASSERT(bytesFilled <= pullIntoDescriptor->ByteLength());
1283 // Step 4. Assert: bytesFilled mod elementSize is 0.
1284 MOZ_ASSERT(bytesFilled % elementSize == 0);
1286 // Step 5. Let buffer be ! TransferArrayBuffer(pullIntoDescriptor’s buffer).
1287 aRv.MightThrowJSException();
1288 JS::Rooted<JSObject*> srcBuffer(aCx, pullIntoDescriptor->Buffer());
1289 JS::Rooted<JSObject*> buffer(aCx, TransferArrayBuffer(aCx, srcBuffer));
1290 if (!buffer) {
1291 aRv.StealExceptionFromJSContext(aCx);
1292 return nullptr;
1295 // Step 6. Return ! Construct(pullIntoDescriptor’s view constructor,
1296 // « buffer, pullIntoDescriptor’s byte offset, bytesFilled ÷ elementSize »).
1297 JS::Rooted<JSObject*> res(
1298 aCx, ConstructFromPullIntoConstructor(
1299 aCx, pullIntoDescriptor->ViewConstructor(), buffer,
1300 pullIntoDescriptor->ByteOffset(), bytesFilled / elementSize));
1301 if (!res) {
1302 aRv.StealExceptionFromJSContext(aCx);
1303 return nullptr;
1305 return res;
1308 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-closed-state
1309 MOZ_CAN_RUN_SCRIPT
1310 static void ReadableByteStreamControllerRespondInClosedState(
1311 JSContext* aCx, ReadableByteStreamController* aController,
1312 RefPtr<PullIntoDescriptor>& aFirstDescriptor, ErrorResult& aRv) {
1313 // Step 1. Assert: firstDescriptor ’s bytes filled is 0.
1314 MOZ_ASSERT(aFirstDescriptor->BytesFilled() == 0);
1316 // Step 2. If firstDescriptor’s reader type is "none",
1317 // perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
1318 if (aFirstDescriptor->GetReaderType() == ReaderType::None) {
1319 RefPtr<PullIntoDescriptor> discarded =
1320 ReadableByteStreamControllerShiftPendingPullInto(aController);
1321 (void)discarded;
1324 // Step 3. Let stream be controller.[[stream]].
1325 RefPtr<ReadableStream> stream = aController->Stream();
1327 // Step 4. If ! ReadableStreamHasBYOBReader(stream) is true,
1328 if (ReadableStreamHasBYOBReader(stream)) {
1329 // Step 4.1. While ! ReadableStreamGetNumReadIntoRequests(stream) > 0,
1330 while (ReadableStreamGetNumReadIntoRequests(stream) > 0) {
1331 // Step 4.1.1. Let pullIntoDescriptor be !
1332 // ReadableByteStreamControllerShiftPendingPullInto(controller).
1333 RefPtr<PullIntoDescriptor> pullIntoDescriptor =
1334 ReadableByteStreamControllerShiftPendingPullInto(aController);
1336 // Step 4.1.2. Perform !
1337 // ReadableByteStreamControllerCommitPullIntoDescriptor(stream,
1338 // pullIntoDescriptor).
1339 ReadableByteStreamControllerCommitPullIntoDescriptor(
1340 aCx, stream, pullIntoDescriptor, aRv);
1345 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-head-pull-into-descriptor
1346 void ReadableByteStreamControllerFillHeadPullIntoDescriptor(
1347 ReadableByteStreamController* aController, size_t aSize,
1348 PullIntoDescriptor* aPullIntoDescriptor) {
1349 // Step 1. Assert: either controller.[[pendingPullIntos]] is empty, or
1350 // controller.[[pendingPullIntos]][0] is pullIntoDescriptor.
1351 MOZ_ASSERT(aController->PendingPullIntos().isEmpty() ||
1352 aController->PendingPullIntos().getFirst() == aPullIntoDescriptor);
1354 // Step 2. Assert: controller.[[byobRequest]] is null.
1355 MOZ_ASSERT(!aController->GetByobRequest());
1357 // Step 3. Set pullIntoDescriptor’s bytes filled to bytes filled + size.
1358 aPullIntoDescriptor->SetBytesFilled(aPullIntoDescriptor->BytesFilled() +
1359 aSize);
1362 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-readable-state
1363 MOZ_CAN_RUN_SCRIPT
1364 static void ReadableByteStreamControllerRespondInReadableState(
1365 JSContext* aCx, ReadableByteStreamController* aController,
1366 uint64_t aBytesWritten, PullIntoDescriptor* aPullIntoDescriptor,
1367 ErrorResult& aRv) {
1368 // Step 1. Assert: pullIntoDescriptor’s bytes filled + bytesWritten ≤
1369 // pullIntoDescriptor’s byte length.
1370 MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() + aBytesWritten <=
1371 aPullIntoDescriptor->ByteLength());
1373 // Step 2. Perform
1374 // !ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,
1375 // bytesWritten, pullIntoDescriptor).
1376 ReadableByteStreamControllerFillHeadPullIntoDescriptor(
1377 aController, aBytesWritten, aPullIntoDescriptor);
1379 // Step 3. If pullIntoDescriptor’s reader type is "none",
1380 if (aPullIntoDescriptor->GetReaderType() == ReaderType::None) {
1381 // Step 3.1. Perform ?
1382 // ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller,
1383 // pullIntoDescriptor).
1384 ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
1385 aCx, aController, aPullIntoDescriptor, aRv);
1386 if (aRv.Failed()) {
1387 return;
1390 // Step 3.2. Perform !
1391 // ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
1392 ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
1393 aCx, aController, aRv);
1395 // Step 3.3. Return.
1396 return;
1399 // Step 4. If pullIntoDescriptor’s bytes filled < pullIntoDescriptor’s element
1400 // size, return.
1401 if (aPullIntoDescriptor->BytesFilled() < aPullIntoDescriptor->ElementSize()) {
1402 return;
1405 // Step 5. Perform
1406 // !ReadableByteStreamControllerShiftPendingPullInto(controller).
1407 RefPtr<PullIntoDescriptor> pullIntoDescriptor =
1408 ReadableByteStreamControllerShiftPendingPullInto(aController);
1409 (void)pullIntoDescriptor;
1411 // Step 6. Let remainderSize be pullIntoDescriptor’s bytes filled mod
1412 // pullIntoDescriptor’s element size.
1413 size_t remainderSize =
1414 aPullIntoDescriptor->BytesFilled() % aPullIntoDescriptor->ElementSize();
1416 // Step 7. If remainderSize > 0,
1417 if (remainderSize > 0) {
1418 // Step 7.1. Let end be pullIntoDescriptor’s byte offset +
1419 // pullIntoDescriptor’s bytes filled.
1420 size_t end =
1421 aPullIntoDescriptor->ByteOffset() + aPullIntoDescriptor->BytesFilled();
1423 // Step 7.2. Perform ?
1424 // ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
1425 // pullIntoDescriptor’s buffer, end − remainderSize, remainderSize).
1426 JS::Rooted<JSObject*> pullIntoBuffer(aCx, aPullIntoDescriptor->Buffer());
1427 ReadableByteStreamControllerEnqueueClonedChunkToQueue(
1428 aCx, aController, pullIntoBuffer, end - remainderSize, remainderSize,
1429 aRv);
1430 if (aRv.Failed()) {
1431 return;
1435 // Step 8. Set pullIntoDescriptor’s bytes filled to pullIntoDescriptor’s bytes
1436 // filled − remainderSize.
1437 aPullIntoDescriptor->SetBytesFilled(aPullIntoDescriptor->BytesFilled() -
1438 remainderSize);
1440 // Step 9. Perform
1441 // !ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
1442 // pullIntoDescriptor).
1443 RefPtr<ReadableStream> stream(aController->Stream());
1444 ReadableByteStreamControllerCommitPullIntoDescriptor(
1445 aCx, stream, aPullIntoDescriptor, aRv);
1446 if (aRv.Failed()) {
1447 return;
1450 // Step 10. Perform
1451 // !ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
1452 ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
1453 aCx, aController, aRv);
1456 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-internal
1457 void ReadableByteStreamControllerRespondInternal(
1458 JSContext* aCx, ReadableByteStreamController* aController,
1459 uint64_t aBytesWritten, ErrorResult& aRv) {
1460 // Step 1.
1461 RefPtr<PullIntoDescriptor> firstDescriptor =
1462 aController->PendingPullIntos().getFirst();
1464 // Step 2.
1465 JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
1466 #ifdef DEBUG
1467 bool canTransferBuffer = CanTransferArrayBuffer(aCx, buffer, aRv);
1468 MOZ_ASSERT(!aRv.Failed());
1469 MOZ_ASSERT(canTransferBuffer);
1470 #endif
1472 // Step 3.
1473 ReadableByteStreamControllerInvalidateBYOBRequest(aController);
1475 // Step 4.
1476 auto state = aController->Stream()->State();
1478 // Step 5.
1479 if (state == ReadableStream::ReaderState::Closed) {
1480 // Step 5.1
1481 MOZ_ASSERT(aBytesWritten == 0);
1483 // Step 5.2
1484 ReadableByteStreamControllerRespondInClosedState(aCx, aController,
1485 firstDescriptor, aRv);
1486 if (aRv.Failed()) {
1487 return;
1489 } else {
1490 // Step 6.1
1491 MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);
1493 // Step 6.2.
1494 MOZ_ASSERT(aBytesWritten > 0);
1496 // Step 6.3
1497 ReadableByteStreamControllerRespondInReadableState(
1498 aCx, aController, aBytesWritten, firstDescriptor, aRv);
1499 if (aRv.Failed()) {
1500 return;
1503 // Step 7.
1504 ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
1507 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond
1508 void ReadableByteStreamControllerRespond(
1509 JSContext* aCx, ReadableByteStreamController* aController,
1510 uint64_t aBytesWritten, ErrorResult& aRv) {
1511 // Step 1.
1512 MOZ_ASSERT(!aController->PendingPullIntos().isEmpty());
1514 // Step 2.
1515 PullIntoDescriptor* firstDescriptor =
1516 aController->PendingPullIntos().getFirst();
1518 // Step 3.
1519 auto state = aController->Stream()->State();
1521 // Step 4.
1522 if (state == ReadableStream::ReaderState::Closed) {
1523 // Step 4.1
1524 if (aBytesWritten != 0) {
1525 aRv.ThrowTypeError("bytesWritten not zero on closed stream");
1526 return;
1528 } else {
1529 // Step 5.1
1530 MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);
1532 // Step 5.2
1533 if (aBytesWritten == 0) {
1534 aRv.ThrowTypeError("bytesWritten 0");
1535 return;
1538 // Step 5.3
1539 if (firstDescriptor->BytesFilled() + aBytesWritten >
1540 firstDescriptor->ByteLength()) {
1541 aRv.ThrowRangeError("bytesFilled + bytesWritten > byteLength");
1542 return;
1546 // Step 6.
1547 aRv.MightThrowJSException();
1548 JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
1549 JS::Rooted<JSObject*> transferredBuffer(aCx,
1550 TransferArrayBuffer(aCx, buffer));
1551 if (!transferredBuffer) {
1552 aRv.StealExceptionFromJSContext(aCx);
1553 return;
1555 firstDescriptor->SetBuffer(transferredBuffer);
1557 // Step 7.
1558 ReadableByteStreamControllerRespondInternal(aCx, aController, aBytesWritten,
1559 aRv);
1562 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-with-new-view
1563 void ReadableByteStreamControllerRespondWithNewView(
1564 JSContext* aCx, ReadableByteStreamController* aController,
1565 JS::Handle<JSObject*> aView, ErrorResult& aRv) {
1566 aRv.MightThrowJSException();
1568 // Step 1.
1569 MOZ_ASSERT(!aController->PendingPullIntos().isEmpty());
1571 // Step 2.
1572 bool isSharedMemory;
1573 JS::Rooted<JSObject*> viewedArrayBuffer(
1574 aCx, JS_GetArrayBufferViewBuffer(aCx, aView, &isSharedMemory));
1575 if (!viewedArrayBuffer) {
1576 aRv.StealExceptionFromJSContext(aCx);
1577 return;
1579 MOZ_ASSERT(!JS::IsDetachedArrayBufferObject(viewedArrayBuffer));
1581 // Step 3.
1582 RefPtr<PullIntoDescriptor> firstDescriptor =
1583 aController->PendingPullIntos().getFirst();
1585 // Step 4.
1586 ReadableStream::ReaderState state = aController->Stream()->State();
1588 // Step 5.
1589 if (state == ReadableStream::ReaderState::Closed) {
1590 // Step 5.1
1591 if (JS_GetArrayBufferViewByteLength(aView) != 0) {
1592 aRv.ThrowTypeError("View has non-zero length in closed stream");
1593 return;
1595 } else {
1596 // Step 6.1
1597 MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);
1599 // Step 6.2
1600 if (JS_GetArrayBufferViewByteLength(aView) == 0) {
1601 aRv.ThrowTypeError("View has zero length in readable stream");
1602 return;
1606 // Step 7.
1607 if (firstDescriptor->ByteOffset() + firstDescriptor->BytesFilled() !=
1608 JS_GetArrayBufferViewByteOffset(aView)) {
1609 aRv.ThrowRangeError("Invalid Offset");
1610 return;
1613 // Step 8.
1614 if (firstDescriptor->BufferByteLength() !=
1615 JS::GetArrayBufferByteLength(viewedArrayBuffer)) {
1616 aRv.ThrowRangeError("Mismatched buffer byte lengths");
1617 return;
1620 // Step 9.
1621 if (firstDescriptor->BytesFilled() + JS_GetArrayBufferViewByteLength(aView) >
1622 firstDescriptor->ByteLength()) {
1623 aRv.ThrowRangeError("Too many bytes");
1624 return;
1627 // Step 10. Let viewByteLength be view.[[ByteLength]].
1628 size_t viewByteLength = JS_GetArrayBufferViewByteLength(aView);
1630 // Step 11. Set firstDescriptor’s buffer to ?
1631 // TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
1632 JS::Rooted<JSObject*> transferedBuffer(
1633 aCx, TransferArrayBuffer(aCx, viewedArrayBuffer));
1634 if (!transferedBuffer) {
1635 aRv.StealExceptionFromJSContext(aCx);
1636 return;
1638 firstDescriptor->SetBuffer(transferedBuffer);
1640 // Step 12. Perform ? ReadableByteStreamControllerRespondInternal(controller,
1641 // viewByteLength).
1642 ReadableByteStreamControllerRespondInternal(aCx, aController, viewByteLength,
1643 aRv);
1646 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-pull-into-descriptor-from-queue
1647 bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
1648 JSContext* aCx, ReadableByteStreamController* aController,
1649 PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv) {
1650 // Step 1. Let elementSize be pullIntoDescriptor.[[elementSize]].
1651 size_t elementSize = aPullIntoDescriptor->ElementSize();
1653 // Step 2. Let currentAlignedBytes be pullIntoDescriptor’s bytes filled −
1654 // (pullIntoDescriptor’s bytes filled mod elementSize).
1655 size_t currentAlignedBytes =
1656 aPullIntoDescriptor->BytesFilled() -
1657 (aPullIntoDescriptor->BytesFilled() % elementSize);
1659 // Step 3. Let maxBytesToCopy be min(controller.[[queueTotalSize]],
1660 // pullIntoDescriptor’s byte length − pullIntoDescriptor’s bytes filled).
1661 size_t maxBytesToCopy =
1662 std::min(static_cast<size_t>(aController->QueueTotalSize()),
1663 static_cast<size_t>((aPullIntoDescriptor->ByteLength() -
1664 aPullIntoDescriptor->BytesFilled())));
1666 // Step 4. Let maxBytesFilled be pullIntoDescriptor’s bytes filled +
1667 // maxBytesToCopy.
1668 size_t maxBytesFilled = aPullIntoDescriptor->BytesFilled() + maxBytesToCopy;
1670 // Step 5. Let maxAlignedBytes be maxBytesFilled − (maxBytesFilled mod
1671 // elementSize).
1672 size_t maxAlignedBytes = maxBytesFilled - (maxBytesFilled % elementSize);
1674 // Step 6. Let totalBytesToCopyRemaining be maxBytesToCopy.
1675 size_t totalBytesToCopyRemaining = maxBytesToCopy;
1677 // Step 7. Let ready be false.
1678 bool ready = false;
1680 // Step 8. If maxAlignedBytes > currentAlignedBytes,
1681 if (maxAlignedBytes > currentAlignedBytes) {
1682 // Step 8.1. Set totalBytesToCopyRemaining to maxAlignedBytes −
1683 // pullIntoDescriptor’s bytes filled.
1684 totalBytesToCopyRemaining =
1685 maxAlignedBytes - aPullIntoDescriptor->BytesFilled();
1686 // Step 8.2. Set ready to true.
1687 ready = true;
1690 // Step 9. Let queue be controller.[[queue]].
1691 LinkedList<RefPtr<ReadableByteStreamQueueEntry>>& queue =
1692 aController->Queue();
1694 // Step 10. While totalBytesToCopyRemaining > 0,
1695 while (totalBytesToCopyRemaining > 0) {
1696 // Step 10.1 Let headOfQueue be queue[0].
1697 ReadableByteStreamQueueEntry* headOfQueue = queue.getFirst();
1699 // Step 10.2. Let bytesToCopy be min(totalBytesToCopyRemaining,
1700 // headOfQueue’s byte length).
1701 size_t bytesToCopy =
1702 std::min(totalBytesToCopyRemaining, headOfQueue->ByteLength());
1704 // Step 10.3. Let destStart be pullIntoDescriptor’s byte offset +
1705 // pullIntoDescriptor’s bytes filled.
1706 size_t destStart =
1707 aPullIntoDescriptor->ByteOffset() + aPullIntoDescriptor->BytesFilled();
1709 // Step 10.4. Perform !CopyDataBlockBytes(pullIntoDescriptor’s
1710 // buffer.[[ArrayBufferData]], destStart, headOfQueue’s
1711 // buffer.[[ArrayBufferData]], headOfQueue’s byte offset,
1712 // bytesToCopy).
1713 JS::Rooted<JSObject*> descriptorBuffer(aCx, aPullIntoDescriptor->Buffer());
1714 JS::Rooted<JSObject*> queueBuffer(aCx, headOfQueue->Buffer());
1715 if (!JS::ArrayBufferCopyData(aCx, descriptorBuffer, destStart, queueBuffer,
1716 headOfQueue->ByteOffset(), bytesToCopy)) {
1717 aRv.StealExceptionFromJSContext(aCx);
1718 return false;
1721 // Step 10.5. If headOfQueue’s byte length is bytesToCopy,
1722 if (headOfQueue->ByteLength() == bytesToCopy) {
1723 // Step 10.5.1. Remove queue[0].
1724 queue.popFirst();
1725 } else {
1726 // Step 10.6. Otherwise,
1728 // Step 10.6.1 Set headOfQueue’s byte offset to
1729 // headOfQueue’s byte offset + bytesToCopy.
1730 headOfQueue->SetByteOffset(headOfQueue->ByteOffset() + bytesToCopy);
1731 // Step 10.6.2 Set headOfQueue’s byte length to
1732 // headOfQueue’s byte length − bytesToCopy.
1733 headOfQueue->SetByteLength(headOfQueue->ByteLength() - bytesToCopy);
1736 // Step 10.7. Set controller.[[queueTotalSize]] to
1737 // controller.[[queueTotalSize]] − bytesToCopy.
1738 aController->SetQueueTotalSize(aController->QueueTotalSize() -
1739 (double)bytesToCopy);
1741 // Step 10.8, Perform
1742 // !ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,
1743 // bytesToCopy, pullIntoDescriptor).
1744 ReadableByteStreamControllerFillHeadPullIntoDescriptor(
1745 aController, bytesToCopy, aPullIntoDescriptor);
1747 // Step 10.9. Set totalBytesToCopyRemaining to totalBytesToCopyRemaining −
1748 // bytesToCopy.
1749 totalBytesToCopyRemaining = totalBytesToCopyRemaining - bytesToCopy;
1752 // Step 11. If ready is false,
1753 if (!ready) {
1754 // Step 11.1. Assert: controller.[[queueTotalSize]] is 0.
1755 MOZ_ASSERT(aController->QueueTotalSize() == 0);
1757 // Step 11.2. Assert: pullIntoDescriptor’s bytes filled > 0.
1758 MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() > 0);
1760 // Step 11.3. Assert: pullIntoDescriptor’s bytes filled <
1761 // pullIntoDescriptor’s
1762 // element size.
1763 MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() <
1764 aPullIntoDescriptor->ElementSize());
1767 // Step 12. Return ready.
1768 return ready;
1771 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-pull-into
1772 void ReadableByteStreamControllerPullInto(
1773 JSContext* aCx, ReadableByteStreamController* aController,
1774 JS::Handle<JSObject*> aView, ReadIntoRequest* aReadIntoRequest,
1775 ErrorResult& aRv) {
1776 aRv.MightThrowJSException();
1778 // Step 1. Let stream be controller.[[stream]].
1779 ReadableStream* stream = aController->Stream();
1781 // Step 2. Let elementSize be 1.
1782 size_t elementSize = 1;
1784 // Step 3. Let ctor be %DataView%.
1785 PullIntoDescriptor::Constructor ctor =
1786 PullIntoDescriptor::Constructor::DataView;
1788 // Step 4. If view has a [[TypedArrayName]] internal slot (i.e., it is not a
1789 // DataView),
1790 if (JS_IsTypedArrayObject(aView)) {
1791 // Step 4.1. Set elementSize to the element size specified in the typed
1792 // array constructors table for view.[[TypedArrayName]].
1793 JS::Scalar::Type type = JS_GetArrayBufferViewType(aView);
1794 elementSize = JS::Scalar::byteSize(type);
1796 // Step 4.2 Set ctor to the constructor specified in the typed array
1797 // constructors table for view.[[TypedArrayName]].
1798 ctor = PullIntoDescriptor::constructorFromScalar(type);
1801 // Step 5. Let byteOffset be view.[[ByteOffset]].
1802 size_t byteOffset = JS_GetArrayBufferViewByteOffset(aView);
1804 // Step 6. Let byteLength be view.[[ByteLength]].
1805 size_t byteLength = JS_GetArrayBufferViewByteLength(aView);
1807 // Step 7. Let bufferResult be
1808 // TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
1809 bool isShared;
1810 JS::Rooted<JSObject*> viewedArrayBuffer(
1811 aCx, JS_GetArrayBufferViewBuffer(aCx, aView, &isShared));
1812 if (!viewedArrayBuffer) {
1813 aRv.StealExceptionFromJSContext(aCx);
1814 return;
1816 JS::Rooted<JSObject*> bufferResult(
1817 aCx, TransferArrayBuffer(aCx, viewedArrayBuffer));
1819 // Step 8. If bufferResult is an abrupt completion,
1820 if (!bufferResult) {
1821 JS::Rooted<JS::Value> pendingException(aCx);
1822 if (!JS_GetPendingException(aCx, &pendingException)) {
1823 // This means an un-catchable exception. Use StealExceptionFromJSContext
1824 // to setup aRv properly.
1825 aRv.StealExceptionFromJSContext(aCx);
1826 return;
1829 // It's not expliclitly stated, but I assume the intention here is that
1830 // we perform a normal completion here; we also need to clear the
1831 // exception state anyhow to succesfully run ErrorSteps.
1832 JS_ClearPendingException(aCx);
1834 // Step 8.1. Perform readIntoRequest’s error steps, given
1835 // bufferResult.[[Value]].
1836 aReadIntoRequest->ErrorSteps(aCx, pendingException, aRv);
1838 // Step 8.2. Return.
1839 return;
1842 // Step 9. Let buffer be bufferResult.[[Value]].
1843 JS::Rooted<JSObject*> buffer(aCx, bufferResult);
1845 // Step 10. Let pullIntoDescriptor be a new pull-into descriptor with
1846 // buffer: buffer,
1847 // buffer byte length: buffer.[[ArrayBufferByteLength]],
1848 // byte offset: byteOffset,
1849 // byte length: byteLength,
1850 // bytes filled: 0,
1851 // element size: elementSize,
1852 // view constructor: ctor,
1853 // and reader type: "byob".
1854 RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
1855 buffer, JS::GetArrayBufferByteLength(buffer), byteOffset, byteLength, 0,
1856 elementSize, ctor, ReaderType::BYOB);
1858 // Step 11. If controller.[[pendingPullIntos]] is not empty,
1859 if (!aController->PendingPullIntos().isEmpty()) {
1860 // Step 11.1. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
1861 aController->PendingPullIntos().insertBack(pullIntoDescriptor);
1863 // Step 11.2. Perform !ReadableStreamAddReadIntoRequest(stream,
1864 // readIntoRequest).
1865 ReadableStreamAddReadIntoRequest(stream, aReadIntoRequest);
1867 // Step 11.3. Return.
1868 return;
1871 // Step 12. If stream.[[state]] is "closed",
1872 if (stream->State() == ReadableStream::ReaderState::Closed) {
1873 // Step 12.1. Let emptyView be !Construct(ctor, « pullIntoDescriptor’s
1874 // buffer, pullIntoDescriptor’s byte offset, 0 »).
1875 JS::Rooted<JSObject*> pullIntoBuffer(aCx, pullIntoDescriptor->Buffer());
1876 JS::Rooted<JSObject*> emptyView(
1877 aCx,
1878 ConstructFromPullIntoConstructor(aCx, ctor, pullIntoBuffer,
1879 pullIntoDescriptor->ByteOffset(), 0));
1880 if (!emptyView) {
1881 aRv.StealExceptionFromJSContext(aCx);
1882 return;
1885 // Step 12.2. Perform readIntoRequest’s close steps, given emptyView.
1886 JS::Rooted<JS::Value> emptyViewValue(aCx, JS::ObjectValue(*emptyView));
1887 aReadIntoRequest->CloseSteps(aCx, emptyViewValue, aRv);
1889 // Step 12.3. Return.
1890 return;
1893 // Step 13,. If controller.[[queueTotalSize]] > 0,
1894 if (aController->QueueTotalSize() > 0) {
1895 // Step 13.1 If
1896 // !ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
1897 // pullIntoDescriptor) is true,
1898 bool ready = ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
1899 aCx, aController, pullIntoDescriptor, aRv);
1900 if (aRv.Failed()) {
1901 return;
1903 if (ready) {
1904 // Step 13.1.1 Let filledView be
1905 // !ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
1906 JS::Rooted<JSObject*> filledView(
1907 aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
1908 aCx, pullIntoDescriptor, aRv));
1909 if (aRv.Failed()) {
1910 return;
1912 // Step 13.1.2. Perform
1913 // !ReadableByteStreamControllerHandleQueueDrain(controller).
1914 ReadableByteStreamControllerHandleQueueDrain(aCx, aController, aRv);
1915 if (aRv.Failed()) {
1916 return;
1918 // Step 13.1.3. Perform readIntoRequest’s chunk steps, given filledView.
1919 JS::Rooted<JS::Value> filledViewValue(aCx, JS::ObjectValue(*filledView));
1920 aReadIntoRequest->ChunkSteps(aCx, filledViewValue, aRv);
1921 // Step 13.1.4. Return.
1922 return;
1925 // Step 13.2 If controller.[[closeRequested]] is true,
1926 if (aController->CloseRequested()) {
1927 // Step 13.2.1. Let e be a TypeError exception.
1928 ErrorResult typeError;
1929 typeError.ThrowTypeError("Close Requested True during Pull Into");
1931 JS::Rooted<JS::Value> e(aCx);
1932 MOZ_RELEASE_ASSERT(ToJSValue(aCx, std::move(typeError), &e));
1934 // Step 13.2.2. Perform !ReadableByteStreamControllerError(controller, e).
1935 ReadableByteStreamControllerError(aController, e, aRv);
1936 if (aRv.Failed()) {
1937 return;
1940 // Step 13.2.3. Perform readIntoRequest’s error steps, given e.
1941 aReadIntoRequest->ErrorSteps(aCx, e, aRv);
1943 // Step 13.2.4. Return.
1944 return;
1948 // Step 14. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
1949 aController->PendingPullIntos().insertBack(pullIntoDescriptor);
1951 // Step 15. Perform !ReadableStreamAddReadIntoRequest(stream,
1952 // readIntoRequest).
1953 ReadableStreamAddReadIntoRequest(stream, aReadIntoRequest);
1955 // Step 16, Perform
1956 // !ReadableByteStreamControllerCallPullIfNeeded(controller).
1957 ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
1960 // https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller
1961 void SetUpReadableByteStreamController(
1962 JSContext* aCx, ReadableStream* aStream,
1963 ReadableByteStreamController* aController,
1964 UnderlyingSourceAlgorithmsBase* aAlgorithms, double aHighWaterMark,
1965 Maybe<uint64_t> aAutoAllocateChunkSize, ErrorResult& aRv) {
1966 // Step 1. Assert: stream.[[controller]] is undefined.
1967 MOZ_ASSERT(!aStream->Controller());
1969 // Step 2. If autoAllocateChunkSize is not undefined,
1970 // Step 2.1. Assert: ! IsInteger(autoAllocateChunkSize) is true. Implicit
1971 // Step 2.2. Assert: autoAllocateChunkSize is positive. (Implicit by
1972 // type.)
1974 // Step 3. Set controller.[[stream]] to stream.
1975 aController->SetStream(aStream);
1977 // Step 4. Set controller.[[pullAgain]] and controller.[[pulling]] to false.
1978 aController->SetPullAgain(false);
1979 aController->SetPulling(false);
1981 // Step 5. Set controller.[[byobRequest]] to null.
1982 aController->SetByobRequest(nullptr);
1984 // Step 6. Perform !ResetQueue(controller).
1985 ResetQueue(aController);
1987 // Step 7. Set controller.[[closeRequested]] and controller.[[started]] to
1988 // false.
1989 aController->SetCloseRequested(false);
1990 aController->SetStarted(false);
1992 // Step 8. Set controller.[[strategyHWM]] to highWaterMark.
1993 aController->SetStrategyHWM(aHighWaterMark);
1995 // Step 9. Set controller.[[pullAlgorithm]] to pullAlgorithm.
1996 // Step 10. Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
1997 aController->SetAlgorithms(*aAlgorithms);
1999 // Step 11. Set controller.[[autoAllocateChunkSize]] to autoAllocateChunkSize.
2000 aController->SetAutoAllocateChunkSize(aAutoAllocateChunkSize);
2002 // Step 12. Set controller.[[pendingPullIntos]] to a new empty list.
2003 aController->PendingPullIntos().clear();
2005 // Step 13. Set stream.[[controller]] to controller.
2006 aStream->SetController(*aController);
2008 // Step 14. Let startResult be the result of performing startAlgorithm.
2009 JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue());
2010 RefPtr<ReadableStreamController> controller = aController;
2011 aAlgorithms->StartCallback(aCx, *controller, &startResult, aRv);
2012 if (aRv.Failed()) {
2013 return;
2016 // Let startPromise be a promise resolved with startResult.
2017 RefPtr<Promise> startPromise =
2018 Promise::CreateInfallible(aStream->GetParentObject());
2019 startPromise->MaybeResolve(startResult);
2021 // Step 16+17
2022 startPromise->AddCallbacksWithCycleCollectedArgs(
2023 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
2024 ReadableByteStreamController* aController)
2025 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
2026 MOZ_ASSERT(aController);
2028 // Step 16.1
2029 aController->SetStarted(true);
2031 // Step 16.2
2032 aController->SetPulling(false);
2034 // Step 16.3
2035 aController->SetPullAgain(false);
2037 // Step 16.4:
2038 ReadableByteStreamControllerCallPullIfNeeded(
2039 aCx, MOZ_KnownLive(aController), aRv);
2041 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
2042 ReadableByteStreamController* aController) {
2043 // Step 17.1
2044 ReadableByteStreamControllerError(aController, aValue, aRv);
2046 RefPtr(aController));
2049 // https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller-from-underlying-source
2050 void SetUpReadableByteStreamControllerFromUnderlyingSource(
2051 JSContext* aCx, ReadableStream* aStream,
2052 JS::Handle<JSObject*> aUnderlyingSource,
2053 UnderlyingSource& aUnderlyingSourceDict, double aHighWaterMark,
2054 ErrorResult& aRv) {
2055 // Step 1. Let controller be a new ReadableByteStreamController.
2056 auto controller =
2057 MakeRefPtr<ReadableByteStreamController>(aStream->GetParentObject());
2059 // Step 2 - 7
2060 auto algorithms = MakeRefPtr<UnderlyingSourceAlgorithms>(
2061 aStream->GetParentObject(), aUnderlyingSource, aUnderlyingSourceDict);
2063 // Step 8. Let autoAllocateChunkSize be
2064 // underlyingSourceDict["autoAllocateChunkSize"], if it exists, or undefined
2065 // otherwise.
2066 Maybe<uint64_t> autoAllocateChunkSize = mozilla::Nothing();
2067 if (aUnderlyingSourceDict.mAutoAllocateChunkSize.WasPassed()) {
2068 uint64_t value = aUnderlyingSourceDict.mAutoAllocateChunkSize.Value();
2069 // Step 9. If autoAllocateChunkSize is 0, then throw a TypeError
2070 // exception.
2071 if (value == 0) {
2072 aRv.ThrowTypeError("autoAllocateChunkSize can not be zero.");
2073 return;
2075 autoAllocateChunkSize = mozilla::Some(value);
2078 // Step 10. Perform ? SetUpReadableByteStreamController(stream, controller,
2079 // startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark,
2080 // autoAllocateChunkSize).
2081 SetUpReadableByteStreamController(aCx, aStream, controller, algorithms,
2082 aHighWaterMark, autoAllocateChunkSize, aRv);
2085 } // namespace streams_abstract
2087 } // namespace mozilla::dom