Bug 1806664 - Use python toolchain tasks to build firefox r=glandium,taskgraph-review...
[gecko.git] / dom / streams / ReadableByteStreamController.cpp
blobf414afc63001961a7c9ec91856f8267d5754cc93
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 NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableByteStreamController)
47 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ReadableByteStreamController,
48 ReadableStreamController)
49 NS_IMPL_CYCLE_COLLECTION_UNLINK(mByobRequest)
50 tmp->ClearPendingPullIntos();
51 tmp->ClearQueue();
52 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
53 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ReadableByteStreamController,
56 ReadableStreamController)
57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mByobRequest)
58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
60 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ReadableByteStreamController,
61 ReadableStreamController)
62 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
63 // Trace the associated queue + list
64 for (const auto& queueEntry : tmp->mQueue) {
65 aCallbacks.Trace(&queueEntry->mBuffer, "mQueue.mBuffer", aClosure);
67 for (const auto& pullInto : tmp->mPendingPullIntos) {
68 aCallbacks.Trace(&pullInto->mBuffer, "mPendingPullIntos.mBuffer", aClosure);
70 NS_IMPL_CYCLE_COLLECTION_TRACE_END
72 NS_IMPL_ADDREF_INHERITED(ReadableByteStreamController, ReadableStreamController)
73 NS_IMPL_RELEASE_INHERITED(ReadableByteStreamController,
74 ReadableStreamController)
75 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableByteStreamController)
76 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
77 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamController)
79 ReadableByteStreamController::ReadableByteStreamController(
80 nsIGlobalObject* aGlobal)
81 : ReadableStreamController(aGlobal) {
82 mozilla::HoldJSObjects(this);
85 ReadableByteStreamController::~ReadableByteStreamController() {
86 ClearPendingPullIntos();
87 ClearQueue();
88 mozilla::DropJSObjects(this);
91 void ReadableByteStreamController::ClearQueue() {
92 // Since the pull intos are traced only by the owning
93 // ReadableByteStreamController, when clearning the list we also clear JS
94 // references to avoid dangling JS references.
95 for (auto* queueEntry : mQueue) {
96 queueEntry->ClearBuffer();
98 mQueue.clear();
101 void ReadableByteStreamController::ClearPendingPullIntos() {
102 // Since the pull intos are traced only by the owning
103 // ReadableByteStreamController, when clearning the list we also clear JS
104 // references to avoid dangling JS references.
105 for (auto* pullInto : mPendingPullIntos) {
106 pullInto->ClearBuffer();
108 mPendingPullIntos.clear();
111 namespace streams_abstract {
112 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollergetbyobrequest
113 already_AddRefed<ReadableStreamBYOBRequest>
114 ReadableByteStreamControllerGetBYOBRequest(
115 JSContext* aCx, ReadableByteStreamController* aController,
116 ErrorResult& aRv) {
117 // Step 1.
118 if (!aController->GetByobRequest() &&
119 !aController->PendingPullIntos().isEmpty()) {
120 // Step 1.1:
121 PullIntoDescriptor* firstDescriptor =
122 aController->PendingPullIntos().getFirst();
124 // Step 1.2:
125 aRv.MightThrowJSException();
126 JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
127 JS::Rooted<JSObject*> view(
128 aCx, JS_NewUint8ArrayWithBuffer(
129 aCx, buffer,
130 firstDescriptor->ByteOffset() + firstDescriptor->BytesFilled(),
131 int64_t(firstDescriptor->ByteLength() -
132 firstDescriptor->BytesFilled())));
133 if (!view) {
134 aRv.StealExceptionFromJSContext(aCx);
135 return nullptr;
138 // Step 1.3:
139 RefPtr<ReadableStreamBYOBRequest> byobRequest =
140 new ReadableStreamBYOBRequest(aController->GetParentObject());
142 // Step 1.4:
143 byobRequest->SetController(aController);
145 // Step 1.5:
146 byobRequest->SetView(view);
148 // Step 1.6:
149 aController->SetByobRequest(byobRequest);
152 // Step 2.
153 RefPtr<ReadableStreamBYOBRequest> request(aController->GetByobRequest());
154 return request.forget();
156 } // namespace streams_abstract
158 already_AddRefed<ReadableStreamBYOBRequest>
159 ReadableByteStreamController::GetByobRequest(JSContext* aCx, ErrorResult& aRv) {
160 return ReadableByteStreamControllerGetBYOBRequest(aCx, this, aRv);
163 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-get-desired-size
164 Nullable<double> ReadableByteStreamControllerGetDesiredSize(
165 const ReadableByteStreamController* aController) {
166 // Step 1.
167 ReadableStream::ReaderState state = aController->Stream()->State();
169 // Step 2.
170 if (state == ReadableStream::ReaderState::Errored) {
171 return nullptr;
174 // Step 3.
175 if (state == ReadableStream::ReaderState::Closed) {
176 return 0.0;
179 // Step 4.
180 return aController->StrategyHWM() - aController->QueueTotalSize();
183 Nullable<double> ReadableByteStreamController::GetDesiredSize() const {
184 // Step 1.
185 return ReadableByteStreamControllerGetDesiredSize(this);
188 JSObject* ReadableByteStreamController::WrapObject(
189 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
190 return ReadableByteStreamController_Binding::Wrap(aCx, this, aGivenProto);
193 namespace streams_abstract {
195 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-invalidate-byob-request
196 static void ReadableByteStreamControllerInvalidateBYOBRequest(
197 ReadableByteStreamController* aController) {
198 // Step 1.
199 if (!aController->GetByobRequest()) {
200 return;
203 // Step 2.
204 aController->GetByobRequest()->SetController(nullptr);
206 // Step 3.
207 aController->GetByobRequest()->SetView(nullptr);
209 // Step 4.
210 aController->SetByobRequest(nullptr);
213 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-pending-pull-intos
214 void ReadableByteStreamControllerClearPendingPullIntos(
215 ReadableByteStreamController* aController) {
216 // Step 1.
217 ReadableByteStreamControllerInvalidateBYOBRequest(aController);
219 // Step 2.
220 aController->ClearPendingPullIntos();
223 // https://streams.spec.whatwg.org/#reset-queue
224 void ResetQueue(ReadableByteStreamController* aContainer) {
225 // Step 1. Implied by type.
226 // Step 2.
227 aContainer->ClearQueue();
229 // Step 3.
230 aContainer->SetQueueTotalSize(0);
233 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-algorithms
234 void ReadableByteStreamControllerClearAlgorithms(
235 ReadableByteStreamController* aController) {
236 // Step 1. Set controller.[[pullAlgorithm]] to undefined.
237 // Step 2. Set controller.[[cancelAlgorithm]] to undefined.
238 aController->ClearAlgorithms();
241 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-error
242 void ReadableByteStreamControllerError(
243 ReadableByteStreamController* aController, JS::Handle<JS::Value> aValue,
244 ErrorResult& aRv) {
245 // Step 1. Let stream be controller.[[stream]].
246 ReadableStream* stream = aController->Stream();
248 // Step 2. If stream.[[state]] is not "readable", return.
249 if (stream->State() != ReadableStream::ReaderState::Readable) {
250 return;
253 // Step 3. Perform
254 // !ReadableByteStreamControllerClearPendingPullIntos(controller).
255 ReadableByteStreamControllerClearPendingPullIntos(aController);
257 // Step 4. Perform !ResetQueue(controller).
258 ResetQueue(aController);
260 // Step 5. Perform !ReadableByteStreamControllerClearAlgorithms(controller).
261 ReadableByteStreamControllerClearAlgorithms(aController);
263 // Step 6. Perform !ReadableStreamError(stream, e).
264 AutoJSAPI jsapi;
265 if (!jsapi.Init(aController->GetParentObject())) {
266 return;
268 ReadableStreamError(jsapi.cx(), stream, aValue, aRv);
271 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-close
272 void ReadableByteStreamControllerClose(
273 JSContext* aCx, ReadableByteStreamController* aController,
274 ErrorResult& aRv) {
275 // Step 1.
276 RefPtr<ReadableStream> stream = aController->Stream();
278 // Step 2.
279 if (aController->CloseRequested() ||
280 stream->State() != ReadableStream::ReaderState::Readable) {
281 return;
284 // Step 3.
285 if (aController->QueueTotalSize() > 0) {
286 // Step 3.1
287 aController->SetCloseRequested(true);
288 // Step 3.2
289 return;
292 // Step 4.
293 if (!aController->PendingPullIntos().isEmpty()) {
294 // Step 4.1
295 PullIntoDescriptor* firstPendingPullInto =
296 aController->PendingPullIntos().getFirst();
297 // Step 4.2
298 if (firstPendingPullInto->BytesFilled() > 0) {
299 // Step 4.2.1
300 ErrorResult rv;
301 rv.ThrowTypeError("Leftover Bytes");
303 JS::Rooted<JS::Value> exception(aCx);
304 MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), &exception));
306 // Step 4.2.2
307 ReadableByteStreamControllerError(aController, exception, aRv);
308 if (aRv.Failed()) {
309 return;
312 aRv.MightThrowJSException();
313 aRv.ThrowJSException(aCx, exception);
314 return;
318 // Step 5.
319 ReadableByteStreamControllerClearAlgorithms(aController);
321 // Step 6.
322 ReadableStreamClose(aCx, stream, aRv);
325 } // namespace streams_abstract
327 // https://streams.spec.whatwg.org/#rbs-controller-close
328 void ReadableByteStreamController::Close(JSContext* aCx, ErrorResult& aRv) {
329 // Step 1.
330 if (mCloseRequested) {
331 aRv.ThrowTypeError("Close already requested");
332 return;
335 // Step 2.
336 if (Stream()->State() != ReadableStream::ReaderState::Readable) {
337 aRv.ThrowTypeError("Closing un-readable stream controller");
338 return;
341 // Step 3.
342 ReadableByteStreamControllerClose(aCx, this, aRv);
345 namespace streams_abstract {
347 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue-chunk-to-queue
348 void ReadableByteStreamControllerEnqueueChunkToQueue(
349 ReadableByteStreamController* aController,
350 JS::Handle<JSObject*> aTransferredBuffer, size_t aByteOffset,
351 size_t aByteLength) {
352 // Step 1.
353 RefPtr<ReadableByteStreamQueueEntry> queueEntry =
354 new ReadableByteStreamQueueEntry(aTransferredBuffer, aByteOffset,
355 aByteLength);
356 aController->Queue().insertBack(queueEntry);
358 // Step 2.
359 aController->AddToQueueTotalSize(double(aByteLength));
362 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueueclonedchunktoqueue
363 void ReadableByteStreamControllerEnqueueClonedChunkToQueue(
364 JSContext* aCx, ReadableByteStreamController* aController,
365 JS::Handle<JSObject*> aBuffer, size_t aByteOffset, size_t aByteLength,
366 ErrorResult& aRv) {
367 // Step 1. Let cloneResult be CloneArrayBuffer(buffer, byteOffset, byteLength,
368 // %ArrayBuffer%).
369 aRv.MightThrowJSException();
370 JS::Rooted<JSObject*> cloneResult(
371 aCx, JS::ArrayBufferClone(aCx, aBuffer, aByteOffset, aByteLength));
373 // Step 2. If cloneResult is an abrupt completion,
374 if (!cloneResult) {
375 JS::Rooted<JS::Value> exception(aCx);
376 if (!JS_GetPendingException(aCx, &exception)) {
377 // Uncatchable exception; we should mark aRv and return.
378 aRv.StealExceptionFromJSContext(aCx);
379 return;
381 JS_ClearPendingException(aCx);
383 // Step 2.1. Perform ! ReadableByteStreamControllerError(controller,
384 // cloneResult.[[Value]]).
385 ReadableByteStreamControllerError(aController, exception, aRv);
386 if (aRv.Failed()) {
387 return;
390 // Step 2.2. Return cloneResult.
391 aRv.ThrowJSException(aCx, exception);
392 return;
395 // Step 3. Perform !
396 // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
397 // cloneResult.[[Value]], 0, byteLength).
398 ReadableByteStreamControllerEnqueueChunkToQueue(aController, cloneResult, 0,
399 aByteLength);
402 already_AddRefed<PullIntoDescriptor>
403 ReadableByteStreamControllerShiftPendingPullInto(
404 ReadableByteStreamController* aController);
406 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueuedetachedpullintotoqueue
407 void ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
408 JSContext* aCx, ReadableByteStreamController* aController,
409 PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv) {
410 // Step 1. Assert: pullIntoDescriptor’s reader type is "none".
411 MOZ_ASSERT(aPullIntoDescriptor->GetReaderType() == ReaderType::None);
413 // Step 2. If pullIntoDescriptor’s bytes filled > 0,
414 // perform ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
415 // pullIntoDescriptor’s buffer, pullIntoDescriptor’s byte offset,
416 // pullIntoDescriptor’s bytes filled).
417 if (aPullIntoDescriptor->BytesFilled() > 0) {
418 JS::Rooted<JSObject*> buffer(aCx, aPullIntoDescriptor->Buffer());
419 ReadableByteStreamControllerEnqueueClonedChunkToQueue(
420 aCx, aController, buffer, aPullIntoDescriptor->ByteOffset(),
421 aPullIntoDescriptor->BytesFilled(), aRv);
422 if (aRv.Failed()) {
423 return;
427 // Step 3. Perform !
428 // ReadableByteStreamControllerShiftPendingPullInto(controller).
429 RefPtr<PullIntoDescriptor> discarded =
430 ReadableByteStreamControllerShiftPendingPullInto(aController);
431 (void)discarded;
434 // https://streams.spec.whatwg.org/#readable-stream-get-num-read-into-requests
435 static size_t ReadableStreamGetNumReadIntoRequests(ReadableStream* aStream) {
436 // Step 1.
437 MOZ_ASSERT(ReadableStreamHasBYOBReader(aStream));
439 // Step 2.
440 return aStream->GetReader()->AsBYOB()->ReadIntoRequests().length();
443 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-should-call-pull
444 bool ReadableByteStreamControllerShouldCallPull(
445 ReadableByteStreamController* aController) {
446 // Step 1. Let stream be controller.[[stream]].
447 ReadableStream* stream = aController->Stream();
449 // Step 2. If stream.[[state]] is not "readable", return false.
450 if (stream->State() != ReadableStream::ReaderState::Readable) {
451 return false;
454 // Step 3. If controller.[[closeRequested]] is true, return false.
455 if (aController->CloseRequested()) {
456 return false;
459 // Step 4. If controller.[[started]] is false, return false.
460 if (!aController->Started()) {
461 return false;
464 // Step 5. If ! ReadableStreamHasDefaultReader(stream) is true
465 // and ! ReadableStreamGetNumReadRequests(stream) > 0, return true.
466 if (ReadableStreamHasDefaultReader(stream) &&
467 ReadableStreamGetNumReadRequests(stream) > 0) {
468 return true;
471 // Step 6. If ! ReadableStreamHasBYOBReader(stream) is true
472 // and ! ReadableStreamGetNumReadIntoRequests(stream) > 0, return true.
473 if (ReadableStreamHasBYOBReader(stream) &&
474 ReadableStreamGetNumReadIntoRequests(stream) > 0) {
475 return true;
478 // Step 7. Let desiredSize be
479 // ! ReadableByteStreamControllerGetDesiredSize(controller).
480 Nullable<double> desiredSize =
481 ReadableByteStreamControllerGetDesiredSize(aController);
483 // Step 8. Assert: desiredSize is not null.
484 MOZ_ASSERT(!desiredSize.IsNull());
486 // Step 9. If desiredSize > 0, return true.
487 // Step 10. Return false.
488 return desiredSize.Value() > 0;
491 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-call-pull-if-needed
492 void ReadableByteStreamControllerCallPullIfNeeded(
493 JSContext* aCx, ReadableByteStreamController* aController,
494 ErrorResult& aRv) {
495 // Step 1.
496 bool shouldPull = ReadableByteStreamControllerShouldCallPull(aController);
498 // Step 2.
499 if (!shouldPull) {
500 return;
503 // Step 3.
504 if (aController->Pulling()) {
505 aController->SetPullAgain(true);
506 return;
509 // Step 4.
510 MOZ_ASSERT(!aController->PullAgain());
512 // Step 5.
513 aController->SetPulling(true);
515 // Step 6.
516 RefPtr<ReadableStreamController> controller(aController);
517 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms =
518 aController->GetAlgorithms();
519 RefPtr<Promise> pullPromise = algorithms->PullCallback(aCx, *controller, aRv);
520 if (aRv.Failed()) {
521 return;
524 // Steps 7+8
525 pullPromise->AddCallbacksWithCycleCollectedArgs(
526 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
527 ReadableByteStreamController* aController)
528 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
529 // Step 7.1
530 aController->SetPulling(false);
531 // Step 7.2
532 if (aController->PullAgain()) {
533 // Step 7.2.1
534 aController->SetPullAgain(false);
536 // Step 7.2.2
537 ReadableByteStreamControllerCallPullIfNeeded(
538 aCx, MOZ_KnownLive(aController), aRv);
541 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
542 ReadableByteStreamController* aController) {
543 // Step 8.1
544 ReadableByteStreamControllerError(aController, aValue, aRv);
546 RefPtr(aController));
549 bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
550 JSContext* aCx, ReadableByteStreamController* aController,
551 PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv);
553 JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
554 JSContext* aCx, PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv);
556 // https://streams.spec.whatwg.org/#readable-stream-fulfill-read-into-request
557 MOZ_CAN_RUN_SCRIPT
558 void ReadableStreamFulfillReadIntoRequest(JSContext* aCx,
559 ReadableStream* aStream,
560 JS::Handle<JS::Value> aChunk,
561 bool done, ErrorResult& aRv) {
562 // Step 1. Assert: !ReadableStreamHasBYOBReader(stream) is true.
563 MOZ_ASSERT(ReadableStreamHasBYOBReader(aStream));
565 // Step 2. Let reader be stream.[[reader]].
566 ReadableStreamBYOBReader* reader = aStream->GetReader()->AsBYOB();
568 // Step 3. Assert: reader.[[readIntoRequests]] is not empty.
569 MOZ_ASSERT(!reader->ReadIntoRequests().isEmpty());
571 // Step 4. Let readIntoRequest be reader.[[readIntoRequests]][0].
572 // Step 5. Remove readIntoRequest from reader.[[readIntoRequests]].
573 RefPtr<ReadIntoRequest> readIntoRequest =
574 reader->ReadIntoRequests().popFirst();
576 // Step 6. If done is true, perform readIntoRequest’s close steps, given
577 // chunk.
578 if (done) {
579 readIntoRequest->CloseSteps(aCx, aChunk, aRv);
580 return;
583 // Step 7. Otherwise, perform readIntoRequest’s chunk steps, given chunk.
584 readIntoRequest->ChunkSteps(aCx, aChunk, aRv);
587 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-commit-pull-into-descriptor
588 MOZ_CAN_RUN_SCRIPT
589 void ReadableByteStreamControllerCommitPullIntoDescriptor(
590 JSContext* aCx, ReadableStream* aStream,
591 PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv) {
592 // Step 1. Assert: stream.[[state]] is not "errored".
593 MOZ_ASSERT(aStream->State() != ReadableStream::ReaderState::Errored);
595 // Step 2. Assert: pullIntoDescriptor.reader type is not "none".
596 MOZ_ASSERT(pullIntoDescriptor->GetReaderType() != ReaderType::None);
598 // Step 3. Let done be false.
599 bool done = false;
601 // Step 4. If stream.[[state]] is "closed",
602 if (aStream->State() == ReadableStream::ReaderState::Closed) {
603 // Step 4.1. Assert: pullIntoDescriptor’s bytes filled is 0.
604 MOZ_ASSERT(pullIntoDescriptor->BytesFilled() == 0);
606 // Step 4.2. Set done to true.
607 done = true;
610 // Step 5. Let filledView be !
611 // ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
612 JS::Rooted<JSObject*> filledView(
613 aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
614 aCx, pullIntoDescriptor, aRv));
615 if (aRv.Failed()) {
616 return;
618 JS::Rooted<JS::Value> filledViewValue(aCx, JS::ObjectValue(*filledView));
620 // Step 6. If pullIntoDescriptor’s reader type is "default",
621 if (pullIntoDescriptor->GetReaderType() == ReaderType::Default) {
622 // Step 6.1. Perform !ReadableStreamFulfillReadRequest(stream, filledView,
623 // done).
624 ReadableStreamFulfillReadRequest(aCx, aStream, filledViewValue, done, aRv);
625 return;
628 // Step 7.1. Assert: pullIntoDescriptor’s reader type is "byob".
629 MOZ_ASSERT(pullIntoDescriptor->GetReaderType() == ReaderType::BYOB);
631 // Step 7.2 Perform !ReadableStreamFulfillReadIntoRequest(stream, filledView,
632 // done).
633 ReadableStreamFulfillReadIntoRequest(aCx, aStream, filledViewValue, done,
634 aRv);
637 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-process-pull-into-descriptors-using-queue
638 MOZ_CAN_RUN_SCRIPT
639 void ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
640 JSContext* aCx, ReadableByteStreamController* aController,
641 ErrorResult& aRv) {
642 // Step 1. Assert: controller.[[closeRequested]] is false.
643 MOZ_ASSERT(!aController->CloseRequested());
645 // Step 2. While controller.[[pendingPullIntos]] is not empty,
646 while (!aController->PendingPullIntos().isEmpty()) {
647 // Step 2.1 If controller.[[queueTotalSize]] is 0, return.
648 if (aController->QueueTotalSize() == 0) {
649 return;
652 // Step 2.2. Let pullIntoDescriptor be controller.[[pendingPullIntos]][0].
653 RefPtr<PullIntoDescriptor> pullIntoDescriptor =
654 aController->PendingPullIntos().getFirst();
656 // Step 2.3. If
657 // !ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
658 // pullIntoDescriptor) is true,
659 bool ready = ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
660 aCx, aController, pullIntoDescriptor, aRv);
661 if (aRv.Failed()) {
662 return;
665 if (ready) {
666 // Step 2.3.1. Perform
667 // !ReadableByteStreamControllerShiftPendingPullInto(controller).
668 RefPtr<PullIntoDescriptor> discardedPullIntoDescriptor =
669 ReadableByteStreamControllerShiftPendingPullInto(aController);
671 // Step 2.3.2. Perform
672 // !ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
673 // pullIntoDescriptor).
674 RefPtr<ReadableStream> stream(aController->Stream());
675 ReadableByteStreamControllerCommitPullIntoDescriptor(
676 aCx, stream, pullIntoDescriptor, aRv);
677 if (aRv.Failed()) {
678 return;
684 MOZ_CAN_RUN_SCRIPT
685 void ReadableByteStreamControllerHandleQueueDrain(
686 JSContext* aCx, ReadableByteStreamController* aController,
687 ErrorResult& aRv);
689 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerfillreadrequestfromqueue
690 MOZ_CAN_RUN_SCRIPT void ReadableByteStreamControllerFillReadRequestFromQueue(
691 JSContext* aCx, ReadableByteStreamController* aController,
692 ReadRequest* aReadRequest, ErrorResult& aRv) {
693 // Step 1. Assert: controller.[[queueTotalSize]] > 0.
694 MOZ_ASSERT(aController->QueueTotalSize() > 0);
695 // Also assert that the queue has a non-zero length;
696 MOZ_ASSERT(aController->Queue().length() > 0);
698 // Step 2. Let entry be controller.[[queue]][0].
699 // Step 3. Remove entry from controller.[[queue]].
700 RefPtr<ReadableByteStreamQueueEntry> entry = aController->Queue().popFirst();
702 // Assert that we actually got an entry.
703 MOZ_ASSERT(entry);
705 // Step 4. Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]]
706 // − entry’s byte length.
707 aController->SetQueueTotalSize(aController->QueueTotalSize() -
708 double(entry->ByteLength()));
710 // Step 5. Perform ! ReadableByteStreamControllerHandleQueueDrain(controller).
711 ReadableByteStreamControllerHandleQueueDrain(aCx, aController, aRv);
712 if (aRv.Failed()) {
713 return;
716 // Step 6. Let view be ! Construct(%Uint8Array%, « entry’s buffer, entry’s
717 // byte offset, entry’s byte length »).
718 aRv.MightThrowJSException();
719 JS::Rooted<JSObject*> buffer(aCx, entry->Buffer());
720 JS::Rooted<JSObject*> view(
721 aCx, JS_NewUint8ArrayWithBuffer(aCx, buffer, entry->ByteOffset(),
722 int64_t(entry->ByteLength())));
723 if (!view) {
724 aRv.StealExceptionFromJSContext(aCx);
725 return;
728 // Step 7. Perform readRequest’s chunk steps, given view.
729 JS::Rooted<JS::Value> viewValue(aCx, JS::ObjectValue(*view));
730 aReadRequest->ChunkSteps(aCx, viewValue, aRv);
733 MOZ_CAN_RUN_SCRIPT void
734 ReadableByteStreamControllerProcessReadRequestsUsingQueue(
735 JSContext* aCx, ReadableByteStreamController* aController,
736 ErrorResult& aRv) {
737 // Step 1. Let reader be controller.[[stream]].[[reader]].
738 // Step 2. Assert: reader implements ReadableStreamDefaultReader.
739 RefPtr<ReadableStreamDefaultReader> reader =
740 aController->Stream()->GetDefaultReader();
742 // Step 3. While reader.[[readRequests]] is not empty,
743 while (!reader->ReadRequests().isEmpty()) {
744 // Step 3.1. If controller.[[queueTotalSize]] is 0, return.
745 if (aController->QueueTotalSize() == 0) {
746 return;
749 // Step 3.2. Let readRequest be reader.[[readRequests]][0].
750 // Step 3.3. Remove readRequest from reader.[[readRequests]].
751 RefPtr<ReadRequest> readRequest = reader->ReadRequests().popFirst();
753 // Step 3.4. Perform !
754 // ReadableByteStreamControllerFillReadRequestFromQueue(controller,
755 // readRequest).
756 ReadableByteStreamControllerFillReadRequestFromQueue(aCx, aController,
757 readRequest, aRv);
758 if (aRv.Failed()) {
759 return;
764 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue
765 void ReadableByteStreamControllerEnqueue(
766 JSContext* aCx, ReadableByteStreamController* aController,
767 JS::Handle<JSObject*> aChunk, ErrorResult& aRv) {
768 aRv.MightThrowJSException();
770 // Step 1.
771 RefPtr<ReadableStream> stream = aController->Stream();
773 // Step 2.
774 if (aController->CloseRequested() ||
775 stream->State() != ReadableStream::ReaderState::Readable) {
776 return;
779 // Step 3.
780 bool isShared;
781 JS::Rooted<JSObject*> buffer(
782 aCx, JS_GetArrayBufferViewBuffer(aCx, aChunk, &isShared));
783 if (!buffer) {
784 aRv.StealExceptionFromJSContext(aCx);
785 return;
788 // Step 4.
789 size_t byteOffset = JS_GetArrayBufferViewByteOffset(aChunk);
791 // Step 5.
792 size_t byteLength = JS_GetArrayBufferViewByteLength(aChunk);
794 // Step 6.
795 if (JS::IsDetachedArrayBufferObject(buffer)) {
796 aRv.ThrowTypeError("Detatched Array Buffer");
797 return;
800 // Step 7.
801 JS::Rooted<JSObject*> transferredBuffer(aCx,
802 TransferArrayBuffer(aCx, buffer));
803 if (!transferredBuffer) {
804 aRv.StealExceptionFromJSContext(aCx);
805 return;
808 // Step 8.
809 if (!aController->PendingPullIntos().isEmpty()) {
810 // Step 8.1
811 RefPtr<PullIntoDescriptor> firstPendingPullInto =
812 aController->PendingPullIntos().getFirst();
814 // Step 8.2
815 JS::Rooted<JSObject*> pendingBuffer(aCx, firstPendingPullInto->Buffer());
816 if (JS::IsDetachedArrayBufferObject(pendingBuffer)) {
817 aRv.ThrowTypeError("Pending PullInto has detached buffer");
818 return;
821 // Step 8.3. Perform !
822 // ReadableByteStreamControllerInvalidateBYOBRequest(controller).
823 ReadableByteStreamControllerInvalidateBYOBRequest(aController);
825 // Step 8.4. Set firstPendingPullInto’s buffer to !
826 // TransferArrayBuffer(firstPendingPullInto’s buffer).
827 pendingBuffer = TransferArrayBuffer(aCx, pendingBuffer);
828 if (!pendingBuffer) {
829 aRv.StealExceptionFromJSContext(aCx);
830 return;
832 firstPendingPullInto->SetBuffer(pendingBuffer);
834 // Step 8.5. If firstPendingPullInto’s reader type is "none", perform ?
835 // ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller,
836 // firstPendingPullInto).
837 if (firstPendingPullInto->GetReaderType() == ReaderType::None) {
838 ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
839 aCx, aController, firstPendingPullInto, aRv);
840 if (aRv.Failed()) {
841 return;
846 // Step 9. If ! ReadableStreamHasDefaultReader(stream) is true,
847 if (ReadableStreamHasDefaultReader(stream)) {
848 // Step 9.1. Perform !
849 // ReadableByteStreamControllerProcessReadRequestsUsingQueue(controller).
850 ReadableByteStreamControllerProcessReadRequestsUsingQueue(aCx, aController,
851 aRv);
852 if (aRv.Failed()) {
853 return;
856 // Step 9.2. If ! ReadableStreamGetNumReadRequests(stream) is 0,
857 if (ReadableStreamGetNumReadRequests(stream) == 0) {
858 // Step 9.2.1 Assert: controller.[[pendingPullIntos]] is empty.
859 MOZ_ASSERT(aController->PendingPullIntos().isEmpty());
861 // Step 9.2.2. Perform !
862 // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
863 // transferredBuffer, byteOffset, byteLength).
864 ReadableByteStreamControllerEnqueueChunkToQueue(
865 aController, transferredBuffer, byteOffset, byteLength);
867 // Step 9.3. Otherwise,
868 } else {
869 // Step 9.3.1 Assert: controller.[[queue]] is empty.
870 MOZ_ASSERT(aController->Queue().isEmpty());
872 // Step 9.3.2. If controller.[[pendingPullIntos]] is not empty,
873 if (!aController->PendingPullIntos().isEmpty()) {
874 // Step 9.3.2.1. Assert: controller.[[pendingPullIntos]][0]'s reader
875 // type is "default".
876 MOZ_ASSERT(
877 aController->PendingPullIntos().getFirst()->GetReaderType() ==
878 ReaderType::Default);
880 // Step 9.3.2.2. Perform !
881 // ReadableByteStreamControllerShiftPendingPullInto(controller).
882 RefPtr<PullIntoDescriptor> pullIntoDescriptor =
883 ReadableByteStreamControllerShiftPendingPullInto(aController);
884 (void)pullIntoDescriptor;
887 // Step 9.3.3. Let transferredView be ! Construct(%Uint8Array%, «
888 // transferredBuffer, byteOffset, byteLength »).
889 JS::Rooted<JSObject*> transferredView(
890 aCx, JS_NewUint8ArrayWithBuffer(aCx, transferredBuffer, byteOffset,
891 int64_t(byteLength)));
892 if (!transferredView) {
893 aRv.StealExceptionFromJSContext(aCx);
894 return;
897 // Step 9.3.4. Perform ! ReadableStreamFulfillReadRequest(stream,
898 // transferredView, false).
899 JS::Rooted<JS::Value> transferredViewValue(
900 aCx, JS::ObjectValue(*transferredView));
901 ReadableStreamFulfillReadRequest(aCx, stream, transferredViewValue, false,
902 aRv);
903 if (aRv.Failed()) {
904 return;
908 // Step 10. Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true,
909 } else if (ReadableStreamHasBYOBReader(stream)) {
910 // Step 10.1. Perform !
911 // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
912 // transferredBuffer, byteOffset, byteLength).
913 ReadableByteStreamControllerEnqueueChunkToQueue(
914 aController, transferredBuffer, byteOffset, byteLength);
916 // Step 10.2 Perform !
917 // ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
918 ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
919 aCx, aController, aRv);
920 if (aRv.Failed()) {
921 return;
924 // Step 11. Otherwise,
925 } else {
926 // Step 11.1. Assert: ! IsReadableStreamLocked(stream) is false.
927 MOZ_ASSERT(!IsReadableStreamLocked(stream));
929 // Step 11.2. Perform !
930 // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
931 // transferredBuffer, byteOffset, byteLength).
932 ReadableByteStreamControllerEnqueueChunkToQueue(
933 aController, transferredBuffer, byteOffset, byteLength);
936 // Step 12. Perform !
937 // ReadableByteStreamControllerCallPullIfNeeded(controller).
938 ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
941 } // namespace streams_abstract
943 // https://streams.spec.whatwg.org/#rbs-controller-enqueue
944 void ReadableByteStreamController::Enqueue(JSContext* aCx,
945 const ArrayBufferView& aChunk,
946 ErrorResult& aRv) {
947 // Step 1.
948 JS::Rooted<JSObject*> chunk(aCx, aChunk.Obj());
949 if (JS_GetArrayBufferViewByteLength(chunk) == 0) {
950 aRv.ThrowTypeError("Zero Length View");
951 return;
954 // Step 2.
955 bool isShared;
956 JS::Rooted<JSObject*> viewedArrayBuffer(
957 aCx, JS_GetArrayBufferViewBuffer(aCx, chunk, &isShared));
958 if (!viewedArrayBuffer) {
959 aRv.StealExceptionFromJSContext(aCx);
960 return;
963 if (JS::GetArrayBufferByteLength(viewedArrayBuffer) == 0) {
964 aRv.ThrowTypeError("Zero Length Buffer");
965 return;
968 // Step 3.
969 if (CloseRequested()) {
970 aRv.ThrowTypeError("close requested");
971 return;
974 // Step 4.
975 if (Stream()->State() != ReadableStream::ReaderState::Readable) {
976 aRv.ThrowTypeError("Not Readable");
977 return;
980 // Step 5.
981 ReadableByteStreamControllerEnqueue(aCx, this, chunk, aRv);
984 // https://streams.spec.whatwg.org/#rbs-controller-error
985 void ReadableByteStreamController::Error(JSContext* aCx,
986 JS::Handle<JS::Value> aErrorValue,
987 ErrorResult& aRv) {
988 // Step 1.
989 ReadableByteStreamControllerError(this, aErrorValue, aRv);
992 // https://streams.spec.whatwg.org/#rbs-controller-private-cancel
993 already_AddRefed<Promise> ReadableByteStreamController::CancelSteps(
994 JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
995 // Step 1.
996 ReadableByteStreamControllerClearPendingPullIntos(this);
998 // Step 2.
999 ResetQueue(this);
1001 // Step 3.
1002 Optional<JS::Handle<JS::Value>> reason(aCx, aReason);
1003 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms = mAlgorithms;
1004 RefPtr<Promise> result = algorithms->CancelCallback(aCx, reason, aRv);
1005 if (NS_WARN_IF(aRv.Failed())) {
1006 return nullptr;
1008 // Step 4.
1009 ReadableByteStreamControllerClearAlgorithms(this);
1011 // Step 5.
1012 return result.forget();
1015 namespace streams_abstract {
1016 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-handle-queue-drain
1017 void ReadableByteStreamControllerHandleQueueDrain(
1018 JSContext* aCx, ReadableByteStreamController* aController,
1019 ErrorResult& aRv) {
1020 // Step 1.
1021 MOZ_ASSERT(aController->Stream()->State() ==
1022 ReadableStream::ReaderState::Readable);
1024 // Step 2.
1025 if (aController->QueueTotalSize() == 0 && aController->CloseRequested()) {
1026 // Step 2.1
1027 ReadableByteStreamControllerClearAlgorithms(aController);
1029 // Step 2.2
1030 RefPtr<ReadableStream> stream = aController->Stream();
1031 ReadableStreamClose(aCx, stream, aRv);
1032 return;
1035 // Step 3.1
1036 ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
1038 } // namespace streams_abstract
1040 // https://streams.spec.whatwg.org/#rbs-controller-private-pull
1041 void ReadableByteStreamController::PullSteps(JSContext* aCx,
1042 ReadRequest* aReadRequest,
1043 ErrorResult& aRv) {
1044 // Step 1.
1045 ReadableStream* stream = Stream();
1047 // Step 2.
1048 MOZ_ASSERT(ReadableStreamHasDefaultReader(stream));
1050 // Step 3.
1051 if (QueueTotalSize() > 0) {
1052 // Step 3.1. Assert: ! ReadableStreamGetNumReadRequests ( stream ) is 0.
1053 MOZ_ASSERT(ReadableStreamGetNumReadRequests(stream) == 0);
1055 // Step 3.2. Perform !
1056 // ReadableByteStreamControllerFillReadRequestFromQueue(this, readRequest).
1057 ReadableByteStreamControllerFillReadRequestFromQueue(aCx, this,
1058 aReadRequest, aRv);
1060 // Step 3.3. Return.
1061 return;
1064 // Step 4.
1065 Maybe<uint64_t> autoAllocateChunkSize = AutoAllocateChunkSize();
1067 // Step 5.
1068 if (autoAllocateChunkSize) {
1069 // Step 5.1
1070 aRv.MightThrowJSException();
1071 JS::Rooted<JSObject*> buffer(
1072 aCx, JS::NewArrayBuffer(aCx, *autoAllocateChunkSize));
1073 // Step 5.2
1074 if (!buffer) {
1075 // Step 5.2.1
1076 JS::Rooted<JS::Value> bufferError(aCx);
1077 if (!JS_GetPendingException(aCx, &bufferError)) {
1078 // Uncatchable exception; we should mark aRv and return.
1079 aRv.StealExceptionFromJSContext(aCx);
1080 return;
1083 // It's not expliclitly stated, but I assume the intention here is that
1084 // we perform a normal completion here.
1085 JS_ClearPendingException(aCx);
1087 aReadRequest->ErrorSteps(aCx, bufferError, aRv);
1089 // Step 5.2.2.
1090 return;
1093 // Step 5.3
1094 RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
1095 buffer, *autoAllocateChunkSize, 0, *autoAllocateChunkSize, 0, 1,
1096 PullIntoDescriptor::Constructor::Uint8, ReaderType::Default);
1098 // Step 5.4
1099 PendingPullIntos().insertBack(pullIntoDescriptor);
1102 // Step 6.
1103 ReadableStreamAddReadRequest(stream, aReadRequest);
1105 // Step 7.
1106 ReadableByteStreamControllerCallPullIfNeeded(aCx, this, aRv);
1109 // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontroller-releasesteps
1110 void ReadableByteStreamController::ReleaseSteps() {
1111 // Step 1. If this.[[pendingPullIntos]] is not empty,
1112 if (!PendingPullIntos().isEmpty()) {
1113 // Step 1.1. Let firstPendingPullInto be this.[[pendingPullIntos]][0].
1114 RefPtr<PullIntoDescriptor> firstPendingPullInto =
1115 PendingPullIntos().popFirst();
1117 // Step 1.2. Set firstPendingPullInto’s reader type to "none".
1118 firstPendingPullInto->SetReaderType(ReaderType::None);
1120 // Step 1.3. Set this.[[pendingPullIntos]] to the list «
1121 // firstPendingPullInto ».
1122 PendingPullIntos().clear();
1123 PendingPullIntos().insertBack(firstPendingPullInto);
1127 namespace streams_abstract {
1129 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-shift-pending-pull-into
1130 already_AddRefed<PullIntoDescriptor>
1131 ReadableByteStreamControllerShiftPendingPullInto(
1132 ReadableByteStreamController* aController) {
1133 // Step 1.
1134 MOZ_ASSERT(!aController->GetByobRequest());
1136 // Step 2 + 3
1137 RefPtr<PullIntoDescriptor> descriptor =
1138 aController->PendingPullIntos().popFirst();
1140 // Step 4.
1141 return descriptor.forget();
1144 JSObject* ConstructFromPullIntoConstructor(
1145 JSContext* aCx, PullIntoDescriptor::Constructor constructor,
1146 JS::Handle<JSObject*> buffer, size_t byteOffset, size_t length) {
1147 switch (constructor) {
1148 case PullIntoDescriptor::Constructor::DataView:
1149 return JS_NewDataView(aCx, buffer, byteOffset, length);
1150 break;
1152 #define CONSTRUCT_TYPED_ARRAY_TYPE(ExternalT, NativeT, Name) \
1153 case PullIntoDescriptor::Constructor::Name: \
1154 return JS_New##Name##ArrayWithBuffer(aCx, buffer, byteOffset, \
1155 int64_t(length)); \
1156 break;
1158 JS_FOR_EACH_TYPED_ARRAY(CONSTRUCT_TYPED_ARRAY_TYPE)
1160 #undef CONSTRUCT_TYPED_ARRAY_TYPE
1162 default:
1163 MOZ_ASSERT_UNREACHABLE("Unknown PullIntoDescriptor::Constructor");
1164 return nullptr;
1168 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-convert-pull-into-descriptor
1169 JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
1170 JSContext* aCx, PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv) {
1171 // Step 1. Let bytesFilled be pullIntoDescriptor’s bytes filled.
1172 uint64_t bytesFilled = pullIntoDescriptor->BytesFilled();
1174 // Step 2. Let elementSize be pullIntoDescriptor’s element size.
1175 uint64_t elementSize = pullIntoDescriptor->ElementSize();
1177 // Step 3. Assert: bytesFilled ≤ pullIntoDescriptor’s byte length.
1178 MOZ_ASSERT(bytesFilled <= pullIntoDescriptor->ByteLength());
1180 // Step 4. Assert: bytesFilled mod elementSize is 0.
1181 MOZ_ASSERT(bytesFilled % elementSize == 0);
1183 // Step 5. Let buffer be ! TransferArrayBuffer(pullIntoDescriptor’s buffer).
1184 aRv.MightThrowJSException();
1185 JS::Rooted<JSObject*> srcBuffer(aCx, pullIntoDescriptor->Buffer());
1186 JS::Rooted<JSObject*> buffer(aCx, TransferArrayBuffer(aCx, srcBuffer));
1187 if (!buffer) {
1188 aRv.StealExceptionFromJSContext(aCx);
1189 return nullptr;
1192 // Step 6. Return ! Construct(pullIntoDescriptor’s view constructor,
1193 // « buffer, pullIntoDescriptor’s byte offset, bytesFilled ÷ elementSize »).
1194 JS::Rooted<JSObject*> res(
1195 aCx, ConstructFromPullIntoConstructor(
1196 aCx, pullIntoDescriptor->ViewConstructor(), buffer,
1197 pullIntoDescriptor->ByteOffset(), bytesFilled / elementSize));
1198 if (!res) {
1199 aRv.StealExceptionFromJSContext(aCx);
1200 return nullptr;
1202 return res;
1205 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-closed-state
1206 MOZ_CAN_RUN_SCRIPT
1207 static void ReadableByteStreamControllerRespondInClosedState(
1208 JSContext* aCx, ReadableByteStreamController* aController,
1209 RefPtr<PullIntoDescriptor>& aFirstDescriptor, ErrorResult& aRv) {
1210 // Step 1. Assert: firstDescriptor ’s bytes filled is 0.
1211 MOZ_ASSERT(aFirstDescriptor->BytesFilled() == 0);
1213 // Step 2. If firstDescriptor’s reader type is "none",
1214 // perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
1215 if (aFirstDescriptor->GetReaderType() == ReaderType::None) {
1216 RefPtr<PullIntoDescriptor> discarded =
1217 ReadableByteStreamControllerShiftPendingPullInto(aController);
1218 (void)discarded;
1221 // Step 3. Let stream be controller.[[stream]].
1222 RefPtr<ReadableStream> stream = aController->Stream();
1224 // Step 4. If ! ReadableStreamHasBYOBReader(stream) is true,
1225 if (ReadableStreamHasBYOBReader(stream)) {
1226 // Step 4.1. While ! ReadableStreamGetNumReadIntoRequests(stream) > 0,
1227 while (ReadableStreamGetNumReadIntoRequests(stream) > 0) {
1228 // Step 4.1.1. Let pullIntoDescriptor be !
1229 // ReadableByteStreamControllerShiftPendingPullInto(controller).
1230 RefPtr<PullIntoDescriptor> pullIntoDescriptor =
1231 ReadableByteStreamControllerShiftPendingPullInto(aController);
1233 // Step 4.1.2. Perform !
1234 // ReadableByteStreamControllerCommitPullIntoDescriptor(stream,
1235 // pullIntoDescriptor).
1236 ReadableByteStreamControllerCommitPullIntoDescriptor(
1237 aCx, stream, pullIntoDescriptor, aRv);
1242 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-head-pull-into-descriptor
1243 void ReadableByteStreamControllerFillHeadPullIntoDescriptor(
1244 ReadableByteStreamController* aController, size_t aSize,
1245 PullIntoDescriptor* aPullIntoDescriptor) {
1246 // Step 1. Assert: either controller.[[pendingPullIntos]] is empty, or
1247 // controller.[[pendingPullIntos]][0] is pullIntoDescriptor.
1248 MOZ_ASSERT(aController->PendingPullIntos().isEmpty() ||
1249 aController->PendingPullIntos().getFirst() == aPullIntoDescriptor);
1251 // Step 2. Assert: controller.[[byobRequest]] is null.
1252 MOZ_ASSERT(!aController->GetByobRequest());
1254 // Step 3. Set pullIntoDescriptor’s bytes filled to bytes filled + size.
1255 aPullIntoDescriptor->SetBytesFilled(aPullIntoDescriptor->BytesFilled() +
1256 aSize);
1259 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-readable-state
1260 MOZ_CAN_RUN_SCRIPT
1261 static void ReadableByteStreamControllerRespondInReadableState(
1262 JSContext* aCx, ReadableByteStreamController* aController,
1263 uint64_t aBytesWritten, PullIntoDescriptor* aPullIntoDescriptor,
1264 ErrorResult& aRv) {
1265 // Step 1. Assert: pullIntoDescriptor’s bytes filled + bytesWritten ≤
1266 // pullIntoDescriptor’s byte length.
1267 MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() + aBytesWritten <=
1268 aPullIntoDescriptor->ByteLength());
1270 // Step 2. Perform
1271 // !ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,
1272 // bytesWritten, pullIntoDescriptor).
1273 ReadableByteStreamControllerFillHeadPullIntoDescriptor(
1274 aController, aBytesWritten, aPullIntoDescriptor);
1276 // Step 3. If pullIntoDescriptor’s reader type is "none",
1277 if (aPullIntoDescriptor->GetReaderType() == ReaderType::None) {
1278 // Step 3.1. Perform ?
1279 // ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller,
1280 // pullIntoDescriptor).
1281 ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
1282 aCx, aController, aPullIntoDescriptor, aRv);
1283 if (aRv.Failed()) {
1284 return;
1287 // Step 3.2. Perform !
1288 // ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
1289 ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
1290 aCx, aController, aRv);
1292 // Step 3.3. Return.
1293 return;
1296 // Step 4. If pullIntoDescriptor’s bytes filled < pullIntoDescriptor’s element
1297 // size, return.
1298 if (aPullIntoDescriptor->BytesFilled() < aPullIntoDescriptor->ElementSize()) {
1299 return;
1302 // Step 5. Perform
1303 // !ReadableByteStreamControllerShiftPendingPullInto(controller).
1304 RefPtr<PullIntoDescriptor> pullIntoDescriptor =
1305 ReadableByteStreamControllerShiftPendingPullInto(aController);
1306 (void)pullIntoDescriptor;
1308 // Step 6. Let remainderSize be pullIntoDescriptor’s bytes filled mod
1309 // pullIntoDescriptor’s element size.
1310 size_t remainderSize =
1311 aPullIntoDescriptor->BytesFilled() % aPullIntoDescriptor->ElementSize();
1313 // Step 7. If remainderSize > 0,
1314 if (remainderSize > 0) {
1315 // Step 7.1. Let end be pullIntoDescriptor’s byte offset +
1316 // pullIntoDescriptor’s bytes filled.
1317 size_t end =
1318 aPullIntoDescriptor->ByteOffset() + aPullIntoDescriptor->BytesFilled();
1320 // Step 7.2. Perform ?
1321 // ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
1322 // pullIntoDescriptor’s buffer, end − remainderSize, remainderSize).
1323 JS::Rooted<JSObject*> pullIntoBuffer(aCx, aPullIntoDescriptor->Buffer());
1324 ReadableByteStreamControllerEnqueueClonedChunkToQueue(
1325 aCx, aController, pullIntoBuffer, end - remainderSize, remainderSize,
1326 aRv);
1327 if (aRv.Failed()) {
1328 return;
1332 // Step 8. Set pullIntoDescriptor’s bytes filled to pullIntoDescriptor’s bytes
1333 // filled − remainderSize.
1334 aPullIntoDescriptor->SetBytesFilled(aPullIntoDescriptor->BytesFilled() -
1335 remainderSize);
1337 // Step 9. Perform
1338 // !ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
1339 // pullIntoDescriptor).
1340 RefPtr<ReadableStream> stream(aController->Stream());
1341 ReadableByteStreamControllerCommitPullIntoDescriptor(
1342 aCx, stream, aPullIntoDescriptor, aRv);
1343 if (aRv.Failed()) {
1344 return;
1347 // Step 10. Perform
1348 // !ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
1349 ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
1350 aCx, aController, aRv);
1353 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-internal
1354 void ReadableByteStreamControllerRespondInternal(
1355 JSContext* aCx, ReadableByteStreamController* aController,
1356 uint64_t aBytesWritten, ErrorResult& aRv) {
1357 // Step 1.
1358 RefPtr<PullIntoDescriptor> firstDescriptor =
1359 aController->PendingPullIntos().getFirst();
1361 // Step 2.
1362 JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
1363 #ifdef DEBUG
1364 bool canTransferBuffer = CanTransferArrayBuffer(aCx, buffer, aRv);
1365 MOZ_ASSERT(!aRv.Failed());
1366 MOZ_ASSERT(canTransferBuffer);
1367 #endif
1369 // Step 3.
1370 ReadableByteStreamControllerInvalidateBYOBRequest(aController);
1372 // Step 4.
1373 auto state = aController->Stream()->State();
1375 // Step 5.
1376 if (state == ReadableStream::ReaderState::Closed) {
1377 // Step 5.1
1378 MOZ_ASSERT(aBytesWritten == 0);
1380 // Step 5.2
1381 ReadableByteStreamControllerRespondInClosedState(aCx, aController,
1382 firstDescriptor, aRv);
1383 if (aRv.Failed()) {
1384 return;
1386 } else {
1387 // Step 6.1
1388 MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);
1390 // Step 6.2.
1391 MOZ_ASSERT(aBytesWritten > 0);
1393 // Step 6.3
1394 ReadableByteStreamControllerRespondInReadableState(
1395 aCx, aController, aBytesWritten, firstDescriptor, aRv);
1396 if (aRv.Failed()) {
1397 return;
1400 // Step 7.
1401 ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
1404 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond
1405 void ReadableByteStreamControllerRespond(
1406 JSContext* aCx, ReadableByteStreamController* aController,
1407 uint64_t aBytesWritten, ErrorResult& aRv) {
1408 // Step 1.
1409 MOZ_ASSERT(!aController->PendingPullIntos().isEmpty());
1411 // Step 2.
1412 PullIntoDescriptor* firstDescriptor =
1413 aController->PendingPullIntos().getFirst();
1415 // Step 3.
1416 auto state = aController->Stream()->State();
1418 // Step 4.
1419 if (state == ReadableStream::ReaderState::Closed) {
1420 // Step 4.1
1421 if (aBytesWritten != 0) {
1422 aRv.ThrowTypeError("bytesWritten not zero on closed stream");
1423 return;
1425 } else {
1426 // Step 5.1
1427 MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);
1429 // Step 5.2
1430 if (aBytesWritten == 0) {
1431 aRv.ThrowTypeError("bytesWritten 0");
1432 return;
1435 // Step 5.3
1436 if (firstDescriptor->BytesFilled() + aBytesWritten >
1437 firstDescriptor->ByteLength()) {
1438 aRv.ThrowRangeError("bytesFilled + bytesWritten > byteLength");
1439 return;
1443 // Step 6.
1444 aRv.MightThrowJSException();
1445 JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
1446 JS::Rooted<JSObject*> transferredBuffer(aCx,
1447 TransferArrayBuffer(aCx, buffer));
1448 if (!transferredBuffer) {
1449 aRv.StealExceptionFromJSContext(aCx);
1450 return;
1452 firstDescriptor->SetBuffer(transferredBuffer);
1454 // Step 7.
1455 ReadableByteStreamControllerRespondInternal(aCx, aController, aBytesWritten,
1456 aRv);
1459 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-with-new-view
1460 void ReadableByteStreamControllerRespondWithNewView(
1461 JSContext* aCx, ReadableByteStreamController* aController,
1462 JS::Handle<JSObject*> aView, ErrorResult& aRv) {
1463 aRv.MightThrowJSException();
1465 // Step 1.
1466 MOZ_ASSERT(!aController->PendingPullIntos().isEmpty());
1468 // Step 2.
1469 bool isSharedMemory;
1470 JS::Rooted<JSObject*> viewedArrayBuffer(
1471 aCx, JS_GetArrayBufferViewBuffer(aCx, aView, &isSharedMemory));
1472 if (!viewedArrayBuffer) {
1473 aRv.StealExceptionFromJSContext(aCx);
1474 return;
1476 MOZ_ASSERT(!JS::IsDetachedArrayBufferObject(viewedArrayBuffer));
1478 // Step 3.
1479 RefPtr<PullIntoDescriptor> firstDescriptor =
1480 aController->PendingPullIntos().getFirst();
1482 // Step 4.
1483 ReadableStream::ReaderState state = aController->Stream()->State();
1485 // Step 5.
1486 if (state == ReadableStream::ReaderState::Closed) {
1487 // Step 5.1
1488 if (JS_GetArrayBufferViewByteLength(aView) != 0) {
1489 aRv.ThrowTypeError("View has non-zero length in closed stream");
1490 return;
1492 } else {
1493 // Step 6.1
1494 MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);
1496 // Step 6.2
1497 if (JS_GetArrayBufferViewByteLength(aView) == 0) {
1498 aRv.ThrowTypeError("View has zero length in readable stream");
1499 return;
1503 // Step 7.
1504 if (firstDescriptor->ByteOffset() + firstDescriptor->BytesFilled() !=
1505 JS_GetArrayBufferViewByteOffset(aView)) {
1506 aRv.ThrowRangeError("Invalid Offset");
1507 return;
1510 // Step 8.
1511 if (firstDescriptor->BufferByteLength() !=
1512 JS::GetArrayBufferByteLength(viewedArrayBuffer)) {
1513 aRv.ThrowRangeError("Mismatched buffer byte lengths");
1514 return;
1517 // Step 9.
1518 if (firstDescriptor->BytesFilled() + JS_GetArrayBufferViewByteLength(aView) >
1519 firstDescriptor->ByteLength()) {
1520 aRv.ThrowRangeError("Too many bytes");
1521 return;
1524 // Step 10. Let viewByteLength be view.[[ByteLength]].
1525 size_t viewByteLength = JS_GetArrayBufferViewByteLength(aView);
1527 // Step 11. Set firstDescriptor’s buffer to ?
1528 // TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
1529 JS::Rooted<JSObject*> transferedBuffer(
1530 aCx, TransferArrayBuffer(aCx, viewedArrayBuffer));
1531 if (!transferedBuffer) {
1532 aRv.StealExceptionFromJSContext(aCx);
1533 return;
1535 firstDescriptor->SetBuffer(transferedBuffer);
1537 // Step 12. Perform ? ReadableByteStreamControllerRespondInternal(controller,
1538 // viewByteLength).
1539 ReadableByteStreamControllerRespondInternal(aCx, aController, viewByteLength,
1540 aRv);
1543 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-pull-into-descriptor-from-queue
1544 bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
1545 JSContext* aCx, ReadableByteStreamController* aController,
1546 PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv) {
1547 // Step 1. Let elementSize be pullIntoDescriptor.[[elementSize]].
1548 size_t elementSize = aPullIntoDescriptor->ElementSize();
1550 // Step 2. Let currentAlignedBytes be pullIntoDescriptor’s bytes filled −
1551 // (pullIntoDescriptor’s bytes filled mod elementSize).
1552 size_t currentAlignedBytes =
1553 aPullIntoDescriptor->BytesFilled() -
1554 (aPullIntoDescriptor->BytesFilled() % elementSize);
1556 // Step 3. Let maxBytesToCopy be min(controller.[[queueTotalSize]],
1557 // pullIntoDescriptor’s byte length − pullIntoDescriptor’s bytes filled).
1558 size_t maxBytesToCopy =
1559 std::min(static_cast<size_t>(aController->QueueTotalSize()),
1560 static_cast<size_t>((aPullIntoDescriptor->ByteLength() -
1561 aPullIntoDescriptor->BytesFilled())));
1563 // Step 4. Let maxBytesFilled be pullIntoDescriptor’s bytes filled +
1564 // maxBytesToCopy.
1565 size_t maxBytesFilled = aPullIntoDescriptor->BytesFilled() + maxBytesToCopy;
1567 // Step 5. Let maxAlignedBytes be maxBytesFilled − (maxBytesFilled mod
1568 // elementSize).
1569 size_t maxAlignedBytes = maxBytesFilled - (maxBytesFilled % elementSize);
1571 // Step 6. Let totalBytesToCopyRemaining be maxBytesToCopy.
1572 size_t totalBytesToCopyRemaining = maxBytesToCopy;
1574 // Step 7. Let ready be false.
1575 bool ready = false;
1577 // Step 8. If maxAlignedBytes > currentAlignedBytes,
1578 if (maxAlignedBytes > currentAlignedBytes) {
1579 // Step 8.1. Set totalBytesToCopyRemaining to maxAlignedBytes −
1580 // pullIntoDescriptor’s bytes filled.
1581 totalBytesToCopyRemaining =
1582 maxAlignedBytes - aPullIntoDescriptor->BytesFilled();
1583 // Step 8.2. Set ready to true.
1584 ready = true;
1587 // Step 9. Let queue be controller.[[queue]].
1588 LinkedList<RefPtr<ReadableByteStreamQueueEntry>>& queue =
1589 aController->Queue();
1591 // Step 10. While totalBytesToCopyRemaining > 0,
1592 while (totalBytesToCopyRemaining > 0) {
1593 // Step 10.1 Let headOfQueue be queue[0].
1594 ReadableByteStreamQueueEntry* headOfQueue = queue.getFirst();
1596 // Step 10.2. Let bytesToCopy be min(totalBytesToCopyRemaining,
1597 // headOfQueue’s byte length).
1598 size_t bytesToCopy =
1599 std::min(totalBytesToCopyRemaining, headOfQueue->ByteLength());
1601 // Step 10.3. Let destStart be pullIntoDescriptor’s byte offset +
1602 // pullIntoDescriptor’s bytes filled.
1603 size_t destStart =
1604 aPullIntoDescriptor->ByteOffset() + aPullIntoDescriptor->BytesFilled();
1606 // Step 10.4. Perform !CopyDataBlockBytes(pullIntoDescriptor’s
1607 // buffer.[[ArrayBufferData]], destStart, headOfQueue’s
1608 // buffer.[[ArrayBufferData]], headOfQueue’s byte offset,
1609 // bytesToCopy).
1610 JS::Rooted<JSObject*> descriptorBuffer(aCx, aPullIntoDescriptor->Buffer());
1611 JS::Rooted<JSObject*> queueBuffer(aCx, headOfQueue->Buffer());
1612 if (!JS::ArrayBufferCopyData(aCx, descriptorBuffer, destStart, queueBuffer,
1613 headOfQueue->ByteOffset(), bytesToCopy)) {
1614 aRv.StealExceptionFromJSContext(aCx);
1615 return false;
1618 // Step 10.5. If headOfQueue’s byte length is bytesToCopy,
1619 if (headOfQueue->ByteLength() == bytesToCopy) {
1620 // Step 10.5.1. Remove queue[0].
1621 queue.popFirst();
1622 } else {
1623 // Step 10.6. Otherwise,
1625 // Step 10.6.1 Set headOfQueue’s byte offset to
1626 // headOfQueue’s byte offset + bytesToCopy.
1627 headOfQueue->SetByteOffset(headOfQueue->ByteOffset() + bytesToCopy);
1628 // Step 10.6.2 Set headOfQueue’s byte length to
1629 // headOfQueue’s byte length − bytesToCopy.
1630 headOfQueue->SetByteLength(headOfQueue->ByteLength() - bytesToCopy);
1633 // Step 10.7. Set controller.[[queueTotalSize]] to
1634 // controller.[[queueTotalSize]] − bytesToCopy.
1635 aController->SetQueueTotalSize(aController->QueueTotalSize() -
1636 (double)bytesToCopy);
1638 // Step 10.8, Perform
1639 // !ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,
1640 // bytesToCopy, pullIntoDescriptor).
1641 ReadableByteStreamControllerFillHeadPullIntoDescriptor(
1642 aController, bytesToCopy, aPullIntoDescriptor);
1644 // Step 10.9. Set totalBytesToCopyRemaining to totalBytesToCopyRemaining −
1645 // bytesToCopy.
1646 totalBytesToCopyRemaining = totalBytesToCopyRemaining - bytesToCopy;
1649 // Step 11. If ready is false,
1650 if (!ready) {
1651 // Step 11.1. Assert: controller.[[queueTotalSize]] is 0.
1652 MOZ_ASSERT(aController->QueueTotalSize() == 0);
1654 // Step 11.2. Assert: pullIntoDescriptor’s bytes filled > 0.
1655 MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() > 0);
1657 // Step 11.3. Assert: pullIntoDescriptor’s bytes filled <
1658 // pullIntoDescriptor’s
1659 // element size.
1660 MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() <
1661 aPullIntoDescriptor->ElementSize());
1664 // Step 12. Return ready.
1665 return ready;
1668 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-pull-into
1669 void ReadableByteStreamControllerPullInto(
1670 JSContext* aCx, ReadableByteStreamController* aController,
1671 JS::Handle<JSObject*> aView, ReadIntoRequest* aReadIntoRequest,
1672 ErrorResult& aRv) {
1673 aRv.MightThrowJSException();
1675 // Step 1. Let stream be controller.[[stream]].
1676 ReadableStream* stream = aController->Stream();
1678 // Step 2. Let elementSize be 1.
1679 size_t elementSize = 1;
1681 // Step 3. Let ctor be %DataView%.
1682 PullIntoDescriptor::Constructor ctor =
1683 PullIntoDescriptor::Constructor::DataView;
1685 // Step 4. If view has a [[TypedArrayName]] internal slot (i.e., it is not a
1686 // DataView),
1687 if (JS_IsTypedArrayObject(aView)) {
1688 // Step 4.1. Set elementSize to the element size specified in the typed
1689 // array constructors table for view.[[TypedArrayName]].
1690 JS::Scalar::Type type = JS_GetArrayBufferViewType(aView);
1691 elementSize = JS::Scalar::byteSize(type);
1693 // Step 4.2 Set ctor to the constructor specified in the typed array
1694 // constructors table for view.[[TypedArrayName]].
1695 ctor = PullIntoDescriptor::constructorFromScalar(type);
1698 // Step 5. Let byteOffset be view.[[ByteOffset]].
1699 size_t byteOffset = JS_GetArrayBufferViewByteOffset(aView);
1701 // Step 6. Let byteLength be view.[[ByteLength]].
1702 size_t byteLength = JS_GetArrayBufferViewByteLength(aView);
1704 // Step 7. Let bufferResult be
1705 // TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
1706 bool isShared;
1707 JS::Rooted<JSObject*> viewedArrayBuffer(
1708 aCx, JS_GetArrayBufferViewBuffer(aCx, aView, &isShared));
1709 if (!viewedArrayBuffer) {
1710 aRv.StealExceptionFromJSContext(aCx);
1711 return;
1713 JS::Rooted<JSObject*> bufferResult(
1714 aCx, TransferArrayBuffer(aCx, viewedArrayBuffer));
1716 // Step 8. If bufferResult is an abrupt completion,
1717 if (!bufferResult) {
1718 JS::Rooted<JS::Value> pendingException(aCx);
1719 if (!JS_GetPendingException(aCx, &pendingException)) {
1720 // This means an un-catchable exception. Use StealExceptionFromJSContext
1721 // to setup aRv properly.
1722 aRv.StealExceptionFromJSContext(aCx);
1723 return;
1726 // It's not expliclitly stated, but I assume the intention here is that
1727 // we perform a normal completion here; we also need to clear the
1728 // exception state anyhow to succesfully run ErrorSteps.
1729 JS_ClearPendingException(aCx);
1731 // Step 8.1. Perform readIntoRequest’s error steps, given
1732 // bufferResult.[[Value]].
1733 aReadIntoRequest->ErrorSteps(aCx, pendingException, aRv);
1735 // Step 8.2. Return.
1736 return;
1739 // Step 9. Let buffer be bufferResult.[[Value]].
1740 JS::Rooted<JSObject*> buffer(aCx, bufferResult);
1742 // Step 10. Let pullIntoDescriptor be a new pull-into descriptor with
1743 // buffer: buffer,
1744 // buffer byte length: buffer.[[ArrayBufferByteLength]],
1745 // byte offset: byteOffset,
1746 // byte length: byteLength,
1747 // bytes filled: 0,
1748 // element size: elementSize,
1749 // view constructor: ctor,
1750 // and reader type: "byob".
1751 RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
1752 buffer, JS::GetArrayBufferByteLength(buffer), byteOffset, byteLength, 0,
1753 elementSize, ctor, ReaderType::BYOB);
1755 // Step 11. If controller.[[pendingPullIntos]] is not empty,
1756 if (!aController->PendingPullIntos().isEmpty()) {
1757 // Step 11.1. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
1758 aController->PendingPullIntos().insertBack(pullIntoDescriptor);
1760 // Step 11.2. Perform !ReadableStreamAddReadIntoRequest(stream,
1761 // readIntoRequest).
1762 ReadableStreamAddReadIntoRequest(stream, aReadIntoRequest);
1764 // Step 11.3. Return.
1765 return;
1768 // Step 12. If stream.[[state]] is "closed",
1769 if (stream->State() == ReadableStream::ReaderState::Closed) {
1770 // Step 12.1. Let emptyView be !Construct(ctor, « pullIntoDescriptor’s
1771 // buffer, pullIntoDescriptor’s byte offset, 0 »).
1772 JS::Rooted<JSObject*> pullIntoBuffer(aCx, pullIntoDescriptor->Buffer());
1773 JS::Rooted<JSObject*> emptyView(
1774 aCx,
1775 ConstructFromPullIntoConstructor(aCx, ctor, pullIntoBuffer,
1776 pullIntoDescriptor->ByteOffset(), 0));
1777 if (!emptyView) {
1778 aRv.StealExceptionFromJSContext(aCx);
1779 return;
1782 // Step 12.2. Perform readIntoRequest’s close steps, given emptyView.
1783 JS::Rooted<JS::Value> emptyViewValue(aCx, JS::ObjectValue(*emptyView));
1784 aReadIntoRequest->CloseSteps(aCx, emptyViewValue, aRv);
1786 // Step 12.3. Return.
1787 return;
1790 // Step 13,. If controller.[[queueTotalSize]] > 0,
1791 if (aController->QueueTotalSize() > 0) {
1792 // Step 13.1 If
1793 // !ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
1794 // pullIntoDescriptor) is true,
1795 bool ready = ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
1796 aCx, aController, pullIntoDescriptor, aRv);
1797 if (aRv.Failed()) {
1798 return;
1800 if (ready) {
1801 // Step 13.1.1 Let filledView be
1802 // !ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
1803 JS::Rooted<JSObject*> filledView(
1804 aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
1805 aCx, pullIntoDescriptor, aRv));
1806 if (aRv.Failed()) {
1807 return;
1809 // Step 13.1.2. Perform
1810 // !ReadableByteStreamControllerHandleQueueDrain(controller).
1811 ReadableByteStreamControllerHandleQueueDrain(aCx, aController, aRv);
1812 if (aRv.Failed()) {
1813 return;
1815 // Step 13.1.3. Perform readIntoRequest’s chunk steps, given filledView.
1816 JS::Rooted<JS::Value> filledViewValue(aCx, JS::ObjectValue(*filledView));
1817 aReadIntoRequest->ChunkSteps(aCx, filledViewValue, aRv);
1818 // Step 13.1.4. Return.
1819 return;
1822 // Step 13.2 If controller.[[closeRequested]] is true,
1823 if (aController->CloseRequested()) {
1824 // Step 13.2.1. Let e be a TypeError exception.
1825 ErrorResult typeError;
1826 typeError.ThrowTypeError("Close Requested True during Pull Into");
1828 JS::Rooted<JS::Value> e(aCx);
1829 MOZ_RELEASE_ASSERT(ToJSValue(aCx, std::move(typeError), &e));
1831 // Step 13.2.2. Perform !ReadableByteStreamControllerError(controller, e).
1832 ReadableByteStreamControllerError(aController, e, aRv);
1833 if (aRv.Failed()) {
1834 return;
1837 // Step 13.2.3. Perform readIntoRequest’s error steps, given e.
1838 aReadIntoRequest->ErrorSteps(aCx, e, aRv);
1840 // Step 13.2.4. Return.
1841 return;
1845 // Step 14. Append pullIntoDescriptor to controller.[[pendingPullIntos]].
1846 aController->PendingPullIntos().insertBack(pullIntoDescriptor);
1848 // Step 15. Perform !ReadableStreamAddReadIntoRequest(stream,
1849 // readIntoRequest).
1850 ReadableStreamAddReadIntoRequest(stream, aReadIntoRequest);
1852 // Step 16, Perform
1853 // !ReadableByteStreamControllerCallPullIfNeeded(controller).
1854 ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
1857 // https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller
1858 void SetUpReadableByteStreamController(
1859 JSContext* aCx, ReadableStream* aStream,
1860 ReadableByteStreamController* aController,
1861 UnderlyingSourceAlgorithmsBase* aAlgorithms, double aHighWaterMark,
1862 Maybe<uint64_t> aAutoAllocateChunkSize, ErrorResult& aRv) {
1863 // Step 1. Assert: stream.[[controller]] is undefined.
1864 MOZ_ASSERT(!aStream->Controller());
1866 // Step 2. If autoAllocateChunkSize is not undefined,
1867 // Step 2.1. Assert: ! IsInteger(autoAllocateChunkSize) is true. Implicit
1868 // Step 2.2. Assert: autoAllocateChunkSize is positive. (Implicit by
1869 // type.)
1871 // Step 3. Set controller.[[stream]] to stream.
1872 aController->SetStream(aStream);
1874 // Step 4. Set controller.[[pullAgain]] and controller.[[pulling]] to false.
1875 aController->SetPullAgain(false);
1876 aController->SetPulling(false);
1878 // Step 5. Set controller.[[byobRequest]] to null.
1879 aController->SetByobRequest(nullptr);
1881 // Step 6. Perform !ResetQueue(controller).
1882 ResetQueue(aController);
1884 // Step 7. Set controller.[[closeRequested]] and controller.[[started]] to
1885 // false.
1886 aController->SetCloseRequested(false);
1887 aController->SetStarted(false);
1889 // Step 8. Set controller.[[strategyHWM]] to highWaterMark.
1890 aController->SetStrategyHWM(aHighWaterMark);
1892 // Step 9. Set controller.[[pullAlgorithm]] to pullAlgorithm.
1893 // Step 10. Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
1894 aController->SetAlgorithms(*aAlgorithms);
1896 // Step 11. Set controller.[[autoAllocateChunkSize]] to autoAllocateChunkSize.
1897 aController->SetAutoAllocateChunkSize(aAutoAllocateChunkSize);
1899 // Step 12. Set controller.[[pendingPullIntos]] to a new empty list.
1900 aController->PendingPullIntos().clear();
1902 // Step 13. Set stream.[[controller]] to controller.
1903 aStream->SetController(*aController);
1905 // Step 14. Let startResult be the result of performing startAlgorithm.
1906 JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue());
1907 RefPtr<ReadableStreamController> controller = aController;
1908 aAlgorithms->StartCallback(aCx, *controller, &startResult, aRv);
1909 if (aRv.Failed()) {
1910 return;
1913 // Let startPromise be a promise resolved with startResult.
1914 RefPtr<Promise> startPromise =
1915 Promise::CreateInfallible(aStream->GetParentObject());
1916 startPromise->MaybeResolve(startResult);
1918 // Step 16+17
1919 startPromise->AddCallbacksWithCycleCollectedArgs(
1920 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
1921 ReadableByteStreamController* aController)
1922 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
1923 MOZ_ASSERT(aController);
1925 // Step 16.1
1926 aController->SetStarted(true);
1928 // Step 16.2
1929 aController->SetPulling(false);
1931 // Step 16.3
1932 aController->SetPullAgain(false);
1934 // Step 16.4:
1935 ReadableByteStreamControllerCallPullIfNeeded(
1936 aCx, MOZ_KnownLive(aController), aRv);
1938 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
1939 ReadableByteStreamController* aController) {
1940 // Step 17.1
1941 ReadableByteStreamControllerError(aController, aValue, aRv);
1943 RefPtr(aController));
1946 // https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller-from-underlying-source
1947 void SetUpReadableByteStreamControllerFromUnderlyingSource(
1948 JSContext* aCx, ReadableStream* aStream,
1949 JS::Handle<JSObject*> aUnderlyingSource,
1950 UnderlyingSource& aUnderlyingSourceDict, double aHighWaterMark,
1951 ErrorResult& aRv) {
1952 // Step 1. Let controller be a new ReadableByteStreamController.
1953 auto controller =
1954 MakeRefPtr<ReadableByteStreamController>(aStream->GetParentObject());
1956 // Step 2 - 7
1957 auto algorithms = MakeRefPtr<UnderlyingSourceAlgorithms>(
1958 aStream->GetParentObject(), aUnderlyingSource, aUnderlyingSourceDict);
1960 // Step 8. Let autoAllocateChunkSize be
1961 // underlyingSourceDict["autoAllocateChunkSize"], if it exists, or undefined
1962 // otherwise.
1963 Maybe<uint64_t> autoAllocateChunkSize = mozilla::Nothing();
1964 if (aUnderlyingSourceDict.mAutoAllocateChunkSize.WasPassed()) {
1965 uint64_t value = aUnderlyingSourceDict.mAutoAllocateChunkSize.Value();
1966 // Step 9. If autoAllocateChunkSize is 0, then throw a TypeError
1967 // exception.
1968 if (value == 0) {
1969 aRv.ThrowTypeError("autoAllocateChunkSize can not be zero.");
1970 return;
1972 autoAllocateChunkSize = mozilla::Some(value);
1975 // Step 10. Perform ? SetUpReadableByteStreamController(stream, controller,
1976 // startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark,
1977 // autoAllocateChunkSize).
1978 SetUpReadableByteStreamController(aCx, aStream, controller, algorithms,
1979 aHighWaterMark, autoAllocateChunkSize, aRv);
1982 } // namespace streams_abstract
1984 } // namespace mozilla::dom