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