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 /* General readable stream abstract operations. */
8 
9 #include "builtin/streams/ReadableStreamOperations.h"
10 
11 #include "mozilla/Assertions.h"  // MOZ_ASSERT{,_IF}
12 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
13 
14 #include "jsapi.h"  // JS_SetPrivate
15 
16 #include "builtin/Array.h"                // js::NewDenseFullyAllocatedArray
17 #include "builtin/Promise.h"              // js::RejectPromiseWithPendingError
18 #include "builtin/streams/PipeToState.h"  // js::PipeToState
19 #include "builtin/streams/ReadableStream.h"  // js::ReadableStream
20 #include "builtin/streams/ReadableStreamController.h"  // js::ReadableStream{,Default}Controller
21 #include "builtin/streams/ReadableStreamDefaultControllerOperations.h"  // js::ReadableStreamDefaultController{Close,Enqueue}, js::ReadableStreamControllerError, js::SourceAlgorithms
22 #include "builtin/streams/ReadableStreamInternals.h"  // js::ReadableStreamCancel
23 #include "builtin/streams/ReadableStreamReader.h"  // js::CreateReadableStreamDefaultReader, js::ForAuthorCodeBool, js::ReadableStream{,Default}Reader, js::ReadableStreamDefaultReaderRead
24 #include "builtin/streams/TeeState.h"              // js::TeeState
25 #include "js/CallArgs.h"                           // JS::CallArgs{,FromVp}
26 #include "js/Promise.h"  // JS::CallOriginalPromiseThen, JS::AddPromiseReactions
27 #include "js/RootingAPI.h"        // JS::{,Mutable}Handle, JS::Rooted
28 #include "js/Value.h"             // JS::Value, JS::UndefinedHandleValue
29 #include "vm/JSContext.h"         // JSContext
30 #include "vm/NativeObject.h"      // js::NativeObject
31 #include "vm/ObjectOperations.h"  // js::GetProperty
32 #include "vm/PromiseObject.h"  // js::PromiseObject, js::PromiseResolvedWithUndefined
33 
34 #include "builtin/streams/HandlerFunction-inl.h"  // js::NewHandler, js::TargetFromHandler
35 #include "builtin/streams/MiscellaneousOperations-inl.h"  // js::ResolveUnwrappedPromiseWithValue
36 #include "builtin/streams/ReadableStreamReader-inl.h"  // js::UnwrapReaderFromStream
37 #include "vm/Compartment-inl.h"  // JS::Compartment::wrap, js::Unwrap{Callee,Internal}Slot
38 #include "vm/JSObject-inl.h"  // js::IsCallable, js::NewObjectWithClassProto
39 #include "vm/Realm-inl.h"     // js::AutoRealm
40 
41 using js::IsCallable;
42 using js::NewHandler;
43 using js::NewObjectWithClassProto;
44 using js::PromiseObject;
45 using js::ReadableStream;
46 using js::ReadableStreamDefaultController;
47 using js::ReadableStreamDefaultControllerEnqueue;
48 using js::ReadableStreamDefaultReader;
49 using js::ReadableStreamReader;
50 using js::SourceAlgorithms;
51 using js::TargetFromHandler;
52 using js::TeeState;
53 using js::UnwrapCalleeSlot;
54 
55 using JS::CallArgs;
56 using JS::CallArgsFromVp;
57 using JS::Handle;
58 using JS::MutableHandle;
59 using JS::ObjectValue;
60 using JS::Rooted;
61 using JS::UndefinedHandleValue;
62 using JS::Value;
63 
64 /*** 3.4. General readable stream abstract operations ***********************/
65 
66 // Streams spec, 3.4.1. AcquireReadableStreamBYOBReader ( stream )
67 // Always inlined.
68 
69 // Streams spec, 3.4.2. AcquireReadableStreamDefaultReader ( stream )
70 // Always inlined. See CreateReadableStreamDefaultReader.
71 
72 /**
73  * Streams spec, 3.4.3. CreateReadableStream (
74  *                          startAlgorithm, pullAlgorithm, cancelAlgorithm
75  *                          [, highWaterMark [, sizeAlgorithm ] ] )
76  *
77  * The start/pull/cancelAlgorithm arguments are represented instead as four
78  * arguments: sourceAlgorithms, underlyingSource, pullMethod, cancelMethod.
79  * See the comment on SetUpReadableStreamDefaultController.
80  */
CreateReadableStream(JSContext * cx,SourceAlgorithms sourceAlgorithms,Handle<Value> underlyingSource,Handle<Value> pullMethod=UndefinedHandleValue,Handle<Value> cancelMethod=UndefinedHandleValue,double highWaterMark=1,Handle<Value> sizeAlgorithm=UndefinedHandleValue,Handle<JSObject * > proto=nullptr)81 static MOZ_MUST_USE ReadableStream* CreateReadableStream(
82     JSContext* cx, SourceAlgorithms sourceAlgorithms,
83     Handle<Value> underlyingSource,
84     Handle<Value> pullMethod = UndefinedHandleValue,
85     Handle<Value> cancelMethod = UndefinedHandleValue, double highWaterMark = 1,
86     Handle<Value> sizeAlgorithm = UndefinedHandleValue,
87     Handle<JSObject*> proto = nullptr) {
88   cx->check(underlyingSource, sizeAlgorithm, proto);
89   MOZ_ASSERT(sizeAlgorithm.isUndefined() || IsCallable(sizeAlgorithm));
90 
91   // Step 1: If highWaterMark was not passed, set it to 1 (implicit).
92   // Step 2: If sizeAlgorithm was not passed, set it to an algorithm that
93   //         returns 1 (implicit).
94   // Step 3: Assert: ! IsNonNegativeNumber(highWaterMark) is true.
95   MOZ_ASSERT(highWaterMark >= 0);
96 
97   // Step 4: Let stream be ObjectCreate(the original value of ReadableStream's
98   //         prototype property).
99   // Step 5: Perform ! InitializeReadableStream(stream).
100   Rooted<ReadableStream*> stream(cx,
101                                  ReadableStream::create(cx, nullptr, proto));
102   if (!stream) {
103     return nullptr;
104   }
105 
106   // Step 6: Let controller be ObjectCreate(the original value of
107   //         ReadableStreamDefaultController's prototype property).
108   // Step 7: Perform ? SetUpReadableStreamDefaultController(stream,
109   //         controller, startAlgorithm, pullAlgorithm, cancelAlgorithm,
110   //         highWaterMark, sizeAlgorithm).
111   if (!SetUpReadableStreamDefaultController(
112           cx, stream, sourceAlgorithms, underlyingSource, pullMethod,
113           cancelMethod, highWaterMark, sizeAlgorithm)) {
114     return nullptr;
115   }
116 
117   // Step 8: Return stream.
118   return stream;
119 }
120 
121 // Streams spec, 3.4.4. CreateReadableByteStream (
122 //                          startAlgorithm, pullAlgorithm, cancelAlgorithm
123 //                          [, highWaterMark [, autoAllocateChunkSize ] ] )
124 // Not implemented.
125 
126 /**
127  * Streams spec, 3.4.5. InitializeReadableStream ( stream )
128  */
create(JSContext * cx,void * nsISupportsObject_alreadyAddreffed,Handle<JSObject * > proto)129 /* static */ MOZ_MUST_USE ReadableStream* ReadableStream::create(
130     JSContext* cx, void* nsISupportsObject_alreadyAddreffed /* = nullptr */,
131     Handle<JSObject*> proto /* = nullptr */) {
132   // In the spec, InitializeReadableStream is always passed a newly created
133   // ReadableStream object. We instead create it here and return it below.
134   Rooted<ReadableStream*> stream(
135       cx, NewObjectWithClassProto<ReadableStream>(cx, proto));
136   if (!stream) {
137     return nullptr;
138   }
139 
140   JS_SetPrivate(stream, nsISupportsObject_alreadyAddreffed);
141 
142   // Step 1: Set stream.[[state]] to "readable".
143   stream->initStateBits(Readable);
144   MOZ_ASSERT(stream->readable());
145 
146   // Step 2: Set stream.[[reader]] and stream.[[storedError]] to
147   //         undefined (implicit).
148   MOZ_ASSERT(!stream->hasReader());
149   MOZ_ASSERT(stream->storedError().isUndefined());
150 
151   // Step 3: Set stream.[[disturbed]] to false (done in step 1).
152   MOZ_ASSERT(!stream->disturbed());
153 
154   return stream;
155 }
156 
157 // Streams spec, 3.4.6. IsReadableStream ( x )
158 // Using UnwrapAndTypeCheck templates instead.
159 
160 // Streams spec, 3.4.7. IsReadableStreamDisturbed ( stream )
161 // Using stream->disturbed() instead.
162 
163 /**
164  * Streams spec, 3.4.8. IsReadableStreamLocked ( stream )
165  */
locked() const166 bool ReadableStream::locked() const {
167   // Step 1: Assert: ! IsReadableStream(stream) is true (implicit).
168   // Step 2: If stream.[[reader]] is undefined, return false.
169   // Step 3: Return true.
170   // Special-casing for streams with external sources. Those can be locked
171   // explicitly via JSAPI, which is indicated by a controller flag.
172   // IsReadableStreamLocked is called from the controller's constructor, at
173   // which point we can't yet call stream->controller(), but the source also
174   // can't be locked yet.
175   if (hasController() && controller()->sourceLocked()) {
176     return true;
177   }
178   return hasReader();
179 }
180 
181 // Streams spec, 3.4.9. IsReadableStreamAsyncIterator ( x )
182 //
183 // Not implemented.
184 
185 /**
186  * Streams spec, 3.4.10. ReadableStreamTee steps 12.c.i-x.
187  */
TeeReaderReadHandler(JSContext * cx,unsigned argc,Value * vp)188 static bool TeeReaderReadHandler(JSContext* cx, unsigned argc, Value* vp) {
189   CallArgs args = CallArgsFromVp(argc, vp);
190 
191   Rooted<TeeState*> unwrappedTeeState(cx,
192                                       UnwrapCalleeSlot<TeeState>(cx, args, 0));
193   if (!unwrappedTeeState) {
194     return false;
195   }
196 
197   Handle<Value> resultVal = args.get(0);
198 
199   // Step 12.c.i: Set reading to false.
200   unwrappedTeeState->unsetReading();
201 
202   // Step 12.c.ii: Assert: Type(result) is Object.
203   Rooted<JSObject*> result(cx, &resultVal.toObject());
204 
205   bool done;
206   {
207     // Step 12.c.iii: Let done be ? Get(result, "done").
208     // (This can fail only if `result` was nuked.)
209     Rooted<Value> doneVal(cx);
210     if (!GetProperty(cx, result, result, cx->names().done, &doneVal)) {
211       return false;
212     }
213 
214     // Step 12.c.iv: Assert: Type(done) is Boolean.
215     done = doneVal.toBoolean();
216   }
217 
218   // Step 12.c.v: If done is true,
219   if (done) {
220     // Step 12.c.v.1: If canceled1 is false,
221     if (!unwrappedTeeState->canceled1()) {
222       // Step 12.c.v.1.a: Perform
223       //                  ! ReadableStreamDefaultControllerClose(
224       //                        branch1.[[readableStreamController]]).
225       Rooted<ReadableStreamDefaultController*> unwrappedBranch1(
226           cx, unwrappedTeeState->branch1());
227       if (!ReadableStreamDefaultControllerClose(cx, unwrappedBranch1)) {
228         return false;
229       }
230     }
231 
232     // Step 12.c.v.2: If canceled2 is false,
233     if (!unwrappedTeeState->canceled2()) {
234       // Step 12.c.v.2.a: Perform
235       //                  ! ReadableStreamDefaultControllerClose(
236       //                        branch2.[[readableStreamController]]).
237       Rooted<ReadableStreamDefaultController*> unwrappedBranch2(
238           cx, unwrappedTeeState->branch2());
239       if (!ReadableStreamDefaultControllerClose(cx, unwrappedBranch2)) {
240         return false;
241       }
242     }
243 
244     args.rval().setUndefined();
245     return true;
246   }
247 
248   // Step 12.c.vi: Let value be ! Get(result, "value").
249   // (This can fail only if `result` was nuked.)
250   Rooted<Value> value(cx);
251   if (!GetProperty(cx, result, result, cx->names().value, &value)) {
252     return false;
253   }
254 
255   // Step 12.c.vii: Let value1 and value2 be value.
256   // Step 12.c.viii: If canceled2 is false and cloneForBranch2 is true, set
257   //                 value2 to
258   //                 ? StructuredDeserialize(? StructuredSerialize(value2),
259   //                                         the current Realm Record).
260   // We don't yet support any specifications that use cloneForBranch2, and
261   // the Streams spec doesn't offer any way for author code to enable it,
262   // so it's always false here.
263   auto& value1 = value;
264   MOZ_ASSERT(!unwrappedTeeState->cloneForBranch2(),
265              "support for cloneForBranch2=true is not yet implemented");
266   auto& value2 = value;
267 
268   Rooted<ReadableStreamDefaultController*> unwrappedController(cx);
269 
270   // Step 12.c.ix: If canceled1 is false, perform
271   //               ? ReadableStreamDefaultControllerEnqueue(
272   //                     branch1.[[readableStreamController]], value1).
273   if (!unwrappedTeeState->canceled1()) {
274     unwrappedController = unwrappedTeeState->branch1();
275     if (!ReadableStreamDefaultControllerEnqueue(cx, unwrappedController,
276                                                 value1)) {
277       return false;
278     }
279   }
280 
281   // Step 12.c.x: If canceled2 is false, perform
282   //              ? ReadableStreamDefaultControllerEnqueue(
283   //                    branch2.[[readableStreamController]], value2).
284   if (!unwrappedTeeState->canceled2()) {
285     unwrappedController = unwrappedTeeState->branch2();
286     if (!ReadableStreamDefaultControllerEnqueue(cx, unwrappedController,
287                                                 value2)) {
288       return false;
289     }
290   }
291 
292   args.rval().setUndefined();
293   return true;
294 }
295 
296 /**
297  * Streams spec, 3.4.10. ReadableStreamTee step 12, "Let pullAlgorithm be the
298  * following steps:"
299  */
ReadableStreamTee_Pull(JSContext * cx,JS::Handle<TeeState * > unwrappedTeeState)300 MOZ_MUST_USE PromiseObject* js::ReadableStreamTee_Pull(
301     JSContext* cx, JS::Handle<TeeState*> unwrappedTeeState) {
302   // Combine step 12.a/12.e far below, and handle steps 12.b-12.d after
303   // inverting step 12.a's "If reading is true" condition.
304   if (!unwrappedTeeState->reading()) {
305     // Step 12.b: Set reading to true.
306     unwrappedTeeState->setReading();
307 
308     // Implicit in the spec: Unpack `reader` from the TeeState (by way of the
309     // stream stored in one of its slots).
310     Rooted<ReadableStreamDefaultReader*> unwrappedReader(cx);
311     {
312       Rooted<ReadableStream*> unwrappedStream(
313           cx, UnwrapInternalSlot<ReadableStream>(cx, unwrappedTeeState,
314                                                  TeeState::Slot_Stream));
315       if (!unwrappedStream) {
316         return nullptr;
317       }
318       ReadableStreamReader* unwrappedReaderObj =
319           UnwrapReaderFromStream(cx, unwrappedStream);
320       if (!unwrappedReaderObj) {
321         return nullptr;
322       }
323 
324       unwrappedReader = &unwrappedReaderObj->as<ReadableStreamDefaultReader>();
325     }
326 
327     // Step 12.c: Let readPromise be the result of reacting to
328     //            ! ReadableStreamDefaultReaderRead(reader) with the following
329     //            fulfillment steps given the argument result: [...]
330     // Step 12.d: Set readPromise.[[PromiseIsHandled]] to true.
331 
332     // First, perform |ReadableStreamDefaultReaderRead(reader)|.
333     Rooted<PromiseObject*> readerReadResultPromise(
334         cx, js::ReadableStreamDefaultReaderRead(cx, unwrappedReader));
335     if (!readerReadResultPromise) {
336       return nullptr;
337     }
338 
339     // Next, create a function to perform the fulfillment steps under step 12.c
340     // (implemented in the |TeeReaderReadHandler| C++ function).
341     Rooted<JSObject*> teeState(cx, unwrappedTeeState);
342     if (!cx->compartment()->wrap(cx, &teeState)) {
343       return nullptr;
344     }
345 
346     Rooted<JSObject*> onFulfilled(
347         cx, NewHandler(cx, TeeReaderReadHandler, teeState));
348     if (!onFulfilled) {
349       return nullptr;
350     }
351 
352     // Finally, perform those fulfillment steps when |readerReadResultPromise|
353     // fulfills.  (Step 12.c doesn't provide rejection steps, so don't handle
354     // rejection.)
355     //
356     // The spec's |readPromise| promise is unobservable, so implement this using
357     // a JSAPI function that acts as if it created |readPromise| but doesn't
358     // actually do so.
359     //
360     // Step 12.d causes |readPromise| to be treated as handled, even if it
361     // rejects.  Use |JS::AddPromiseReactionsIgnoringUnhandledRejection|, not
362     // |JS::AddPromiseReactions|, to avoid reporting a freshly-consed-up promise
363     // as rejected if |readerReadResultPromise| rejects.
364     if (!JS::AddPromiseReactionsIgnoringUnhandledRejection(
365             cx, readerReadResultPromise, onFulfilled, nullptr)) {
366       return nullptr;
367     }
368   }
369 
370   // Step 12.a: (If reading is true,) return a promise resolved with undefined.
371   // Step 12.e: Return a promise resolved with undefined.
372   return PromiseResolvedWithUndefined(cx);
373 }
374 
375 /**
376  * Cancel one branch of a tee'd stream with the given |reason_|.
377  *
378  * Streams spec, 3.4.10. ReadableStreamTee steps 13 and 14: "Let
379  * cancel1Algorithm/cancel2Algorithm be the following steps, taking a reason
380  * argument:"
381  */
ReadableStreamTee_Cancel(JSContext * cx,JS::Handle<TeeState * > unwrappedTeeState,JS::Handle<ReadableStreamDefaultController * > unwrappedBranch,JS::Handle<Value> reason)382 MOZ_MUST_USE JSObject* js::ReadableStreamTee_Cancel(
383     JSContext* cx, JS::Handle<TeeState*> unwrappedTeeState,
384     JS::Handle<ReadableStreamDefaultController*> unwrappedBranch,
385     JS::Handle<Value> reason) {
386   Rooted<ReadableStream*> unwrappedStream(
387       cx, UnwrapInternalSlot<ReadableStream>(cx, unwrappedTeeState,
388                                              TeeState::Slot_Stream));
389   if (!unwrappedStream) {
390     return nullptr;
391   }
392 
393   bool bothBranchesCanceled = false;
394 
395   // Step 13/14.a: Set canceled1/canceled2 to true.
396   // Step 13/14.b: Set reason1/reason2 to reason.
397   {
398     AutoRealm ar(cx, unwrappedTeeState);
399 
400     Rooted<Value> unwrappedReason(cx, reason);
401     if (!cx->compartment()->wrap(cx, &unwrappedReason)) {
402       return nullptr;
403     }
404 
405     if (unwrappedBranch->isTeeBranch1()) {
406       unwrappedTeeState->setCanceled1(unwrappedReason);
407       bothBranchesCanceled = unwrappedTeeState->canceled2();
408     } else {
409       MOZ_ASSERT(unwrappedBranch->isTeeBranch2());
410       unwrappedTeeState->setCanceled2(unwrappedReason);
411       bothBranchesCanceled = unwrappedTeeState->canceled1();
412     }
413   }
414 
415   Rooted<PromiseObject*> unwrappedCancelPromise(
416       cx, unwrappedTeeState->cancelPromise());
417   MOZ_ASSERT(unwrappedCancelPromise != nullptr);
418 
419   // Step 13/14.c: If canceled2/canceled1 is true,
420   if (bothBranchesCanceled) {
421     // Step 13/14.c.i: Let compositeReason be
422     //                 ! CreateArrayFromList(« reason1, reason2 »).
423     Rooted<Value> compositeReason(cx);
424     {
425       Rooted<Value> reason1(cx, unwrappedTeeState->reason1());
426       Rooted<Value> reason2(cx, unwrappedTeeState->reason2());
427       if (!cx->compartment()->wrap(cx, &reason1) ||
428           !cx->compartment()->wrap(cx, &reason2)) {
429         return nullptr;
430       }
431 
432       ArrayObject* reasonArray = NewDenseFullyAllocatedArray(cx, 2);
433       if (!reasonArray) {
434         return nullptr;
435       }
436       reasonArray->setDenseInitializedLength(2);
437       reasonArray->initDenseElement(0, reason1);
438       reasonArray->initDenseElement(1, reason2);
439 
440       compositeReason = ObjectValue(*reasonArray);
441     }
442 
443     // Step 13/14.c.ii: Let cancelResult be
444     //                  ! ReadableStreamCancel(stream, compositeReason).
445     // In our implementation, this can fail with OOM. The best course then
446     // is to reject cancelPromise with an OOM error.
447     Rooted<JSObject*> cancelResult(
448         cx, js::ReadableStreamCancel(cx, unwrappedStream, compositeReason));
449     if (!cancelResult) {
450       // Handle the OOM case mentioned above.
451       AutoRealm ar(cx, unwrappedCancelPromise);
452       if (!RejectPromiseWithPendingError(cx, unwrappedCancelPromise)) {
453         return nullptr;
454       }
455     } else {
456       // Step 13/14.c.iii: Resolve cancelPromise with cancelResult.
457       Rooted<Value> cancelResultVal(cx, ObjectValue(*cancelResult));
458       if (!ResolveUnwrappedPromiseWithValue(cx, unwrappedCancelPromise,
459                                             cancelResultVal)) {
460         return nullptr;
461       }
462     }
463   }
464 
465   // Step 13/14.d: Return cancelPromise.
466   Rooted<JSObject*> cancelPromise(cx, unwrappedCancelPromise);
467   if (!cx->compartment()->wrap(cx, &cancelPromise)) {
468     return nullptr;
469   }
470 
471   return cancelPromise;
472 }
473 
474 /**
475  * Streams spec, 3.4.10. step 18:
476  * Upon rejection of reader.[[closedPromise]] with reason r,
477  */
TeeReaderErroredHandler(JSContext * cx,unsigned argc,JS::Value * vp)478 static bool TeeReaderErroredHandler(JSContext* cx, unsigned argc,
479                                     JS::Value* vp) {
480   CallArgs args = CallArgsFromVp(argc, vp);
481 
482   Rooted<TeeState*> teeState(cx, TargetFromHandler<TeeState>(args));
483   Handle<Value> reason = args.get(0);
484 
485   Rooted<ReadableStreamDefaultController*> unwrappedBranchController(cx);
486 
487   // Step 18.a.i: Perform
488   //               ! ReadableStreamDefaultControllerError(
489   //                   branch1.[[readableStreamController]], r).
490   unwrappedBranchController = teeState->branch1();
491   if (!ReadableStreamControllerError(cx, unwrappedBranchController, reason)) {
492     return false;
493   }
494 
495   // Step a.ii: Perform
496   //            ! ReadableStreamDefaultControllerError(
497   //                branch2.[[readableStreamController]], r).
498   unwrappedBranchController = teeState->branch2();
499   if (!ReadableStreamControllerError(cx, unwrappedBranchController, reason)) {
500     return false;
501   }
502 
503   args.rval().setUndefined();
504   return true;
505 }
506 
507 /**
508  * Streams spec, 3.4.10. ReadableStreamTee ( stream, cloneForBranch2 )
509  */
ReadableStreamTee(JSContext * cx,JS::Handle<ReadableStream * > unwrappedStream,bool cloneForBranch2,JS::MutableHandle<ReadableStream * > branch1Stream,JS::MutableHandle<ReadableStream * > branch2Stream)510 MOZ_MUST_USE bool js::ReadableStreamTee(
511     JSContext* cx, JS::Handle<ReadableStream*> unwrappedStream,
512     bool cloneForBranch2, JS::MutableHandle<ReadableStream*> branch1Stream,
513     JS::MutableHandle<ReadableStream*> branch2Stream) {
514   // Step 1: Assert: ! IsReadableStream(stream) is true (implicit).
515 
516   // Step 2: Assert: Type(cloneForBranch2) is Boolean (implicit).
517   //
518   // The streams spec only ever passes |cloneForBranch2 = false|.  It's expected
519   // that external specs that pass |cloneForBranch2 = true| will at some point
520   // come into existence, but we don't presently implement any such specs.
521   MOZ_ASSERT(!cloneForBranch2,
522              "support for cloneForBranch2=true is not yet implemented");
523 
524   // Step 3: Let reader be ? AcquireReadableStreamDefaultReader(stream).
525   Rooted<ReadableStreamDefaultReader*> reader(
526       cx, CreateReadableStreamDefaultReader(cx, unwrappedStream,
527                                             ForAuthorCodeBool::No));
528   if (!reader) {
529     return false;
530   }
531 
532   // Several algorithms close over the variables initialized in the next few
533   // steps, so we allocate them in an object, the TeeState. The algorithms
534   // also close over `stream` and `reader`, so TeeState gets a reference to
535   // the stream.
536   //
537   // Step 4: Let reading be false.
538   // Step 5: Let canceled1 be false.
539   // Step 6: Let canceled2 be false.
540   // Step 7: Let reason1 be undefined.
541   // Step 8: Let reason2 be undefined.
542   // Step 9: Let branch1 be undefined.
543   // Step 10: Let branch2 be undefined.
544   // Step 11: Let cancelPromise be a new promise.
545   Rooted<TeeState*> teeState(cx, TeeState::create(cx, unwrappedStream));
546   if (!teeState) {
547     return false;
548   }
549 
550   MOZ_ASSERT(!teeState->reading());
551   MOZ_ASSERT(!teeState->canceled1());
552   MOZ_ASSERT(!teeState->canceled2());
553 
554   // Step 12: Let pullAlgorithm be the following steps: [...]
555   // Step 13: Let cancel1Algorithm be the following steps: [...]
556   // Step 14: Let cancel2Algorithm be the following steps: [...]
557   // Step 15: Let startAlgorithm be an algorithm that returns undefined.
558   //
559   // Implicit. Our implementation does not use objects to represent
560   // [[pullAlgorithm]], [[cancelAlgorithm]], and so on. Instead, we decide
561   // which one to perform based on class checks. For example, our
562   // implementation of ReadableStreamControllerCallPullIfNeeded checks
563   // whether the stream's underlyingSource is a TeeState object.
564 
565   // Step 16: Set branch1 to
566   //          ! CreateReadableStream(startAlgorithm, pullAlgorithm,
567   //                                 cancel1Algorithm).
568   Rooted<Value> underlyingSource(cx, ObjectValue(*teeState));
569   branch1Stream.set(
570       CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource));
571   if (!branch1Stream) {
572     return false;
573   }
574 
575   Rooted<ReadableStreamDefaultController*> branch1(cx);
576   branch1 = &branch1Stream->controller()->as<ReadableStreamDefaultController>();
577   branch1->setTeeBranch1();
578   teeState->setBranch1(branch1);
579 
580   // Step 17: Set branch2 to
581   //          ! CreateReadableStream(startAlgorithm, pullAlgorithm,
582   //                                 cancel2Algorithm).
583   branch2Stream.set(
584       CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource));
585   if (!branch2Stream) {
586     return false;
587   }
588 
589   Rooted<ReadableStreamDefaultController*> branch2(cx);
590   branch2 = &branch2Stream->controller()->as<ReadableStreamDefaultController>();
591   branch2->setTeeBranch2();
592   teeState->setBranch2(branch2);
593 
594   // Step 18: Upon rejection of reader.[[closedPromise]] with reason r, [...]
595   Rooted<JSObject*> closedPromise(cx, reader->closedPromise());
596 
597   Rooted<JSObject*> onRejected(
598       cx, NewHandler(cx, TeeReaderErroredHandler, teeState));
599   if (!onRejected) {
600     return false;
601   }
602 
603   if (!JS::AddPromiseReactions(cx, closedPromise, nullptr, onRejected)) {
604     return false;
605   }
606 
607   // Step 19: Return « branch1, branch2 ».
608   return true;
609 }
610 
611 /**
612  * Streams spec, 3.4.10.
613  *      ReadableStreamPipeTo ( source, dest, preventClose, preventAbort,
614  *                             preventCancel, signal )
615  */
ReadableStreamPipeTo(JSContext * cx,Handle<ReadableStream * > unwrappedSource,Handle<WritableStream * > unwrappedDest,bool preventClose,bool preventAbort,bool preventCancel,Handle<JSObject * > signal)616 PromiseObject* js::ReadableStreamPipeTo(JSContext* cx,
617                                         Handle<ReadableStream*> unwrappedSource,
618                                         Handle<WritableStream*> unwrappedDest,
619                                         bool preventClose, bool preventAbort,
620                                         bool preventCancel,
621                                         Handle<JSObject*> signal) {
622   // Step 1. Assert: ! IsReadableStream(source) is true.
623   // Step 2. Assert: ! IsWritableStream(dest) is true.
624   // Step 3. Assert: Type(preventClose) is Boolean, Type(preventAbort) is
625   //         Boolean, and Type(preventCancel) is Boolean.
626   // (These are guaranteed by the type system.)
627 
628   // Step 12: Let promise be a new promise.
629   //
630   // We reorder this so that this promise can be rejected and returned in case
631   // of internal error.
632   Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
633   if (!promise) {
634     return nullptr;
635   }
636 
637   // Steps 4-11, 13-14.
638   Rooted<PipeToState*> pipeToState(
639       cx,
640       PipeToState::create(cx, promise, unwrappedSource, unwrappedDest,
641                           preventClose, preventAbort, preventCancel, signal));
642   if (!pipeToState) {
643     if (!RejectPromiseWithPendingError(cx, promise)) {
644       return nullptr;
645     }
646 
647     return promise;
648   }
649 
650   // Step 15.
651   return promise;
652 }
653