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  */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 #include "jsapi.h"
9 #include "jsfriendapi.h"
10 #include "js/experimental/TypedData.h"  // JS_GetArrayBufferViewData, JS_IsUint8Array
11 #include "js/Stream.h"
12 #include "jsapi-tests/tests.h"
13 
14 using namespace JS;
15 
16 char testBufferData[] =
17     "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
18 
19 struct StubExternalUnderlyingSource
20     : public JS::ReadableStreamUnderlyingSource {
21   void* buffer = testBufferData;
22   bool dataRequestCBCalled = false;
23   bool writeIntoRequestBufferCBCalled = false;
24   bool cancelStreamCBCalled = false;
25   Value cancelStreamReason;
26   bool streamClosedCBCalled = false;
27   Value streamClosedReason;
28   bool streamErroredCBCalled = false;
29   Value streamErroredReason;
30   bool finalizeStreamCBCalled = false;
31   void* finalizedStreamUnderlyingSource;
32 
33   static StubExternalUnderlyingSource instance;
34 
requestDataStubExternalUnderlyingSource35   void requestData(JSContext* cx, HandleObject stream,
36                    size_t desiredSize) override {
37     js::AssertSameCompartment(cx, stream);
38     MOZ_RELEASE_ASSERT(!dataRequestCBCalled, "Invalid test setup");
39     dataRequestCBCalled = true;
40   }
41 
writeIntoReadRequestBufferStubExternalUnderlyingSource42   void writeIntoReadRequestBuffer(JSContext* cx, HandleObject stream,
43                                   void* buffer, size_t length,
44                                   size_t* bytesWritten) override {
45     js::AssertSameCompartment(cx, stream);
46     MOZ_RELEASE_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup");
47     writeIntoRequestBufferCBCalled = true;
48 
49     MOZ_RELEASE_ASSERT(this == &StubExternalUnderlyingSource::instance);
50     MOZ_RELEASE_ASSERT(StubExternalUnderlyingSource::instance.buffer ==
51                        testBufferData);
52     MOZ_RELEASE_ASSERT(length <= sizeof(testBufferData));
53     memcpy(buffer, testBufferData, length);
54     *bytesWritten = length;
55   }
56 
cancelStubExternalUnderlyingSource57   Value cancel(JSContext* cx, HandleObject stream,
58                HandleValue reason) override {
59     js::AssertSameCompartment(cx, stream);
60     js::AssertSameCompartment(cx, reason);
61     MOZ_RELEASE_ASSERT(!cancelStreamCBCalled, "Invalid test setup");
62     cancelStreamCBCalled = true;
63     cancelStreamReason = reason;
64     return reason;
65   }
66 
onClosedStubExternalUnderlyingSource67   void onClosed(JSContext* cx, HandleObject stream) override {
68     js::AssertSameCompartment(cx, stream);
69     MOZ_RELEASE_ASSERT(!streamClosedCBCalled, "Invalid test setup");
70     streamClosedCBCalled = true;
71   }
72 
onErroredStubExternalUnderlyingSource73   void onErrored(JSContext* cx, HandleObject stream,
74                  HandleValue reason) override {
75     js::AssertSameCompartment(cx, stream);
76     js::AssertSameCompartment(cx, reason);
77     MOZ_RELEASE_ASSERT(!streamErroredCBCalled, "Invalid test setup");
78     streamErroredCBCalled = true;
79     streamErroredReason = reason;
80   }
81 
finalizeStubExternalUnderlyingSource82   void finalize() override {
83     MOZ_RELEASE_ASSERT(!finalizeStreamCBCalled, "Invalid test setup");
84     finalizeStreamCBCalled = true;
85     finalizedStreamUnderlyingSource = this;
86   }
87 
resetStubExternalUnderlyingSource88   void reset() {
89     dataRequestCBCalled = false;
90     writeIntoRequestBufferCBCalled = false;
91     cancelStreamReason = UndefinedValue();
92     cancelStreamCBCalled = false;
93     streamClosedCBCalled = false;
94     streamErroredCBCalled = false;
95     finalizeStreamCBCalled = false;
96   }
97 };
98 
99 StubExternalUnderlyingSource StubExternalUnderlyingSource::instance;
100 
101 static_assert(MOZ_ALIGNOF(StubExternalUnderlyingSource) > 1,
102               "UnderlyingSource pointers must not have the low bit set");
103 
NewDefaultStream(JSContext * cx,HandleObject source=nullptr,HandleFunction size=nullptr,double highWaterMark=1,HandleObject proto=nullptr)104 static JSObject* NewDefaultStream(JSContext* cx, HandleObject source = nullptr,
105                                   HandleFunction size = nullptr,
106                                   double highWaterMark = 1,
107                                   HandleObject proto = nullptr) {
108   RootedObject stream(cx, NewReadableDefaultStreamObject(cx, source, size,
109                                                          highWaterMark, proto));
110   if (stream) {
111     MOZ_RELEASE_ASSERT(IsReadableStream(stream));
112   }
113   return stream;
114 }
115 
GetIterResult(JSContext * cx,HandleObject promise,MutableHandleValue value,bool * done)116 static bool GetIterResult(JSContext* cx, HandleObject promise,
117                           MutableHandleValue value, bool* done) {
118   RootedObject iterResult(cx, &GetPromiseResult(promise).toObject());
119 
120   bool found;
121   if (!JS_HasProperty(cx, iterResult, "value", &found)) {
122     return false;
123   }
124   MOZ_RELEASE_ASSERT(found);
125   if (!JS_HasProperty(cx, iterResult, "done", &found)) {
126     return false;
127   }
128   MOZ_RELEASE_ASSERT(found);
129 
130   RootedValue doneVal(cx);
131   if (!JS_GetProperty(cx, iterResult, "value", value)) {
132     return false;
133   }
134   if (!JS_GetProperty(cx, iterResult, "done", &doneVal)) {
135     return false;
136   }
137 
138   *done = doneVal.toBoolean();
139   if (*done) {
140     MOZ_RELEASE_ASSERT(value.isUndefined());
141   }
142 
143   return true;
144 }
145 
GetReadChunk(JSContext * cx,HandleObject readRequest)146 static JSObject* GetReadChunk(JSContext* cx, HandleObject readRequest) {
147   MOZ_RELEASE_ASSERT(GetPromiseState(readRequest) == PromiseState::Fulfilled);
148   RootedValue resultVal(cx, GetPromiseResult(readRequest));
149   MOZ_RELEASE_ASSERT(resultVal.isObject());
150   RootedObject result(cx, &resultVal.toObject());
151   RootedValue chunkVal(cx);
152   JS_GetProperty(cx, result, "value", &chunkVal);
153   return &chunkVal.toObject();
154 }
155 
156 struct StreamTestFixture : public JSAPITest {
~StreamTestFixtureStreamTestFixture157   virtual ~StreamTestFixture() {}
158 };
159 
BEGIN_FIXTURE_TEST(StreamTestFixture,testReadableStream_NewReadableStream)160 BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_NewReadableStream) {
161   RootedObject stream(cx, NewDefaultStream(cx));
162   CHECK(stream);
163   ReadableStreamMode mode;
164   CHECK(ReadableStreamGetMode(cx, stream, &mode));
165   CHECK(mode == ReadableStreamMode::Default);
166   return true;
167 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_NewReadableStream)168 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_NewReadableStream)
169 
170 BEGIN_FIXTURE_TEST(StreamTestFixture,
171                    testReadableStream_ReadableStreamGetReaderDefault) {
172   RootedObject stream(cx, NewDefaultStream(cx));
173   CHECK(stream);
174 
175   RootedObject reader(cx, ReadableStreamGetReader(
176                               cx, stream, ReadableStreamReaderMode::Default));
177   CHECK(reader);
178   CHECK(IsReadableStreamDefaultReader(reader));
179   bool locked;
180   CHECK(ReadableStreamIsLocked(cx, stream, &locked));
181   CHECK(locked);
182   bool closed;
183   CHECK(ReadableStreamReaderIsClosed(cx, reader, &closed));
184   CHECK(!closed);
185 
186   return true;
187 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamGetReaderDefault)188 END_FIXTURE_TEST(StreamTestFixture,
189                  testReadableStream_ReadableStreamGetReaderDefault)
190 
191 BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamTee) {
192   RootedObject stream(cx, NewDefaultStream(cx));
193   CHECK(stream);
194 
195   RootedObject leftStream(cx);
196   RootedObject rightStream(cx);
197   CHECK(ReadableStreamTee(cx, stream, &leftStream, &rightStream));
198   bool locked;
199   CHECK(ReadableStreamIsLocked(cx, stream, &locked));
200   CHECK(locked);
201   CHECK(leftStream);
202   CHECK(IsReadableStream(leftStream));
203   CHECK(rightStream);
204   CHECK(IsReadableStream(rightStream));
205 
206   return true;
207 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamTee)208 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamTee)
209 
210 BEGIN_FIXTURE_TEST(StreamTestFixture,
211                    testReadableStream_ReadableStreamEnqueue) {
212   RootedObject stream(cx, NewDefaultStream(cx));
213   CHECK(stream);
214 
215   RootedObject chunk(cx, JS_NewPlainObject(cx));
216   CHECK(chunk);
217   RootedValue chunkVal(cx, ObjectValue(*chunk));
218   CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
219 
220   return true;
221 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamEnqueue)222 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamEnqueue)
223 
224 BEGIN_FIXTURE_TEST(StreamTestFixture,
225                    testReadableStream_ReadableStreamDefaultReaderRead) {
226   RootedObject stream(cx, NewDefaultStream(cx));
227   CHECK(stream);
228   RootedObject reader(cx, ReadableStreamGetReader(
229                               cx, stream, ReadableStreamReaderMode::Default));
230   CHECK(reader);
231 
232   RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
233   CHECK(request);
234   CHECK(IsPromiseObject(request));
235   CHECK(GetPromiseState(request) == PromiseState::Pending);
236 
237   RootedObject chunk(cx, JS_NewPlainObject(cx));
238   CHECK(chunk);
239   RootedValue chunkVal(cx, ObjectValue(*chunk));
240   CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
241 
242   CHECK(GetReadChunk(cx, request) == chunk);
243 
244   return true;
245 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamDefaultReaderRead)246 END_FIXTURE_TEST(StreamTestFixture,
247                  testReadableStream_ReadableStreamDefaultReaderRead)
248 
249 BEGIN_FIXTURE_TEST(StreamTestFixture,
250                    testReadableStream_ReadableStreamDefaultReaderClose) {
251   RootedObject stream(cx, NewDefaultStream(cx));
252   CHECK(stream);
253   RootedObject reader(cx, ReadableStreamGetReader(
254                               cx, stream, ReadableStreamReaderMode::Default));
255   CHECK(reader);
256 
257   RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
258   CHECK(request);
259   CHECK(IsPromiseObject(request));
260   CHECK(GetPromiseState(request) == PromiseState::Pending);
261 
262   CHECK(ReadableStreamClose(cx, stream));
263 
264   bool done;
265   RootedValue value(cx);
266   CHECK(GetPromiseState(request) == PromiseState::Fulfilled);
267   CHECK(GetIterResult(cx, request, &value, &done));
268   CHECK(value.isUndefined());
269   CHECK(done);
270 
271   // The callbacks are only invoked for external streams.
272   CHECK(!StubExternalUnderlyingSource::instance.streamClosedCBCalled);
273 
274   return true;
275 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamDefaultReaderClose)276 END_FIXTURE_TEST(StreamTestFixture,
277                  testReadableStream_ReadableStreamDefaultReaderClose)
278 
279 BEGIN_FIXTURE_TEST(StreamTestFixture,
280                    testReadableStream_ReadableStreamDefaultReaderError) {
281   StubExternalUnderlyingSource::instance.reset();
282   RootedObject stream(cx, NewDefaultStream(cx));
283   CHECK(stream);
284   RootedObject reader(cx, ReadableStreamGetReader(
285                               cx, stream, ReadableStreamReaderMode::Default));
286   CHECK(reader);
287 
288   RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
289   CHECK(request);
290   CHECK(IsPromiseObject(request));
291   CHECK(GetPromiseState(request) == PromiseState::Pending);
292 
293   bool locked;
294   CHECK(ReadableStreamIsLocked(cx, stream, &locked));
295   CHECK(locked);
296   bool readable;
297   CHECK(ReadableStreamIsReadable(cx, stream, &readable));
298   CHECK(readable);
299   RootedValue error(cx, Int32Value(42));
300   CHECK(ReadableStreamError(cx, stream, error));
301 
302   CHECK(GetPromiseState(request) == PromiseState::Rejected);
303   RootedValue reason(cx, GetPromiseResult(request));
304   CHECK(reason.isInt32());
305   CHECK(reason.toInt32() == 42);
306 
307   // The callbacks are only invoked for external streams.
308   CHECK(!StubExternalUnderlyingSource::instance.streamErroredCBCalled);
309 
310   return true;
311 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamDefaultReaderError)312 END_FIXTURE_TEST(StreamTestFixture,
313                  testReadableStream_ReadableStreamDefaultReaderError)
314 
315 static JSObject* NewExternalSourceStream(
316     JSContext* cx, ReadableStreamUnderlyingSource* source) {
317   RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, source));
318   if (stream) {
319     MOZ_RELEASE_ASSERT(IsReadableStream(stream));
320   }
321   return stream;
322 }
323 
NewExternalSourceStream(JSContext * cx)324 static JSObject* NewExternalSourceStream(JSContext* cx) {
325   return NewExternalSourceStream(cx, &StubExternalUnderlyingSource::instance);
326 }
327 
BEGIN_FIXTURE_TEST(StreamTestFixture,testReadableStream_CreateReadableByteStreamWithExternalSource)328 BEGIN_FIXTURE_TEST(
329     StreamTestFixture,
330     testReadableStream_CreateReadableByteStreamWithExternalSource) {
331   StubExternalUnderlyingSource::instance.reset();
332 
333   RootedObject stream(cx, NewExternalSourceStream(cx));
334   CHECK(stream);
335   ReadableStreamMode mode;
336   CHECK(ReadableStreamGetMode(cx, stream, &mode));
337   CHECK(mode == ReadableStreamMode::ExternalSource);
338   ReadableStreamUnderlyingSource* underlyingSource;
339   CHECK(
340       ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
341   CHECK(underlyingSource == &StubExternalUnderlyingSource::instance);
342   bool locked;
343   CHECK(ReadableStreamIsLocked(cx, stream, &locked));
344   CHECK(locked);
345   CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
346 
347   return true;
348 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_CreateReadableByteStreamWithExternalSource)349 END_FIXTURE_TEST(StreamTestFixture,
350                  testReadableStream_CreateReadableByteStreamWithExternalSource)
351 
352 BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceCancel) {
353   StubExternalUnderlyingSource::instance.reset();
354 
355   RootedObject stream(cx, NewExternalSourceStream(cx));
356   CHECK(stream);
357   RootedValue reason(cx, Int32Value(42));
358   CHECK(ReadableStreamCancel(cx, stream, reason));
359   CHECK(StubExternalUnderlyingSource::instance.cancelStreamCBCalled);
360   CHECK(StubExternalUnderlyingSource::instance.cancelStreamReason == reason);
361 
362   return true;
363 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ExternalSourceCancel)364 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceCancel)
365 
366 BEGIN_FIXTURE_TEST(StreamTestFixture,
367                    testReadableStream_ExternalSourceGetReader) {
368   StubExternalUnderlyingSource::instance.reset();
369 
370   RootedObject stream(cx, NewExternalSourceStream(cx));
371   CHECK(stream);
372 
373   RootedValue streamVal(cx, ObjectValue(*stream));
374   CHECK(JS_SetProperty(cx, global, "stream", streamVal));
375   RootedValue rval(cx);
376   EVAL("stream.getReader()", &rval);
377   CHECK(rval.isObject());
378   RootedObject reader(cx, &rval.toObject());
379   CHECK(IsReadableStreamDefaultReader(reader));
380 
381   return true;
382 }
383 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceGetReader)
384 
385 enum class CompartmentMode {
386   Same,
387   Cross,
388 };
389 
390 struct ReadFromExternalSourceFixture : public StreamTestFixture {
~ReadFromExternalSourceFixtureReadFromExternalSourceFixture391   virtual ~ReadFromExternalSourceFixture() {}
392 
393   // On success, streamGlobal is a global object (not a wrapper)
394   // and stream is in the same compartment as cx (it may be a CCW).
createExternalSourceStreamReadFromExternalSourceFixture395   bool createExternalSourceStream(CompartmentMode compartmentMode,
396                                   MutableHandleObject streamGlobal,
397                                   MutableHandleObject stream) {
398     if (compartmentMode == CompartmentMode::Same) {
399       streamGlobal.set(global);
400       stream.set(NewExternalSourceStream(cx));
401       if (!stream) {
402         return false;
403       }
404     } else {
405       RootedObject savedGlobal(cx, global);
406       streamGlobal.set(createGlobal());
407       if (!streamGlobal) {
408         return false;
409       }
410       global = savedGlobal;
411 
412       {
413         JSAutoRealm ar(cx, streamGlobal);
414         stream.set(NewExternalSourceStream(cx));
415         if (!stream) {
416           return false;
417         }
418       }
419       if (!JS_WrapObject(cx, stream)) {
420         return false;
421       }
422     }
423     return true;
424   }
425 
readWithoutDataAvailableReadFromExternalSourceFixture426   bool readWithoutDataAvailable(CompartmentMode compartmentMode,
427                                 const char* evalSrc, const char* evalSrc2,
428                                 uint32_t writtenLength) {
429     StubExternalUnderlyingSource::instance.reset();
430     definePrint();
431 
432     // Create the stream.
433     RootedObject streamGlobal(cx);
434     RootedObject stream(cx);  // can be a wrapper
435     CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream));
436     js::RunJobs(cx);
437 
438     // GetExternalUnderlyingSource locks the stream.
439     ReadableStreamUnderlyingSource* underlyingSource;
440     CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream,
441                                                     &underlyingSource));
442     CHECK(underlyingSource == &StubExternalUnderlyingSource::instance);
443     bool locked;
444     CHECK(ReadableStreamIsLocked(cx, stream, &locked));
445     CHECK(locked);
446     CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
447 
448     // Run caller-supplied JS code to read from the stream.
449     RootedValue streamVal(cx, ObjectValue(*stream));
450     CHECK(JS_SetProperty(cx, global, "stream", streamVal));
451     RootedValue rval(cx);
452     EVAL(evalSrc, &rval);
453     CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled);
454     CHECK(
455         !StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
456     CHECK(rval.isObject());
457     RootedObject unwrappedPromise(cx,
458                                   js::CheckedUnwrapStatic(&rval.toObject()));
459     CHECK(unwrappedPromise);
460     CHECK(IsPromiseObject(unwrappedPromise));
461     CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Pending);
462 
463     // Stream in some data; this resolves the read() result promise.
464     size_t length = sizeof(testBufferData);
465     CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length));
466     CHECK(
467         StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
468     CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled);
469     RootedObject chunk(cx);
470     {
471       JSAutoRealm ar(cx, unwrappedPromise);
472       RootedValue iterVal(cx);
473       bool done;
474       if (!GetIterResult(cx, unwrappedPromise, &iterVal, &done)) {
475         return false;
476       }
477       CHECK(!done);
478       chunk = &iterVal.toObject();
479     }
480     CHECK(JS_WrapObject(cx, &chunk));
481     CHECK(JS_IsUint8Array(chunk));
482 
483     {
484       JS::AutoCheckCannotGC noGC(cx);
485       bool dummy;
486       void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
487       CHECK(!memcmp(buffer, testBufferData, writtenLength));
488     }
489 
490     // Check the callbacks fired by calling read() again.
491     StubExternalUnderlyingSource::instance.dataRequestCBCalled = false;
492     StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled =
493         false;
494     EVAL(evalSrc2, &rval);
495     CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled);
496     CHECK(
497         !StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
498 
499     return true;
500   }
501 
readWithDataAvailableReadFromExternalSourceFixture502   bool readWithDataAvailable(CompartmentMode compartmentMode,
503                              const char* evalSrc, uint32_t writtenLength) {
504     StubExternalUnderlyingSource::instance.reset();
505     definePrint();
506 
507     // Create a stream.
508     RootedObject streamGlobal(cx);
509     RootedObject stream(cx);
510     CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream));
511 
512     // Getting the underlying source locks the stream.
513     ReadableStreamUnderlyingSource* underlyingSource;
514     CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream,
515                                                     &underlyingSource));
516     CHECK(underlyingSource == &StubExternalUnderlyingSource::instance);
517     bool locked;
518     CHECK(ReadableStreamIsLocked(cx, stream, &locked));
519     CHECK(locked);
520     CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
521 
522     // Make some data available.
523     size_t length = sizeof(testBufferData);
524     CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length));
525 
526     // Read from the stream.
527     RootedValue streamVal(cx, ObjectValue(*stream));
528     CHECK(JS_SetProperty(cx, global, "stream", streamVal));
529     RootedValue rval(cx);
530     EVAL(evalSrc, &rval);
531     CHECK(
532         StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
533     CHECK(rval.isObject());
534     RootedObject unwrappedPromise(cx,
535                                   js::CheckedUnwrapStatic(&rval.toObject()));
536     CHECK(unwrappedPromise);
537     CHECK(IsPromiseObject(unwrappedPromise));
538     CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled);
539     RootedObject chunk(cx);
540     {
541       JSAutoRealm ar(cx, unwrappedPromise);
542       RootedValue iterVal(cx);
543       bool done;
544       if (!GetIterResult(cx, unwrappedPromise, &iterVal, &done)) {
545         return false;
546       }
547       CHECK(!done);
548       chunk = &iterVal.toObject();
549     }
550     CHECK(JS_WrapObject(cx, &chunk));
551     CHECK(JS_IsUint8Array(chunk));
552 
553     {
554       JS::AutoCheckCannotGC noGC(cx);
555       bool dummy;
556       void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
557       CHECK(!memcmp(buffer, testBufferData, writtenLength));
558     }
559 
560     return true;
561   }
562 };
563 
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)564 BEGIN_FIXTURE_TEST(
565     ReadFromExternalSourceFixture,
566     testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable) {
567   return readWithoutDataAvailable(CompartmentMode::Same,
568                                   "r = stream.getReader(); r.read()",
569                                   "r.read()", sizeof(testBufferData));
570 }
END_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)571 END_FIXTURE_TEST(
572     ReadFromExternalSourceFixture,
573     testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)
574 
575 BEGIN_FIXTURE_TEST(
576     ReadFromExternalSourceFixture,
577     testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment1) {
578   // Scenario 1: The stream and reader are both in the same compartment, but
579   // ReadableStreamUpdateDataAvailableFromSource is applied to a wrapper.
580   return readWithoutDataAvailable(CompartmentMode::Cross,
581                                   "r = stream.getReader(); r.read()",
582                                   "r.read()", sizeof(testBufferData));
583 }
END_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment1)584 END_FIXTURE_TEST(
585     ReadFromExternalSourceFixture,
586     testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment1)
587 
588 BEGIN_FIXTURE_TEST(
589     ReadFromExternalSourceFixture,
590     testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment2) {
591   // Scenario 2: The stream and reader are in the same compartment, but a
592   // `read` method from another compartment is used on the reader.
593   return readWithoutDataAvailable(
594       CompartmentMode::Cross,
595       "r = stream.getReader(); read = new "
596       "ReadableStream({start(){}}).getReader().read; read.call(r)",
597       "read.call(r)", sizeof(testBufferData));
598 }
END_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment2)599 END_FIXTURE_TEST(
600     ReadFromExternalSourceFixture,
601     testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment2)
602 
603 BEGIN_FIXTURE_TEST(
604     ReadFromExternalSourceFixture,
605     testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment3) {
606   // Scenario 3: The stream and reader are in different compartments.
607   return readWithoutDataAvailable(
608       CompartmentMode::Cross,
609       "r = ReadableStream.prototype.getReader.call(stream); r.read()",
610       "r.read()", sizeof(testBufferData));
611 }
END_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment3)612 END_FIXTURE_TEST(
613     ReadFromExternalSourceFixture,
614     testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment3)
615 
616 BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
617                    testReadableStream_ExternalSourceCloseWithPendingRead) {
618   CHECK(readWithoutDataAvailable(CompartmentMode::Same,
619                                  "r = stream.getReader(); request0 = r.read(); "
620                                  "request1 = r.read(); request0",
621                                  "r.read()", sizeof(testBufferData)));
622 
623   RootedValue val(cx);
624   CHECK(JS_GetProperty(cx, global, "request1", &val));
625   CHECK(val.isObject());
626   RootedObject request(cx, &val.toObject());
627   CHECK(IsPromiseObject(request));
628   CHECK(GetPromiseState(request) == PromiseState::Pending);
629 
630   CHECK(JS_GetProperty(cx, global, "stream", &val));
631   RootedObject stream(cx, &val.toObject());
632   ReadableStreamClose(cx, stream);
633 
634   val = GetPromiseResult(request);
635   CHECK(val.isObject());
636   RootedObject result(cx, &val.toObject());
637 
638   JS_GetProperty(cx, result, "done", &val);
639   CHECK(val.isBoolean());
640   CHECK(val.toBoolean() == true);
641 
642   JS_GetProperty(cx, result, "value", &val);
643   CHECK(val.isUndefined());
644   return true;
645 }
END_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceCloseWithPendingRead)646 END_FIXTURE_TEST(ReadFromExternalSourceFixture,
647                  testReadableStream_ExternalSourceCloseWithPendingRead)
648 
649 BEGIN_FIXTURE_TEST(
650     ReadFromExternalSourceFixture,
651     testReadableStream_ExternalSourceReadDefaultWithDataAvailable) {
652   return readWithDataAvailable(CompartmentMode::Same,
653                                "r = stream.getReader(); r.read()",
654                                sizeof(testBufferData));
655 }
END_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceReadDefaultWithDataAvailable)656 END_FIXTURE_TEST(ReadFromExternalSourceFixture,
657                  testReadableStream_ExternalSourceReadDefaultWithDataAvailable)
658 
659 BEGIN_FIXTURE_TEST(
660     ReadFromExternalSourceFixture,
661     testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment1) {
662   // Scenario 1: The stream and reader are both in the same compartment, but
663   // ReadableStreamUpdateDataAvailableFromSource is applied to a wrapper.
664   return readWithDataAvailable(CompartmentMode::Cross,
665                                "r = stream.getReader(); r.read()",
666                                sizeof(testBufferData));
667 }
END_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment1)668 END_FIXTURE_TEST(
669     ReadFromExternalSourceFixture,
670     testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment1)
671 
672 BEGIN_FIXTURE_TEST(
673     ReadFromExternalSourceFixture,
674     testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment2) {
675   // Scenario 2: The stream and reader are in the same compartment, but a
676   // `read` method from another compartment is used on the reader.
677   return readWithDataAvailable(
678       CompartmentMode::Cross,
679       "r = stream.getReader(); read = new "
680       "ReadableStream({start(){}}).getReader().read; read.call(r)",
681       sizeof(testBufferData));
682 }
END_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment2)683 END_FIXTURE_TEST(
684     ReadFromExternalSourceFixture,
685     testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment2)
686 
687 BEGIN_FIXTURE_TEST(
688     ReadFromExternalSourceFixture,
689     testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment3) {
690   // Scenario 3: The stream and reader are in different compartments.
691   return readWithDataAvailable(
692       CompartmentMode::Cross,
693       "r = ReadableStream.prototype.getReader.call(stream); r.read()",
694       sizeof(testBufferData));
695 }
END_FIXTURE_TEST(ReadFromExternalSourceFixture,testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment3)696 END_FIXTURE_TEST(
697     ReadFromExternalSourceFixture,
698     testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment3)
699 
700 // Cross-global tests:
701 BEGIN_FIXTURE_TEST(
702     StreamTestFixture,
703     testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead) {
704   RootedObject stream(cx, NewDefaultStream(cx));
705   CHECK(stream);
706   RootedObject otherGlobal(cx, createGlobal());
707   CHECK(otherGlobal);
708 
709   {
710     JSAutoRealm ar(cx, otherGlobal);
711     CHECK(JS_WrapObject(cx, &stream));
712     RootedObject reader(cx, ReadableStreamGetReader(
713                                 cx, stream, ReadableStreamReaderMode::Default));
714     CHECK(reader);
715 
716     RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
717     CHECK(request);
718     CHECK(IsPromiseObject(request));
719     CHECK(!js::IsWrapper(request));
720     CHECK(GetPromiseState(request) == PromiseState::Pending);
721 
722     RootedObject chunk(cx, JS_NewPlainObject(cx));
723     CHECK(chunk);
724     RootedValue chunkVal(cx, ObjectValue(*chunk));
725     CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
726 
727     CHECK(GetReadChunk(cx, request) == chunk);
728   }
729 
730   return true;
731 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead)732 END_FIXTURE_TEST(StreamTestFixture,
733                  testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead)
734 
735 BEGIN_FIXTURE_TEST(
736     StreamTestFixture,
737     testReadableStream_ReadableStreamGetExternalUnderlyingSource) {
738   StubExternalUnderlyingSource::instance.reset();
739 
740   RootedObject stream(cx, NewExternalSourceStream(cx));
741   CHECK(stream);
742   ReadableStreamUnderlyingSource* source;
743   CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source));
744   CHECK(source == &StubExternalUnderlyingSource::instance);
745   CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
746 
747   RootedObject otherGlobal(cx, createGlobal());
748   CHECK(otherGlobal);
749   {
750     JSAutoRealm ar(cx, otherGlobal);
751     CHECK(JS_WrapObject(cx, &stream));
752     ReadableStreamUnderlyingSource* source;
753     CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source));
754     CHECK(source == &StubExternalUnderlyingSource::instance);
755     CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
756   }
757 
758   return true;
759 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamGetExternalUnderlyingSource)760 END_FIXTURE_TEST(StreamTestFixture,
761                  testReadableStream_ReadableStreamGetExternalUnderlyingSource)
762 
763 BEGIN_FIXTURE_TEST(
764     StreamTestFixture,
765     testReadableStream_ReadableStreamUpdateDataAvailableFromSource) {
766   RootedObject stream(cx, NewExternalSourceStream(cx));
767   CHECK(stream);
768   CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, 0));
769 
770   RootedObject otherGlobal(cx, createGlobal());
771   CHECK(otherGlobal);
772   {
773     JSAutoRealm ar(cx, otherGlobal);
774     CHECK(JS_WrapObject(cx, &stream));
775     CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, 1));
776   }
777 
778   return true;
779 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamUpdateDataAvailableFromSource)780 END_FIXTURE_TEST(StreamTestFixture,
781                  testReadableStream_ReadableStreamUpdateDataAvailableFromSource)
782 
783 BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStream) {
784   RootedObject stream(cx, NewDefaultStream(cx));
785   CHECK(stream);
786   CHECK(IsReadableStream(stream));
787 
788   RootedObject otherGlobal(cx, createGlobal());
789   CHECK(otherGlobal);
790   {
791     JSAutoRealm ar(cx, otherGlobal);
792     CHECK(JS_WrapObject(cx, &stream));
793     CHECK(IsReadableStream(stream));
794   }
795 
796   return true;
797 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_IsReadableStream)798 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStream)
799 
800 BEGIN_FIXTURE_TEST(StreamTestFixture,
801                    testReadableStream_ReadableStreamGetMode) {
802   RootedObject stream(cx, NewDefaultStream(cx));
803   CHECK(stream);
804   ReadableStreamMode mode;
805   CHECK(ReadableStreamGetMode(cx, stream, &mode));
806   CHECK(mode == ReadableStreamMode::Default);
807 
808   RootedObject otherGlobal(cx, createGlobal());
809   CHECK(otherGlobal);
810   {
811     JSAutoRealm ar(cx, otherGlobal);
812     CHECK(JS_WrapObject(cx, &stream));
813     CHECK(ReadableStreamGetMode(cx, stream, &mode));
814     CHECK(mode == ReadableStreamMode::Default);
815   }
816 
817   return true;
818 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamGetMode)819 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamGetMode)
820 
821 BEGIN_FIXTURE_TEST(StreamTestFixture,
822                    testReadableStream_ReadableStreamIsReadable) {
823   RootedObject stream(cx, NewDefaultStream(cx));
824   CHECK(stream);
825   bool result;
826   CHECK(ReadableStreamIsReadable(cx, stream, &result));
827   CHECK(result);
828 
829   RootedObject otherGlobal(cx, createGlobal());
830   CHECK(otherGlobal);
831   {
832     JSAutoRealm ar(cx, otherGlobal);
833     CHECK(JS_WrapObject(cx, &stream));
834     CHECK(ReadableStreamIsReadable(cx, stream, &result));
835     CHECK(result);
836   }
837 
838   return true;
839 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamIsReadable)840 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamIsReadable)
841 
842 BEGIN_FIXTURE_TEST(StreamTestFixture,
843                    testReadableStream_ReadableStreamIsLocked) {
844   RootedObject stream(cx, NewDefaultStream(cx));
845   CHECK(stream);
846   bool result;
847   CHECK(ReadableStreamIsLocked(cx, stream, &result));
848   CHECK_EQUAL(result, false);
849 
850   RootedObject otherGlobal(cx, createGlobal());
851   CHECK(otherGlobal);
852   {
853     JSAutoRealm ar(cx, otherGlobal);
854     CHECK(JS_WrapObject(cx, &stream));
855     CHECK(ReadableStreamIsLocked(cx, stream, &result));
856     CHECK_EQUAL(result, false);
857   }
858 
859   return true;
860 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamIsLocked)861 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamIsLocked)
862 
863 BEGIN_FIXTURE_TEST(StreamTestFixture,
864                    testReadableStream_ReadableStreamIsDisturbed) {
865   RootedObject stream(cx, NewDefaultStream(cx));
866   CHECK(stream);
867   bool result;
868   CHECK(ReadableStreamIsDisturbed(cx, stream, &result));
869   CHECK_EQUAL(result, false);
870 
871   RootedObject otherGlobal(cx, createGlobal());
872   CHECK(otherGlobal);
873   {
874     JSAutoRealm ar(cx, otherGlobal);
875     CHECK(JS_WrapObject(cx, &stream));
876     CHECK(ReadableStreamIsDisturbed(cx, stream, &result));
877     CHECK_EQUAL(result, false);
878   }
879 
880   return true;
881 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamIsDisturbed)882 END_FIXTURE_TEST(StreamTestFixture,
883                  testReadableStream_ReadableStreamIsDisturbed)
884 
885 BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamCancel) {
886   RootedObject stream(cx, NewDefaultStream(cx));
887   CHECK(stream);
888 
889   RootedValue reason(cx);
890   JSObject* callResult = ReadableStreamCancel(cx, stream, reason);
891   CHECK(callResult);
892 
893   RootedObject otherGlobal(cx, createGlobal());
894   CHECK(otherGlobal);
895   {
896     JSAutoRealm ar(cx, otherGlobal);
897     CHECK(JS_WrapObject(cx, &stream));
898     RootedValue reason(cx);
899     JSObject* callResult = ReadableStreamCancel(cx, stream, reason);
900     CHECK(callResult);
901   }
902 
903   return true;
904 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamCancel)905 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamCancel)
906 
907 BEGIN_FIXTURE_TEST(StreamTestFixture,
908                    testReadableStream_ReadableStreamGetReader) {
909   RootedObject stream(cx, NewDefaultStream(cx));
910   CHECK(stream);
911 
912   RootedObject reader(cx);
913   reader =
914       ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default);
915   CHECK(reader);
916   CHECK(IsReadableStreamDefaultReader(reader));
917   CHECK(ReadableStreamReaderReleaseLock(cx, reader));
918 
919   RootedObject otherGlobal(cx, createGlobal());
920   CHECK(otherGlobal);
921   {
922     JSAutoRealm ar(cx, otherGlobal);
923     CHECK(JS_WrapObject(cx, &stream));
924     JSObject* callResult =
925         ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default);
926     CHECK(callResult);
927   }
928 
929   return true;
930 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamGetReader)931 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamGetReader)
932 
933 BEGIN_FIXTURE_TEST(StreamTestFixture,
934                    testReadableStream_ReadableStreamTee_CrossCompartment) {
935   RootedObject stream(cx, NewDefaultStream(cx));
936   CHECK(stream);
937 
938   RootedObject branch1Stream(cx);
939   RootedObject branch2Stream(cx);
940   CHECK(ReadableStreamTee(cx, stream, &branch1Stream, &branch2Stream));
941   CHECK(IsReadableStream(branch1Stream));
942   CHECK(IsReadableStream(branch2Stream));
943   stream = branch1Stream;
944 
945   RootedObject otherGlobal(cx, createGlobal());
946   CHECK(otherGlobal);
947   {
948     JSAutoRealm ar(cx, otherGlobal);
949     CHECK(JS_WrapObject(cx, &stream));
950     CHECK(ReadableStreamTee(cx, stream, &branch1Stream, &branch2Stream));
951     CHECK(IsReadableStream(branch1Stream));
952     CHECK(IsReadableStream(branch2Stream));
953   }
954 
955   return true;
956 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamTee_CrossCompartment)957 END_FIXTURE_TEST(StreamTestFixture,
958                  testReadableStream_ReadableStreamTee_CrossCompartment)
959 
960 BEGIN_FIXTURE_TEST(StreamTestFixture,
961                    testReadableStream_ReadableStreamGetDesiredSize) {
962   RootedObject stream(cx, NewDefaultStream(cx));
963   CHECK(stream);
964   bool hasValue;
965   double value;
966   CHECK(ReadableStreamGetDesiredSize(cx, stream, &hasValue, &value));
967   CHECK_EQUAL(hasValue, true);
968   CHECK_EQUAL(value, 1.0);
969 
970   RootedObject otherGlobal(cx, createGlobal());
971   CHECK(otherGlobal);
972   {
973     JSAutoRealm ar(cx, otherGlobal);
974     CHECK(JS_WrapObject(cx, &stream));
975     hasValue = false;
976     value = 0;
977     CHECK(ReadableStreamGetDesiredSize(cx, stream, &hasValue, &value));
978     CHECK_EQUAL(hasValue, true);
979     CHECK_EQUAL(value, 1.0);
980   }
981 
982   return true;
983 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamGetDesiredSize)984 END_FIXTURE_TEST(StreamTestFixture,
985                  testReadableStream_ReadableStreamGetDesiredSize)
986 
987 BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamClose) {
988   RootedObject stream(cx, NewDefaultStream(cx));
989   CHECK(stream);
990   CHECK(ReadableStreamClose(cx, stream));
991 
992   stream = NewDefaultStream(cx);
993   CHECK(stream);
994   RootedObject otherGlobal(cx, createGlobal());
995   CHECK(otherGlobal);
996   {
997     JSAutoRealm ar(cx, otherGlobal);
998     CHECK(JS_WrapObject(cx, &stream));
999     CHECK(ReadableStreamClose(cx, stream));
1000   }
1001 
1002   return true;
1003 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamClose)1004 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamClose)
1005 
1006 BEGIN_FIXTURE_TEST(StreamTestFixture,
1007                    testReadableStream_ReadableStreamEnqueue_CrossCompartment) {
1008   RootedObject stream(cx, NewDefaultStream(cx));
1009   CHECK(stream);
1010   RootedValue chunk(cx);
1011   CHECK(ReadableStreamEnqueue(cx, stream, chunk));
1012 
1013   RootedObject otherGlobal(cx, createGlobal());
1014   CHECK(otherGlobal);
1015   {
1016     JSAutoRealm ar(cx, otherGlobal);
1017     CHECK(JS_WrapObject(cx, &stream));
1018     RootedValue chunk(cx);
1019     CHECK(ReadableStreamEnqueue(cx, stream, chunk));
1020   }
1021 
1022   return true;
1023 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamEnqueue_CrossCompartment)1024 END_FIXTURE_TEST(StreamTestFixture,
1025                  testReadableStream_ReadableStreamEnqueue_CrossCompartment)
1026 
1027 BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamError) {
1028   RootedObject stream(cx, NewDefaultStream(cx));
1029   CHECK(stream);
1030   RootedValue error(cx);
1031   CHECK(ReadableStreamError(cx, stream, error));
1032 
1033   stream = NewDefaultStream(cx);
1034   RootedObject otherGlobal(cx, createGlobal());
1035   CHECK(otherGlobal);
1036   {
1037     JSAutoRealm ar(cx, otherGlobal);
1038     CHECK(JS_WrapObject(cx, &stream));
1039     RootedValue error(cx);
1040     CHECK(ReadableStreamError(cx, stream, error));
1041   }
1042 
1043   return true;
1044 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamError)1045 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamError)
1046 
1047 BEGIN_FIXTURE_TEST(StreamTestFixture,
1048                    testReadableStream_IsReadableStreamReader) {
1049   RootedObject stream(cx, NewDefaultStream(cx));
1050   CHECK(stream);
1051   RootedObject reader(cx, ReadableStreamGetReader(
1052                               cx, stream, ReadableStreamReaderMode::Default));
1053   CHECK(reader);
1054   CHECK(IsReadableStreamReader(reader));
1055 
1056   RootedObject otherGlobal(cx, createGlobal());
1057   CHECK(otherGlobal);
1058   {
1059     JSAutoRealm ar(cx, otherGlobal);
1060     CHECK(JS_WrapObject(cx, &reader));
1061     CHECK(IsReadableStreamReader(reader));
1062   }
1063 
1064   return true;
1065 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_IsReadableStreamReader)1066 END_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStreamReader)
1067 
1068 BEGIN_FIXTURE_TEST(StreamTestFixture,
1069                    testReadableStream_IsReadableStreamDefaultReader) {
1070   RootedObject stream(cx, NewDefaultStream(cx));
1071   CHECK(stream);
1072   RootedObject reader(cx, ReadableStreamGetReader(
1073                               cx, stream, ReadableStreamReaderMode::Default));
1074   CHECK(IsReadableStreamDefaultReader(reader));
1075 
1076   RootedObject otherGlobal(cx, createGlobal());
1077   CHECK(otherGlobal);
1078   {
1079     JSAutoRealm ar(cx, otherGlobal);
1080     CHECK(JS_WrapObject(cx, &reader));
1081     CHECK(IsReadableStreamDefaultReader(reader));
1082   }
1083 
1084   return true;
1085 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_IsReadableStreamDefaultReader)1086 END_FIXTURE_TEST(StreamTestFixture,
1087                  testReadableStream_IsReadableStreamDefaultReader)
1088 
1089 BEGIN_FIXTURE_TEST(StreamTestFixture,
1090                    testReadableStream_ReadableStreamReaderIsClosed) {
1091   RootedObject stream(cx, NewDefaultStream(cx));
1092   CHECK(stream);
1093 
1094   RootedObject reader(cx, ReadableStreamGetReader(
1095                               cx, stream, ReadableStreamReaderMode::Default));
1096   bool result;
1097   CHECK(ReadableStreamReaderIsClosed(cx, reader, &result));
1098   CHECK_EQUAL(result, false);
1099 
1100   RootedObject otherGlobal(cx, createGlobal());
1101   CHECK(otherGlobal);
1102   {
1103     JSAutoRealm ar(cx, otherGlobal);
1104     CHECK(JS_WrapObject(cx, &reader));
1105     bool result;
1106     CHECK(ReadableStreamReaderIsClosed(cx, reader, &result));
1107   }
1108 
1109   return true;
1110 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamReaderIsClosed)1111 END_FIXTURE_TEST(StreamTestFixture,
1112                  testReadableStream_ReadableStreamReaderIsClosed)
1113 
1114 BEGIN_FIXTURE_TEST(StreamTestFixture,
1115                    testReadableStream_ReadableStreamReaderCancel) {
1116   RootedObject stream(cx, NewDefaultStream(cx));
1117   CHECK(stream);
1118   RootedObject reader(cx, ReadableStreamGetReader(
1119                               cx, stream, ReadableStreamReaderMode::Default));
1120   RootedValue reason(cx);
1121   CHECK(ReadableStreamReaderCancel(cx, reader, reason));
1122 
1123   RootedObject otherGlobal(cx, createGlobal());
1124   CHECK(otherGlobal);
1125   {
1126     JSAutoRealm ar(cx, otherGlobal);
1127     CHECK(JS_WrapObject(cx, &reader));
1128     RootedValue reason(cx);
1129     CHECK(ReadableStreamReaderCancel(cx, reader, reason));
1130   }
1131 
1132   return true;
1133 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamReaderCancel)1134 END_FIXTURE_TEST(StreamTestFixture,
1135                  testReadableStream_ReadableStreamReaderCancel)
1136 
1137 BEGIN_FIXTURE_TEST(StreamTestFixture,
1138                    testReadableStream_ReadableStreamReaderReleaseLock) {
1139   RootedObject stream(cx, NewDefaultStream(cx));
1140   CHECK(stream);
1141   RootedObject reader(cx, ReadableStreamGetReader(
1142                               cx, stream, ReadableStreamReaderMode::Default));
1143   CHECK(reader);
1144   CHECK(ReadableStreamReaderReleaseLock(cx, reader));
1145 
1146   // Repeat the test cross-compartment. This creates a new reader, since
1147   // releasing the lock above deactivated the first reader.
1148   reader =
1149       ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default);
1150   CHECK(reader);
1151   RootedObject otherGlobal(cx, createGlobal());
1152   CHECK(otherGlobal);
1153   {
1154     JSAutoRealm ar(cx, otherGlobal);
1155     CHECK(JS_WrapObject(cx, &reader));
1156     CHECK(ReadableStreamReaderReleaseLock(cx, reader));
1157   }
1158 
1159   return true;
1160 }
END_FIXTURE_TEST(StreamTestFixture,testReadableStream_ReadableStreamReaderReleaseLock)1161 END_FIXTURE_TEST(StreamTestFixture,
1162                  testReadableStream_ReadableStreamReaderReleaseLock)
1163 
1164 BEGIN_FIXTURE_TEST(
1165     StreamTestFixture,
1166     testReadableStream_ReadableStreamDefaultReaderRead_CrossCompartment) {
1167   RootedObject stream(cx, NewDefaultStream(cx));
1168   CHECK(stream);
1169   RootedObject reader(cx, ReadableStreamGetReader(
1170                               cx, stream, ReadableStreamReaderMode::Default));
1171   JSObject* callResult = ReadableStreamDefaultReaderRead(cx, reader);
1172   CHECK(callResult);
1173 
1174   RootedObject otherGlobal(cx, createGlobal());
1175   CHECK(otherGlobal);
1176   {
1177     JSAutoRealm ar(cx, otherGlobal);
1178     CHECK(JS_WrapObject(cx, &reader));
1179     JSObject* callResult = ReadableStreamDefaultReaderRead(cx, reader);
1180     CHECK(callResult);
1181   }
1182 
1183   return true;
1184 }
1185 END_FIXTURE_TEST(
1186     StreamTestFixture,
1187     testReadableStream_ReadableStreamDefaultReaderRead_CrossCompartment)
1188