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