Source code

Revision control

Other Tools

/* -*- 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/PropertyAndElement.h" // JS_GetProperty, JS_HasProperty, JS_SetProperty
#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)