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