Bug 1886946: Remove incorrect assertion that buffer is not-pinned. r=sfink
[gecko.git] / dom / streams / ReadableStreamDefaultController.cpp
blob7a5ae3a80f7ae00c7db241b5a44ee8c73c0b0982
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 if (!JS_GetPendingException(aCx, &errorValue)) {
325 // Uncatchable exception; we should mark aRv and return.
326 aRv.StealExceptionFromJSContext(aCx);
327 return;
329 JS_ClearPendingException(aCx);
331 // Step 4.5.1
332 ReadableStreamDefaultControllerError(aCx, aController, errorValue, aRv);
333 if (aRv.Failed()) {
334 return;
337 // Step 4.5.2 Caller must treat aRv as if it were a completion
338 // value
339 aRv.MightThrowJSException();
340 aRv.ThrowJSException(aCx, errorValue);
341 return;
345 // Step 5.
346 ReadableStreamDefaultControllerCallPullIfNeeded(aCx, aController, aRv);
349 } // namespace streams_abstract
351 // https://streams.spec.whatwg.org/#rs-default-controller-close
352 void ReadableStreamDefaultController::Enqueue(JSContext* aCx,
353 JS::Handle<JS::Value> aChunk,
354 ErrorResult& aRv) {
355 // Step 1.
356 if (!ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
357 this, CloseOrEnqueue::Enqueue, aRv)) {
358 return;
361 // Step 2.
362 ReadableStreamDefaultControllerEnqueue(aCx, this, aChunk, aRv);
365 void ReadableStreamDefaultController::Error(JSContext* aCx,
366 JS::Handle<JS::Value> aError,
367 ErrorResult& aRv) {
368 ReadableStreamDefaultControllerError(aCx, this, aError, aRv);
371 namespace streams_abstract {
373 // https://streams.spec.whatwg.org/#readable-stream-default-controller-should-call-pull
374 bool ReadableStreamDefaultControllerShouldCallPull(
375 ReadableStreamDefaultController* aController) {
376 // Step 1.
377 ReadableStream* stream = aController->Stream();
379 // Step 2.
380 if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(aController)) {
381 return false;
384 // Step 3.
385 if (!aController->Started()) {
386 return false;
389 // Step 4.
390 if (IsReadableStreamLocked(stream) &&
391 ReadableStreamGetNumReadRequests(stream) > 0) {
392 return true;
395 // Step 5.
396 Nullable<double> desiredSize =
397 ReadableStreamDefaultControllerGetDesiredSize(aController);
399 // Step 6.
400 MOZ_ASSERT(!desiredSize.IsNull());
402 // Step 7 + 8
403 return desiredSize.Value() > 0;
406 // https://streams.spec.whatwg.org/#readable-stream-default-controller-error
407 void ReadableStreamDefaultControllerError(
408 JSContext* aCx, ReadableStreamDefaultController* aController,
409 JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
410 // Step 1.
411 ReadableStream* stream = aController->Stream();
413 // Step 2.
414 if (stream->State() != ReadableStream::ReaderState::Readable) {
415 return;
418 // Step 3.
419 ResetQueue(aController);
421 // Step 4.
422 ReadableStreamDefaultControllerClearAlgorithms(aController);
424 // Step 5.
425 ReadableStreamError(aCx, stream, aValue, aRv);
428 // https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed
429 static void ReadableStreamDefaultControllerCallPullIfNeeded(
430 JSContext* aCx, ReadableStreamDefaultController* aController,
431 ErrorResult& aRv) {
432 // Step 1.
433 bool shouldPull = ReadableStreamDefaultControllerShouldCallPull(aController);
435 // Step 2.
436 if (!shouldPull) {
437 return;
440 // Step 3.
441 if (aController->Pulling()) {
442 // Step 3.1
443 aController->SetPullAgain(true);
444 // Step 3.2
445 return;
448 // Step 4.
449 MOZ_ASSERT(!aController->PullAgain());
451 // Step 5.
452 aController->SetPulling(true);
454 // Step 6.
455 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms =
456 aController->GetAlgorithms();
457 RefPtr<Promise> pullPromise =
458 algorithms->PullCallback(aCx, *aController, aRv);
459 if (aRv.Failed()) {
460 return;
463 // Step 7 + 8:
464 pullPromise->AddCallbacksWithCycleCollectedArgs(
465 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
466 ReadableStreamDefaultController* mController)
467 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
468 // Step 7.1
469 mController->SetPulling(false);
470 // Step 7.2
471 if (mController->PullAgain()) {
472 // Step 7.2.1
473 mController->SetPullAgain(false);
475 // Step 7.2.2
476 ErrorResult rv;
477 ReadableStreamDefaultControllerCallPullIfNeeded(
478 aCx, MOZ_KnownLive(mController), aRv);
481 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
482 ReadableStreamDefaultController* mController) {
483 // Step 8.1
484 ReadableStreamDefaultControllerError(aCx, mController, aValue, aRv);
486 RefPtr(aController));
489 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller
490 void SetUpReadableStreamDefaultController(
491 JSContext* aCx, ReadableStream* aStream,
492 ReadableStreamDefaultController* aController,
493 UnderlyingSourceAlgorithmsBase* aAlgorithms, double aHighWaterMark,
494 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
495 // Step 1.
496 MOZ_ASSERT(!aStream->Controller());
498 // Step 2.
499 aController->SetStream(aStream);
501 // Step 3.
502 ResetQueue(aController);
504 // Step 4.
505 aController->SetStarted(false);
506 aController->SetCloseRequested(false);
507 aController->SetPullAgain(false);
508 aController->SetPulling(false);
510 // Step 5.
511 aController->setStrategySizeAlgorithm(aSizeAlgorithm);
512 aController->SetStrategyHWM(aHighWaterMark);
514 // Step 6.
515 // Step 7.
516 aController->SetAlgorithms(*aAlgorithms);
518 // Step 8.
519 aStream->SetController(*aController);
521 // Step 9. Default algorithm returns undefined. See Step 2 of
522 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller
523 JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue());
524 RefPtr<ReadableStreamDefaultController> controller = aController;
525 aAlgorithms->StartCallback(aCx, *controller, &startResult, aRv);
526 if (aRv.Failed()) {
527 return;
530 // Step 10.
531 RefPtr<Promise> startPromise =
532 Promise::CreateInfallible(aStream->GetParentObject());
533 startPromise->MaybeResolve(startResult);
535 // Step 11 & 12:
536 startPromise->AddCallbacksWithCycleCollectedArgs(
537 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
538 ReadableStreamDefaultController* aController)
539 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
540 MOZ_ASSERT(aController);
542 // Step 11.1
543 aController->SetStarted(true);
545 // Step 11.2
546 aController->SetPulling(false);
548 // Step 11.3
549 aController->SetPullAgain(false);
551 // Step 11.4:
552 ReadableStreamDefaultControllerCallPullIfNeeded(
553 aCx, MOZ_KnownLive(aController), aRv);
556 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
557 ReadableStreamDefaultController* aController) {
558 // Step 12.1
559 ReadableStreamDefaultControllerError(aCx, aController, aValue, aRv);
561 RefPtr(aController));
564 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller-from-underlying-source
565 void SetupReadableStreamDefaultControllerFromUnderlyingSource(
566 JSContext* aCx, ReadableStream* aStream,
567 JS::Handle<JSObject*> aUnderlyingSource,
568 UnderlyingSource& aUnderlyingSourceDict, double aHighWaterMark,
569 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
570 // Step 1.
571 RefPtr<ReadableStreamDefaultController> controller =
572 new ReadableStreamDefaultController(aStream->GetParentObject());
574 // Step 2 - 7
575 RefPtr<UnderlyingSourceAlgorithms> algorithms =
576 new UnderlyingSourceAlgorithms(aStream->GetParentObject(),
577 aUnderlyingSource, aUnderlyingSourceDict);
579 // Step 8:
580 SetUpReadableStreamDefaultController(aCx, aStream, controller, algorithms,
581 aHighWaterMark, aSizeAlgorithm, aRv);
584 } // namespace streams_abstract
586 // https://streams.spec.whatwg.org/#rs-default-controller-private-cancel
587 already_AddRefed<Promise> ReadableStreamDefaultController::CancelSteps(
588 JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
589 // Step 1.
590 ResetQueue(this);
592 // Step 2.
593 Optional<JS::Handle<JS::Value>> errorOption(aCx, aReason);
594 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms = mAlgorithms;
595 RefPtr<Promise> result = algorithms->CancelCallback(aCx, errorOption, aRv);
596 if (aRv.Failed()) {
597 return nullptr;
600 // Step 3.
601 ReadableStreamDefaultControllerClearAlgorithms(this);
603 // Step 4.
604 return result.forget();
607 // https://streams.spec.whatwg.org/#rs-default-controller-private-pull
608 void ReadableStreamDefaultController::PullSteps(JSContext* aCx,
609 ReadRequest* aReadRequest,
610 ErrorResult& aRv) {
611 // Step 1.
612 RefPtr<ReadableStream> stream = mStream;
614 // Step 2.
615 if (!mQueue.isEmpty()) {
616 // Step 2.1
617 JS::Rooted<JS::Value> chunk(aCx);
618 DequeueValue(this, &chunk);
620 // Step 2.2
621 if (CloseRequested() && mQueue.isEmpty()) {
622 // Step 2.2.1
623 ReadableStreamDefaultControllerClearAlgorithms(this);
624 // Step 2.2.2
625 ReadableStreamClose(aCx, stream, aRv);
626 if (aRv.Failed()) {
627 return;
629 } else {
630 // Step 2.3
631 ReadableStreamDefaultControllerCallPullIfNeeded(aCx, this, aRv);
632 if (aRv.Failed()) {
633 return;
637 // Step 2.4
638 aReadRequest->ChunkSteps(aCx, chunk, aRv);
639 } else {
640 // Step 3.
641 // Step 3.1
642 ReadableStreamAddReadRequest(stream, aReadRequest);
643 // Step 3.2
644 ReadableStreamDefaultControllerCallPullIfNeeded(aCx, this, aRv);
648 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultcontroller-releasesteps
649 void ReadableStreamDefaultController::ReleaseSteps() {
650 // Step 1. Return.
653 } // namespace mozilla::dom