Bug 1769952 - Fix running raptor on a Win10-64 VM r=sparky
[gecko.git] / dom / streams / ReadableStreamDefaultController.cpp
blob57437c18a49208fcaf210737b52cf70a1690363d
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 "js/Exception.h"
8 #include "js/TypeDecls.h"
9 #include "js/Value.h"
10 #include "mozilla/AlreadyAddRefed.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/HoldDropJSObjects.h"
13 #include "mozilla/dom/Promise.h"
14 #include "mozilla/dom/Promise-inl.h"
15 #include "mozilla/dom/PromiseNativeHandler.h"
16 #include "mozilla/dom/ReadableStream.h"
17 #include "mozilla/dom/ReadableStreamController.h"
18 #include "mozilla/dom/ReadableStreamDefaultController.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 NS_IMPL_CYCLE_COLLECTION(ReadableStreamController, mGlobal)
29 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadableStreamController)
30 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadableStreamController)
32 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamController)
33 NS_INTERFACE_MAP_ENTRY(nsISupports)
34 NS_INTERFACE_MAP_END
36 // Note: Using the individual macros vs NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE
37 // because I need to specificy a manual implementation of
38 // NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN.
39 NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableStreamDefaultController)
41 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ReadableStreamDefaultController)
42 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAlgorithms, mStrategySizeAlgorithm, mStream)
43 tmp->mQueue.clear();
44 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
45 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
47 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
48 ReadableStreamDefaultController, ReadableStreamController)
49 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAlgorithms, mStrategySizeAlgorithm,
50 mStream)
51 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
53 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ReadableStreamDefaultController,
54 ReadableStreamController)
55 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
56 // Trace the associated queue.
57 for (const auto& queueEntry : tmp->mQueue) {
58 aCallbacks.Trace(&queueEntry->mValue, "mQueue.mValue", aClosure);
60 NS_IMPL_CYCLE_COLLECTION_TRACE_END
62 NS_IMPL_ADDREF_INHERITED(ReadableStreamDefaultController,
63 ReadableStreamController)
64 NS_IMPL_RELEASE_INHERITED(ReadableStreamDefaultController,
65 ReadableStreamController)
67 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamDefaultController)
68 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
69 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamController)
71 ReadableStreamDefaultController::ReadableStreamDefaultController(
72 nsIGlobalObject* aGlobal)
73 : ReadableStreamController(aGlobal) {
74 mozilla::HoldJSObjects(this);
77 ReadableStreamDefaultController::~ReadableStreamDefaultController() {
78 // MG:XXX: LinkedLists are required to be empty at destruction, but it seems
79 // it is possible to have a controller be destructed while still
80 // having entries in its queue.
82 // This needs to be verified as not indicating some other issue.
83 mozilla::DropJSObjects(this);
84 mQueue.clear();
87 JSObject* ReadableStreamDefaultController::WrapObject(
88 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
89 return ReadableStreamDefaultController_Binding::Wrap(aCx, this, aGivenProto);
92 void ReadableStreamDefaultController::SetStream(ReadableStream* aStream) {
93 mStream = aStream;
96 // https://streams.spec.whatwg.org/#readable-stream-default-controller-can-close-or-enqueue
97 static bool ReadableStreamDefaultControllerCanCloseOrEnqueue(
98 ReadableStreamDefaultController* aController) {
99 // Step 1. Let state be controller.[[stream]].[[state]].
100 ReadableStream::ReaderState state = aController->GetStream()->State();
102 // Step 2. If controller.[[closeRequested]] is false and state is "readable",
103 // return true.
104 // Step 3. Return false.
105 return !aController->CloseRequested() &&
106 state == ReadableStream::ReaderState::Readable;
109 // https://streams.spec.whatwg.org/#readable-stream-default-controller-can-close-or-enqueue
110 // This is a variant of ReadableStreamDefaultControllerCanCloseOrEnqueue
111 // that also throws when the function would return false to improve error
112 // messages.
113 bool ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
114 ReadableStreamDefaultController* aController,
115 CloseOrEnqueue aCloseOrEnqueue, ErrorResult& aRv) {
116 // Step 1. Let state be controller.[[stream]].[[state]].
117 ReadableStream::ReaderState state = aController->GetStream()->State();
119 nsCString prefix;
120 if (aCloseOrEnqueue == CloseOrEnqueue::Close) {
121 prefix = "Cannot close a stream that "_ns;
122 } else {
123 prefix = "Cannot enqueue into a stream that "_ns;
126 switch (state) {
127 case ReadableStream::ReaderState::Readable:
128 // Step 2. If controller.[[closeRequested]] is false and
129 // state is "readable", return true.
130 // Note: We don't error/check for [[closeRequest]] first, because
131 // [[closedRequest]] is still true even after the state is "closed".
132 // This doesn't cause any spec observable difference.
133 if (!aController->CloseRequested()) {
134 return true;
137 // Step 3. Return false.
138 aRv.ThrowTypeError(prefix + "has already been requested to close."_ns);
139 return false;
141 case ReadableStream::ReaderState::Closed:
142 aRv.ThrowTypeError(prefix + "is already closed."_ns);
143 return false;
145 case ReadableStream::ReaderState::Errored:
146 aRv.ThrowTypeError(prefix + "has errored."_ns);
147 return false;
149 default:
150 MOZ_ASSERT_UNREACHABLE("Unknown ReaderState");
151 return false;
155 Nullable<double> ReadableStreamDefaultControllerGetDesiredSize(
156 ReadableStreamDefaultController* aController) {
157 ReadableStream::ReaderState state = aController->GetStream()->State();
158 if (state == ReadableStream::ReaderState::Errored) {
159 return nullptr;
162 if (state == ReadableStream::ReaderState::Closed) {
163 return 0.0;
166 return aController->StrategyHWM() - aController->QueueTotalSize();
169 // https://streams.spec.whatwg.org/#rs-default-controller-desired-size
170 Nullable<double> ReadableStreamDefaultController::GetDesiredSize() {
171 // Step 1.
172 return ReadableStreamDefaultControllerGetDesiredSize(this);
175 // https://streams.spec.whatwg.org/#readable-stream-default-controller-clear-algorithms
177 // Note: nullptr is used to indicate we run the default algorithm at the
178 // moment,
179 // so the below doesn't quite match the spec, but serves the correct
180 // purpose for disconnecting the algorithms from the object graph to allow
181 // collection.
183 // As far as I know, this isn't currently visible, but we need to keep
184 // this in mind. This is a weakness of this current implementation, and
185 // I'd prefer to have a better answer here eventually.
186 void ReadableStreamDefaultControllerClearAlgorithms(
187 ReadableStreamDefaultController* aController) {
188 // Step 1.
189 // Step 2.
190 aController->SetAlgorithms(nullptr);
192 // Step 3.
193 aController->setStrategySizeAlgorithm(nullptr);
196 // https://streams.spec.whatwg.org/#readable-stream-default-controller-close
197 void ReadableStreamDefaultControllerClose(
198 JSContext* aCx, ReadableStreamDefaultController* aController,
199 ErrorResult& aRv) {
200 // Step 1.
201 if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(aController)) {
202 return;
205 // Step 2.
206 RefPtr<ReadableStream> stream = aController->GetStream();
208 // Step 3.
209 aController->SetCloseRequested(true);
211 // Step 4.
212 if (aController->Queue().isEmpty()) {
213 // Step 4.1
214 ReadableStreamDefaultControllerClearAlgorithms(aController);
216 // Step 4.2
217 ReadableStreamClose(aCx, stream, aRv);
221 // https://streams.spec.whatwg.org/#rs-default-controller-close
222 void ReadableStreamDefaultController::Close(JSContext* aCx, ErrorResult& aRv) {
223 // Step 1.
224 if (!ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
225 this, CloseOrEnqueue::Close, aRv)) {
226 return;
229 // Step 2.
230 ReadableStreamDefaultControllerClose(aCx, this, aRv);
233 MOZ_CAN_RUN_SCRIPT static void ReadableStreamDefaultControllerCallPullIfNeeded(
234 JSContext* aCx, ReadableStreamDefaultController* aController,
235 ErrorResult& aRv);
237 // https://streams.spec.whatwg.org/#readable-stream-default-controller-enqueue
238 void ReadableStreamDefaultControllerEnqueue(
239 JSContext* aCx, ReadableStreamDefaultController* aController,
240 JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
241 // Step 1.
242 if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(aController)) {
243 return;
246 // Step 2.
247 RefPtr<ReadableStream> stream = aController->GetStream();
249 // Step 3.
250 if (IsReadableStreamLocked(stream) &&
251 ReadableStreamGetNumReadRequests(stream) > 0) {
252 ReadableStreamFulfillReadRequest(aCx, stream, aChunk, false, aRv);
253 } else {
254 // Step 4.1
255 Optional<JS::Handle<JS::Value>> optionalChunk(aCx, aChunk);
257 // Step 4.3 (Re-ordered);
258 RefPtr<QueuingStrategySize> sizeAlgorithm(
259 aController->StrategySizeAlgorithm());
261 // If !sizeAlgorithm, we return 1, which is inlined from
262 // https://streams.spec.whatwg.org/#make-size-algorithm-from-size-function
263 double chunkSize =
264 sizeAlgorithm
265 ? sizeAlgorithm->Call(
266 optionalChunk, aRv,
267 "ReadableStreamDefaultController.[[strategySizeAlgorithm]]",
268 CallbackObject::eRethrowExceptions)
269 : 1.0;
271 // If this is an uncatchable exception we can't continue.
272 if (aRv.IsUncatchableException()) {
273 return;
276 // Step 4.2:
277 if (aRv.MaybeSetPendingException(
278 aCx, "ReadableStreamDefaultController.enqueue")) {
279 JS::Rooted<JS::Value> errorValue(aCx);
281 JS_GetPendingException(aCx, &errorValue);
283 // Step 4.2.1
285 ReadableStreamDefaultControllerError(aCx, aController, errorValue, aRv);
286 if (aRv.Failed()) {
287 return;
290 // Step 4.2.2 Caller must treat aRv as if it were a completion
291 // value
292 aRv.MightThrowJSException();
293 aRv.ThrowJSException(aCx, errorValue);
294 return;
297 // Step 4.4
298 EnqueueValueWithSize(aController, aChunk, chunkSize, aRv);
300 // Step 4.5
301 // Note we convert the pending exception to a JS value here, and then
302 // re-throw it because we save this exception and re-expose it elsewhere
303 // and there are tests to ensure the identity of these errors are the same.
304 if (aRv.MaybeSetPendingException(
305 aCx, "ReadableStreamDefaultController.enqueue")) {
306 JS::Rooted<JS::Value> errorValue(aCx);
308 JS_GetPendingException(aCx, &errorValue);
310 // Step 4.5.1
311 ReadableStreamDefaultControllerError(aCx, aController, errorValue, aRv);
312 if (aRv.Failed()) {
313 return;
316 // Step 4.5.2 Caller must treat aRv as if it were a completion
317 // value
318 aRv.MightThrowJSException();
319 aRv.ThrowJSException(aCx, errorValue);
320 return;
324 // Step 5.
325 ReadableStreamDefaultControllerCallPullIfNeeded(aCx, aController, aRv);
328 // https://streams.spec.whatwg.org/#rs-default-controller-close
329 void ReadableStreamDefaultController::Enqueue(JSContext* aCx,
330 JS::Handle<JS::Value> aChunk,
331 ErrorResult& aRv) {
332 // Step 1.
333 if (!ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
334 this, CloseOrEnqueue::Enqueue, aRv)) {
335 return;
338 // Step 2.
339 ReadableStreamDefaultControllerEnqueue(aCx, this, aChunk, aRv);
342 void ReadableStreamDefaultController::Error(JSContext* aCx,
343 JS::Handle<JS::Value> aError,
344 ErrorResult& aRv) {
345 ReadableStreamDefaultControllerError(aCx, this, aError, aRv);
348 // https://streams.spec.whatwg.org/#readable-stream-default-controller-should-call-pull
349 bool ReadableStreamDefaultControllerShouldCallPull(
350 ReadableStreamDefaultController* aController) {
351 // Step 1.
352 ReadableStream* stream = aController->GetStream();
354 // Step 2.
355 if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(aController)) {
356 return false;
359 // Step 3.
360 if (!aController->Started()) {
361 return false;
364 // Step 4.
365 if (IsReadableStreamLocked(stream) &&
366 ReadableStreamGetNumReadRequests(stream) > 0) {
367 return true;
370 // Step 5.
371 Nullable<double> desiredSize =
372 ReadableStreamDefaultControllerGetDesiredSize(aController);
374 // Step 6.
375 MOZ_ASSERT(!desiredSize.IsNull());
377 // Step 7 + 8
378 return desiredSize.Value() > 0;
381 // https://streams.spec.whatwg.org/#readable-stream-default-controller-error
382 void ReadableStreamDefaultControllerError(
383 JSContext* aCx, ReadableStreamDefaultController* aController,
384 JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
385 // Step 1.
386 ReadableStream* stream = aController->GetStream();
388 // Step 2.
389 if (stream->State() != ReadableStream::ReaderState::Readable) {
390 return;
393 // Step 3.
394 ResetQueue(aController);
396 // Step 4.
397 ReadableStreamDefaultControllerClearAlgorithms(aController);
399 // Step 5.
400 ReadableStreamError(aCx, stream, aValue, aRv);
403 // https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed
404 static void ReadableStreamDefaultControllerCallPullIfNeeded(
405 JSContext* aCx, ReadableStreamDefaultController* aController,
406 ErrorResult& aRv) {
407 // Step 1.
408 bool shouldPull = ReadableStreamDefaultControllerShouldCallPull(aController);
410 // Step 2.
411 if (!shouldPull) {
412 return;
415 // Step 3.
416 if (aController->Pulling()) {
417 // Step 3.1
418 aController->SetPullAgain(true);
419 // Step 3.2
420 return;
423 // Step 4.
424 MOZ_ASSERT(!aController->PullAgain());
426 // Step 5.
427 aController->SetPulling(true);
429 // Step 6.
430 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms =
431 aController->GetAlgorithms();
432 RefPtr<Promise> pullPromise =
433 algorithms->PullCallback(aCx, *aController, aRv);
434 if (aRv.Failed()) {
435 return;
438 // Step 7 + 8:
439 pullPromise->AddCallbacksWithCycleCollectedArgs(
440 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
441 ReadableStreamDefaultController* mController)
442 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
443 // Step 7.1
444 mController->SetPulling(false);
445 // Step 7.2
446 if (mController->PullAgain()) {
447 // Step 7.2.1
448 mController->SetPullAgain(false);
450 // Step 7.2.2
451 ErrorResult rv;
452 ReadableStreamDefaultControllerCallPullIfNeeded(
453 aCx, MOZ_KnownLive(mController), aRv);
456 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
457 ReadableStreamDefaultController* mController) {
458 // Step 8.1
459 ReadableStreamDefaultControllerError(aCx, mController, aValue, aRv);
461 RefPtr(aController));
464 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller
465 void SetUpReadableStreamDefaultController(
466 JSContext* aCx, ReadableStream* aStream,
467 ReadableStreamDefaultController* aController,
468 UnderlyingSourceAlgorithmsBase* aAlgorithms, double aHighWaterMark,
469 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
470 // Step 1.
471 MOZ_ASSERT(!aStream->Controller());
473 // Step 2.
474 aController->SetStream(aStream);
476 // Step 3.
477 ResetQueue(aController);
479 // Step 4.
480 aController->SetStarted(false);
481 aController->SetCloseRequested(false);
482 aController->SetPullAgain(false);
483 aController->SetPulling(false);
485 // Step 5.
486 aController->setStrategySizeAlgorithm(aSizeAlgorithm);
487 aController->SetStrategyHWM(aHighWaterMark);
489 // Step 6.
490 // Step 7.
491 aController->SetAlgorithms(aAlgorithms);
493 // Step 8.
494 aStream->SetController(*aController);
496 // Step 9. Default algorithm returns undefined. See Step 2 of
497 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller
498 JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue());
499 RefPtr<ReadableStreamDefaultController> controller = aController;
500 aAlgorithms->StartCallback(aCx, *controller, &startResult, aRv);
501 if (aRv.Failed()) {
502 return;
505 // Step 10.
506 RefPtr<Promise> startPromise = Promise::Create(GetIncumbentGlobal(), aRv);
507 if (aRv.Failed()) {
508 return;
510 startPromise->MaybeResolve(startResult);
512 // Step 11 & 12:
513 startPromise->AddCallbacksWithCycleCollectedArgs(
514 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
515 ReadableStreamDefaultController* aController)
516 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
517 MOZ_ASSERT(aController);
519 // Step 11.1
520 aController->SetStarted(true);
522 // Step 11.2
523 aController->SetPulling(false);
525 // Step 11.3
526 aController->SetPullAgain(false);
528 // Step 11.4:
529 ReadableStreamDefaultControllerCallPullIfNeeded(
530 aCx, MOZ_KnownLive(aController), aRv);
533 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
534 ReadableStreamDefaultController* aController) {
535 // Step 12.1
536 ReadableStreamDefaultControllerError(aCx, aController, aValue, aRv);
538 RefPtr(aController));
541 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller-from-underlying-source
542 void SetupReadableStreamDefaultControllerFromUnderlyingSource(
543 JSContext* aCx, ReadableStream* aStream,
544 JS::Handle<JSObject*> aUnderlyingSource,
545 UnderlyingSource& aUnderlyingSourceDict, double aHighWaterMark,
546 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
547 // Step 1.
548 RefPtr<ReadableStreamDefaultController> controller =
549 new ReadableStreamDefaultController(aStream->GetParentObject());
551 // Step 2 - 7
552 RefPtr<UnderlyingSourceAlgorithms> algorithms =
553 new UnderlyingSourceAlgorithms(aStream->GetParentObject(),
554 aUnderlyingSource, aUnderlyingSourceDict);
556 // Step 8:
557 SetUpReadableStreamDefaultController(aCx, aStream, controller, algorithms,
558 aHighWaterMark, aSizeAlgorithm, aRv);
561 // https://streams.spec.whatwg.org/#rs-default-controller-private-cancel
562 already_AddRefed<Promise> ReadableStreamDefaultController::CancelSteps(
563 JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
564 // Step 1.
565 ResetQueue(this);
567 // Step 2.
568 Optional<JS::Handle<JS::Value>> errorOption(aCx, aReason);
569 RefPtr<UnderlyingSourceAlgorithmsBase> algorithms = mAlgorithms;
570 RefPtr<Promise> result = algorithms->CancelCallback(aCx, errorOption, aRv);
571 if (aRv.Failed()) {
572 return nullptr;
575 // Step 3.
576 ReadableStreamDefaultControllerClearAlgorithms(this);
578 // Step 4.
579 return result.forget();
582 // https://streams.spec.whatwg.org/#rs-default-controller-private-pull
583 void ReadableStreamDefaultController::PullSteps(JSContext* aCx,
584 ReadRequest* aReadRequest,
585 ErrorResult& aRv) {
586 // Step 1.
587 RefPtr<ReadableStream> stream = mStream;
589 // Step 2.
590 if (!mQueue.isEmpty()) {
591 // Step 2.1
592 JS::Rooted<JS::Value> chunk(aCx);
593 DequeueValue(this, &chunk);
595 // Step 2.2
596 if (CloseRequested() && mQueue.isEmpty()) {
597 // Step 2.2.1
598 ReadableStreamDefaultControllerClearAlgorithms(this);
599 // Step 2.2.2
600 ReadableStreamClose(aCx, stream, aRv);
601 if (aRv.Failed()) {
602 return;
604 } else {
605 // Step 2.3
606 ReadableStreamDefaultControllerCallPullIfNeeded(aCx, this, aRv);
607 if (aRv.Failed()) {
608 return;
612 // Step 2.4
613 aReadRequest->ChunkSteps(aCx, chunk, aRv);
614 } else {
615 // Step 3.
616 // Step 3.1
617 ReadableStreamAddReadRequest(stream, aReadRequest);
618 // Step 3.2
619 ReadableStreamDefaultControllerCallPullIfNeeded(aCx, this, aRv);
623 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultcontroller-releasesteps
624 void ReadableStreamDefaultController::ReleaseSteps() {
625 // Step 1. Return.
628 } // namespace mozilla::dom