1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
6
7 /* Readable stream default controller abstract operations. */
8
9 #include "builtin/streams/ReadableStreamDefaultControllerOperations.h"
10
11 #include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF}
12
13 #include "jsfriendapi.h" // js::AssertSameCompartment
14
15 #include "builtin/Stream.h" // js::ReadableByteStreamControllerClearPendingPullIntos
16 #include "builtin/streams/MiscellaneousOperations.h" // js::CreateAlgorithmFromUnderlyingMethod, js::InvokeOrNoop, js::IsMaybeWrapped
17 #include "builtin/streams/QueueWithSizes.h" // js::EnqueueValueWithSize, js::ResetQueue
18 #include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableByteStreamController, js::ReadableStreamControllerStart{,Failed}Handler
19 #include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{CloseInternal,ErrorInternal,FulfillReadOrReadIntoRequest,GetNumReadRequests}
20 #include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStreamTee_Pull, js::SetUpReadableStreamDefaultController
21 #include "builtin/streams/TeeState.h" // js::TeeState
22 #include "js/CallArgs.h" // JS::CallArgs{,FromVp}
23 #include "js/Promise.h" // JS::AddPromiseReactions
24 #include "js/RootingAPI.h" // JS::Handle, JS::Rooted
25 #include "js/Stream.h" // JS::ReadableStreamUnderlyingSource
26 #include "js/Value.h" // JS::{,Int32,Object}Value, JS::UndefinedHandleValue
27 #include "vm/Compartment.h" // JS::Compartment
28 #include "vm/Interpreter.h" // js::Call, js::GetAndClearExceptionAndStack
29 #include "vm/JSContext.h" // JSContext
30 #include "vm/JSObject.h" // JSObject
31 #include "vm/List.h" // js::ListObject
32 #include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined
33 #include "vm/Runtime.h" // JSAtomState
34 #include "vm/SavedFrame.h" // js::SavedFrame
35
36 #include "builtin/HandlerFunction-inl.h" // js::NewHandler
37 #include "builtin/streams/MiscellaneousOperations-inl.h" // js::PromiseCall
38 #include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapCalleeSlot
39 #include "vm/JSContext-inl.h" // JSContext::check
40 #include "vm/JSObject-inl.h" // js::IsCallable, js::NewBuiltinClassInstance
41 #include "vm/Realm-inl.h" // js::AutoRealm
42
43 using js::ReadableByteStreamController;
44 using js::ReadableStream;
45 using js::ReadableStreamController;
46 using js::ReadableStreamControllerCallPullIfNeeded;
47 using js::ReadableStreamControllerError;
48 using js::ReadableStreamGetNumReadRequests;
49 using js::UnwrapCalleeSlot;
50
51 using JS::CallArgs;
52 using JS::CallArgsFromVp;
53 using JS::Handle;
54 using JS::Rooted;
55 using JS::UndefinedHandleValue;
56 using JS::Value;
57
58 /*** 3.10. Readable stream default controller abstract operations ***********/
59
60 // Streams spec, 3.10.1. IsReadableStreamDefaultController ( x )
61 // Implemented via is<ReadableStreamDefaultController>()
62
63 /**
64 * Streams spec, 3.10.2 and 3.13.3. step 7:
65 * Upon fulfillment of pullPromise, [...]
66 */
ControllerPullHandler(JSContext * cx,unsigned argc,Value * vp)67 static bool ControllerPullHandler(JSContext* cx, unsigned argc, Value* vp) {
68 CallArgs args = CallArgsFromVp(argc, vp);
69
70 Rooted<ReadableStreamController*> unwrappedController(
71 cx, UnwrapCalleeSlot<ReadableStreamController>(cx, args, 0));
72 if (!unwrappedController) {
73 return false;
74 }
75
76 bool pullAgain = unwrappedController->pullAgain();
77
78 // Step a: Set controller.[[pulling]] to false.
79 // Step b.i: Set controller.[[pullAgain]] to false.
80 unwrappedController->clearPullFlags();
81
82 // Step b: If controller.[[pullAgain]] is true,
83 if (pullAgain) {
84 // Step ii: Perform
85 // ! ReadableStreamDefaultControllerCallPullIfNeeded(controller)
86 // (or ReadableByteStreamControllerCallPullIfNeeded(controller)).
87 if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) {
88 return false;
89 }
90 }
91
92 args.rval().setUndefined();
93 return true;
94 }
95
96 /**
97 * Streams spec, 3.10.2 and 3.13.3. step 8:
98 * Upon rejection of pullPromise with reason e,
99 */
ControllerPullFailedHandler(JSContext * cx,unsigned argc,Value * vp)100 static bool ControllerPullFailedHandler(JSContext* cx, unsigned argc,
101 Value* vp) {
102 CallArgs args = CallArgsFromVp(argc, vp);
103 Handle<Value> e = args.get(0);
104
105 Rooted<ReadableStreamController*> controller(
106 cx, UnwrapCalleeSlot<ReadableStreamController>(cx, args, 0));
107 if (!controller) {
108 return false;
109 }
110
111 // Step a: Perform ! ReadableStreamDefaultControllerError(controller, e).
112 // (ReadableByteStreamControllerError in 3.12.3.)
113 if (!ReadableStreamControllerError(cx, controller, e)) {
114 return false;
115 }
116
117 args.rval().setUndefined();
118 return true;
119 }
120
121 static bool ReadableStreamControllerShouldCallPull(
122 ReadableStreamController* unwrappedController);
123
124 /**
125 * Streams spec, 3.10.2
126 * ReadableStreamDefaultControllerCallPullIfNeeded ( controller )
127 * Streams spec, 3.13.3.
128 * ReadableByteStreamControllerCallPullIfNeeded ( controller )
129 */
ReadableStreamControllerCallPullIfNeeded(JSContext * cx,Handle<ReadableStreamController * > unwrappedController)130 [[nodiscard]] bool js::ReadableStreamControllerCallPullIfNeeded(
131 JSContext* cx, Handle<ReadableStreamController*> unwrappedController) {
132 // Step 1: Let shouldPull be
133 // ! ReadableStreamDefaultControllerShouldCallPull(controller).
134 // (ReadableByteStreamDefaultControllerShouldCallPull in 3.13.3.)
135 bool shouldPull = ReadableStreamControllerShouldCallPull(unwrappedController);
136
137 // Step 2: If shouldPull is false, return.
138 if (!shouldPull) {
139 return true;
140 }
141
142 // Step 3: If controller.[[pulling]] is true,
143 if (unwrappedController->pulling()) {
144 // Step a: Set controller.[[pullAgain]] to true.
145 unwrappedController->setPullAgain();
146
147 // Step b: Return.
148 return true;
149 }
150
151 // Step 4: Assert: controller.[[pullAgain]] is false.
152 MOZ_ASSERT(!unwrappedController->pullAgain());
153
154 // Step 5: Set controller.[[pulling]] to true.
155 unwrappedController->setPulling();
156
157 // We use this variable in step 7. For ease of error-handling, we wrap it
158 // early.
159 Rooted<JSObject*> wrappedController(cx, unwrappedController);
160 if (!cx->compartment()->wrap(cx, &wrappedController)) {
161 return false;
162 }
163
164 // Step 6: Let pullPromise be the result of performing
165 // controller.[[pullAlgorithm]].
166 // Our representation of pull algorithms is a bit awkward, for performance,
167 // so we must figure out which algorithm is being invoked.
168 Rooted<JSObject*> pullPromise(cx);
169 Rooted<Value> unwrappedUnderlyingSource(
170 cx, unwrappedController->underlyingSource());
171
172 if (IsMaybeWrapped<TeeState>(unwrappedUnderlyingSource)) {
173 // The pull algorithm given in ReadableStreamTee step 12.
174 MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is<TeeState>(),
175 "tee streams and controllers are always same-compartment with "
176 "the TeeState object");
177 Rooted<TeeState*> unwrappedTeeState(
178 cx, &unwrappedUnderlyingSource.toObject().as<TeeState>());
179 pullPromise = ReadableStreamTee_Pull(cx, unwrappedTeeState);
180 } else if (unwrappedController->hasExternalSource()) {
181 // An embedding-provided pull algorithm.
182 {
183 AutoRealm ar(cx, unwrappedController);
184 JS::ReadableStreamUnderlyingSource* source =
185 unwrappedController->externalSource();
186 Rooted<ReadableStream*> stream(cx, unwrappedController->stream());
187 double desiredSize =
188 ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController);
189 source->requestData(cx, stream, desiredSize);
190 }
191 pullPromise = PromiseResolvedWithUndefined(cx);
192 } else {
193 // The pull algorithm created in
194 // SetUpReadableStreamDefaultControllerFromUnderlyingSource step 4.
195 Rooted<Value> unwrappedPullMethod(cx, unwrappedController->pullMethod());
196 if (unwrappedPullMethod.isUndefined()) {
197 // CreateAlgorithmFromUnderlyingMethod step 7.
198 pullPromise = PromiseResolvedWithUndefined(cx);
199 } else {
200 // CreateAlgorithmFromUnderlyingMethod step 6.b.i.
201 {
202 AutoRealm ar(cx, unwrappedController);
203
204 // |unwrappedPullMethod| and |unwrappedUnderlyingSource| come directly
205 // from |unwrappedController| slots so must be same-compartment with it.
206 cx->check(unwrappedPullMethod);
207 cx->check(unwrappedUnderlyingSource);
208
209 Rooted<Value> controller(cx, ObjectValue(*unwrappedController));
210 cx->check(controller);
211
212 pullPromise = PromiseCall(cx, unwrappedPullMethod,
213 unwrappedUnderlyingSource, controller);
214 if (!pullPromise) {
215 return false;
216 }
217 }
218 if (!cx->compartment()->wrap(cx, &pullPromise)) {
219 return false;
220 }
221 }
222 }
223 if (!pullPromise) {
224 return false;
225 }
226
227 // Step 7: Upon fulfillment of pullPromise, [...]
228 // Step 8. Upon rejection of pullPromise with reason e, [...]
229 Rooted<JSObject*> onPullFulfilled(
230 cx, NewHandler(cx, ControllerPullHandler, wrappedController));
231 if (!onPullFulfilled) {
232 return false;
233 }
234 Rooted<JSObject*> onPullRejected(
235 cx, NewHandler(cx, ControllerPullFailedHandler, wrappedController));
236 if (!onPullRejected) {
237 return false;
238 }
239 return JS::AddPromiseReactions(cx, pullPromise, onPullFulfilled,
240 onPullRejected);
241 }
242
243 /**
244 * Streams spec, 3.10.3.
245 * ReadableStreamDefaultControllerShouldCallPull ( controller )
246 * Streams spec, 3.13.25.
247 * ReadableByteStreamControllerShouldCallPull ( controller )
248 */
ReadableStreamControllerShouldCallPull(ReadableStreamController * unwrappedController)249 static bool ReadableStreamControllerShouldCallPull(
250 ReadableStreamController* unwrappedController) {
251 // Step 1: Let stream be controller.[[controlledReadableStream]]
252 // (or [[controlledReadableByteStream]]).
253 ReadableStream* unwrappedStream = unwrappedController->stream();
254
255 // 3.10.3. Step 2:
256 // If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)
257 // is false, return false.
258 // This turns out to be the same as 3.13.25 steps 2-3.
259
260 // 3.13.25 Step 2: If stream.[[state]] is not "readable", return false.
261 if (!unwrappedStream->readable()) {
262 return false;
263 }
264
265 // 3.13.25 Step 3: If controller.[[closeRequested]] is true, return false.
266 if (unwrappedController->closeRequested()) {
267 return false;
268 }
269
270 // Step 3 (or 4):
271 // If controller.[[started]] is false, return false.
272 if (!unwrappedController->started()) {
273 return false;
274 }
275
276 // 3.10.3.
277 // Step 4: If ! IsReadableStreamLocked(stream) is true and
278 // ! ReadableStreamGetNumReadRequests(stream) > 0, return true.
279 //
280 // 3.13.25.
281 // Step 5: If ! ReadableStreamHasDefaultReader(stream) is true and
282 // ! ReadableStreamGetNumReadRequests(stream) > 0, return true.
283 // Step 6: If ! ReadableStreamHasBYOBReader(stream) is true and
284 // ! ReadableStreamGetNumReadIntoRequests(stream) > 0, return true.
285 //
286 // All of these amount to the same thing in this implementation:
287 if (unwrappedStream->locked() &&
288 ReadableStreamGetNumReadRequests(unwrappedStream) > 0) {
289 return true;
290 }
291
292 // Step 5 (or 7):
293 // Let desiredSize be
294 // ! ReadableStreamDefaultControllerGetDesiredSize(controller).
295 // (ReadableByteStreamControllerGetDesiredSize in 3.13.25.)
296 double desiredSize =
297 ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController);
298
299 // Step 6 (or 8): Assert: desiredSize is not null (implicit).
300 // Step 7 (or 9): If desiredSize > 0, return true.
301 // Step 8 (or 10): Return false.
302 return desiredSize > 0;
303 }
304
305 /**
306 * Streams spec, 3.10.4.
307 * ReadableStreamDefaultControllerClearAlgorithms ( controller )
308 * and 3.13.4.
309 * ReadableByteStreamControllerClearAlgorithms ( controller )
310 */
ReadableStreamControllerClearAlgorithms(Handle<ReadableStreamController * > controller)311 void js::ReadableStreamControllerClearAlgorithms(
312 Handle<ReadableStreamController*> controller) {
313 // Step 1: Set controller.[[pullAlgorithm]] to undefined.
314 // Step 2: Set controller.[[cancelAlgorithm]] to undefined.
315 // (In this implementation, the UnderlyingSource slot is part of the
316 // representation of these algorithms.)
317 controller->setPullMethod(UndefinedHandleValue);
318 controller->setCancelMethod(UndefinedHandleValue);
319 ReadableStreamController::clearUnderlyingSource(controller);
320
321 // Step 3 (of 3.10.4 only) : Set controller.[[strategySizeAlgorithm]] to
322 // undefined.
323 if (controller->is<ReadableStreamDefaultController>()) {
324 controller->as<ReadableStreamDefaultController>().setStrategySize(
325 UndefinedHandleValue);
326 }
327 }
328
329 /**
330 * Streams spec, 3.10.5. ReadableStreamDefaultControllerClose ( controller )
331 */
ReadableStreamDefaultControllerClose(JSContext * cx,Handle<ReadableStreamDefaultController * > unwrappedController)332 [[nodiscard]] bool js::ReadableStreamDefaultControllerClose(
333 JSContext* cx,
334 Handle<ReadableStreamDefaultController*> unwrappedController) {
335 // Step 1: Let stream be controller.[[controlledReadableStream]].
336 Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
337
338 // Step 2: Assert:
339 // ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)
340 // is true.
341 MOZ_ASSERT(!unwrappedController->closeRequested());
342 MOZ_ASSERT(unwrappedStream->readable());
343
344 // Step 3: Set controller.[[closeRequested]] to true.
345 unwrappedController->setCloseRequested();
346
347 // Step 4: If controller.[[queue]] is empty,
348 Rooted<ListObject*> unwrappedQueue(cx, unwrappedController->queue());
349 if (unwrappedQueue->length() == 0) {
350 // Step a: Perform
351 // ! ReadableStreamDefaultControllerClearAlgorithms(controller).
352 ReadableStreamControllerClearAlgorithms(unwrappedController);
353
354 // Step b: Perform ! ReadableStreamClose(stream).
355 return ReadableStreamCloseInternal(cx, unwrappedStream);
356 }
357
358 return true;
359 }
360
361 /**
362 * Streams spec, 3.10.6.
363 * ReadableStreamDefaultControllerEnqueue ( controller, chunk )
364 */
ReadableStreamDefaultControllerEnqueue(JSContext * cx,Handle<ReadableStreamDefaultController * > unwrappedController,Handle<Value> chunk)365 [[nodiscard]] bool js::ReadableStreamDefaultControllerEnqueue(
366 JSContext* cx, Handle<ReadableStreamDefaultController*> unwrappedController,
367 Handle<Value> chunk) {
368 AssertSameCompartment(cx, chunk);
369
370 // Step 1: Let stream be controller.[[controlledReadableStream]].
371 Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
372
373 // Step 2: Assert:
374 // ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) is
375 // true.
376 MOZ_ASSERT(!unwrappedController->closeRequested());
377 MOZ_ASSERT(unwrappedStream->readable());
378
379 // Step 3: If ! IsReadableStreamLocked(stream) is true and
380 // ! ReadableStreamGetNumReadRequests(stream) > 0, perform
381 // ! ReadableStreamFulfillReadRequest(stream, chunk, false).
382 if (unwrappedStream->locked() &&
383 ReadableStreamGetNumReadRequests(unwrappedStream) > 0) {
384 if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, unwrappedStream, chunk,
385 false)) {
386 return false;
387 }
388 } else {
389 // Step 4: Otherwise,
390 // Step a: Let result be the result of performing
391 // controller.[[strategySizeAlgorithm]], passing in chunk, and
392 // interpreting the result as an ECMAScript completion value.
393 // Step c: (on success) Let chunkSize be result.[[Value]].
394 Rooted<Value> chunkSize(cx, Int32Value(1));
395 bool success = true;
396 Rooted<Value> strategySize(cx, unwrappedController->strategySize());
397 if (!strategySize.isUndefined()) {
398 if (!cx->compartment()->wrap(cx, &strategySize)) {
399 return false;
400 }
401 success = Call(cx, strategySize, UndefinedHandleValue, chunk, &chunkSize);
402 }
403
404 // Step d: Let enqueueResult be
405 // EnqueueValueWithSize(controller, chunk, chunkSize).
406 if (success) {
407 success = EnqueueValueWithSize(cx, unwrappedController, chunk, chunkSize);
408 }
409
410 // Step b: If result is an abrupt completion,
411 // and
412 // Step e: If enqueueResult is an abrupt completion,
413 if (!success) {
414 Rooted<Value> exn(cx);
415 Rooted<SavedFrame*> stack(cx);
416 if (!cx->isExceptionPending() ||
417 !GetAndClearExceptionAndStack(cx, &exn, &stack)) {
418 // Uncatchable error. Die immediately without erroring the
419 // stream.
420 return false;
421 }
422
423 // Step b.i: Perform ! ReadableStreamDefaultControllerError(
424 // controller, result.[[Value]]).
425 // Step e.i: Perform ! ReadableStreamDefaultControllerError(
426 // controller, enqueueResult.[[Value]]).
427 if (!ReadableStreamControllerError(cx, unwrappedController, exn)) {
428 return false;
429 }
430
431 // Step b.ii: Return result.
432 // Step e.ii: Return enqueueResult.
433 // (I.e., propagate the exception.)
434 cx->setPendingException(exn, stack);
435 return false;
436 }
437 }
438
439 // Step 5: Perform
440 // ! ReadableStreamDefaultControllerCallPullIfNeeded(controller).
441 return ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController);
442 }
443
444 /**
445 * Streams spec, 3.10.7. ReadableStreamDefaultControllerError ( controller, e )
446 * Streams spec, 3.13.11. ReadableByteStreamControllerError ( controller, e )
447 */
ReadableStreamControllerError(JSContext * cx,Handle<ReadableStreamController * > unwrappedController,Handle<Value> e)448 [[nodiscard]] bool js::ReadableStreamControllerError(
449 JSContext* cx, Handle<ReadableStreamController*> unwrappedController,
450 Handle<Value> e) {
451 MOZ_ASSERT(!cx->isExceptionPending());
452 AssertSameCompartment(cx, e);
453
454 // Step 1: Let stream be controller.[[controlledReadableStream]]
455 // (or controller.[[controlledReadableByteStream]]).
456 Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
457
458 // Step 2: If stream.[[state]] is not "readable", return.
459 if (!unwrappedStream->readable()) {
460 return true;
461 }
462
463 // Step 3 of 3.13.10:
464 // Perform ! ReadableByteStreamControllerClearPendingPullIntos(controller).
465 if (unwrappedController->is<ReadableByteStreamController>()) {
466 Rooted<ReadableByteStreamController*> unwrappedByteStreamController(
467 cx, &unwrappedController->as<ReadableByteStreamController>());
468 if (!ReadableByteStreamControllerClearPendingPullIntos(
469 cx, unwrappedByteStreamController)) {
470 return false;
471 }
472 }
473
474 // Step 3 (or 4): Perform ! ResetQueue(controller).
475 if (!ResetQueue(cx, unwrappedController)) {
476 return false;
477 }
478
479 // Step 4 (or 5):
480 // Perform ! ReadableStreamDefaultControllerClearAlgorithms(controller)
481 // (or ReadableByteStreamControllerClearAlgorithms(controller)).
482 ReadableStreamControllerClearAlgorithms(unwrappedController);
483
484 // Step 5 (or 6): Perform ! ReadableStreamError(stream, e).
485 return ReadableStreamErrorInternal(cx, unwrappedStream, e);
486 }
487
488 /**
489 * Streams spec, 3.10.8.
490 * ReadableStreamDefaultControllerGetDesiredSize ( controller )
491 * Streams spec 3.13.14.
492 * ReadableByteStreamControllerGetDesiredSize ( controller )
493 */
ReadableStreamControllerGetDesiredSizeUnchecked(ReadableStreamController * controller)494 [[nodiscard]] double js::ReadableStreamControllerGetDesiredSizeUnchecked(
495 ReadableStreamController* controller) {
496 // Steps 1-4 done at callsites, so only assert that they have been done.
497 #if DEBUG
498 ReadableStream* stream = controller->stream();
499 MOZ_ASSERT(!(stream->errored() || stream->closed()));
500 #endif // DEBUG
501
502 // Step 5: Return controller.[[strategyHWM]] − controller.[[queueTotalSize]].
503 return controller->strategyHWM() - controller->queueTotalSize();
504 }
505
506 /**
507 * Streams spec, 3.10.11.
508 * SetUpReadableStreamDefaultController(stream, controller,
509 * startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark,
510 * sizeAlgorithm )
511 *
512 * The standard algorithm takes a `controller` argument which must be a new,
513 * blank object. This implementation creates a new controller instead.
514 *
515 * In the spec, three algorithms (startAlgorithm, pullAlgorithm,
516 * cancelAlgorithm) are passed as arguments to this routine. This
517 * implementation passes these "algorithms" as data, using four arguments:
518 * sourceAlgorithms, underlyingSource, pullMethod, and cancelMethod. The
519 * sourceAlgorithms argument tells how to interpret the other three:
520 *
521 * - SourceAlgorithms::Script - We're creating a stream from a JS source.
522 * The caller is `new ReadableStream(underlyingSource)` or
523 * `JS::NewReadableDefaultStreamObject`. `underlyingSource` is the
524 * source; `pullMethod` and `cancelMethod` are its .pull and
525 * .cancel methods, which the caller has already extracted and
526 * type-checked: each one must be either a callable JS object or undefined.
527 *
528 * Script streams use the start/pull/cancel algorithms defined in
529 * 3.10.12. SetUpReadableStreamDefaultControllerFromUnderlyingSource, which
530 * call JS methods of the underlyingSource.
531 *
532 * - SourceAlgorithms::Tee - We're creating a tee stream. `underlyingSource`
533 * is a TeeState object. `pullMethod` and `cancelMethod` are undefined.
534 *
535 * Tee streams use the start/pull/cancel algorithms given in
536 * 3.4.10. ReadableStreamTee.
537 *
538 * Note: All arguments must be same-compartment with cx. ReadableStream
539 * controllers are always created in the same compartment as the stream.
540 */
SetUpReadableStreamDefaultController(JSContext * cx,Handle<ReadableStream * > stream,SourceAlgorithms sourceAlgorithms,Handle<Value> underlyingSource,Handle<Value> pullMethod,Handle<Value> cancelMethod,double highWaterMark,Handle<Value> size)541 [[nodiscard]] bool js::SetUpReadableStreamDefaultController(
542 JSContext* cx, Handle<ReadableStream*> stream,
543 SourceAlgorithms sourceAlgorithms, Handle<Value> underlyingSource,
544 Handle<Value> pullMethod, Handle<Value> cancelMethod, double highWaterMark,
545 Handle<Value> size) {
546 cx->check(stream, underlyingSource, size);
547 MOZ_ASSERT(pullMethod.isUndefined() || IsCallable(pullMethod));
548 MOZ_ASSERT(cancelMethod.isUndefined() || IsCallable(cancelMethod));
549 MOZ_ASSERT_IF(sourceAlgorithms != SourceAlgorithms::Script,
550 pullMethod.isUndefined());
551 MOZ_ASSERT_IF(sourceAlgorithms != SourceAlgorithms::Script,
552 cancelMethod.isUndefined());
553 MOZ_ASSERT(highWaterMark >= 0);
554 MOZ_ASSERT(size.isUndefined() || IsCallable(size));
555
556 // Done elsewhere in the standard: Create the new controller.
557 Rooted<ReadableStreamDefaultController*> controller(
558 cx, NewBuiltinClassInstance<ReadableStreamDefaultController>(cx));
559 if (!controller) {
560 return false;
561 }
562
563 // Step 1: Assert: stream.[[readableStreamController]] is undefined.
564 MOZ_ASSERT(!stream->hasController());
565
566 // Step 2: Set controller.[[controlledReadableStream]] to stream.
567 controller->setStream(stream);
568
569 // Step 3: Set controller.[[queue]] and controller.[[queueTotalSize]] to
570 // undefined (implicit), then perform ! ResetQueue(controller).
571 if (!ResetQueue(cx, controller)) {
572 return false;
573 }
574
575 // Step 4: Set controller.[[started]], controller.[[closeRequested]],
576 // controller.[[pullAgain]], and controller.[[pulling]] to false.
577 controller->setFlags(0);
578
579 // Step 5: Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm
580 // and controller.[[strategyHWM]] to highWaterMark.
581 controller->setStrategySize(size);
582 controller->setStrategyHWM(highWaterMark);
583
584 // Step 6: Set controller.[[pullAlgorithm]] to pullAlgorithm.
585 // (In this implementation, the pullAlgorithm is determined by the
586 // underlyingSource in combination with the pullMethod field.)
587 controller->setUnderlyingSource(underlyingSource);
588 controller->setPullMethod(pullMethod);
589
590 // Step 7: Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
591 controller->setCancelMethod(cancelMethod);
592
593 // Step 8: Set stream.[[readableStreamController]] to controller.
594 stream->setController(controller);
595
596 // Step 9: Let startResult be the result of performing startAlgorithm.
597 Rooted<Value> startResult(cx);
598 if (sourceAlgorithms == SourceAlgorithms::Script) {
599 Rooted<Value> controllerVal(cx, ObjectValue(*controller));
600 if (!InvokeOrNoop(cx, underlyingSource, cx->names().start, controllerVal,
601 &startResult)) {
602 return false;
603 }
604 }
605
606 // Step 10: Let startPromise be a promise resolved with startResult.
607 Rooted<JSObject*> startPromise(
608 cx, PromiseObject::unforgeableResolve(cx, startResult));
609 if (!startPromise) {
610 return false;
611 }
612
613 // Step 11: Upon fulfillment of startPromise, [...]
614 // Step 12: Upon rejection of startPromise with reason r, [...]
615 Rooted<JSObject*> onStartFulfilled(
616 cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller));
617 if (!onStartFulfilled) {
618 return false;
619 }
620 Rooted<JSObject*> onStartRejected(
621 cx,
622 NewHandler(cx, ReadableStreamControllerStartFailedHandler, controller));
623 if (!onStartRejected) {
624 return false;
625 }
626 if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled,
627 onStartRejected)) {
628 return false;
629 }
630
631 return true;
632 }
633
634 /**
635 * Streams spec, 3.10.12.
636 * SetUpReadableStreamDefaultControllerFromUnderlyingSource( stream,
637 * underlyingSource, highWaterMark, sizeAlgorithm )
638 */
SetUpReadableStreamDefaultControllerFromUnderlyingSource(JSContext * cx,Handle<ReadableStream * > stream,Handle<Value> underlyingSource,double highWaterMark,Handle<Value> sizeAlgorithm)639 [[nodiscard]] bool js::SetUpReadableStreamDefaultControllerFromUnderlyingSource(
640 JSContext* cx, Handle<ReadableStream*> stream,
641 Handle<Value> underlyingSource, double highWaterMark,
642 Handle<Value> sizeAlgorithm) {
643 // Step 1: Assert: underlyingSource is not undefined.
644 MOZ_ASSERT(!underlyingSource.isUndefined());
645
646 // Step 2: Let controller be ObjectCreate(the original value of
647 // ReadableStreamDefaultController's prototype property).
648 // (Deferred to SetUpReadableStreamDefaultController.)
649
650 // Step 3: Let startAlgorithm be the following steps:
651 // a. Return ? InvokeOrNoop(underlyingSource, "start",
652 // « controller »).
653 SourceAlgorithms sourceAlgorithms = SourceAlgorithms::Script;
654
655 // Step 4: Let pullAlgorithm be
656 // ? CreateAlgorithmFromUnderlyingMethod(underlyingSource, "pull",
657 // 0, « controller »).
658 Rooted<Value> pullMethod(cx);
659 if (!CreateAlgorithmFromUnderlyingMethod(cx, underlyingSource,
660 "ReadableStream source.pull method",
661 cx->names().pull, &pullMethod)) {
662 return false;
663 }
664
665 // Step 5. Let cancelAlgorithm be
666 // ? CreateAlgorithmFromUnderlyingMethod(underlyingSource,
667 // "cancel", 1, « »).
668 Rooted<Value> cancelMethod(cx);
669 if (!CreateAlgorithmFromUnderlyingMethod(
670 cx, underlyingSource, "ReadableStream source.cancel method",
671 cx->names().cancel, &cancelMethod)) {
672 return false;
673 }
674
675 // Step 6. Perform ? SetUpReadableStreamDefaultController(stream,
676 // controller, startAlgorithm, pullAlgorithm, cancelAlgorithm,
677 // highWaterMark, sizeAlgorithm).
678 return SetUpReadableStreamDefaultController(
679 cx, stream, sourceAlgorithms, underlyingSource, pullMethod, cancelMethod,
680 highWaterMark, sizeAlgorithm);
681 }
682