Source code

Revision control

Other Tools

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
#include "builtin/Stream.h"
8
9
#include "js/Stream.h"
10
11
#include <stdint.h> // int32_t
12
13
#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC
14
#include "builtin/streams/MiscellaneousOperations.h" // js::CreateAlgorithmFromUnderlyingMethod, js::InvokeOrNoop, js::IsMaybeWrapped, js::PromiseCall, js::PromiseRejectedWithPendingError
15
#include "builtin/streams/PullIntoDescriptor.h" // js::PullIntoDescriptor
16
#include "builtin/streams/QueueWithSizes.h" // js::{EnqueueValueWithSize,ResetQueue}
17
#include "builtin/streams/ReadableStream.h" // js::ReadableStream, js::SetUpExternalReadableByteStreamController
18
#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableStreamDefaultControllerPullSteps, js::ReadableStreamControllerStart{,Failed}Handler
19
#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamControllerClearAlgorithms
20
#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{AddReadOrReadIntoRequest,CloseInternal,CreateReadResult,ErrorInternal,FulfillReadOrReadIntoRequest,GetNumReadRequests,HasDefaultReader}
21
#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader, js::CreateReadableStreamDefaultReader, js::ReadableStreamReaderGeneric{Cancel,Initialize,Release}, js::ReadableStreamDefaultReaderRead
22
#include "gc/Heap.h"
23
#include "js/ArrayBuffer.h" // JS::NewArrayBuffer
24
#include "js/PropertySpec.h"
25
#include "vm/Interpreter.h"
26
#include "vm/JSContext.h"
27
#include "vm/SelfHosting.h"
28
29
#include "builtin/streams/HandlerFunction-inl.h" // js::NewHandler
30
#include "builtin/streams/ReadableStreamReader-inl.h" // js::Unwrap{ReaderFromStream{,NoThrow},StreamFromReader}
31
#include "vm/Compartment-inl.h"
32
#include "vm/List-inl.h" // js::ListObject, js::StoreNewListInFixedSlot
33
#include "vm/NativeObject-inl.h"
34
35
using namespace js;
36
37
#if 0 // disable user-defined byte streams
38
39
class ByteStreamChunk : public NativeObject
40
{
41
private:
42
enum Slots {
43
Slot_Buffer = 0,
44
Slot_ByteOffset,
45
Slot_ByteLength,
46
SlotCount
47
};
48
49
public:
50
static const JSClass class_;
51
52
ArrayBufferObject* buffer() {
53
return &getFixedSlot(Slot_Buffer).toObject().as<ArrayBufferObject>();
54
}
55
uint32_t byteOffset() { return getFixedSlot(Slot_ByteOffset).toInt32(); }
56
void SetByteOffset(uint32_t offset) {
57
setFixedSlot(Slot_ByteOffset, Int32Value(offset));
58
}
59
uint32_t byteLength() { return getFixedSlot(Slot_ByteLength).toInt32(); }
60
void SetByteLength(uint32_t length) {
61
setFixedSlot(Slot_ByteLength, Int32Value(length));
62
}
63
64
static ByteStreamChunk* create(JSContext* cx, HandleObject buffer, uint32_t byteOffset,
65
uint32_t byteLength)
66
{
67
Rooted<ByteStreamChunk*> chunk(cx, NewBuiltinClassInstance<ByteStreamChunk>(cx));
68
if (!chunk) {
69
return nullptr;
70
}
71
72
chunk->setFixedSlot(Slot_Buffer, ObjectValue(*buffer));
73
chunk->setFixedSlot(Slot_ByteOffset, Int32Value(byteOffset));
74
chunk->setFixedSlot(Slot_ByteLength, Int32Value(byteLength));
75
return chunk;
76
}
77
};
78
79
const JSClass ByteStreamChunk::class_ = {
80
"ByteStreamChunk",
81
JSCLASS_HAS_RESERVED_SLOTS(SlotCount)
82
};
83
84
#endif // user-defined byte streams
85
86
/*** 3.3. ReadableStreamAsyncIteratorPrototype ******************************/
87
88
// Not implemented.
89
90
/*** 3.7. Class ReadableStreamBYOBReader ************************************/
91
92
// Not implemented.
93
94
/*** 3.11. Class ReadableByteStreamController *******************************/
95
96
#if 0 // disable user-defined byte streams
97
98
/**
99
* Streams spec, 3.10.3
100
* new ReadableByteStreamController ( stream, underlyingSource,
101
* highWaterMark )
102
* Steps 3 - 16.
103
*
104
* Note: All arguments must be same-compartment with cx. ReadableStream
105
* controllers are always created in the same compartment as the stream.
106
*/
107
static MOZ_MUST_USE ReadableByteStreamController*
108
CreateReadableByteStreamController(JSContext* cx,
109
Handle<ReadableStream*> stream,
110
HandleValue underlyingByteSource,
111
HandleValue highWaterMarkVal)
112
{
113
cx->check(stream, underlyingByteSource, highWaterMarkVal);
114
115
Rooted<ReadableByteStreamController*> controller(cx,
116
NewBuiltinClassInstance<ReadableByteStreamController>(cx));
117
if (!controller) {
118
return nullptr;
119
}
120
121
// Step 3: Set this.[[controlledReadableStream]] to stream.
122
controller->setStream(stream);
123
124
// Step 4: Set this.[[underlyingByteSource]] to underlyingByteSource.
125
controller->setUnderlyingSource(underlyingByteSource);
126
127
// Step 5: Set this.[[pullAgain]], and this.[[pulling]] to false.
128
controller->setFlags(0);
129
130
// Step 6: Perform ! ReadableByteStreamControllerClearPendingPullIntos(this).
131
if (!ReadableByteStreamControllerClearPendingPullIntos(cx, controller)) {
132
return nullptr;
133
}
134
135
// Step 7: Perform ! ResetQueue(this).
136
if (!ResetQueue(cx, controller)) {
137
return nullptr;
138
}
139
140
// Step 8: Set this.[[started]] and this.[[closeRequested]] to false.
141
// These should be false by default, unchanged since step 5.
142
MOZ_ASSERT(controller->flags() == 0);
143
144
// Step 9: Set this.[[strategyHWM]] to
145
// ? ValidateAndNormalizeHighWaterMark(highWaterMark).
146
double highWaterMark;
147
if (!ValidateAndNormalizeHighWaterMark(cx, highWaterMarkVal, &highWaterMark)) {
148
return nullptr;
149
}
150
controller->setStrategyHWM(highWaterMark);
151
152
// Step 10: Let autoAllocateChunkSize be
153
// ? GetV(underlyingByteSource, "autoAllocateChunkSize").
154
RootedValue autoAllocateChunkSize(cx);
155
if (!GetProperty(cx, underlyingByteSource, cx->names().autoAllocateChunkSize,
156
&autoAllocateChunkSize))
157
{
158
return nullptr;
159
}
160
161
// Step 11: If autoAllocateChunkSize is not undefined,
162
if (!autoAllocateChunkSize.isUndefined()) {
163
// Step a: If ! IsInteger(autoAllocateChunkSize) is false, or if
164
// autoAllocateChunkSize ≤ 0, throw a RangeError exception.
165
if (!IsInteger(autoAllocateChunkSize) || autoAllocateChunkSize.toNumber() <= 0) {
166
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
167
JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE);
168
return nullptr;
169
}
170
}
171
172
// Step 12: Set this.[[autoAllocateChunkSize]] to autoAllocateChunkSize.
173
controller->setAutoAllocateChunkSize(autoAllocateChunkSize);
174
175
// Step 13: Set this.[[pendingPullIntos]] to a new empty List.
176
if (!StoreNewListInFixedSlot(cx, controller,
177
ReadableByteStreamController::Slot_PendingPullIntos)) {
178
return nullptr;
179
}
180
181
// Step 14: Let controller be this (implicit).
182
183
// Step 15: Let startResult be
184
// ? InvokeOrNoop(underlyingSource, "start", « this »).
185
RootedValue startResult(cx);
186
RootedValue controllerVal(cx, ObjectValue(*controller));
187
if (!InvokeOrNoop(cx, underlyingByteSource, cx->names().start, controllerVal, &startResult)) {
188
return nullptr;
189
}
190
191
// Step 16: Let startPromise be a promise resolved with startResult:
192
RootedObject startPromise(cx, PromiseObject::unforgeableResolve(cx, startResult));
193
if (!startPromise) {
194
return nullptr;
195
}
196
197
RootedObject onStartFulfilled(cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller));
198
if (!onStartFulfilled) {
199
return nullptr;
200
}
201
202
RootedObject onStartRejected(cx, NewHandler(cx, ControllerStartFailedHandler, controller));
203
if (!onStartRejected) {
204
return nullptr;
205
}
206
207
if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled, onStartRejected)) {
208
return nullptr;
209
}
210
211
return controller;
212
}
213
214
#endif // user-defined byte streams
215
216
/**
217
* Streams spec, 3.11.3.
218
* new ReadableByteStreamController ( stream, underlyingByteSource,
219
* highWaterMark )
220
*/
221
bool ReadableByteStreamController::constructor(JSContext* cx, unsigned argc,
222
Value* vp) {
223
// Step 1: Throw a TypeError exception.
224
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
225
JSMSG_BOGUS_CONSTRUCTOR,
226
"ReadableByteStreamController");
227
return false;
228
}
229
230
// Disconnect the source from a controller without calling finalize() on it,
231
// unless this class is reset(). This ensures that finalize() will not be called
232
// on the source if setting up the controller fails.
233
class MOZ_RAII AutoClearUnderlyingSource {
234
Rooted<ReadableStreamController*> controller_;
235
236
public:
237
AutoClearUnderlyingSource(JSContext* cx, ReadableStreamController* controller)
238
: controller_(cx, controller) {}
239
240
~AutoClearUnderlyingSource() {
241
if (controller_) {
242
ReadableStreamController::clearUnderlyingSource(
243
controller_, /* finalizeSource */ false);
244
}
245
}
246
247
void reset() { controller_ = nullptr; }
248
};
249
250
/**
251
* Version of SetUpReadableByteStreamController that's specialized for handling
252
* external, embedding-provided, underlying sources.
253
*/
254
MOZ_MUST_USE bool js::SetUpExternalReadableByteStreamController(
255
JSContext* cx, Handle<ReadableStream*> stream,
256
JS::ReadableStreamUnderlyingSource* source) {
257
// Done elsewhere in the standard: Create the controller object.
258
Rooted<ReadableByteStreamController*> controller(
259
cx, NewBuiltinClassInstance<ReadableByteStreamController>(cx));
260
if (!controller) {
261
return false;
262
}
263
264
AutoClearUnderlyingSource autoClear(cx, controller);
265
266
// Step 1: Assert: stream.[[readableStreamController]] is undefined.
267
MOZ_ASSERT(!stream->hasController());
268
269
// Step 2: If autoAllocateChunkSize is not undefined, [...]
270
// (It's treated as undefined.)
271
272
// Step 3: Set controller.[[controlledReadableByteStream]] to stream.
273
controller->setStream(stream);
274
275
// Step 4: Set controller.[[pullAgain]] and controller.[[pulling]] to false.
276
controller->setFlags(0);
277
MOZ_ASSERT(!controller->pullAgain());
278
MOZ_ASSERT(!controller->pulling());
279
280
// Step 5: Perform
281
// ! ReadableByteStreamControllerClearPendingPullIntos(controller).
282
// Omitted. This step is apparently redundant; see
284
285
// Step 6: Perform ! ResetQueue(this).
286
controller->setQueueTotalSize(0);
287
288
// Step 7: Set controller.[[closeRequested]] and controller.[[started]] to
289
// false (implicit).
290
MOZ_ASSERT(!controller->closeRequested());
291
MOZ_ASSERT(!controller->started());
292
293
// Step 8: Set controller.[[strategyHWM]] to
294
// ? ValidateAndNormalizeHighWaterMark(highWaterMark).
295
controller->setStrategyHWM(0);
296
297
// Step 9: Set controller.[[pullAlgorithm]] to pullAlgorithm.
298
// Step 10: Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
299
// (These algorithms are given by source's virtual methods.)
300
controller->setExternalSource(source);
301
302
// Step 11: Set controller.[[autoAllocateChunkSize]] to
303
// autoAllocateChunkSize (implicit).
304
MOZ_ASSERT(controller->autoAllocateChunkSize().isUndefined());
305
306
// Step 12: Set this.[[pendingPullIntos]] to a new empty List.
307
if (!StoreNewListInFixedSlot(
308
cx, controller,
309
ReadableByteStreamController::Slot_PendingPullIntos)) {
310
return false;
311
}
312
313
// Step 13: Set stream.[[readableStreamController]] to controller.
314
stream->setController(controller);
315
316
// Step 14: Let startResult be the result of performing startAlgorithm.
317
// (For external sources, this algorithm does nothing and returns undefined.)
318
// Step 15: Let startPromise be a promise resolved with startResult.
319
RootedObject startPromise(
320
cx, PromiseObject::unforgeableResolve(cx, UndefinedHandleValue));
321
if (!startPromise) {
322
return false;
323
}
324
325
// Step 16: Upon fulfillment of startPromise, [...]
326
// Step 17: Upon rejection of startPromise with reason r, [...]
327
RootedObject onStartFulfilled(
328
cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller));
329
if (!onStartFulfilled) {
330
return false;
331
}
332
RootedObject onStartRejected(
333
cx,
334
NewHandler(cx, ReadableStreamControllerStartFailedHandler, controller));
335
if (!onStartRejected) {
336
return false;
337
}
338
if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled,
339
onStartRejected)) {
340
return false;
341
}
342
343
autoClear.reset();
344
return true;
345
}
346
347
static const JSPropertySpec ReadableByteStreamController_properties[] = {
348
JS_PS_END};
349
350
static const JSFunctionSpec ReadableByteStreamController_methods[] = {
351
JS_FS_END};
352
353
static void ReadableByteStreamControllerFinalize(JSFreeOp* fop, JSObject* obj) {
354
ReadableByteStreamController& controller =
355
obj->as<ReadableByteStreamController>();
356
357
if (controller.getFixedSlot(ReadableStreamController::Slot_Flags)
358
.isUndefined()) {
359
return;
360
}
361
362
if (!controller.hasExternalSource()) {
363
return;
364
}
365
366
controller.externalSource()->finalize();
367
}
368
369
static const JSClassOps ReadableByteStreamControllerClassOps = {
370
nullptr, /* addProperty */
371
nullptr, /* delProperty */
372
nullptr, /* enumerate */
373
nullptr, /* newEnumerate */
374
nullptr, /* resolve */
375
nullptr, /* mayResolve */
376
ReadableByteStreamControllerFinalize,
377
nullptr, /* call */
378
nullptr, /* hasInstance */
379
nullptr, /* construct */
380
nullptr, /* trace */
381
};
382
383
JS_STREAMS_CLASS_SPEC(ReadableByteStreamController, 0, SlotCount,
384
ClassSpec::DontDefineConstructor,
385
JSCLASS_BACKGROUND_FINALIZE,
386
&ReadableByteStreamControllerClassOps);
387
388
// Streams spec, 3.11.5.1. [[CancelSteps]] ()
389
// Unified with 3.9.5.1 above.
390
391
static MOZ_MUST_USE bool ReadableByteStreamControllerHandleQueueDrain(
392
JSContext* cx, Handle<ReadableStreamController*> unwrappedController);
393
394
/**
395
* Streams spec, 3.11.5.2. [[PullSteps]] ( forAuthorCode )
396
*/
397
static MOZ_MUST_USE JSObject* ReadableByteStreamControllerPullSteps(
398
JSContext* cx, Handle<ReadableByteStreamController*> unwrappedController) {
399
// Step 1: Let stream be this.[[controlledReadableByteStream]].
400
Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
401
402
// Step 2: Assert: ! ReadableStreamHasDefaultReader(stream) is true.
403
#ifdef DEBUG
404
bool result;
405
if (!ReadableStreamHasDefaultReader(cx, unwrappedStream, &result)) {
406
return nullptr;
407
}
408
MOZ_ASSERT(result);
409
#endif
410
411
RootedValue val(cx);
412
// Step 3: If this.[[queueTotalSize]] > 0,
413
double queueTotalSize = unwrappedController->queueTotalSize();
414
if (queueTotalSize > 0) {
415
// Step 3.a: Assert: ! ReadableStreamGetNumReadRequests(_stream_) is 0.
416
MOZ_ASSERT(ReadableStreamGetNumReadRequests(unwrappedStream) == 0);
417
418
RootedObject view(cx);
419
420
MOZ_RELEASE_ASSERT(unwrappedStream->mode() ==
421
JS::ReadableStreamMode::ExternalSource);
422
#if 0 // disable user-defined byte streams
423
if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource)
424
#endif // user-defined byte streams
425
{
426
JS::ReadableStreamUnderlyingSource* source =
427
unwrappedController->externalSource();
428
429
view = JS_NewUint8Array(cx, queueTotalSize);
430
if (!view) {
431
return nullptr;
432
}
433
434
size_t bytesWritten;
435
{
436
AutoRealm ar(cx, unwrappedStream);
437
JS::AutoSuppressGCAnalysis suppressGC(cx);
438
JS::AutoCheckCannotGC noGC;
439
bool dummy;
440
void* buffer = JS_GetArrayBufferViewData(view, &dummy, noGC);
441
442
source->writeIntoReadRequestBuffer(cx, unwrappedStream, buffer,
443
queueTotalSize, &bytesWritten);
444
}
445
446
queueTotalSize = queueTotalSize - bytesWritten;
447
}
448
449
#if 0 // disable user-defined byte streams
450
else {
451
// Step 3.b: Let entry be the first element of this.[[queue]].
452
// Step 3.c: Remove entry from this.[[queue]], shifting all other
453
// elements downward (so that the second becomes the
454
// first, and so on).
455
Rooted<ListObject*> unwrappedQueue(cx, unwrappedController->queue());
456
Rooted<ByteStreamChunk*> unwrappedEntry(cx,
457
UnwrapAndDowncastObject<ByteStreamChunk>(
458
cx, &unwrappedQueue->popFirstAs<JSObject>(cx)));
459
if (!unwrappedEntry) {
460
return nullptr;
461
}
462
463
queueTotalSize = queueTotalSize - unwrappedEntry->byteLength();
464
465
// Step 3.f: Let view be ! Construct(%Uint8Array%,
466
// « entry.[[buffer]],
467
// entry.[[byteOffset]],
468
// entry.[[byteLength]] »).
469
// (reordered)
470
RootedObject buffer(cx, unwrappedEntry->buffer());
471
if (!cx->compartment()->wrap(cx, &buffer)) {
472
return nullptr;
473
}
474
475
uint32_t byteOffset = unwrappedEntry->byteOffset();
476
view = JS_NewUint8ArrayWithBuffer(cx, buffer, byteOffset, unwrappedEntry->byteLength());
477
if (!view) {
478
return nullptr;
479
}
480
}
481
#endif // user-defined byte streams
482
483
// Step 3.d: Set this.[[queueTotalSize]] to
484
// this.[[queueTotalSize]] − entry.[[byteLength]].
485
// (reordered)
486
unwrappedController->setQueueTotalSize(queueTotalSize);
487
488
// Step 3.e: Perform ! ReadableByteStreamControllerHandleQueueDrain(this).
489
// (reordered)
490
if (!ReadableByteStreamControllerHandleQueueDrain(cx,
491
unwrappedController)) {
492
return nullptr;
493
}
494
495
// Step 3.g: Return a promise resolved with
496
// ! ReadableStreamCreateReadResult(view, false, forAuthorCode).
497
val.setObject(*view);
498
ReadableStreamReader* unwrappedReader =
499
UnwrapReaderFromStream(cx, unwrappedStream);
500
if (!unwrappedReader) {
501
return nullptr;
502
}
503
RootedObject readResult(
504
cx, ReadableStreamCreateReadResult(cx, val, false,
505
unwrappedReader->forAuthorCode()));
506
if (!readResult) {
507
return nullptr;
508
}
509
val.setObject(*readResult);
510
511
return PromiseObject::unforgeableResolve(cx, val);
512
}
513
514
// Step 4: Let autoAllocateChunkSize be this.[[autoAllocateChunkSize]].
515
val = unwrappedController->autoAllocateChunkSize();
516
517
// Step 5: If autoAllocateChunkSize is not undefined,
518
if (!val.isUndefined()) {
519
double autoAllocateChunkSize = val.toNumber();
520
521
// Step 5.a: Let buffer be
522
// Construct(%ArrayBuffer%, « autoAllocateChunkSize »).
523
JSObject* bufferObj = JS::NewArrayBuffer(cx, autoAllocateChunkSize);
524
525
// Step 5.b: If buffer is an abrupt completion,
526
// return a promise rejected with buffer.[[Value]].
527
if (!bufferObj) {
528
return PromiseRejectedWithPendingError(cx);
529
}
530
531
RootedArrayBufferObject buffer(cx, &bufferObj->as<ArrayBufferObject>());
532
533
// Step 5.c: Let pullIntoDescriptor be
534
// Record {[[buffer]]: buffer.[[Value]],
535
// [[byteOffset]]: 0,
536
// [[byteLength]]: autoAllocateChunkSize,
537
// [[bytesFilled]]: 0,
538
// [[elementSize]]: 1,
539
// [[ctor]]: %Uint8Array%,
540
// [[readerType]]: `"default"`}.
541
RootedObject pullIntoDescriptor(
542
cx, PullIntoDescriptor::create(cx, buffer, 0, autoAllocateChunkSize, 0,
543
1, nullptr, ReaderType::Default));
544
if (!pullIntoDescriptor) {
545
return PromiseRejectedWithPendingError(cx);
546
}
547
548
// Step 5.d: Append pullIntoDescriptor as the last element of
549
// this.[[pendingPullIntos]].
550
if (!AppendToListInFixedSlot(
551
cx, unwrappedController,
552
ReadableByteStreamController::Slot_PendingPullIntos,
553
pullIntoDescriptor)) {
554
return nullptr;
555
}
556
}
557
558
// Step 6: Let promise be ! ReadableStreamAddReadRequest(stream,
559
// forAuthorCode).
560
RootedObject promise(
561
cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream));
562
if (!promise) {
563
return nullptr;
564
}
565
566
// Step 7: Perform ! ReadableByteStreamControllerCallPullIfNeeded(this).
567
if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) {
568
return nullptr;
569
}
570
571
// Step 8: Return promise.
572
return promise;
573
}
574
575
/**
576
* Unified implementation of ReadableStream controllers' [[PullSteps]] internal
577
* methods.
578
* Streams spec, 3.9.5.2. [[PullSteps]] ( forAuthorCode )
579
* and
580
* Streams spec, 3.11.5.2. [[PullSteps]] ( forAuthorCode )
581
*/
582
MOZ_MUST_USE JSObject* js::ReadableStreamControllerPullSteps(
583
JSContext* cx, Handle<ReadableStreamController*> unwrappedController) {
584
if (unwrappedController->is<ReadableStreamDefaultController>()) {
585
Rooted<ReadableStreamDefaultController*> unwrappedDefaultController(
586
cx, &unwrappedController->as<ReadableStreamDefaultController>());
587
return ReadableStreamDefaultControllerPullSteps(cx,
588
unwrappedDefaultController);
589
}
590
591
Rooted<ReadableByteStreamController*> unwrappedByteController(
592
cx, &unwrappedController->as<ReadableByteStreamController>());
593
return ReadableByteStreamControllerPullSteps(cx, unwrappedByteController);
594
}
595
596
/*** 3.13. Readable stream BYOB controller abstract operations **************/
597
598
// Streams spec, 3.13.1. IsReadableStreamBYOBRequest ( x )
599
// Implemented via is<ReadableStreamBYOBRequest>()
600
601
// Streams spec, 3.13.2. IsReadableByteStreamController ( x )
602
// Implemented via is<ReadableByteStreamController>()
603
604
// Streams spec, 3.13.3.
605
// ReadableByteStreamControllerCallPullIfNeeded ( controller )
606
// Unified with 3.9.2 above.
607
608
static MOZ_MUST_USE bool ReadableByteStreamControllerInvalidateBYOBRequest(
609
JSContext* cx, Handle<ReadableByteStreamController*> unwrappedController);
610
611
/**
612
* Streams spec, 3.13.5.
613
* ReadableByteStreamControllerClearPendingPullIntos ( controller )
614
*/
615
MOZ_MUST_USE bool js::ReadableByteStreamControllerClearPendingPullIntos(
616
JSContext* cx, Handle<ReadableByteStreamController*> unwrappedController) {
617
// Step 1: Perform
618
// ! ReadableByteStreamControllerInvalidateBYOBRequest(controller).
619
if (!ReadableByteStreamControllerInvalidateBYOBRequest(cx,
620
unwrappedController)) {
621
return false;
622
}
623
624
// Step 2: Set controller.[[pendingPullIntos]] to a new empty List.
625
return StoreNewListInFixedSlot(
626
cx, unwrappedController,
627
ReadableByteStreamController::Slot_PendingPullIntos);
628
}
629
630
/**
631
* Streams spec, 3.13.6. ReadableByteStreamControllerClose ( controller )
632
*/
633
MOZ_MUST_USE bool js::ReadableByteStreamControllerClose(
634
JSContext* cx, Handle<ReadableByteStreamController*> unwrappedController) {
635
// Step 1: Let stream be controller.[[controlledReadableByteStream]].
636
Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
637
638
// Step 2: Assert: controller.[[closeRequested]] is false.
639
MOZ_ASSERT(!unwrappedController->closeRequested());
640
641
// Step 3: Assert: stream.[[state]] is "readable".
642
MOZ_ASSERT(unwrappedStream->readable());
643
644
// Step 4: If controller.[[queueTotalSize]] > 0,
645
if (unwrappedController->queueTotalSize() > 0) {
646
// Step a: Set controller.[[closeRequested]] to true.
647
unwrappedController->setCloseRequested();
648
649
// Step b: Return.
650
return true;
651
}
652
653
// Step 5: If controller.[[pendingPullIntos]] is not empty,
654
Rooted<ListObject*> unwrappedPendingPullIntos(
655
cx, unwrappedController->pendingPullIntos());
656
if (unwrappedPendingPullIntos->length() != 0) {
657
// Step a: Let firstPendingPullInto be the first element of
658
// controller.[[pendingPullIntos]].
659
Rooted<PullIntoDescriptor*> unwrappedFirstPendingPullInto(
660
cx, UnwrapAndDowncastObject<PullIntoDescriptor>(
661
cx, &unwrappedPendingPullIntos->get(0).toObject()));
662
if (!unwrappedFirstPendingPullInto) {
663
return false;
664
}
665
666
// Step b: If firstPendingPullInto.[[bytesFilled]] > 0,
667
if (unwrappedFirstPendingPullInto->bytesFilled() > 0) {
668
// Step i: Let e be a new TypeError exception.
669
JS_ReportErrorNumberASCII(
670
cx, GetErrorMessage, nullptr,
671
JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL);
672
RootedValue e(cx);
673
RootedSavedFrame stack(cx);
674
if (!cx->isExceptionPending() ||
675
!GetAndClearExceptionAndStack(cx, &e, &stack)) {
676
// Uncatchable error. Die immediately without erroring the
677
// stream.
678
return false;
679
}
680
681
// Step ii: Perform ! ReadableByteStreamControllerError(controller, e).
682
if (!ReadableStreamControllerError(cx, unwrappedController, e)) {
683
return false;
684
}
685
686
// Step iii: Throw e.
687
cx->setPendingException(e, stack);
688
return false;
689
}
690
}
691
692
// Step 6: Perform ! ReadableByteStreamControllerClearAlgorithms(controller).
693
ReadableStreamControllerClearAlgorithms(unwrappedController);
694
695
// Step 7: Perform ! ReadableStreamClose(stream).
696
return ReadableStreamCloseInternal(cx, unwrappedStream);
697
}
698
699
// Streams spec, 3.13.11. ReadableByteStreamControllerError ( controller, e )
700
// Unified with 3.10.7 above.
701
702
// Streams spec 3.13.14.
703
// ReadableByteStreamControllerGetDesiredSize ( controller )
704
// Unified with 3.10.8 above.
705
706
/**
707
* Streams spec, 3.13.15.
708
* ReadableByteStreamControllerHandleQueueDrain ( controller )
709
*/
710
static MOZ_MUST_USE bool ReadableByteStreamControllerHandleQueueDrain(
711
JSContext* cx, Handle<ReadableStreamController*> unwrappedController) {
712
MOZ_ASSERT(unwrappedController->is<ReadableByteStreamController>());
713
714
// Step 1: Assert: controller.[[controlledReadableStream]].[[state]]
715
// is "readable".
716
Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
717
MOZ_ASSERT(unwrappedStream->readable());
718
719
// Step 2: If controller.[[queueTotalSize]] is 0 and
720
// controller.[[closeRequested]] is true,
721
if (unwrappedController->queueTotalSize() == 0 &&
722
unwrappedController->closeRequested()) {
723
// Step a: Perform
724
// ! ReadableByteStreamControllerClearAlgorithms(controller).
725
ReadableStreamControllerClearAlgorithms(unwrappedController);
726
727
// Step b: Perform
728
// ! ReadableStreamClose(controller.[[controlledReadableStream]]).
729
return ReadableStreamCloseInternal(cx, unwrappedStream);
730
}
731
732
// Step 3: Otherwise,
733
// Step a: Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
734
return ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController);
735
}
736
737
enum BYOBRequestSlots {
738
BYOBRequestSlot_Controller,
739
BYOBRequestSlot_View,
740
BYOBRequestSlotCount
741
};
742
743
/**
744
* Streams spec 3.13.16.
745
* ReadableByteStreamControllerInvalidateBYOBRequest ( controller )
746
*/
747
static MOZ_MUST_USE bool ReadableByteStreamControllerInvalidateBYOBRequest(
748
JSContext* cx, Handle<ReadableByteStreamController*> unwrappedController) {
749
// Step 1: If controller.[[byobRequest]] is undefined, return.
750
RootedValue unwrappedBYOBRequestVal(cx, unwrappedController->byobRequest());
751
if (unwrappedBYOBRequestVal.isUndefined()) {
752
return true;
753
}
754
755
RootedNativeObject unwrappedBYOBRequest(
756
cx, UnwrapAndDowncastValue<NativeObject>(cx, unwrappedBYOBRequestVal));
757
if (!unwrappedBYOBRequest) {
758
return false;
759
}
760
761
// Step 2: Set controller.[[byobRequest]]
762
// .[[associatedReadableByteStreamController]]
763
// to undefined.
764
unwrappedBYOBRequest->setFixedSlot(BYOBRequestSlot_Controller,
765
UndefinedValue());
766
767
// Step 3: Set controller.[[byobRequest]].[[view]] to undefined.
768
unwrappedBYOBRequest->setFixedSlot(BYOBRequestSlot_View, UndefinedValue());
769
770
// Step 4: Set controller.[[byobRequest]] to undefined.
771
unwrappedController->clearBYOBRequest();
772
773
return true;
774
}
775
776
// Streams spec, 3.13.25.
777
// ReadableByteStreamControllerShouldCallPull ( controller )
778
// Unified with 3.10.3 above.