Bug 1806664 - Use python toolchain tasks to build firefox r=glandium,taskgraph-review...
[gecko.git] / dom / streams / ReadableStreamDefaultController.cpp
blob9540bb8e6c8152c61090f79b970b606e97fbfd29
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/ReadableStreamDefaultController.h"
9 #include "js/Exception.h"
10 #include "js/TypeDecls.h"
11 #include "js/Value.h"
12 #include "mozilla/AlreadyAddRefed.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/HoldDropJSObjects.h"
15 #include "mozilla/dom/Promise.h"
16 #include "mozilla/dom/Promise-inl.h"
17 #include "mozilla/dom/ReadableStream.h"
18 #include "mozilla/dom/ReadableStreamController.h"
19 #include "mozilla/dom/ReadableStreamDefaultControllerBinding.h"
20 #include "mozilla/dom/ReadableStreamDefaultReaderBinding.h"
21 #include "mozilla/dom/UnderlyingSourceBinding.h"
22 #include "mozilla/dom/UnderlyingSourceCallbackHelpers.h"
23 #include "nsCycleCollectionParticipant.h"
24 #include "nsISupports.h"
26 namespace mozilla::dom {
28 using namespace streams_abstract;
30 NS_IMPL_CYCLE_COLLECTION(ReadableStreamController, mGlobal, mAlgorithms,
31 mStream)
32 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadableStreamController)
33 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadableStreamController)
35 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamController)
36 NS_INTERFACE_MAP_ENTRY(nsISupports)
37 NS_INTERFACE_MAP_END
39 ReadableStreamController::ReadableStreamController(nsIGlobalObject* aGlobal)
40 : mGlobal(aGlobal) {}
42 void ReadableStreamController::SetStream(ReadableStream* aStream) {
43 mStream = aStream;
46 // Note: Using the individual macros vs NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE
47 // because I need to specify a manual implementation of
48 // NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN.
49 NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableStreamDefaultController)
51 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ReadableStreamDefaultController,
52 ReadableStreamController)
53 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStrategySizeAlgorithm)
54 tmp->mQueue.clear();
55 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
56 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
59 ReadableStreamDefaultController, ReadableStreamController)
60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStrategySizeAlgorithm)
61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
63 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ReadableStreamDefaultController,
64 ReadableStreamController)
65 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
66 // Trace the associated queue.
67 for (const auto& queueEntry : tmp->mQueue) {
68 aCallbacks.Trace(&queueEntry->mValue, "mQueue.mValue", aClosure);
70 NS_IMPL_CYCLE_COLLECTION_TRACE_END
72 NS_IMPL_ADDREF_INHERITED(ReadableStreamDefaultController,
73 ReadableStreamController)
74 NS_IMPL_RELEASE_INHERITED(ReadableStreamDefaultController,
75 ReadableStreamController)
77 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamDefaultController)
78 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
79 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamController)
81 ReadableStreamDefaultController::ReadableStreamDefaultController(
82 nsIGlobalObject* aGlobal)
83 : ReadableStreamController(aGlobal) {
84 mozilla::HoldJSObjects(this);
87 ReadableStreamDefaultController::~ReadableStreamDefaultController() {
88 // MG:XXX: LinkedLists are required to be empty at destruction, but it seems
89 // it is possible to have a controller be destructed while still
90 // having entries in its queue.
92 // This needs to be verified as not indicating some other issue.
93 mozilla::DropJSObjects(this);
94 mQueue.clear();
97 JSObject* ReadableStreamDefaultController::WrapObject(
98 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
99 return ReadableStreamDefaultController_Binding::Wrap(aCx, this, aGivenProto);
102 namespace streams_abstract {
104 // https://streams.spec.whatwg.org/#readable-stream-default-controller-can-close-or-enqueue
105 static bool ReadableStreamDefaultControllerCanCloseOrEnqueue(
106 ReadableStreamDefaultController* aController) {
107 // Step 1. Let state be controller.[[stream]].[[state]].
108 ReadableStream::ReaderState state = aController->Stream()->State();
110 // Step 2. If controller.[[closeRequested]] is false and state is "readable",
111 // return true.
112 // Step 3. Return false.
113 return !aController->CloseRequested() &&
114 state == ReadableStream::ReaderState::Readable;
117 // https://streams.spec.whatwg.org/#readable-stream-default-controller-can-close-or-enqueue
118 // This is a variant of ReadableStreamDefaultControllerCanCloseOrEnqueue
119 // that also throws when the function would return false to improve error
120 // messages.
121 bool ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
122 ReadableStreamDefaultController* aController,
123 CloseOrEnqueue aCloseOrEnqueue, ErrorResult& aRv) {
124 // Step 1. Let state be controller.[[stream]].[[state]].
125 ReadableStream::ReaderState state = aController->Stream()->State();
127 nsCString prefix;
128 if (aCloseOrEnqueue == CloseOrEnqueue::Close) {
129 prefix = "Cannot close a stream that "_ns;
130 } else {
131 prefix = "Cannot enqueue into a stream that "_ns;
134 switch (state) {
135 case ReadableStream::ReaderState::Readable:
136 // Step 2. If controller.[[closeRequested]] is false and
137 // state is "readable", return true.
138 // Note: We don't error/check for [[closeRequest]] first, because
139 // [[closedRequest]] is still true even after the state is "closed".
140 // This doesn't cause any spec observable difference.
141 if (!aController->CloseRequested()) {
142 return true;
145 // Step 3. Return false.
146 aRv.ThrowTypeError(prefix + "has already been requested to close."_ns);
147 return false;
149 case ReadableStream::ReaderState::Closed:
150 aRv.ThrowTypeError(prefix + "is already closed."_ns);
151 return false;
153 case ReadableStream::ReaderState::Errored:
154 aRv.ThrowTypeError(prefix + "has errored."_ns);
155 return false;
157 default:
158 MOZ_ASSERT_UNREACHABLE("Unknown ReaderState");
159 return false;
163 Nullable<double> ReadableStreamDefaultControllerGetDesiredSize(
164 ReadableStreamDefaultController* aController) {
165 ReadableStream::ReaderState state = aController->Stream()->State();
166 if (state == ReadableStream::ReaderState::Errored) {
167 return nullptr;
170 if (state == ReadableStream::ReaderState::Closed) {
171 return 0.0;
174 return aController->StrategyHWM() - aController->QueueTotalSize();
177 } // namespace streams_abstract
179 // https://streams.spec.whatwg.org/#rs-default-controller-desired-size
180 Nullable<double> ReadableStreamDefaultController::GetDesiredSize() {
181 // Step 1.
182 return ReadableStreamDefaultControllerGetDesiredSize(this);
185 namespace streams_abstract {
187 // https://streams.spec.whatwg.org/#readable-stream-default-controller-clear-algorithms
189 // Note: nullptr is used to indicate we run the default algorithm at the
190 // moment,
191 // so the below doesn't quite match the spec, but serves the correct
192 // purpose for disconnecting the algorithms from the object graph to allow
193 // collection.
195 // As far as I know, this isn't currently visible, but we need to keep
196 // this in mind. This is a weakness of this current implementation, and
197 // I'd prefer to have a better answer here eventually.
198 void ReadableStreamDefaultControllerClearAlgorithms(
199 ReadableStreamDefaultController* aController) {
200 // Step 1.
201 // Step 2.
202 aController->ClearAlgorithms();
204 // Step 3.
205 aController->setStrategySizeAlgorithm(nullptr);
208 // https://streams.spec.whatwg.org/#readable-stream-default-controller-close
209 void ReadableStreamDefaultControllerClose(
210 JSContext* aCx, ReadableStreamDefaultController* aController,
211 ErrorResult& aRv) {
212 // Step 1.
213 if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(aController)) {
214 return;
217 // Step 2.
218 RefPtr<ReadableStream> stream = aController->Stream();
220 // Step 3.
221 aController->SetCloseRequested(true);
223 // Step 4.
224 if (aController->Queue().isEmpty()) {
225 // Step 4.1
226 ReadableStreamDefaultControllerClearAlgorithms(aController);
228 // Step 4.2
229 ReadableStreamClose(aCx, stream, aRv);
233 } // namespace streams_abstract
235 // https://streams.spec.whatwg.org/#rs-default-controller-close
236 void ReadableStreamDefaultController::Close(JSContext* aCx, ErrorResult& aRv) {
237 // Step 1.
238 if (!ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
239 this, CloseOrEnqueue::Close, aRv)) {
240 return;
243 // Step 2.
244 ReadableStreamDefaultControllerClose(aCx, this, aRv);
247 namespace streams_abstract {
249 MOZ_CAN_RUN_SCRIPT static void ReadableStreamDefaultControllerCallPullIfNeeded(
250 JSContext* aCx, ReadableStreamDefaultController* aController,
251 ErrorResult& aRv);
253 // https://streams.spec.whatwg.org/#readable-stream-default-controller-enqueue
254 void ReadableStreamDefaultControllerEnqueue(
255 JSContext* aCx, ReadableStreamDefaultController* aController,
256 JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
257 // Step 1.
258 if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(aController)) {
259 return;
262 // Step 2.
263 RefPtr<ReadableStream> stream = aController->Stream();
265 // Step 3.
266 if (IsReadableStreamLocked(stream) &&
267 ReadableStreamGetNumReadRequests(stream) > 0) {
268 ReadableStreamFulfillReadRequest(aCx, stream, aChunk, false, aRv);
269 } else {
270 // Step 4.1
271 Optional<JS::Handle<JS::Value>> optionalChunk(aCx, aChunk);
273 // Step 4.3 (Re-ordered);
274 RefPtr<QueuingStrategySize> sizeAlgorithm(
275 aController->StrategySizeAlgorithm());
277 // If !sizeAlgorithm, we return 1, which is inlined from
278 // https://streams.spec.whatwg.org/#make-size-algorithm-from-size-function
279 double chunkSize =
280 sizeAlgorithm
281 ? sizeAlgorithm->Call(
282 optionalChunk, aRv,
283 "ReadableStreamDefaultController.[[strategySizeAlgorithm]]",
284 CallbackObject::eRethrowExceptions)
285 : 1.0;
287 // If this is an uncatchable exception we can't continue.
288 if (aRv.IsUncatchableException()) {
289 return;
292 // Step 4.2:
293 if (aRv.MaybeSetPendingException(
294 aCx, "ReadableStreamDefaultController.enqueue")) {
295 JS::Rooted<JS::Value> errorValue(aCx);
297 JS_GetPendingException(aCx, &errorValue);
299 // Step 4.2.1
301 ReadableStreamDefaultControllerError(aCx, aController, errorValue, aRv);
302 if (aRv.Failed()) {
303 return;
306 // Step 4.2.2 Caller must treat aRv as if it were a completion
307 // value
308 aRv.MightThrowJSException();
309 aRv.ThrowJSException(aCx, errorValue);
310 return;
313 // Step 4.4
314 EnqueueValueWithSize(aController, aChunk, chunkSize, aRv);
316 // Step 4.5
317 // Note we convert the pending exception to a JS value here, and then
318 // re-throw it because we save this exception and re-expose it elsewhere
319 // and there are tests to ensure the identity of these errors are the same.
320 if (aRv.MaybeSetPendingException(
321 aCx, "ReadableStreamDefaultController.enqueue")) {
322 JS::Rooted<JS::Value> errorValue(aCx);
324 JS_GetPendingException(aCx, &errorValue);
326 // Step 4.5.1
327 ReadableStreamDefaultControllerError(aCx, aController, errorValue, aRv);
328 if (aRv.Failed()) {
329 return;
332 // Step 4.5.2 Caller must treat aRv as if it were a completion
333 // value
334 aRv.MightThrowJSException();
335 aRv.ThrowJSException(aCx, errorValue);
336 return;
340 // Step 5.
341 ReadableStreamDefaultControllerCallPullIfNeeded(aCx, aController, aRv);
344 } // namespace streams_abstract
346 // https://streams.spec.whatwg.org/#rs-default-controller-close
347 void ReadableStreamDefaultController::Enqueue(JSContext* aCx,
348 JS::Handle<JS::Value> aChunk,
349 ErrorResult& aRv) {
350 // Step 1.
351 if (!ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
352 this, CloseOrEnqueue::Enqueue, aRv)) {
353 return;
356 // Step 2.
357 ReadableStreamDefaultControllerEnqueue(aCx, this, aChunk, aRv);
360 void ReadableStreamDefaultController::Error(JSContext* aCx,
361 JS::Handle<JS::Value> aError,
362 ErrorResult& aRv) {
363 ReadableStreamDefaultControllerError(aCx, this, aError, aRv);
366 namespace streams_abstract {
368 // https://streams.spec.whatwg.org/#readable-stream-default-controller-should-call-pull
369 bool ReadableStreamDefaultControllerShouldCallPull(
370 ReadableStreamDefaultController* aController) {
371 // Step 1.
372 ReadableStream* stream = aController->Stream();
374 // Step 2.
375 if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(aController)) {
376 return false;
379 // Step 3.
380 if (!aController->Started()) {
381 return false;
384 // Step 4.
385 if (IsReadableStreamLocked(stream) &&
386 ReadableStreamGetNumReadRequests(stream) > 0) {
387 return true;
390 // Step 5.
391 Nullable<double> desiredSize =
392 ReadableStreamDefaultControllerGetDesiredSize(aController);
394 // Step 6.
395 MOZ_ASSERT(!desiredSize.IsNull());
397 // Step 7 + 8
398 return desiredSize.Value() > 0;
401 // https://streams.spec.whatwg.org/#readable-stream-default-controller-error
402 void ReadableStreamDefaultControllerError(
403 JSContext* aCx, ReadableStreamDefaultController* aController,
404 JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
405 // Step 1.
406 ReadableStream* stream = aController->Stream();
408 // Step 2.
409 if (stream->State() != ReadableStream::ReaderState::Readable) {
410 return;
413 // Step 3.
414 ResetQueue(aController);
416 // Step 4.
417 ReadableStreamDefaultControllerClearAlgorithms(aController);
419 // Step 5.
420 ReadableStreamError(aCx, stream, aValue, aRv);
423 // https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed
424 static void ReadableStreamDefaultControllerCallPullIfNeeded(
425 JSContext* aCx, ReadableStreamDefaultController* aController,
426 ErrorResult& aRv) {
427 // Step 1.
428 bool shouldPull = ReadableStreamDefaultControllerShouldCallPull(aController);
430 // Step 2.
431 if (!shouldPull) {
432 return;
435 // Step 3.
436 if (aController->Pulling()) {
437 // Step 3.1
438 aController->SetPullAgain(true);
439 // Step 3.2
440 return;
443 // Step 4.
444 MOZ_ASSERT(!aController->PullAgain());
446 // Step 5.
447 aController->SetPulling(true);
449 // Step 6.
450 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms =
451 aController->GetAlgorithms();
452 RefPtr<Promise> pullPromise =
453 algorithms->PullCallback(aCx, *aController, aRv);
454 if (aRv.Failed()) {
455 return;
458 // Step 7 + 8:
459 pullPromise->AddCallbacksWithCycleCollectedArgs(
460 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
461 ReadableStreamDefaultController* mController)
462 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
463 // Step 7.1
464 mController->SetPulling(false);
465 // Step 7.2
466 if (mController->PullAgain()) {
467 // Step 7.2.1
468 mController->SetPullAgain(false);
470 // Step 7.2.2
471 ErrorResult rv;
472 ReadableStreamDefaultControllerCallPullIfNeeded(
473 aCx, MOZ_KnownLive(mController), aRv);
476 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
477 ReadableStreamDefaultController* mController) {
478 // Step 8.1
479 ReadableStreamDefaultControllerError(aCx, mController, aValue, aRv);
481 RefPtr(aController));
484 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller
485 void SetUpReadableStreamDefaultController(
486 JSContext* aCx, ReadableStream* aStream,
487 ReadableStreamDefaultController* aController,
488 UnderlyingSourceAlgorithmsBase* aAlgorithms, double aHighWaterMark,
489 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
490 // Step 1.
491 MOZ_ASSERT(!aStream->Controller());
493 // Step 2.
494 aController->SetStream(aStream);
496 // Step 3.
497 ResetQueue(aController);
499 // Step 4.
500 aController->SetStarted(false);
501 aController->SetCloseRequested(false);
502 aController->SetPullAgain(false);
503 aController->SetPulling(false);
505 // Step 5.
506 aController->setStrategySizeAlgorithm(aSizeAlgorithm);
507 aController->SetStrategyHWM(aHighWaterMark);
509 // Step 6.
510 // Step 7.
511 aController->SetAlgorithms(*aAlgorithms);
513 // Step 8.
514 aStream->SetController(*aController);
516 // Step 9. Default algorithm returns undefined. See Step 2 of
517 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller
518 JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue());
519 RefPtr<ReadableStreamDefaultController> controller = aController;
520 aAlgorithms->StartCallback(aCx, *controller, &startResult, aRv);
521 if (aRv.Failed()) {
522 return;
525 // Step 10.
526 RefPtr<Promise> startPromise =
527 Promise::CreateInfallible(aStream->GetParentObject());
528 startPromise->MaybeResolve(startResult);
530 // Step 11 & 12:
531 startPromise->AddCallbacksWithCycleCollectedArgs(
532 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
533 ReadableStreamDefaultController* aController)
534 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
535 MOZ_ASSERT(aController);
537 // Step 11.1
538 aController->SetStarted(true);
540 // Step 11.2
541 aController->SetPulling(false);
543 // Step 11.3
544 aController->SetPullAgain(false);
546 // Step 11.4:
547 ReadableStreamDefaultControllerCallPullIfNeeded(
548 aCx, MOZ_KnownLive(aController), aRv);
551 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
552 ReadableStreamDefaultController* aController) {
553 // Step 12.1
554 ReadableStreamDefaultControllerError(aCx, aController, aValue, aRv);
556 RefPtr(aController));
559 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller-from-underlying-source
560 void SetupReadableStreamDefaultControllerFromUnderlyingSource(
561 JSContext* aCx, ReadableStream* aStream,
562 JS::Handle<JSObject*> aUnderlyingSource,
563 UnderlyingSource& aUnderlyingSourceDict, double aHighWaterMark,
564 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
565 // Step 1.
566 RefPtr<ReadableStreamDefaultController> controller =
567 new ReadableStreamDefaultController(aStream->GetParentObject());
569 // Step 2 - 7
570 RefPtr<UnderlyingSourceAlgorithms> algorithms =
571 new UnderlyingSourceAlgorithms(aStream->GetParentObject(),
572 aUnderlyingSource, aUnderlyingSourceDict);
574 // Step 8:
575 SetUpReadableStreamDefaultController(aCx, aStream, controller, algorithms,
576 aHighWaterMark, aSizeAlgorithm, aRv);
579 } // namespace streams_abstract
581 // https://streams.spec.whatwg.org/#rs-default-controller-private-cancel
582 already_AddRefed<Promise> ReadableStreamDefaultController::CancelSteps(
583 JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
584 // Step 1.
585 ResetQueue(this);
587 // Step 2.
588 Optional<JS::Handle<JS::Value>> errorOption(aCx, aReason);
589 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms = mAlgorithms;
590 RefPtr<Promise> result = algorithms->CancelCallback(aCx, errorOption, aRv);
591 if (aRv.Failed()) {
592 return nullptr;
595 // Step 3.
596 ReadableStreamDefaultControllerClearAlgorithms(this);
598 // Step 4.
599 return result.forget();
602 // https://streams.spec.whatwg.org/#rs-default-controller-private-pull
603 void ReadableStreamDefaultController::PullSteps(JSContext* aCx,
604 ReadRequest* aReadRequest,
605 ErrorResult& aRv) {
606 // Step 1.
607 RefPtr<ReadableStream> stream = mStream;
609 // Step 2.
610 if (!mQueue.isEmpty()) {
611 // Step 2.1
612 JS::Rooted<JS::Value> chunk(aCx);
613 DequeueValue(this, &chunk);
615 // Step 2.2
616 if (CloseRequested() && mQueue.isEmpty()) {
617 // Step 2.2.1
618 ReadableStreamDefaultControllerClearAlgorithms(this);
619 // Step 2.2.2
620 ReadableStreamClose(aCx, stream, aRv);
621 if (aRv.Failed()) {
622 return;
624 } else {
625 // Step 2.3
626 ReadableStreamDefaultControllerCallPullIfNeeded(aCx, this, aRv);
627 if (aRv.Failed()) {
628 return;
632 // Step 2.4
633 aReadRequest->ChunkSteps(aCx, chunk, aRv);
634 } else {
635 // Step 3.
636 // Step 3.1
637 ReadableStreamAddReadRequest(stream, aReadRequest);
638 // Step 3.2
639 ReadableStreamDefaultControllerCallPullIfNeeded(aCx, this, aRv);
643 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultcontroller-releasesteps
644 void ReadableStreamDefaultController::ReleaseSteps() {
645 // Step 1. Return.
648 } // namespace mozilla::dom