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 /* Public and friend stream APIs for external use. */
8
9 #include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF}
10
11 #include <stdint.h> // uint32_t, uintptr_t
12
13 #include "jsapi.h" // js::AssertHeapIsIdle, JS_ReportErrorNumberASCII
14 #include "jsfriendapi.h" // js::IsObjectInContextCompartment
15 #include "jstypes.h" // JS_{FRIEND,PUBLIC}_API
16
17 #include "builtin/Stream.h" // js::ReadableByteStreamController{,Close}, js::ReadableStreamDefaultController{,Close}, js::StreamController
18 #include "builtin/streams/ReadableStream.h" // js::ReadableStream
19 #include "builtin/streams/ReadableStreamController.h" // js::CheckReadableStreamControllerCanCloseOrEnqueue
20 #include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamController{Error,GetDesiredSizeUnchecked}, js::SetUpReadableStreamDefaultControllerFromUnderlyingSource
21 #include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{Cancel,FulfillReadOrReadIntoRequest,GetNumReadRequests,HasDefaultReader}
22 #include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStreamTee
23 #include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader, js::ForAuthorCodeBool
24 #include "builtin/streams/StreamController.h" // js::StreamController
25 #include "gc/Zone.h" // JS::Zone
26 #include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_NewUint8Array
27 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
28 #include "js/GCAPI.h" // JS::AutoCheckCannotGC, JS::AutoSuppressGCAnalysis
29 #include "js/Object.h" // JS::SetPrivate
30 #include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted
31 #include "js/Stream.h" // JS::ReadableStreamUnderlyingSource
32 #include "js/Value.h" // JS::{,Object,Undefined}Value
33 #include "vm/ArrayBufferViewObject.h" // js::ArrayBufferViewObject
34 #include "vm/JSContext.h" // JSContext, CHECK_THREAD
35 #include "vm/JSObject.h" // JSObject
36 #include "vm/PlainObject.h" // js::PlainObject
37 #include "vm/PromiseObject.h" // js::PromiseObject
38
39 #include "builtin/streams/ReadableStreamReader-inl.h" // js::UnwrapStreamFromReader
40 #include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapAndDowncastObject
41 #include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance
42 #include "vm/Realm-inl.h" // js::AutoRealm
43
44 using js::ArrayBufferViewObject;
45 using js::AssertHeapIsIdle;
46 using js::AutoRealm;
47 using js::CheckReadableStreamControllerCanCloseOrEnqueue;
48 using js::ForAuthorCodeBool;
49 using js::GetErrorMessage;
50 using js::IsObjectInContextCompartment;
51 using js::NewBuiltinClassInstance;
52 using js::PlainObject;
53 using js::ReadableByteStreamController;
54 using js::ReadableByteStreamControllerClose;
55 using js::ReadableStream;
56 using js::ReadableStreamController;
57 using js::ReadableStreamControllerError;
58 using js::ReadableStreamControllerGetDesiredSizeUnchecked;
59 using js::ReadableStreamDefaultController;
60 using js::ReadableStreamDefaultControllerClose;
61 using js::ReadableStreamDefaultReader;
62 using js::ReadableStreamFulfillReadOrReadIntoRequest;
63 using js::ReadableStreamGetNumReadRequests;
64 using js::ReadableStreamHasDefaultReader;
65 using js::ReadableStreamReader;
66 using js::ReadableStreamTee;
67 using js::SetUpReadableStreamDefaultControllerFromUnderlyingSource;
68 using js::StreamController;
69 using js::UnwrapAndDowncastObject;
70 using js::UnwrapStreamFromReader;
71
UnwrapReadableStream(JSObject * obj)72 JS_PUBLIC_API JSObject* js::UnwrapReadableStream(JSObject* obj) {
73 return obj->maybeUnwrapIf<ReadableStream>();
74 }
75
NewReadableDefaultStreamObject(JSContext * cx,JS::Handle<JSObject * > underlyingSource,JS::Handle<JSFunction * > size,double highWaterMark,JS::Handle<JSObject * > proto)76 JS_PUBLIC_API JSObject* JS::NewReadableDefaultStreamObject(
77 JSContext* cx, JS::Handle<JSObject*> underlyingSource /* = nullptr */,
78 JS::Handle<JSFunction*> size /* = nullptr */,
79 double highWaterMark /* = 1 */,
80 JS::Handle<JSObject*> proto /* = nullptr */) {
81 MOZ_ASSERT(!cx->zone()->isAtomsZone());
82 AssertHeapIsIdle();
83 CHECK_THREAD(cx);
84 cx->check(underlyingSource, size, proto);
85 MOZ_ASSERT(highWaterMark >= 0);
86
87 // A copy of ReadableStream::constructor, with most of the
88 // argument-checking done implicitly by C++ type checking.
89 Rooted<ReadableStream*> stream(cx, ReadableStream::create(cx));
90 if (!stream) {
91 return nullptr;
92 }
93 Rooted<Value> sourceVal(cx);
94 if (underlyingSource) {
95 sourceVal.setObject(*underlyingSource);
96 } else {
97 JSObject* source = NewBuiltinClassInstance<PlainObject>(cx);
98 if (!source) {
99 return nullptr;
100 }
101 sourceVal.setObject(*source);
102 }
103 Rooted<Value> sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue());
104
105 if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource(
106 cx, stream, sourceVal, highWaterMark, sizeVal)) {
107 return nullptr;
108 }
109
110 return stream;
111 }
112
NewReadableExternalSourceStreamObject(JSContext * cx,JS::ReadableStreamUnderlyingSource * underlyingSource,void * nsISupportsObject_alreadyAddreffed,Handle<JSObject * > proto)113 JS_PUBLIC_API JSObject* JS::NewReadableExternalSourceStreamObject(
114 JSContext* cx, JS::ReadableStreamUnderlyingSource* underlyingSource,
115 void* nsISupportsObject_alreadyAddreffed /* = nullptr */,
116 Handle<JSObject*> proto /* = nullptr */) {
117 MOZ_ASSERT(!cx->zone()->isAtomsZone());
118 AssertHeapIsIdle();
119 CHECK_THREAD(cx);
120 MOZ_ASSERT(underlyingSource);
121 MOZ_ASSERT((uintptr_t(underlyingSource) & 1) == 0,
122 "external underlying source pointers must be aligned");
123 cx->check(proto);
124
125 return ReadableStream::createExternalSourceStream(
126 cx, underlyingSource, nsISupportsObject_alreadyAddreffed, proto);
127 }
128
IsReadableStream(JSObject * obj)129 JS_PUBLIC_API bool JS::IsReadableStream(JSObject* obj) {
130 return obj->canUnwrapAs<ReadableStream>();
131 }
132
IsReadableStreamReader(JSObject * obj)133 JS_PUBLIC_API bool JS::IsReadableStreamReader(JSObject* obj) {
134 return obj->canUnwrapAs<ReadableStreamDefaultReader>();
135 }
136
IsReadableStreamDefaultReader(JSObject * obj)137 JS_PUBLIC_API bool JS::IsReadableStreamDefaultReader(JSObject* obj) {
138 return obj->canUnwrapAs<ReadableStreamDefaultReader>();
139 }
140
141 template <class T>
APIUnwrapAndDowncast(JSContext * cx,JSObject * obj)142 [[nodiscard]] static T* APIUnwrapAndDowncast(JSContext* cx, JSObject* obj) {
143 cx->check(obj);
144 return UnwrapAndDowncastObject<T>(cx, obj);
145 }
146
ReadableStreamIsReadable(JSContext * cx,Handle<JSObject * > streamObj,bool * result)147 JS_PUBLIC_API bool JS::ReadableStreamIsReadable(JSContext* cx,
148 Handle<JSObject*> streamObj,
149 bool* result) {
150 ReadableStream* unwrappedStream =
151 APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
152 if (!unwrappedStream) {
153 return false;
154 }
155
156 *result = unwrappedStream->readable();
157 return true;
158 }
159
ReadableStreamIsLocked(JSContext * cx,Handle<JSObject * > streamObj,bool * result)160 JS_PUBLIC_API bool JS::ReadableStreamIsLocked(JSContext* cx,
161 Handle<JSObject*> streamObj,
162 bool* result) {
163 ReadableStream* unwrappedStream =
164 APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
165 if (!unwrappedStream) {
166 return false;
167 }
168
169 *result = unwrappedStream->locked();
170 return true;
171 }
172
ReadableStreamIsDisturbed(JSContext * cx,Handle<JSObject * > streamObj,bool * result)173 JS_PUBLIC_API bool JS::ReadableStreamIsDisturbed(JSContext* cx,
174 Handle<JSObject*> streamObj,
175 bool* result) {
176 ReadableStream* unwrappedStream =
177 APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
178 if (!unwrappedStream) {
179 return false;
180 }
181
182 *result = unwrappedStream->disturbed();
183 return true;
184 }
185
ReadableStreamCancel(JSContext * cx,Handle<JSObject * > streamObj,Handle<Value> reason)186 JS_PUBLIC_API JSObject* JS::ReadableStreamCancel(JSContext* cx,
187 Handle<JSObject*> streamObj,
188 Handle<Value> reason) {
189 AssertHeapIsIdle();
190 CHECK_THREAD(cx);
191 cx->check(reason);
192
193 Rooted<ReadableStream*> unwrappedStream(
194 cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
195 if (!unwrappedStream) {
196 return nullptr;
197 }
198
199 return js::ReadableStreamCancel(cx, unwrappedStream, reason);
200 }
201
ReadableStreamGetMode(JSContext * cx,Handle<JSObject * > streamObj,JS::ReadableStreamMode * mode)202 JS_PUBLIC_API bool JS::ReadableStreamGetMode(JSContext* cx,
203 Handle<JSObject*> streamObj,
204 JS::ReadableStreamMode* mode) {
205 ReadableStream* unwrappedStream =
206 APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
207 if (!unwrappedStream) {
208 return false;
209 }
210
211 *mode = unwrappedStream->mode();
212 return true;
213 }
214
ReadableStreamGetReader(JSContext * cx,Handle<JSObject * > streamObj,ReadableStreamReaderMode mode)215 JS_PUBLIC_API JSObject* JS::ReadableStreamGetReader(
216 JSContext* cx, Handle<JSObject*> streamObj, ReadableStreamReaderMode mode) {
217 AssertHeapIsIdle();
218 CHECK_THREAD(cx);
219
220 Rooted<ReadableStream*> unwrappedStream(
221 cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
222 if (!unwrappedStream) {
223 return nullptr;
224 }
225
226 JSObject* result = CreateReadableStreamDefaultReader(cx, unwrappedStream,
227 ForAuthorCodeBool::No);
228 MOZ_ASSERT_IF(result, IsObjectInContextCompartment(result, cx));
229 return result;
230 }
231
ReadableStreamGetExternalUnderlyingSource(JSContext * cx,Handle<JSObject * > streamObj,JS::ReadableStreamUnderlyingSource ** source)232 JS_PUBLIC_API bool JS::ReadableStreamGetExternalUnderlyingSource(
233 JSContext* cx, Handle<JSObject*> streamObj,
234 JS::ReadableStreamUnderlyingSource** source) {
235 AssertHeapIsIdle();
236 CHECK_THREAD(cx);
237
238 Rooted<ReadableStream*> unwrappedStream(
239 cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
240 if (!unwrappedStream) {
241 return false;
242 }
243
244 MOZ_ASSERT(unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource);
245 if (unwrappedStream->locked()) {
246 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
247 JSMSG_READABLESTREAM_LOCKED);
248 return false;
249 }
250 if (!unwrappedStream->readable()) {
251 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
252 JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE,
253 "ReadableStreamGetExternalUnderlyingSource");
254 return false;
255 }
256
257 auto unwrappedController =
258 &unwrappedStream->controller()->as<ReadableByteStreamController>();
259 unwrappedController->setSourceLocked();
260 *source = unwrappedController->externalSource();
261 return true;
262 }
263
ReadableStreamReleaseExternalUnderlyingSource(JSContext * cx,Handle<JSObject * > streamObj)264 JS_PUBLIC_API bool JS::ReadableStreamReleaseExternalUnderlyingSource(
265 JSContext* cx, Handle<JSObject*> streamObj) {
266 ReadableStream* unwrappedStream =
267 APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
268 if (!unwrappedStream) {
269 return false;
270 }
271
272 MOZ_ASSERT(unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource);
273 MOZ_ASSERT(unwrappedStream->locked());
274 MOZ_ASSERT(unwrappedStream->controller()->sourceLocked());
275 unwrappedStream->controller()->clearSourceLocked();
276 return true;
277 }
278
ReadableStreamUpdateDataAvailableFromSource(JSContext * cx,JS::Handle<JSObject * > streamObj,uint32_t availableData)279 JS_PUBLIC_API bool JS::ReadableStreamUpdateDataAvailableFromSource(
280 JSContext* cx, JS::Handle<JSObject*> streamObj, uint32_t availableData) {
281 AssertHeapIsIdle();
282 CHECK_THREAD(cx);
283
284 Rooted<ReadableStream*> unwrappedStream(
285 cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
286 if (!unwrappedStream) {
287 return false;
288 }
289
290 // This is based on Streams spec 3.11.4.4. enqueue(chunk) steps 1-3 and
291 // 3.13.9. ReadableByteStreamControllerEnqueue(controller, chunk) steps
292 // 8-9.
293 //
294 // Adapted to handling updates signaled by the embedding for streams with
295 // external underlying sources.
296 //
297 // The remaining steps of those two functions perform checks and asserts
298 // that don't apply to streams with external underlying sources.
299
300 Rooted<ReadableByteStreamController*> unwrappedController(
301 cx, &unwrappedStream->controller()->as<ReadableByteStreamController>());
302
303 // Step 2: If this.[[closeRequested]] is true, throw a TypeError exception.
304 if (unwrappedController->closeRequested()) {
305 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
306 JSMSG_READABLESTREAMCONTROLLER_CLOSED, "enqueue");
307 return false;
308 }
309
310 // Step 3: If this.[[controlledReadableStream]].[[state]] is not "readable",
311 // throw a TypeError exception.
312 if (!unwrappedController->stream()->readable()) {
313 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
314 JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE,
315 "enqueue");
316 return false;
317 }
318
319 unwrappedController->clearPullFlags();
320
321 #if DEBUG
322 uint32_t oldAvailableData =
323 unwrappedController->getFixedSlot(StreamController::Slot_TotalSize)
324 .toInt32();
325 #endif // DEBUG
326 unwrappedController->setQueueTotalSize(availableData);
327
328 // 3.139. ReadableByteStreamControllerEnqueue
329 // Step 8.a: If ! ReadableStreamGetNumReadRequests(stream) is 0,
330 // Reordered because for externally-sourced streams it applies regardless
331 // of reader type.
332 if (ReadableStreamGetNumReadRequests(unwrappedStream) == 0) {
333 return true;
334 }
335
336 // Step 8: If ! ReadableStreamHasDefaultReader(stream) is true
337 bool hasDefaultReader;
338 if (!ReadableStreamHasDefaultReader(cx, unwrappedStream, &hasDefaultReader)) {
339 return false;
340 }
341 if (hasDefaultReader) {
342 // Step b: Otherwise,
343 // Step i: Assert: controller.[[queue]] is empty.
344 MOZ_ASSERT(oldAvailableData == 0);
345
346 // Step ii: Let transferredView be
347 // ! Construct(%Uint8Array%, transferredBuffer,
348 // byteOffset, byteLength).
349 JSObject* viewObj = JS_NewUint8Array(cx, availableData);
350 if (!viewObj) {
351 return false;
352 }
353 Rooted<ArrayBufferViewObject*> transferredView(
354 cx, &viewObj->as<ArrayBufferViewObject>());
355 if (!transferredView) {
356 return false;
357 }
358
359 JS::ReadableStreamUnderlyingSource* source =
360 unwrappedController->externalSource();
361
362 size_t bytesWritten;
363 {
364 AutoRealm ar(cx, unwrappedStream);
365 JS::AutoSuppressGCAnalysis suppressGC(cx);
366 JS::AutoCheckCannotGC noGC;
367 bool dummy;
368 void* buffer = JS_GetArrayBufferViewData(transferredView, &dummy, noGC);
369 source->writeIntoReadRequestBuffer(cx, unwrappedStream, buffer,
370 availableData, &bytesWritten);
371 }
372
373 // Step iii: Perform ! ReadableStreamFulfillReadRequest(stream,
374 // transferredView,
375 // false).
376 Rooted<Value> chunk(cx, ObjectValue(*transferredView));
377 if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, unwrappedStream, chunk,
378 false)) {
379 return false;
380 }
381
382 unwrappedController->setQueueTotalSize(availableData - bytesWritten);
383 } else {
384 // Step 9: Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true,
385 // [...]
386 // (Omitted. BYOB readers are not implemented.)
387
388 // Step 10: Otherwise,
389 // Step a: Assert: ! IsReadableStreamLocked(stream) is false.
390 MOZ_ASSERT(!unwrappedStream->locked());
391
392 // Step b: Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(
393 // controller, transferredBuffer, byteOffset, byteLength).
394 // (Not needed for external underlying sources.)
395 }
396
397 return true;
398 }
399
ReadableStreamReleaseCCObject(JSObject * streamObj)400 JS_PUBLIC_API void JS::ReadableStreamReleaseCCObject(JSObject* streamObj) {
401 MOZ_ASSERT(JS::IsReadableStream(streamObj));
402 JS::SetPrivate(streamObj, nullptr);
403 }
404
ReadableStreamTee(JSContext * cx,Handle<JSObject * > streamObj,MutableHandle<JSObject * > branch1Obj,MutableHandle<JSObject * > branch2Obj)405 JS_PUBLIC_API bool JS::ReadableStreamTee(JSContext* cx,
406 Handle<JSObject*> streamObj,
407 MutableHandle<JSObject*> branch1Obj,
408 MutableHandle<JSObject*> branch2Obj) {
409 AssertHeapIsIdle();
410 CHECK_THREAD(cx);
411
412 Rooted<ReadableStream*> unwrappedStream(
413 cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
414 if (!unwrappedStream) {
415 return false;
416 }
417
418 Rooted<ReadableStream*> branch1Stream(cx);
419 Rooted<ReadableStream*> branch2Stream(cx);
420 if (!ReadableStreamTee(cx, unwrappedStream, false, &branch1Stream,
421 &branch2Stream)) {
422 return false;
423 }
424
425 branch1Obj.set(branch1Stream);
426 branch2Obj.set(branch2Stream);
427 return true;
428 }
429
ReadableStreamGetDesiredSize(JSContext * cx,JSObject * streamObj,bool * hasValue,double * value)430 JS_PUBLIC_API bool JS::ReadableStreamGetDesiredSize(JSContext* cx,
431 JSObject* streamObj,
432 bool* hasValue,
433 double* value) {
434 ReadableStream* unwrappedStream =
435 APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
436 if (!unwrappedStream) {
437 return false;
438 }
439
440 if (unwrappedStream->errored()) {
441 *hasValue = false;
442 return true;
443 }
444
445 *hasValue = true;
446
447 if (unwrappedStream->closed()) {
448 *value = 0;
449 return true;
450 }
451
452 *value = ReadableStreamControllerGetDesiredSizeUnchecked(
453 unwrappedStream->controller());
454 return true;
455 }
456
ReadableStreamClose(JSContext * cx,Handle<JSObject * > streamObj)457 JS_PUBLIC_API bool JS::ReadableStreamClose(JSContext* cx,
458 Handle<JSObject*> streamObj) {
459 AssertHeapIsIdle();
460 CHECK_THREAD(cx);
461
462 Rooted<ReadableStream*> unwrappedStream(
463 cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
464 if (!unwrappedStream) {
465 return false;
466 }
467
468 Rooted<ReadableStreamController*> unwrappedControllerObj(
469 cx, unwrappedStream->controller());
470 if (!CheckReadableStreamControllerCanCloseOrEnqueue(
471 cx, unwrappedControllerObj, "close")) {
472 return false;
473 }
474
475 if (unwrappedControllerObj->is<ReadableStreamDefaultController>()) {
476 Rooted<ReadableStreamDefaultController*> unwrappedController(cx);
477 unwrappedController =
478 &unwrappedControllerObj->as<ReadableStreamDefaultController>();
479 return ReadableStreamDefaultControllerClose(cx, unwrappedController);
480 }
481
482 Rooted<ReadableByteStreamController*> unwrappedController(cx);
483 unwrappedController =
484 &unwrappedControllerObj->as<ReadableByteStreamController>();
485 return ReadableByteStreamControllerClose(cx, unwrappedController);
486 }
487
ReadableStreamEnqueue(JSContext * cx,Handle<JSObject * > streamObj,Handle<Value> chunk)488 JS_PUBLIC_API bool JS::ReadableStreamEnqueue(JSContext* cx,
489 Handle<JSObject*> streamObj,
490 Handle<Value> chunk) {
491 AssertHeapIsIdle();
492 CHECK_THREAD(cx);
493 cx->check(chunk);
494
495 Rooted<ReadableStream*> unwrappedStream(
496 cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
497 if (!unwrappedStream) {
498 return false;
499 }
500
501 if (unwrappedStream->mode() != JS::ReadableStreamMode::Default) {
502 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
503 JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER,
504 "JS::ReadableStreamEnqueue");
505 return false;
506 }
507
508 Rooted<ReadableStreamDefaultController*> unwrappedController(cx);
509 unwrappedController =
510 &unwrappedStream->controller()->as<ReadableStreamDefaultController>();
511
512 MOZ_ASSERT(!unwrappedController->closeRequested());
513 MOZ_ASSERT(unwrappedStream->readable());
514
515 return ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, chunk);
516 }
517
ReadableStreamError(JSContext * cx,Handle<JSObject * > streamObj,Handle<Value> error)518 JS_PUBLIC_API bool JS::ReadableStreamError(JSContext* cx,
519 Handle<JSObject*> streamObj,
520 Handle<Value> error) {
521 AssertHeapIsIdle();
522 CHECK_THREAD(cx);
523 cx->check(error);
524
525 Rooted<ReadableStream*> unwrappedStream(
526 cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
527 if (!unwrappedStream) {
528 return false;
529 }
530
531 Rooted<ReadableStreamController*> unwrappedController(
532 cx, unwrappedStream->controller());
533 return ReadableStreamControllerError(cx, unwrappedController, error);
534 }
535
ReadableStreamReaderIsClosed(JSContext * cx,Handle<JSObject * > readerObj,bool * result)536 JS_PUBLIC_API bool JS::ReadableStreamReaderIsClosed(JSContext* cx,
537 Handle<JSObject*> readerObj,
538 bool* result) {
539 Rooted<ReadableStreamReader*> unwrappedReader(
540 cx, APIUnwrapAndDowncast<ReadableStreamReader>(cx, readerObj));
541 if (!unwrappedReader) {
542 return false;
543 }
544
545 *result = unwrappedReader->isClosed();
546 return true;
547 }
548
ReadableStreamReaderCancel(JSContext * cx,Handle<JSObject * > readerObj,Handle<Value> reason)549 JS_PUBLIC_API bool JS::ReadableStreamReaderCancel(JSContext* cx,
550 Handle<JSObject*> readerObj,
551 Handle<Value> reason) {
552 AssertHeapIsIdle();
553 CHECK_THREAD(cx);
554 cx->check(reason);
555
556 Rooted<ReadableStreamReader*> unwrappedReader(
557 cx, APIUnwrapAndDowncast<ReadableStreamReader>(cx, readerObj));
558 if (!unwrappedReader) {
559 return false;
560 }
561 MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
562 "C++ code should not touch readers created by scripts");
563
564 return ReadableStreamReaderGenericCancel(cx, unwrappedReader, reason);
565 }
566
ReadableStreamReaderReleaseLock(JSContext * cx,Handle<JSObject * > readerObj)567 JS_PUBLIC_API bool JS::ReadableStreamReaderReleaseLock(
568 JSContext* cx, Handle<JSObject*> readerObj) {
569 AssertHeapIsIdle();
570 CHECK_THREAD(cx);
571
572 Rooted<ReadableStreamReader*> unwrappedReader(
573 cx, APIUnwrapAndDowncast<ReadableStreamReader>(cx, readerObj));
574 if (!unwrappedReader) {
575 return false;
576 }
577 MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
578 "C++ code should not touch readers created by scripts");
579
580 #ifdef DEBUG
581 Rooted<ReadableStream*> unwrappedStream(
582 cx, UnwrapStreamFromReader(cx, unwrappedReader));
583 if (!unwrappedStream) {
584 return false;
585 }
586 MOZ_ASSERT(ReadableStreamGetNumReadRequests(unwrappedStream) == 0);
587 #endif // DEBUG
588
589 return ReadableStreamReaderGenericRelease(cx, unwrappedReader);
590 }
591
ReadableStreamDefaultReaderRead(JSContext * cx,Handle<JSObject * > readerObj)592 JS_PUBLIC_API JSObject* JS::ReadableStreamDefaultReaderRead(
593 JSContext* cx, Handle<JSObject*> readerObj) {
594 AssertHeapIsIdle();
595 CHECK_THREAD(cx);
596
597 Rooted<ReadableStreamDefaultReader*> unwrappedReader(
598 cx, APIUnwrapAndDowncast<ReadableStreamDefaultReader>(cx, readerObj));
599 if (!unwrappedReader) {
600 return nullptr;
601 }
602 MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
603 "C++ code should not touch readers created by scripts");
604
605 return js::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
606 }
607
InitPipeToHandling(const JSClass * abortSignalClass,AbortSignalIsAborted isAborted,JSContext * cx)608 void JS::InitPipeToHandling(const JSClass* abortSignalClass,
609 AbortSignalIsAborted isAborted, JSContext* cx) {
610 cx->runtime()->initPipeToHandling(abortSignalClass, isAborted);
611 }
612