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