summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/testReadableStream.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jsapi-tests/testReadableStream.cpp
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jsapi-tests/testReadableStream.cpp')
-rw-r--r--js/src/jsapi-tests/testReadableStream.cpp1187
1 files changed, 1187 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/testReadableStream.cpp b/js/src/jsapi-tests/testReadableStream.cpp
new file mode 100644
index 0000000000..1aa5926c34
--- /dev/null
+++ b/js/src/jsapi-tests/testReadableStream.cpp
@@ -0,0 +1,1187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_IsUint8Array
+#include "js/Stream.h"
+#include "jsapi-tests/tests.h"
+
+using namespace JS;
+
+char testBufferData[] =
+ "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+struct StubExternalUnderlyingSource
+ : public JS::ReadableStreamUnderlyingSource {
+ void* buffer = testBufferData;
+ bool dataRequestCBCalled = false;
+ bool writeIntoRequestBufferCBCalled = false;
+ bool cancelStreamCBCalled = false;
+ Value cancelStreamReason;
+ bool streamClosedCBCalled = false;
+ Value streamClosedReason;
+ bool streamErroredCBCalled = false;
+ Value streamErroredReason;
+ bool finalizeStreamCBCalled = false;
+ void* finalizedStreamUnderlyingSource;
+
+ static StubExternalUnderlyingSource instance;
+
+ void requestData(JSContext* cx, HandleObject stream,
+ size_t desiredSize) override {
+ js::AssertSameCompartment(cx, stream);
+ MOZ_RELEASE_ASSERT(!dataRequestCBCalled, "Invalid test setup");
+ dataRequestCBCalled = true;
+ }
+
+ void writeIntoReadRequestBuffer(JSContext* cx, HandleObject stream,
+ void* buffer, size_t length,
+ size_t* bytesWritten) override {
+ js::AssertSameCompartment(cx, stream);
+ MOZ_RELEASE_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup");
+ writeIntoRequestBufferCBCalled = true;
+
+ MOZ_RELEASE_ASSERT(this == &StubExternalUnderlyingSource::instance);
+ MOZ_RELEASE_ASSERT(StubExternalUnderlyingSource::instance.buffer ==
+ testBufferData);
+ MOZ_RELEASE_ASSERT(length <= sizeof(testBufferData));
+ memcpy(buffer, testBufferData, length);
+ *bytesWritten = length;
+ }
+
+ Value cancel(JSContext* cx, HandleObject stream,
+ HandleValue reason) override {
+ js::AssertSameCompartment(cx, stream);
+ js::AssertSameCompartment(cx, reason);
+ MOZ_RELEASE_ASSERT(!cancelStreamCBCalled, "Invalid test setup");
+ cancelStreamCBCalled = true;
+ cancelStreamReason = reason;
+ return reason;
+ }
+
+ void onClosed(JSContext* cx, HandleObject stream) override {
+ js::AssertSameCompartment(cx, stream);
+ MOZ_RELEASE_ASSERT(!streamClosedCBCalled, "Invalid test setup");
+ streamClosedCBCalled = true;
+ }
+
+ void onErrored(JSContext* cx, HandleObject stream,
+ HandleValue reason) override {
+ js::AssertSameCompartment(cx, stream);
+ js::AssertSameCompartment(cx, reason);
+ MOZ_RELEASE_ASSERT(!streamErroredCBCalled, "Invalid test setup");
+ streamErroredCBCalled = true;
+ streamErroredReason = reason;
+ }
+
+ void finalize() override {
+ MOZ_RELEASE_ASSERT(!finalizeStreamCBCalled, "Invalid test setup");
+ finalizeStreamCBCalled = true;
+ finalizedStreamUnderlyingSource = this;
+ }
+
+ void reset() {
+ dataRequestCBCalled = false;
+ writeIntoRequestBufferCBCalled = false;
+ cancelStreamReason = UndefinedValue();
+ cancelStreamCBCalled = false;
+ streamClosedCBCalled = false;
+ streamErroredCBCalled = false;
+ finalizeStreamCBCalled = false;
+ }
+};
+
+StubExternalUnderlyingSource StubExternalUnderlyingSource::instance;
+
+static_assert(MOZ_ALIGNOF(StubExternalUnderlyingSource) > 1,
+ "UnderlyingSource pointers must not have the low bit set");
+
+static JSObject* NewDefaultStream(JSContext* cx, HandleObject source = nullptr,
+ HandleFunction size = nullptr,
+ double highWaterMark = 1,
+ HandleObject proto = nullptr) {
+ RootedObject stream(cx, NewReadableDefaultStreamObject(cx, source, size,
+ highWaterMark, proto));
+ if (stream) {
+ MOZ_RELEASE_ASSERT(IsReadableStream(stream));
+ }
+ return stream;
+}
+
+static bool GetIterResult(JSContext* cx, HandleObject promise,
+ MutableHandleValue value, bool* done) {
+ RootedObject iterResult(cx, &GetPromiseResult(promise).toObject());
+
+ bool found;
+ if (!JS_HasProperty(cx, iterResult, "value", &found)) {
+ return false;
+ }
+ MOZ_RELEASE_ASSERT(found);
+ if (!JS_HasProperty(cx, iterResult, "done", &found)) {
+ return false;
+ }
+ MOZ_RELEASE_ASSERT(found);
+
+ RootedValue doneVal(cx);
+ if (!JS_GetProperty(cx, iterResult, "value", value)) {
+ return false;
+ }
+ if (!JS_GetProperty(cx, iterResult, "done", &doneVal)) {
+ return false;
+ }
+
+ *done = doneVal.toBoolean();
+ if (*done) {
+ MOZ_RELEASE_ASSERT(value.isUndefined());
+ }
+
+ return true;
+}
+
+static JSObject* GetReadChunk(JSContext* cx, HandleObject readRequest) {
+ MOZ_RELEASE_ASSERT(GetPromiseState(readRequest) == PromiseState::Fulfilled);
+ RootedValue resultVal(cx, GetPromiseResult(readRequest));
+ MOZ_RELEASE_ASSERT(resultVal.isObject());
+ RootedObject result(cx, &resultVal.toObject());
+ RootedValue chunkVal(cx);
+ JS_GetProperty(cx, result, "value", &chunkVal);
+ return &chunkVal.toObject();
+}
+
+struct StreamTestFixture : public JSAPITest {
+ virtual ~StreamTestFixture() {}
+};
+
+BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_NewReadableStream) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ ReadableStreamMode mode;
+ CHECK(ReadableStreamGetMode(cx, stream, &mode));
+ CHECK(mode == ReadableStreamMode::Default);
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_NewReadableStream)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamGetReaderDefault) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ CHECK(reader);
+ CHECK(IsReadableStreamDefaultReader(reader));
+ bool locked;
+ CHECK(ReadableStreamIsLocked(cx, stream, &locked));
+ CHECK(locked);
+ bool closed;
+ CHECK(ReadableStreamReaderIsClosed(cx, reader, &closed));
+ CHECK(!closed);
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamGetReaderDefault)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamTee) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+
+ RootedObject leftStream(cx);
+ RootedObject rightStream(cx);
+ CHECK(ReadableStreamTee(cx, stream, &leftStream, &rightStream));
+ bool locked;
+ CHECK(ReadableStreamIsLocked(cx, stream, &locked));
+ CHECK(locked);
+ CHECK(leftStream);
+ CHECK(IsReadableStream(leftStream));
+ CHECK(rightStream);
+ CHECK(IsReadableStream(rightStream));
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamTee)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamEnqueue) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+
+ RootedObject chunk(cx, JS_NewPlainObject(cx));
+ CHECK(chunk);
+ RootedValue chunkVal(cx, ObjectValue(*chunk));
+ CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamEnqueue)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamDefaultReaderRead) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ CHECK(reader);
+
+ RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
+ CHECK(request);
+ CHECK(IsPromiseObject(request));
+ CHECK(GetPromiseState(request) == PromiseState::Pending);
+
+ RootedObject chunk(cx, JS_NewPlainObject(cx));
+ CHECK(chunk);
+ RootedValue chunkVal(cx, ObjectValue(*chunk));
+ CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
+
+ CHECK(GetReadChunk(cx, request) == chunk);
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamDefaultReaderRead)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamDefaultReaderClose) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ CHECK(reader);
+
+ RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
+ CHECK(request);
+ CHECK(IsPromiseObject(request));
+ CHECK(GetPromiseState(request) == PromiseState::Pending);
+
+ CHECK(ReadableStreamClose(cx, stream));
+
+ bool done;
+ RootedValue value(cx);
+ CHECK(GetPromiseState(request) == PromiseState::Fulfilled);
+ CHECK(GetIterResult(cx, request, &value, &done));
+ CHECK(value.isUndefined());
+ CHECK(done);
+
+ // The callbacks are only invoked for external streams.
+ CHECK(!StubExternalUnderlyingSource::instance.streamClosedCBCalled);
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamDefaultReaderClose)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamDefaultReaderError) {
+ StubExternalUnderlyingSource::instance.reset();
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ CHECK(reader);
+
+ RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
+ CHECK(request);
+ CHECK(IsPromiseObject(request));
+ CHECK(GetPromiseState(request) == PromiseState::Pending);
+
+ bool locked;
+ CHECK(ReadableStreamIsLocked(cx, stream, &locked));
+ CHECK(locked);
+ bool readable;
+ CHECK(ReadableStreamIsReadable(cx, stream, &readable));
+ CHECK(readable);
+ RootedValue error(cx, Int32Value(42));
+ CHECK(ReadableStreamError(cx, stream, error));
+
+ CHECK(GetPromiseState(request) == PromiseState::Rejected);
+ RootedValue reason(cx, GetPromiseResult(request));
+ CHECK(reason.isInt32());
+ CHECK(reason.toInt32() == 42);
+
+ // The callbacks are only invoked for external streams.
+ CHECK(!StubExternalUnderlyingSource::instance.streamErroredCBCalled);
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamDefaultReaderError)
+
+static JSObject* NewExternalSourceStream(
+ JSContext* cx, ReadableStreamUnderlyingSource* source) {
+ RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, source));
+ if (stream) {
+ MOZ_RELEASE_ASSERT(IsReadableStream(stream));
+ }
+ return stream;
+}
+
+static JSObject* NewExternalSourceStream(JSContext* cx) {
+ return NewExternalSourceStream(cx, &StubExternalUnderlyingSource::instance);
+}
+
+BEGIN_FIXTURE_TEST(
+ StreamTestFixture,
+ testReadableStream_CreateReadableByteStreamWithExternalSource) {
+ StubExternalUnderlyingSource::instance.reset();
+
+ RootedObject stream(cx, NewExternalSourceStream(cx));
+ CHECK(stream);
+ ReadableStreamMode mode;
+ CHECK(ReadableStreamGetMode(cx, stream, &mode));
+ CHECK(mode == ReadableStreamMode::ExternalSource);
+ ReadableStreamUnderlyingSource* underlyingSource;
+ CHECK(
+ ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
+ CHECK(underlyingSource == &StubExternalUnderlyingSource::instance);
+ bool locked;
+ CHECK(ReadableStreamIsLocked(cx, stream, &locked));
+ CHECK(locked);
+ CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_CreateReadableByteStreamWithExternalSource)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceCancel) {
+ StubExternalUnderlyingSource::instance.reset();
+
+ RootedObject stream(cx, NewExternalSourceStream(cx));
+ CHECK(stream);
+ RootedValue reason(cx, Int32Value(42));
+ CHECK(ReadableStreamCancel(cx, stream, reason));
+ CHECK(StubExternalUnderlyingSource::instance.cancelStreamCBCalled);
+ CHECK(StubExternalUnderlyingSource::instance.cancelStreamReason == reason);
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceCancel)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ExternalSourceGetReader) {
+ StubExternalUnderlyingSource::instance.reset();
+
+ RootedObject stream(cx, NewExternalSourceStream(cx));
+ CHECK(stream);
+
+ RootedValue streamVal(cx, ObjectValue(*stream));
+ CHECK(JS_SetProperty(cx, global, "stream", streamVal));
+ RootedValue rval(cx);
+ EVAL("stream.getReader()", &rval);
+ CHECK(rval.isObject());
+ RootedObject reader(cx, &rval.toObject());
+ CHECK(IsReadableStreamDefaultReader(reader));
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceGetReader)
+
+enum class CompartmentMode {
+ Same,
+ Cross,
+};
+
+struct ReadFromExternalSourceFixture : public StreamTestFixture {
+ virtual ~ReadFromExternalSourceFixture() {}
+
+ // On success, streamGlobal is a global object (not a wrapper)
+ // and stream is in the same compartment as cx (it may be a CCW).
+ bool createExternalSourceStream(CompartmentMode compartmentMode,
+ MutableHandleObject streamGlobal,
+ MutableHandleObject stream) {
+ if (compartmentMode == CompartmentMode::Same) {
+ streamGlobal.set(global);
+ stream.set(NewExternalSourceStream(cx));
+ if (!stream) {
+ return false;
+ }
+ } else {
+ RootedObject savedGlobal(cx, global);
+ streamGlobal.set(createGlobal());
+ if (!streamGlobal) {
+ return false;
+ }
+ global = savedGlobal;
+
+ {
+ JSAutoRealm ar(cx, streamGlobal);
+ stream.set(NewExternalSourceStream(cx));
+ if (!stream) {
+ return false;
+ }
+ }
+ if (!JS_WrapObject(cx, stream)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool readWithoutDataAvailable(CompartmentMode compartmentMode,
+ const char* evalSrc, const char* evalSrc2,
+ uint32_t writtenLength) {
+ StubExternalUnderlyingSource::instance.reset();
+ definePrint();
+
+ // Create the stream.
+ RootedObject streamGlobal(cx);
+ RootedObject stream(cx); // can be a wrapper
+ CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream));
+ js::RunJobs(cx);
+
+ // GetExternalUnderlyingSource locks the stream.
+ ReadableStreamUnderlyingSource* underlyingSource;
+ CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream,
+ &underlyingSource));
+ CHECK(underlyingSource == &StubExternalUnderlyingSource::instance);
+ bool locked;
+ CHECK(ReadableStreamIsLocked(cx, stream, &locked));
+ CHECK(locked);
+ CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
+
+ // Run caller-supplied JS code to read from the stream.
+ RootedValue streamVal(cx, ObjectValue(*stream));
+ CHECK(JS_SetProperty(cx, global, "stream", streamVal));
+ RootedValue rval(cx);
+ EVAL(evalSrc, &rval);
+ CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled);
+ CHECK(
+ !StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
+ CHECK(rval.isObject());
+ RootedObject unwrappedPromise(cx,
+ js::CheckedUnwrapStatic(&rval.toObject()));
+ CHECK(unwrappedPromise);
+ CHECK(IsPromiseObject(unwrappedPromise));
+ CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Pending);
+
+ // Stream in some data; this resolves the read() result promise.
+ size_t length = sizeof(testBufferData);
+ CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length));
+ CHECK(
+ StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
+ CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled);
+ RootedObject chunk(cx);
+ {
+ JSAutoRealm ar(cx, unwrappedPromise);
+ RootedValue iterVal(cx);
+ bool done;
+ if (!GetIterResult(cx, unwrappedPromise, &iterVal, &done)) {
+ return false;
+ }
+ CHECK(!done);
+ chunk = &iterVal.toObject();
+ }
+ CHECK(JS_WrapObject(cx, &chunk));
+ CHECK(JS_IsUint8Array(chunk));
+
+ {
+ JS::AutoCheckCannotGC noGC(cx);
+ bool dummy;
+ void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
+ CHECK(!memcmp(buffer, testBufferData, writtenLength));
+ }
+
+ // Check the callbacks fired by calling read() again.
+ StubExternalUnderlyingSource::instance.dataRequestCBCalled = false;
+ StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled =
+ false;
+ EVAL(evalSrc2, &rval);
+ CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled);
+ CHECK(
+ !StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
+
+ return true;
+ }
+
+ bool readWithDataAvailable(CompartmentMode compartmentMode,
+ const char* evalSrc, uint32_t writtenLength) {
+ StubExternalUnderlyingSource::instance.reset();
+ definePrint();
+
+ // Create a stream.
+ RootedObject streamGlobal(cx);
+ RootedObject stream(cx);
+ CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream));
+
+ // Getting the underlying source locks the stream.
+ ReadableStreamUnderlyingSource* underlyingSource;
+ CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream,
+ &underlyingSource));
+ CHECK(underlyingSource == &StubExternalUnderlyingSource::instance);
+ bool locked;
+ CHECK(ReadableStreamIsLocked(cx, stream, &locked));
+ CHECK(locked);
+ CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
+
+ // Make some data available.
+ size_t length = sizeof(testBufferData);
+ CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length));
+
+ // Read from the stream.
+ RootedValue streamVal(cx, ObjectValue(*stream));
+ CHECK(JS_SetProperty(cx, global, "stream", streamVal));
+ RootedValue rval(cx);
+ EVAL(evalSrc, &rval);
+ CHECK(
+ StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
+ CHECK(rval.isObject());
+ RootedObject unwrappedPromise(cx,
+ js::CheckedUnwrapStatic(&rval.toObject()));
+ CHECK(unwrappedPromise);
+ CHECK(IsPromiseObject(unwrappedPromise));
+ CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled);
+ RootedObject chunk(cx);
+ {
+ JSAutoRealm ar(cx, unwrappedPromise);
+ RootedValue iterVal(cx);
+ bool done;
+ if (!GetIterResult(cx, unwrappedPromise, &iterVal, &done)) {
+ return false;
+ }
+ CHECK(!done);
+ chunk = &iterVal.toObject();
+ }
+ CHECK(JS_WrapObject(cx, &chunk));
+ CHECK(JS_IsUint8Array(chunk));
+
+ {
+ JS::AutoCheckCannotGC noGC(cx);
+ bool dummy;
+ void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
+ CHECK(!memcmp(buffer, testBufferData, writtenLength));
+ }
+
+ return true;
+ }
+};
+
+BEGIN_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable) {
+ return readWithoutDataAvailable(CompartmentMode::Same,
+ "r = stream.getReader(); r.read()",
+ "r.read()", sizeof(testBufferData));
+}
+END_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)
+
+BEGIN_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment1) {
+ // Scenario 1: The stream and reader are both in the same compartment, but
+ // ReadableStreamUpdateDataAvailableFromSource is applied to a wrapper.
+ return readWithoutDataAvailable(CompartmentMode::Cross,
+ "r = stream.getReader(); r.read()",
+ "r.read()", sizeof(testBufferData));
+}
+END_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment1)
+
+BEGIN_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment2) {
+ // Scenario 2: The stream and reader are in the same compartment, but a
+ // `read` method from another compartment is used on the reader.
+ return readWithoutDataAvailable(
+ CompartmentMode::Cross,
+ "r = stream.getReader(); read = new "
+ "ReadableStream({start(){}}).getReader().read; read.call(r)",
+ "read.call(r)", sizeof(testBufferData));
+}
+END_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment2)
+
+BEGIN_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment3) {
+ // Scenario 3: The stream and reader are in different compartments.
+ return readWithoutDataAvailable(
+ CompartmentMode::Cross,
+ "r = ReadableStream.prototype.getReader.call(stream); r.read()",
+ "r.read()", sizeof(testBufferData));
+}
+END_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment3)
+
+BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceCloseWithPendingRead) {
+ CHECK(readWithoutDataAvailable(CompartmentMode::Same,
+ "r = stream.getReader(); request0 = r.read(); "
+ "request1 = r.read(); request0",
+ "r.read()", sizeof(testBufferData)));
+
+ RootedValue val(cx);
+ CHECK(JS_GetProperty(cx, global, "request1", &val));
+ CHECK(val.isObject());
+ RootedObject request(cx, &val.toObject());
+ CHECK(IsPromiseObject(request));
+ CHECK(GetPromiseState(request) == PromiseState::Pending);
+
+ CHECK(JS_GetProperty(cx, global, "stream", &val));
+ RootedObject stream(cx, &val.toObject());
+ ReadableStreamClose(cx, stream);
+
+ val = GetPromiseResult(request);
+ CHECK(val.isObject());
+ RootedObject result(cx, &val.toObject());
+
+ JS_GetProperty(cx, result, "done", &val);
+ CHECK(val.isBoolean());
+ CHECK(val.toBoolean() == true);
+
+ JS_GetProperty(cx, result, "value", &val);
+ CHECK(val.isUndefined());
+ return true;
+}
+END_FIXTURE_TEST(ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceCloseWithPendingRead)
+
+BEGIN_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithDataAvailable) {
+ return readWithDataAvailable(CompartmentMode::Same,
+ "r = stream.getReader(); r.read()",
+ sizeof(testBufferData));
+}
+END_FIXTURE_TEST(ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithDataAvailable)
+
+BEGIN_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment1) {
+ // Scenario 1: The stream and reader are both in the same compartment, but
+ // ReadableStreamUpdateDataAvailableFromSource is applied to a wrapper.
+ return readWithDataAvailable(CompartmentMode::Cross,
+ "r = stream.getReader(); r.read()",
+ sizeof(testBufferData));
+}
+END_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment1)
+
+BEGIN_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment2) {
+ // Scenario 2: The stream and reader are in the same compartment, but a
+ // `read` method from another compartment is used on the reader.
+ return readWithDataAvailable(
+ CompartmentMode::Cross,
+ "r = stream.getReader(); read = new "
+ "ReadableStream({start(){}}).getReader().read; read.call(r)",
+ sizeof(testBufferData));
+}
+END_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment2)
+
+BEGIN_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment3) {
+ // Scenario 3: The stream and reader are in different compartments.
+ return readWithDataAvailable(
+ CompartmentMode::Cross,
+ "r = ReadableStream.prototype.getReader.call(stream); r.read()",
+ sizeof(testBufferData));
+}
+END_FIXTURE_TEST(
+ ReadFromExternalSourceFixture,
+ testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment3)
+
+// Cross-global tests:
+BEGIN_FIXTURE_TEST(
+ StreamTestFixture,
+ testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ CHECK(reader);
+
+ RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
+ CHECK(request);
+ CHECK(IsPromiseObject(request));
+ CHECK(!js::IsWrapper(request));
+ CHECK(GetPromiseState(request) == PromiseState::Pending);
+
+ RootedObject chunk(cx, JS_NewPlainObject(cx));
+ CHECK(chunk);
+ RootedValue chunkVal(cx, ObjectValue(*chunk));
+ CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
+
+ CHECK(GetReadChunk(cx, request) == chunk);
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead)
+
+BEGIN_FIXTURE_TEST(
+ StreamTestFixture,
+ testReadableStream_ReadableStreamGetExternalUnderlyingSource) {
+ StubExternalUnderlyingSource::instance.reset();
+
+ RootedObject stream(cx, NewExternalSourceStream(cx));
+ CHECK(stream);
+ ReadableStreamUnderlyingSource* source;
+ CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source));
+ CHECK(source == &StubExternalUnderlyingSource::instance);
+ CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ ReadableStreamUnderlyingSource* source;
+ CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source));
+ CHECK(source == &StubExternalUnderlyingSource::instance);
+ CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamGetExternalUnderlyingSource)
+
+BEGIN_FIXTURE_TEST(
+ StreamTestFixture,
+ testReadableStream_ReadableStreamUpdateDataAvailableFromSource) {
+ RootedObject stream(cx, NewExternalSourceStream(cx));
+ CHECK(stream);
+ CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, 0));
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, 1));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamUpdateDataAvailableFromSource)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStream) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ CHECK(IsReadableStream(stream));
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ CHECK(IsReadableStream(stream));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStream)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamGetMode) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ ReadableStreamMode mode;
+ CHECK(ReadableStreamGetMode(cx, stream, &mode));
+ CHECK(mode == ReadableStreamMode::Default);
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ CHECK(ReadableStreamGetMode(cx, stream, &mode));
+ CHECK(mode == ReadableStreamMode::Default);
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamGetMode)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamIsReadable) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ bool result;
+ CHECK(ReadableStreamIsReadable(cx, stream, &result));
+ CHECK(result);
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ CHECK(ReadableStreamIsReadable(cx, stream, &result));
+ CHECK(result);
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamIsReadable)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamIsLocked) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ bool result;
+ CHECK(ReadableStreamIsLocked(cx, stream, &result));
+ CHECK_EQUAL(result, false);
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ CHECK(ReadableStreamIsLocked(cx, stream, &result));
+ CHECK_EQUAL(result, false);
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamIsLocked)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamIsDisturbed) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ bool result;
+ CHECK(ReadableStreamIsDisturbed(cx, stream, &result));
+ CHECK_EQUAL(result, false);
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ CHECK(ReadableStreamIsDisturbed(cx, stream, &result));
+ CHECK_EQUAL(result, false);
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamIsDisturbed)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamCancel) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+
+ RootedValue reason(cx);
+ JSObject* callResult = ReadableStreamCancel(cx, stream, reason);
+ CHECK(callResult);
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ RootedValue reason(cx);
+ JSObject* callResult = ReadableStreamCancel(cx, stream, reason);
+ CHECK(callResult);
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamCancel)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamGetReader) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+
+ RootedObject reader(cx);
+ reader =
+ ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default);
+ CHECK(reader);
+ CHECK(IsReadableStreamDefaultReader(reader));
+ CHECK(ReadableStreamReaderReleaseLock(cx, reader));
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ JSObject* callResult =
+ ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default);
+ CHECK(callResult);
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamGetReader)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamTee_CrossCompartment) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+
+ RootedObject branch1Stream(cx);
+ RootedObject branch2Stream(cx);
+ CHECK(ReadableStreamTee(cx, stream, &branch1Stream, &branch2Stream));
+ CHECK(IsReadableStream(branch1Stream));
+ CHECK(IsReadableStream(branch2Stream));
+ stream = branch1Stream;
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ CHECK(ReadableStreamTee(cx, stream, &branch1Stream, &branch2Stream));
+ CHECK(IsReadableStream(branch1Stream));
+ CHECK(IsReadableStream(branch2Stream));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamTee_CrossCompartment)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamGetDesiredSize) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ bool hasValue;
+ double value;
+ CHECK(ReadableStreamGetDesiredSize(cx, stream, &hasValue, &value));
+ CHECK_EQUAL(hasValue, true);
+ CHECK_EQUAL(value, 1.0);
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ hasValue = false;
+ value = 0;
+ CHECK(ReadableStreamGetDesiredSize(cx, stream, &hasValue, &value));
+ CHECK_EQUAL(hasValue, true);
+ CHECK_EQUAL(value, 1.0);
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamGetDesiredSize)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamClose) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ CHECK(ReadableStreamClose(cx, stream));
+
+ stream = NewDefaultStream(cx);
+ CHECK(stream);
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ CHECK(ReadableStreamClose(cx, stream));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamClose)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamEnqueue_CrossCompartment) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedValue chunk(cx);
+ CHECK(ReadableStreamEnqueue(cx, stream, chunk));
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ RootedValue chunk(cx);
+ CHECK(ReadableStreamEnqueue(cx, stream, chunk));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamEnqueue_CrossCompartment)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamError) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedValue error(cx);
+ CHECK(ReadableStreamError(cx, stream, error));
+
+ stream = NewDefaultStream(cx);
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &stream));
+ RootedValue error(cx);
+ CHECK(ReadableStreamError(cx, stream, error));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamError)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_IsReadableStreamReader) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ CHECK(reader);
+ CHECK(IsReadableStreamReader(reader));
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &reader));
+ CHECK(IsReadableStreamReader(reader));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStreamReader)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_IsReadableStreamDefaultReader) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ CHECK(IsReadableStreamDefaultReader(reader));
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &reader));
+ CHECK(IsReadableStreamDefaultReader(reader));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_IsReadableStreamDefaultReader)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamReaderIsClosed) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ bool result;
+ CHECK(ReadableStreamReaderIsClosed(cx, reader, &result));
+ CHECK_EQUAL(result, false);
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &reader));
+ bool result;
+ CHECK(ReadableStreamReaderIsClosed(cx, reader, &result));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamReaderIsClosed)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamReaderCancel) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ RootedValue reason(cx);
+ CHECK(ReadableStreamReaderCancel(cx, reader, reason));
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &reader));
+ RootedValue reason(cx);
+ CHECK(ReadableStreamReaderCancel(cx, reader, reason));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamReaderCancel)
+
+BEGIN_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamReaderReleaseLock) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ CHECK(reader);
+ CHECK(ReadableStreamReaderReleaseLock(cx, reader));
+
+ // Repeat the test cross-compartment. This creates a new reader, since
+ // releasing the lock above deactivated the first reader.
+ reader =
+ ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default);
+ CHECK(reader);
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &reader));
+ CHECK(ReadableStreamReaderReleaseLock(cx, reader));
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(StreamTestFixture,
+ testReadableStream_ReadableStreamReaderReleaseLock)
+
+BEGIN_FIXTURE_TEST(
+ StreamTestFixture,
+ testReadableStream_ReadableStreamDefaultReaderRead_CrossCompartment) {
+ RootedObject stream(cx, NewDefaultStream(cx));
+ CHECK(stream);
+ RootedObject reader(cx, ReadableStreamGetReader(
+ cx, stream, ReadableStreamReaderMode::Default));
+ JSObject* callResult = ReadableStreamDefaultReaderRead(cx, reader);
+ CHECK(callResult);
+
+ RootedObject otherGlobal(cx, createGlobal());
+ CHECK(otherGlobal);
+ {
+ JSAutoRealm ar(cx, otherGlobal);
+ CHECK(JS_WrapObject(cx, &reader));
+ JSObject* callResult = ReadableStreamDefaultReaderRead(cx, reader);
+ CHECK(callResult);
+ }
+
+ return true;
+}
+END_FIXTURE_TEST(
+ StreamTestFixture,
+ testReadableStream_ReadableStreamDefaultReaderRead_CrossCompartment)