Bug 1885337 - Part 2: Implement to/from base64 methods. r=dminor
[gecko.git] / dom / streams / WritableStreamDefaultController.cpp
blobc1ee4bd8ea2689b43352fa92d24b6f7ba4959edd
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/dom/AbortSignal.h"
13 #include "mozilla/dom/Promise.h"
14 #include "mozilla/dom/Promise-inl.h"
15 #include "mozilla/dom/WritableStream.h"
16 #include "mozilla/dom/WritableStreamDefaultController.h"
17 #include "mozilla/dom/WritableStreamDefaultControllerBinding.h"
18 #include "mozilla/dom/UnderlyingSinkBinding.h"
19 #include "nsCycleCollectionParticipant.h"
20 #include "nsDebug.h"
21 #include "nsISupports.h"
23 namespace mozilla::dom {
25 using namespace streams_abstract;
27 // Note: Using the individual macros vs NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE
28 // because I need to specificy a manual implementation of
29 // NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN.
30 NS_IMPL_CYCLE_COLLECTION_CLASS(WritableStreamDefaultController)
31 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WritableStreamDefaultController)
32 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mSignal, mStrategySizeAlgorithm,
33 mAlgorithms, mStream)
34 tmp->mQueue.clear();
35 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WritableStreamDefaultController)
38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mSignal, mStrategySizeAlgorithm,
39 mAlgorithms, mStream)
40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
42 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WritableStreamDefaultController)
43 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
44 // Trace the associated queue.
45 for (const auto& queueEntry : tmp->mQueue) {
46 aCallbacks.Trace(&queueEntry->mValue, "mQueue.mValue", aClosure);
48 NS_IMPL_CYCLE_COLLECTION_TRACE_END
50 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStreamDefaultController)
51 NS_IMPL_CYCLE_COLLECTING_RELEASE(WritableStreamDefaultController)
52 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStreamDefaultController)
53 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
54 NS_INTERFACE_MAP_ENTRY(nsISupports)
55 NS_INTERFACE_MAP_END
57 WritableStreamDefaultController::WritableStreamDefaultController(
58 nsISupports* aGlobal, WritableStream& aStream)
59 : mGlobal(do_QueryInterface(aGlobal)), mStream(&aStream) {
60 mozilla::HoldJSObjects(this);
63 WritableStreamDefaultController::~WritableStreamDefaultController() {
64 // MG:XXX: LinkedLists are required to be empty at destruction, but it seems
65 // it is possible to have a controller be destructed while still
66 // having entries in its queue.
68 // This needs to be verified as not indicating some other issue.
69 mQueue.clear();
70 mozilla::DropJSObjects(this);
73 JSObject* WritableStreamDefaultController::WrapObject(
74 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
75 return WritableStreamDefaultController_Binding::Wrap(aCx, this, aGivenProto);
78 // https://streams.spec.whatwg.org/#ws-default-controller-error
79 void WritableStreamDefaultController::Error(JSContext* aCx,
80 JS::Handle<JS::Value> aError,
81 ErrorResult& aRv) {
82 // Step 1. Let state be this.[[stream]].[[state]].
83 // Step 2. If state is not "writable", return.
84 if (mStream->State() != WritableStream::WriterState::Writable) {
85 return;
87 // Step 3. Perform ! WritableStreamDefaultControllerError(this, e).
88 RefPtr<WritableStreamDefaultController> thisRefPtr = this;
89 WritableStreamDefaultControllerError(aCx, thisRefPtr, aError, aRv);
92 // https://streams.spec.whatwg.org/#ws-default-controller-private-abort
93 already_AddRefed<Promise> WritableStreamDefaultController::AbortSteps(
94 JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
95 // Step 1. Let result be the result of performing this.[[abortAlgorithm]],
96 // passing reason.
97 RefPtr<UnderlyingSinkAlgorithmsBase> algorithms = mAlgorithms;
98 Optional<JS::Handle<JS::Value>> optionalReason(aCx, aReason);
99 RefPtr<Promise> abortPromise =
100 algorithms->AbortCallback(aCx, optionalReason, aRv);
101 if (aRv.Failed()) {
102 return nullptr;
105 // Step 2. Perform ! WritableStreamDefaultControllerClearAlgorithms(this).
106 ClearAlgorithms();
108 // Step 3. Return result.
109 return abortPromise.forget();
112 // https://streams.spec.whatwg.org/#ws-default-controller-private-error
113 void WritableStreamDefaultController::ErrorSteps() {
114 // Step 1. Perform ! ResetQueue(this).
115 ResetQueue(this);
118 void WritableStreamDefaultController::SetSignal(AbortSignal* aSignal) {
119 MOZ_ASSERT(aSignal);
120 mSignal = aSignal;
123 namespace streams_abstract {
125 MOZ_CAN_RUN_SCRIPT static void
126 WritableStreamDefaultControllerAdvanceQueueIfNeeded(
127 JSContext* aCx, WritableStreamDefaultController* aController,
128 ErrorResult& aRv);
130 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller
131 void SetUpWritableStreamDefaultController(
132 JSContext* aCx, WritableStream* aStream,
133 WritableStreamDefaultController* aController,
134 UnderlyingSinkAlgorithmsBase* aAlgorithms, double aHighWaterMark,
135 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
136 // Step 1. Assert: stream implements WritableStream.
137 // Step 2. Assert: stream.[[controller]] is undefined.
138 MOZ_ASSERT(!aStream->Controller());
140 // Step 3. Set controller.[[stream]] to stream.
141 // Note: Already set in
142 // SetUpWritableStreamDefaultControllerFromUnderlyingSink.
143 MOZ_ASSERT(aController->Stream() == aStream);
145 // Step 4. Set stream.[[controller]] to controller.
146 aStream->SetController(*aController);
148 // Step 5. Perform ! ResetQueue(controller).
149 ResetQueue(aController);
151 // Step 6. Set controller.[[signal]] to a new AbortSignal.
152 RefPtr<AbortSignal> signal = new AbortSignal(aController->GetParentObject(),
153 false, JS::UndefinedHandleValue);
154 aController->SetSignal(signal);
156 // Step 7. Set controller.[[started]] to false.
157 aController->SetStarted(false);
159 // Step 8. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm.
160 aController->SetStrategySizeAlgorithm(aSizeAlgorithm);
162 // Step 9. Set controller.[[strategyHWM]] to highWaterMark.
163 aController->SetStrategyHWM(aHighWaterMark);
165 // Step 10. Set controller.[[writeAlgorithm]] to writeAlgorithm.
166 // Step 11. Set controller.[[closeAlgorithm]] to closeAlgorithm.
167 // Step 12. Set controller.[[abortAlgorithm]] to abortAlgorithm.
168 aController->SetAlgorithms(*aAlgorithms);
170 // Step 13. Let backpressure be !
171 // WritableStreamDefaultControllerGetBackpressure(controller).
172 bool backpressure = aController->GetBackpressure();
174 // Step 14. Perform ! WritableStreamUpdateBackpressure(stream, backpressure).
175 aStream->UpdateBackpressure(backpressure);
177 // Step 15. Let startResult be the result of performing startAlgorithm. (This
178 // may throw an exception.)
179 JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue());
180 RefPtr<WritableStreamDefaultController> controller(aController);
181 aAlgorithms->StartCallback(aCx, *controller, &startResult, aRv);
182 if (aRv.Failed()) {
183 return;
186 // Step 16. Let startPromise be a promise resolved with startResult.
187 RefPtr<Promise> startPromise =
188 Promise::CreateInfallible(aStream->GetParentObject());
189 startPromise->MaybeResolve(startResult);
191 // Step 17/18.
192 startPromise->AddCallbacksWithCycleCollectedArgs(
193 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
194 WritableStreamDefaultController* aController)
195 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
196 // Step 17. Upon fulfillment of startPromise,
197 // Step 17.1. Assert: stream.[[state]] is "writable" or "erroring".
198 MOZ_ASSERT(aController->Stream()->State() ==
199 WritableStream::WriterState::Writable ||
200 aController->Stream()->State() ==
201 WritableStream::WriterState::Erroring);
202 // Step 17.2. Set controller.[[started]] to true.
203 aController->SetStarted(true);
204 // Step 17.3 Perform
205 // !WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
206 WritableStreamDefaultControllerAdvanceQueueIfNeeded(
207 aCx, MOZ_KnownLive(aController), aRv);
209 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
210 WritableStreamDefaultController* aController)
211 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
212 RefPtr<WritableStream> stream = aController->Stream();
213 // Step 18. Upon rejection of startPromise with reason r,
214 // Step 18.1. Assert: stream.[[state]] is "writable" or "erroring".
215 MOZ_ASSERT(
216 stream->State() == WritableStream::WriterState::Writable ||
217 stream->State() == WritableStream::WriterState::Erroring);
218 // Step 18.2. Set controller.[[started]] to true.
219 aController->SetStarted(true);
220 // Step 18.3. Perform ! WritableStreamDealWithRejection(stream, r).
221 stream->DealWithRejection(aCx, aValue, aRv);
223 RefPtr(aController));
226 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink
227 void SetUpWritableStreamDefaultControllerFromUnderlyingSink(
228 JSContext* aCx, WritableStream* aStream,
229 JS::Handle<JSObject*> aUnderlyingSink, UnderlyingSink& aUnderlyingSinkDict,
230 double aHighWaterMark, QueuingStrategySize* aSizeAlgorithm,
231 ErrorResult& aRv) {
232 // Step 1.
233 RefPtr<WritableStreamDefaultController> controller =
234 new WritableStreamDefaultController(aStream->GetParentObject(), *aStream);
236 // Step 2 - 9.
237 auto algorithms = MakeRefPtr<UnderlyingSinkAlgorithms>(
238 aStream->GetParentObject(), aUnderlyingSink, aUnderlyingSinkDict);
240 // Step 10.
241 SetUpWritableStreamDefaultController(aCx, aStream, controller, algorithms,
242 aHighWaterMark, aSizeAlgorithm, aRv);
245 // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close
246 MOZ_CAN_RUN_SCRIPT static void WritableStreamDefaultControllerProcessClose(
247 JSContext* aCx, WritableStreamDefaultController* aController,
248 ErrorResult& aRv) {
249 // Step 1. Let stream be controller.[[stream]].
250 RefPtr<WritableStream> stream = aController->Stream();
252 // Step 2. Perform ! WritableStreamMarkCloseRequestInFlight(stream).
253 stream->MarkCloseRequestInFlight();
255 // Step 3. Perform ! DequeueValue(controller).
256 JS::Rooted<JS::Value> value(aCx);
257 DequeueValue(aController, &value);
259 // Step 4. Assert: controller.[[queue]] is empty.
260 MOZ_ASSERT(aController->Queue().isEmpty());
262 // Step 5. Let sinkClosePromise be the result of performing
263 // controller.[[closeAlgorithm]].
264 RefPtr<UnderlyingSinkAlgorithmsBase> algorithms =
265 aController->GetAlgorithms();
266 RefPtr<Promise> sinkClosePromise = algorithms->CloseCallback(aCx, aRv);
267 if (aRv.Failed()) {
268 return;
271 // Step 6. Perform !
272 // WritableStreamDefaultControllerClearAlgorithms(controller).
273 aController->ClearAlgorithms();
275 // Step 7 + 8.
276 sinkClosePromise->AddCallbacksWithCycleCollectedArgs(
277 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
278 WritableStreamDefaultController* aController) {
279 RefPtr<WritableStream> stream = aController->Stream();
280 // Step 7. Upon fulfillment of sinkClosePromise,
281 // Step 7.1. Perform ! WritableStreamFinishInFlightClose(stream).
282 stream->FinishInFlightClose();
284 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
285 WritableStreamDefaultController* aController)
286 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
287 RefPtr<WritableStream> stream = aController->Stream();
288 // Step 8. Upon rejection of sinkClosePromise with reason reason,
289 // Step 8.1. Perform
290 // ! WritableStreamFinishInFlightCloseWithError(stream, reason).
291 stream->FinishInFlightCloseWithError(aCx, aValue, aRv);
293 RefPtr(aController));
296 // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write
297 MOZ_CAN_RUN_SCRIPT static void WritableStreamDefaultControllerProcessWrite(
298 JSContext* aCx, WritableStreamDefaultController* aController,
299 JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
300 // Step 1. Let stream be controller.[[stream]].
301 RefPtr<WritableStream> stream = aController->Stream();
303 // Step 2. Perform ! WritableStreamMarkFirstWriteRequestInFlight(stream).
304 stream->MarkFirstWriteRequestInFlight();
306 // Step 3. Let sinkWritePromise be the result of performing
307 // controller.[[writeAlgorithm]], passing in chunk.
308 RefPtr<UnderlyingSinkAlgorithmsBase> algorithms =
309 aController->GetAlgorithms();
310 RefPtr<Promise> sinkWritePromise =
311 algorithms->WriteCallback(aCx, aChunk, *aController, aRv);
312 if (aRv.Failed()) {
313 return;
316 // Step 4 + 5:
317 sinkWritePromise->AddCallbacksWithCycleCollectedArgs(
318 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
319 WritableStreamDefaultController* aController)
320 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
321 RefPtr<WritableStream> stream = aController->Stream();
323 // Step 4.1. Perform ! WritableStreamFinishInFlightWrite(stream).
324 stream->FinishInFlightWrite();
326 // Step 4.2. Let state be stream.[[state]].
327 WritableStream::WriterState state = stream->State();
329 // Step 4.3. Assert: state is "writable" or "erroring".
330 MOZ_ASSERT(state == WritableStream::WriterState::Writable ||
331 state == WritableStream::WriterState::Erroring);
333 // Step 4.4. Perform ! DequeueValue(controller).
334 JS::Rooted<JS::Value> value(aCx);
335 DequeueValue(aController, &value);
337 // Step 4.5. If ! WritableStreamCloseQueuedOrInFlight(stream) is
338 // false and state is "writable",
339 if (!stream->CloseQueuedOrInFlight() &&
340 state == WritableStream::WriterState::Writable) {
341 // Step 4.5.1. Let backpressure be !
342 // WritableStreamDefaultControllerGetBackpressure(controller).
343 bool backpressure = aController->GetBackpressure();
344 // Step 4.5.2. Perform ! WritableStreamUpdateBackpressure(stream,
345 // backpressure).
346 stream->UpdateBackpressure(backpressure);
349 // Step 4.6. Perform !
350 // WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
351 WritableStreamDefaultControllerAdvanceQueueIfNeeded(
352 aCx, MOZ_KnownLive(aController), aRv);
354 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
355 WritableStreamDefaultController* aController)
356 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
357 RefPtr<WritableStream> stream = aController->Stream();
359 // Step 5.1. If stream.[[state]] is "writable", perform !
360 // WritableStreamDefaultControllerClearAlgorithms(controller).
361 if (stream->State() == WritableStream::WriterState::Writable) {
362 aController->ClearAlgorithms();
365 // Step 5.2. Perform !
366 // WritableStreamFinishInFlightWriteWithError(stream, reason)
367 stream->FinishInFlightWriteWithError(aCx, aValue, aRv);
369 RefPtr(aController));
372 // We use a JS::MagicValue to represent the close sentinel required by the spec.
373 // Normal JavaScript code can not generate magic values, so we can use this
374 // as a special value. However care has to be taken to not leak the magic value
375 // to other code.
376 constexpr JSWhyMagic CLOSE_SENTINEL = JS_GENERIC_MAGIC;
378 // https://streams.spec.whatwg.org/#writable-stream-default-controller-advance-queue-if-needed
379 static void WritableStreamDefaultControllerAdvanceQueueIfNeeded(
380 JSContext* aCx, WritableStreamDefaultController* aController,
381 ErrorResult& aRv) {
382 // Step 1. Let stream be controller.[[stream]].
383 RefPtr<WritableStream> stream = aController->Stream();
385 // Step 2. If controller.[[started]] is false, return.
386 if (!aController->Started()) {
387 return;
390 // Step 3. If stream.[[inFlightWriteRequest]] is not undefined, return.
391 if (stream->GetInFlightWriteRequest()) {
392 return;
395 // Step 4. Let state be stream.[[state]].
396 WritableStream::WriterState state = stream->State();
398 // Step 5. Assert: state is not "closed" or "errored".
399 MOZ_ASSERT(state != WritableStream::WriterState::Closed &&
400 state != WritableStream::WriterState::Errored);
402 // Step 6. If state is "erroring",
403 if (state == WritableStream::WriterState::Erroring) {
404 // Step 6.1. Perform ! WritableStreamFinishErroring(stream).
405 stream->FinishErroring(aCx, aRv);
407 // Step 6.2. Return.
408 return;
411 // Step 7. If controller.[[queue]] is empty, return.
412 if (aController->Queue().isEmpty()) {
413 return;
416 // Step 8. Let value be ! PeekQueueValue(controller).
417 JS::Rooted<JS::Value> value(aCx);
418 PeekQueueValue(aController, &value);
420 // Step 9. If value is the close sentinel, perform !
421 // WritableStreamDefaultControllerProcessClose(controller).
422 if (value.isMagic(CLOSE_SENTINEL)) {
423 WritableStreamDefaultControllerProcessClose(aCx, aController, aRv);
424 return;
427 // Step 10. Otherwise, perform !
428 // WritableStreamDefaultControllerProcessWrite(controller, value).
429 WritableStreamDefaultControllerProcessWrite(aCx, aController, value, aRv);
432 // https://streams.spec.whatwg.org/#writable-stream-default-controller-close
433 void WritableStreamDefaultControllerClose(
434 JSContext* aCx, WritableStreamDefaultController* aController,
435 ErrorResult& aRv) {
436 // Step 1. Perform ! EnqueueValueWithSize(controller, close sentinel, 0).
437 JS::Rooted<JS::Value> aCloseSentinel(aCx, JS::MagicValue(CLOSE_SENTINEL));
438 EnqueueValueWithSize(aController, aCloseSentinel, 0, aRv);
439 MOZ_ASSERT(!aRv.Failed());
441 // Step 2. Perform !
442 // WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
443 WritableStreamDefaultControllerAdvanceQueueIfNeeded(aCx, aController, aRv);
446 // https://streams.spec.whatwg.org/#writable-stream-default-controller-write
447 void WritableStreamDefaultControllerWrite(
448 JSContext* aCx, WritableStreamDefaultController* aController,
449 JS::Handle<JS::Value> aChunk, double chunkSize, ErrorResult& aRv) {
450 // Step 1. Let enqueueResult be EnqueueValueWithSize(controller, chunk,
451 // chunkSize).
452 IgnoredErrorResult rv;
453 EnqueueValueWithSize(aController, aChunk, chunkSize, rv);
455 // Step 2. If enqueueResult is an abrupt completion,
456 if (rv.MaybeSetPendingException(aCx,
457 "WritableStreamDefaultController.write")) {
458 JS::Rooted<JS::Value> error(aCx);
459 JS_GetPendingException(aCx, &error);
460 JS_ClearPendingException(aCx);
462 // Step 2.1. Perform !
463 // WritableStreamDefaultControllerErrorIfNeeded(controller,
464 // enqueueResult.[[Value]]).
465 WritableStreamDefaultControllerErrorIfNeeded(aCx, aController, error, aRv);
467 // Step 2.2. Return.
468 return;
471 // Step 3. Let stream be controller.[[stream]].
472 RefPtr<WritableStream> stream = aController->Stream();
474 // Step 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and
475 // stream.[[state]] is "writable",
476 if (!stream->CloseQueuedOrInFlight() &&
477 stream->State() == WritableStream::WriterState::Writable) {
478 // Step 4.1. Let backpressure be
479 // !WritableStreamDefaultControllerGetBackpressure(controller).
480 bool backpressure = aController->GetBackpressure();
482 // Step 4.2. Perform ! WritableStreamUpdateBackpressure(stream,
483 // backpressure).
484 stream->UpdateBackpressure(backpressure);
487 // Step 5. Perform
488 // ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
489 WritableStreamDefaultControllerAdvanceQueueIfNeeded(aCx, aController, aRv);
492 void WritableStreamDefaultControllerError(
493 JSContext* aCx, WritableStreamDefaultController* aController,
494 JS::Handle<JS::Value> aError, ErrorResult& aRv) {
495 // Step 1. Let stream be controller.[[stream]].
496 RefPtr<WritableStream> stream = aController->Stream();
498 // Step 2. Assert: stream.[[state]] is "writable".
499 MOZ_ASSERT(stream->State() == WritableStream::WriterState::Writable);
501 // Step 3. Perform
502 // ! WritableStreamDefaultControllerClearAlgorithms(controller).
503 aController->ClearAlgorithms();
505 // Step 4.Perform ! WritableStreamStartErroring(stream, error).
506 stream->StartErroring(aCx, aError, aRv);
509 // https://streams.spec.whatwg.org/#writable-stream-default-controller-error-if-needed
510 void WritableStreamDefaultControllerErrorIfNeeded(
511 JSContext* aCx, WritableStreamDefaultController* aController,
512 JS::Handle<JS::Value> aError, ErrorResult& aRv) {
513 // Step 1. If controller.[[stream]].[[state]] is "writable", perform
514 // !WritableStreamDefaultControllerError(controller, error).
515 if (aController->Stream()->State() == WritableStream::WriterState::Writable) {
516 WritableStreamDefaultControllerError(aCx, aController, aError, aRv);
520 // https://streams.spec.whatwg.org/#writable-stream-default-controller-get-chunk-size
521 double WritableStreamDefaultControllerGetChunkSize(
522 JSContext* aCx, WritableStreamDefaultController* aController,
523 JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
524 // Step 1. Let returnValue be the result of performing
525 // controller.[[strategySizeAlgorithm]], passing in chunk, and interpreting
526 // the result as a completion record.
527 RefPtr<QueuingStrategySize> sizeAlgorithm(
528 aController->StrategySizeAlgorithm());
530 // If !sizeAlgorithm, we return 1, which is inlined from
531 // https://streams.spec.whatwg.org/#make-size-algorithm-from-size-function
532 Optional<JS::Handle<JS::Value>> optionalChunk(aCx, aChunk);
534 double chunkSize =
535 sizeAlgorithm
536 ? sizeAlgorithm->Call(
537 optionalChunk, aRv,
538 "WritableStreamDefaultController.[[strategySizeAlgorithm]]",
539 CallbackObject::eRethrowExceptions)
540 : 1.0;
542 // Step 2. If returnValue is an abrupt completion,
543 if (aRv.MaybeSetPendingException(
544 aCx, "WritableStreamDefaultController.[[strategySizeAlgorithm]]")) {
545 JS::Rooted<JS::Value> error(aCx);
546 JS_GetPendingException(aCx, &error);
547 JS_ClearPendingException(aCx);
549 // Step 2.1. Perform !
550 // WritableStreamDefaultControllerErrorIfNeeded(controller,
551 // returnValue.[[Value]]).
552 WritableStreamDefaultControllerErrorIfNeeded(aCx, aController, error, aRv);
554 // Step 2.2. Return 1.
555 return 1.0;
558 // Step 3. Return returnValue.[[Value]].
559 return chunkSize;
562 } // namespace streams_abstract
564 } // namespace mozilla::dom