зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1503324 - Implement ReadableStreamCreateReadResult. r=arai
This prevents author code from using Object.prototype.then to observe or tamper with a stream that is locked by another consumer. Differential Revision: https://phabricator.services.mozilla.com/D12081 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
ec618b5047
Коммит
daac4d31de
|
@ -451,37 +451,38 @@ extern JS_PUBLIC_API bool
|
||||||
ReadableStreamError(JSContext* cx, HandleObject stream, HandleValue error);
|
ReadableStreamError(JSContext* cx, HandleObject stream, HandleValue error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels the given ReadableStream reader's associated stream.
|
* C++ equivalent of `reader.cancel(reason)`
|
||||||
|
* (both <https://streams.spec.whatwg.org/#default-reader-cancel> and
|
||||||
|
* <https://streams.spec.whatwg.org/#byob-reader-cancel>).
|
||||||
*
|
*
|
||||||
* Throws a TypeError and returns false if the given reader isn't active.
|
* `reader` must be a stream reader created using `JS::ReadableStreamGetReader`
|
||||||
*
|
* or an unwrappable wrapper for one. (This function is meant to support using
|
||||||
* Asserts that |reader| is a ReadableStreamDefaultReader or
|
* C++ to read from streams. It's not meant to allow C++ code to operate on
|
||||||
* ReadableStreamBYOBReader object or an unwrappable wrapper for one.
|
* readers created by scripts.)
|
||||||
*/
|
*/
|
||||||
extern JS_PUBLIC_API bool
|
extern JS_PUBLIC_API bool
|
||||||
ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason);
|
ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels the given ReadableStream reader's associated stream.
|
* C++ equivalent of `reader.releaseLock()`
|
||||||
|
* (both <https://streams.spec.whatwg.org/#default-reader-release-lock> and
|
||||||
|
* <https://streams.spec.whatwg.org/#byob-reader-release-lock>).
|
||||||
*
|
*
|
||||||
* Throws a TypeError and returns false if the given reader has pending
|
* `reader` must be a stream reader created using `JS::ReadableStreamGetReader`
|
||||||
* read or readInto (for default or byob readers, respectively) requests.
|
* or an unwrappable wrapper for one.
|
||||||
*
|
|
||||||
* Asserts that |reader| is a ReadableStreamDefaultReader or
|
|
||||||
* ReadableStreamBYOBReader object or an unwrappable wrapper for one.
|
|
||||||
*/
|
*/
|
||||||
extern JS_PUBLIC_API bool
|
extern JS_PUBLIC_API bool
|
||||||
ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader);
|
ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests a read from the reader's associated ReadableStream and returns the
|
* C++ equivalent of the `reader.read()` method on default readers
|
||||||
* resulting PromiseObject.
|
* (<https://streams.spec.whatwg.org/#default-reader-read>).
|
||||||
*
|
*
|
||||||
* Returns a Promise that's resolved with the read result once available or
|
* The result is a new Promise object, or null on OOM.
|
||||||
* rejected immediately if the stream is errored or the operation failed.
|
|
||||||
*
|
*
|
||||||
* Asserts that |reader| is a ReadableStreamDefaultReader object or an
|
* `reader` must be the result of calling `JS::ReadableStreamGetReader` with
|
||||||
* unwrappable wrapper for one.
|
* `ReadableStreamReaderMode::Default` mode, or an unwrappable wrapper for such
|
||||||
|
* a reader.
|
||||||
*/
|
*/
|
||||||
extern JS_PUBLIC_API JSObject*
|
extern JS_PUBLIC_API JSObject*
|
||||||
ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject reader);
|
ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject reader);
|
||||||
|
|
|
@ -742,7 +742,9 @@ ReadableStream_cancel(JSContext* cx, unsigned argc, Value* vp)
|
||||||
}
|
}
|
||||||
|
|
||||||
static MOZ_MUST_USE ReadableStreamDefaultReader*
|
static MOZ_MUST_USE ReadableStreamDefaultReader*
|
||||||
CreateReadableStreamDefaultReader(JSContext* cx, Handle<ReadableStream*> unwrappedStream);
|
CreateReadableStreamDefaultReader(JSContext* cx,
|
||||||
|
Handle<ReadableStream*> unwrappedStream,
|
||||||
|
ForAuthorCodeBool forAuthorCode = ForAuthorCodeBool::No);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streams spec, 3.2.5.3. getReader()
|
* Streams spec, 3.2.5.3. getReader()
|
||||||
|
@ -772,7 +774,7 @@ ReadableStream_getReader(JSContext* cx, unsigned argc, Value* vp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modeVal.isUndefined()) {
|
if (modeVal.isUndefined()) {
|
||||||
reader = CreateReadableStreamDefaultReader(cx, unwrappedStream);
|
reader = CreateReadableStreamDefaultReader(cx, unwrappedStream, ForAuthorCodeBool::Yes);
|
||||||
} else {
|
} else {
|
||||||
// Step 3: Set mode to ? ToString(mode) (implicit).
|
// Step 3: Set mode to ? ToString(mode) (implicit).
|
||||||
RootedString mode(cx, ToString<CanGC>(cx, modeVal));
|
RootedString mode(cx, ToString<CanGC>(cx, modeVal));
|
||||||
|
@ -1292,8 +1294,12 @@ AppendToListAtSlot(JSContext* cx,
|
||||||
HandleObject obj);
|
HandleObject obj);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streams spec, 3.4.1. ReadableStreamAddReadIntoRequest ( stream )
|
* Streams spec, 3.4.1. ReadableStreamAddReadIntoRequest ( stream, forAuthorCode )
|
||||||
* Streams spec, 3.4.2. ReadableStreamAddReadRequest ( stream )
|
* Streams spec, 3.4.2. ReadableStreamAddReadRequest ( stream, forAuthorCode )
|
||||||
|
*
|
||||||
|
* Our implementation does not pass around forAuthorCode parameters in the same
|
||||||
|
* places as the standard, but the effect is the same. See the comment on
|
||||||
|
* `ReadableStreamReader::forAuthorCode()`.
|
||||||
*/
|
*/
|
||||||
static MOZ_MUST_USE JSObject*
|
static MOZ_MUST_USE JSObject*
|
||||||
ReadableStreamAddReadOrReadIntoRequest(JSContext* cx, Handle<ReadableStream*> unwrappedStream)
|
ReadableStreamAddReadOrReadIntoRequest(JSContext* cx, Handle<ReadableStream*> unwrappedStream)
|
||||||
|
@ -1314,10 +1320,13 @@ ReadableStreamAddReadOrReadIntoRequest(JSContext* cx, Handle<ReadableStream*> un
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4: Let read{Into}Request be Record {[[promise]]: promise}.
|
// Step 4: Let read{Into}Request be
|
||||||
|
// Record {[[promise]]: promise, [[forAuthorCode]]: forAuthorCode}.
|
||||||
// Step 5: Append read{Into}Request as the last element of
|
// Step 5: Append read{Into}Request as the last element of
|
||||||
// stream.[[reader]].[[read{Into}Requests]].
|
// stream.[[reader]].[[read{Into}Requests]].
|
||||||
// Since [[promise]] is the Record's only field, we store it directly.
|
// Since we don't need the [[forAuthorCode]] field (see the comment on
|
||||||
|
// `ReadableStreamReader::forAuthorCode()`), we elide the Record and store
|
||||||
|
// only the promise.
|
||||||
if (!AppendToListAtSlot(cx, unwrappedReader, ReadableStreamReader::Slot_Requests, promise)) {
|
if (!AppendToListAtSlot(cx, unwrappedReader, ReadableStreamReader::Slot_Requests, promise)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1396,6 +1405,12 @@ ReadableStreamCancel(JSContext* cx, Handle<ReadableStream*> unwrappedStream, Han
|
||||||
return JS::CallOriginalPromiseThen(cx, sourceCancelPromise, returnUndefined, nullptr);
|
return JS::CallOriginalPromiseThen(cx, sourceCancelPromise, returnUndefined, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MOZ_MUST_USE JSObject*
|
||||||
|
ReadableStreamCreateReadResult(JSContext* cx,
|
||||||
|
HandleValue value,
|
||||||
|
bool done,
|
||||||
|
ForAuthorCodeBool forAuthorCode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streams spec, 3.4.4. ReadableStreamClose ( stream )
|
* Streams spec, 3.4.4. ReadableStreamClose ( stream )
|
||||||
*/
|
*/
|
||||||
|
@ -1421,6 +1436,8 @@ ReadableStreamCloseInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStre
|
||||||
|
|
||||||
// Step 5: If ! IsReadableStreamDefaultReader(reader) is true,
|
// Step 5: If ! IsReadableStreamDefaultReader(reader) is true,
|
||||||
if (unwrappedReader->is<ReadableStreamDefaultReader>()) {
|
if (unwrappedReader->is<ReadableStreamDefaultReader>()) {
|
||||||
|
ForAuthorCodeBool forAuthorCode = unwrappedReader->forAuthorCode();
|
||||||
|
|
||||||
// Step a: Repeat for each readRequest that is an element of
|
// Step a: Repeat for each readRequest that is an element of
|
||||||
// reader.[[readRequests]],
|
// reader.[[readRequests]],
|
||||||
RootedNativeObject unwrappedReadRequests(cx, unwrappedReader->requests());
|
RootedNativeObject unwrappedReadRequests(cx, unwrappedReader->requests());
|
||||||
|
@ -1430,13 +1447,15 @@ ReadableStreamCloseInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStre
|
||||||
RootedValue resultVal(cx);
|
RootedValue resultVal(cx);
|
||||||
for (uint32_t i = 0; i < len; i++) {
|
for (uint32_t i = 0; i < len; i++) {
|
||||||
// Step i: Resolve readRequest.[[promise]] with
|
// Step i: Resolve readRequest.[[promise]] with
|
||||||
// ! CreateIterResultObject(undefined, true).
|
// ! ReadableStreamCreateReadResult(undefined, true,
|
||||||
|
// readRequest.[[forAuthorCode]]).
|
||||||
readRequest = &unwrappedReadRequests->getDenseElement(i).toObject();
|
readRequest = &unwrappedReadRequests->getDenseElement(i).toObject();
|
||||||
if (!cx->compartment()->wrap(cx, &readRequest)) {
|
if (!cx->compartment()->wrap(cx, &readRequest)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
resultObj = CreateIterResultObject(cx, UndefinedHandleValue, true);
|
resultObj = ReadableStreamCreateReadResult(cx, UndefinedHandleValue, true,
|
||||||
|
forAuthorCode);
|
||||||
if (!resultObj) {
|
if (!resultObj) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1476,6 +1495,40 @@ ReadableStreamCloseInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStre
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Streams spec, 3.4.5. ReadableStreamCreateReadResult ( value, done, forAuthorCode )
|
||||||
|
*/
|
||||||
|
static MOZ_MUST_USE JSObject*
|
||||||
|
ReadableStreamCreateReadResult(JSContext* cx,
|
||||||
|
HandleValue value,
|
||||||
|
bool done,
|
||||||
|
ForAuthorCodeBool forAuthorCode)
|
||||||
|
{
|
||||||
|
// Step 1: Let prototype be null.
|
||||||
|
// Step 2: If forAuthorCode is true, set prototype to %ObjectPrototype%.
|
||||||
|
RootedObject templateObject(cx,
|
||||||
|
forAuthorCode == ForAuthorCodeBool::Yes
|
||||||
|
? cx->realm()->getOrCreateIterResultTemplateObject(cx)
|
||||||
|
: cx->realm()->getOrCreateIterResultWithoutPrototypeTemplateObject(cx));
|
||||||
|
|
||||||
|
// Step 3: Assert: Type(done) is Boolean (implicit).
|
||||||
|
|
||||||
|
// Step 4: Let obj be ObjectCreate(prototype).
|
||||||
|
NativeObject* obj;
|
||||||
|
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, NativeObject::createWithTemplate(cx, gc::DefaultHeap,
|
||||||
|
templateObject));
|
||||||
|
|
||||||
|
// Step 5: Perform CreateDataProperty(obj, "value", value).
|
||||||
|
obj->setSlot(Realm::IterResultObjectValueSlot, value);
|
||||||
|
|
||||||
|
// Step 6: Perform CreateDataProperty(obj, "done", done).
|
||||||
|
obj->setSlot(Realm::IterResultObjectDoneSlot,
|
||||||
|
done ? TrueHandleValue : FalseHandleValue);
|
||||||
|
|
||||||
|
// Step 7: Return obj.
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streams spec, 3.4.6. ReadableStreamError ( stream, e )
|
* Streams spec, 3.4.6. ReadableStreamError ( stream, e )
|
||||||
*/
|
*/
|
||||||
|
@ -1610,9 +1663,10 @@ ReadableStreamFulfillReadOrReadIntoRequest(JSContext* cx,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4: Resolve readIntoRequest.[[promise]] with
|
// Step 4: Resolve read{Into}Request.[[promise]] with
|
||||||
// ! CreateIterResultObject(chunk, done).
|
// ! ReadableStreamCreateReadResult(chunk, done, readIntoRequest.[[forAuthorCode]]).
|
||||||
RootedObject iterResult(cx, CreateIterResultObject(cx, chunk, done));
|
RootedObject iterResult(cx,
|
||||||
|
ReadableStreamCreateReadResult(cx, chunk, done, unwrappedReader->forAuthorCode()));
|
||||||
if (!iterResult) {
|
if (!iterResult) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1677,14 +1731,17 @@ ReadableStreamHasDefaultReader(JSContext* cx,
|
||||||
static MOZ_MUST_USE bool
|
static MOZ_MUST_USE bool
|
||||||
ReadableStreamReaderGenericInitialize(JSContext* cx,
|
ReadableStreamReaderGenericInitialize(JSContext* cx,
|
||||||
Handle<ReadableStreamReader*> reader,
|
Handle<ReadableStreamReader*> reader,
|
||||||
Handle<ReadableStream*> unwrappedStream);
|
Handle<ReadableStream*> unwrappedStream,
|
||||||
|
ForAuthorCodeBool forAuthorCode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream spec, 3.5.3. new ReadableStreamDefaultReader ( stream )
|
* Stream spec, 3.5.3. new ReadableStreamDefaultReader ( stream )
|
||||||
* Steps 2-4.
|
* Steps 2-4.
|
||||||
*/
|
*/
|
||||||
static MOZ_MUST_USE ReadableStreamDefaultReader*
|
static MOZ_MUST_USE ReadableStreamDefaultReader*
|
||||||
CreateReadableStreamDefaultReader(JSContext* cx, Handle<ReadableStream*> unwrappedStream)
|
CreateReadableStreamDefaultReader(JSContext* cx,
|
||||||
|
Handle<ReadableStream*> unwrappedStream,
|
||||||
|
ForAuthorCodeBool forAuthorCode)
|
||||||
{
|
{
|
||||||
Rooted<ReadableStreamDefaultReader*> reader(cx,
|
Rooted<ReadableStreamDefaultReader*> reader(cx,
|
||||||
NewBuiltinClassInstance<ReadableStreamDefaultReader>(cx));
|
NewBuiltinClassInstance<ReadableStreamDefaultReader>(cx));
|
||||||
|
@ -1700,7 +1757,7 @@ CreateReadableStreamDefaultReader(JSContext* cx, Handle<ReadableStream*> unwrapp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Perform ! ReadableStreamReaderGenericInitialize(this, stream).
|
// Step 3: Perform ! ReadableStreamReaderGenericInitialize(this, stream).
|
||||||
if (!ReadableStreamReaderGenericInitialize(cx, reader, unwrappedStream)) {
|
if (!ReadableStreamReaderGenericInitialize(cx, reader, unwrappedStream, forAuthorCode)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1831,7 +1888,7 @@ ReadableStreamDefaultReader_read(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return ReturnPromiseRejectedWithPendingError(cx, args);
|
return ReturnPromiseRejectedWithPendingError(cx, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Return ! ReadableStreamDefaultReaderRead(this).
|
// Step 3: Return ! ReadableStreamDefaultReaderRead(this, true).
|
||||||
JSObject* readPromise = ::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
|
JSObject* readPromise = ::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
|
||||||
if (!readPromise) {
|
if (!readPromise) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1937,8 +1994,10 @@ ReadableStreamReaderGenericCancel(JSContext* cx,
|
||||||
* Streams spec, 3.7.4. ReadableStreamReaderGenericInitialize ( reader, stream )
|
* Streams spec, 3.7.4. ReadableStreamReaderGenericInitialize ( reader, stream )
|
||||||
*/
|
*/
|
||||||
static MOZ_MUST_USE bool
|
static MOZ_MUST_USE bool
|
||||||
ReadableStreamReaderGenericInitialize(JSContext* cx, Handle<ReadableStreamReader*> reader,
|
ReadableStreamReaderGenericInitialize(JSContext* cx,
|
||||||
Handle<ReadableStream*> unwrappedStream)
|
Handle<ReadableStreamReader*> reader,
|
||||||
|
Handle<ReadableStream*> unwrappedStream,
|
||||||
|
ForAuthorCodeBool forAuthorCode)
|
||||||
{
|
{
|
||||||
cx->check(reader);
|
cx->check(reader);
|
||||||
|
|
||||||
|
@ -1991,6 +2050,11 @@ ReadableStreamReaderGenericInitialize(JSContext* cx, Handle<ReadableStreamReader
|
||||||
}
|
}
|
||||||
|
|
||||||
reader->setClosedPromise(promise);
|
reader->setClosedPromise(promise);
|
||||||
|
|
||||||
|
// Extra step not in the standard. See the comment on
|
||||||
|
// `ReadableStreamReader::forAuthorCode()`.
|
||||||
|
reader->setForAuthorCode(forAuthorCode);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2066,26 +2130,30 @@ ReadableStreamControllerPullSteps(JSContext* cx,
|
||||||
Handle<ReadableStreamController*> unwrappedController);
|
Handle<ReadableStreamController*> unwrappedController);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streams spec, 3.7.7. ReadableStreamDefaultReaderRead ( reader )
|
* Streams spec, 3.7.7. ReadableStreamDefaultReaderRead ( reader [, forAuthorCode ] )
|
||||||
*/
|
*/
|
||||||
static MOZ_MUST_USE JSObject*
|
static MOZ_MUST_USE JSObject*
|
||||||
ReadableStreamDefaultReaderRead(JSContext* cx,
|
ReadableStreamDefaultReaderRead(JSContext* cx,
|
||||||
Handle<ReadableStreamDefaultReader*> unwrappedReader)
|
Handle<ReadableStreamDefaultReader*> unwrappedReader)
|
||||||
{
|
{
|
||||||
// Step 1: Let stream be reader.[[ownerReadableStream]].
|
// Step 1: If forAuthorCode was not passed, set it to false (implicit).
|
||||||
// Step 2: Assert: stream is not undefined.
|
|
||||||
|
// Step 2: Let stream be reader.[[ownerReadableStream]].
|
||||||
|
// Step 3: Assert: stream is not undefined.
|
||||||
Rooted<ReadableStream*> unwrappedStream(cx, UnwrapStreamFromReader(cx, unwrappedReader));
|
Rooted<ReadableStream*> unwrappedStream(cx, UnwrapStreamFromReader(cx, unwrappedReader));
|
||||||
if (!unwrappedStream) {
|
if (!unwrappedStream) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Set stream.[[disturbed]] to true.
|
// Step 4: Set stream.[[disturbed]] to true.
|
||||||
unwrappedStream->setDisturbed();
|
unwrappedStream->setDisturbed();
|
||||||
|
|
||||||
// Step 4: If stream.[[state]] is "closed", return a new promise resolved with
|
// Step 5: If stream.[[state]] is "closed", return a new promise resolved with
|
||||||
// ! CreateIterResultObject(undefined, true).
|
// ! ReadableStreamCreateReadResult(undefined, true, forAuthorCode).
|
||||||
if (unwrappedStream->closed()) {
|
if (unwrappedStream->closed()) {
|
||||||
RootedObject iterResult(cx, CreateIterResultObject(cx, UndefinedHandleValue, true));
|
RootedObject iterResult(cx,
|
||||||
|
ReadableStreamCreateReadResult(cx, UndefinedHandleValue, true,
|
||||||
|
unwrappedReader->forAuthorCode()));
|
||||||
if (!iterResult) {
|
if (!iterResult) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -2093,7 +2161,7 @@ ReadableStreamDefaultReaderRead(JSContext* cx,
|
||||||
return PromiseObject::unforgeableResolve(cx, iterResultVal);
|
return PromiseObject::unforgeableResolve(cx, iterResultVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: If stream.[[state]] is "errored", return a new promise rejected with
|
// Step 6: If stream.[[state]] is "errored", return a new promise rejected with
|
||||||
// stream.[[storedError]].
|
// stream.[[storedError]].
|
||||||
if (unwrappedStream->errored()) {
|
if (unwrappedStream->errored()) {
|
||||||
RootedValue storedError(cx, unwrappedStream->storedError());
|
RootedValue storedError(cx, unwrappedStream->storedError());
|
||||||
|
@ -2103,10 +2171,10 @@ ReadableStreamDefaultReaderRead(JSContext* cx,
|
||||||
return PromiseObject::unforgeableReject(cx, storedError);
|
return PromiseObject::unforgeableReject(cx, storedError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6: Assert: stream.[[state]] is "readable".
|
// Step 7: Assert: stream.[[state]] is "readable".
|
||||||
MOZ_ASSERT(unwrappedStream->readable());
|
MOZ_ASSERT(unwrappedStream->readable());
|
||||||
|
|
||||||
// Step 7: Return ! stream.[[readableStreamController]].[[PullSteps]]().
|
// Step 8: Return ! stream.[[readableStreamController]].[[PullSteps]]().
|
||||||
Rooted<ReadableStreamController*> unwrappedController(cx, unwrappedStream->controller());
|
Rooted<ReadableStreamController*> unwrappedController(cx, unwrappedStream->controller());
|
||||||
return ReadableStreamControllerPullSteps(cx, unwrappedController);
|
return ReadableStreamControllerPullSteps(cx, unwrappedController);
|
||||||
}
|
}
|
||||||
|
@ -2580,7 +2648,7 @@ DequeueValue(JSContext* cx,
|
||||||
MutableHandleValue chunk);
|
MutableHandleValue chunk);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streams spec, 3.8.5.2. ReadableStreamDefaultController [[PullSteps]]()
|
* Streams spec, 3.8.5.2. ReadableStreamDefaultController [[PullSteps]]( forAuthorCode )
|
||||||
*/
|
*/
|
||||||
static JSObject*
|
static JSObject*
|
||||||
ReadableStreamDefaultControllerPullSteps(JSContext* cx,
|
ReadableStreamDefaultControllerPullSteps(JSContext* cx,
|
||||||
|
@ -2620,17 +2688,24 @@ ReadableStreamDefaultControllerPullSteps(JSContext* cx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step d: Return a promise resolved with ! CreateIterResultObject(chunk, false).
|
// Step d: Return a promise resolved with
|
||||||
|
// ! ReadableStreamCreateReadResult(chunk, false, forAuthorCode).
|
||||||
cx->check(chunk);
|
cx->check(chunk);
|
||||||
RootedObject iterResultObj(cx, CreateIterResultObject(cx, chunk, false));
|
ReadableStreamReader* unwrappedReader = UnwrapReaderFromStream(cx, unwrappedStream);
|
||||||
if (!iterResultObj) {
|
if (!unwrappedReader) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
RootedValue iterResult(cx, ObjectValue(*iterResultObj));
|
RootedObject readResultObj(cx,
|
||||||
return PromiseObject::unforgeableResolve(cx, iterResult);
|
ReadableStreamCreateReadResult(cx, chunk, false, unwrappedReader->forAuthorCode()));
|
||||||
|
if (!readResultObj) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
RootedValue readResult(cx, ObjectValue(*readResultObj));
|
||||||
|
return PromiseObject::unforgeableResolve(cx, readResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Let pendingPromise be ! ReadableStreamAddReadRequest(stream).
|
// Step 3: Let pendingPromise be
|
||||||
|
// ! ReadableStreamAddReadRequest(stream, forAuthorCode).
|
||||||
RootedObject pendingPromise(cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream));
|
RootedObject pendingPromise(cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream));
|
||||||
if (!pendingPromise) {
|
if (!pendingPromise) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -3297,7 +3372,7 @@ ReadableByteStreamControllerHandleQueueDrain(JSContext* cx,
|
||||||
Handle<ReadableStreamController*> unwrappedController);
|
Handle<ReadableStreamController*> unwrappedController);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streams spec, 3.10.5.2. [[PullSteps]] ()
|
* Streams spec, 3.10.5.2. [[PullSteps]] ( forAuthorCode )
|
||||||
*/
|
*/
|
||||||
static MOZ_MUST_USE JSObject*
|
static MOZ_MUST_USE JSObject*
|
||||||
ReadableByteStreamControllerPullSteps(JSContext* cx,
|
ReadableByteStreamControllerPullSteps(JSContext* cx,
|
||||||
|
@ -3386,13 +3461,19 @@ ReadableByteStreamControllerPullSteps(JSContext* cx,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3.g: Return a promise resolved with ! CreateIterResultObject(view, false).
|
// Step 3.g: Return a promise resolved with
|
||||||
|
// ! ReadableStreamCreateReadResult(view, false, forAuthorCode).
|
||||||
val.setObject(*view);
|
val.setObject(*view);
|
||||||
RootedObject iterResult(cx, CreateIterResultObject(cx, val, false));
|
ReadableStreamReader* unwrappedReader = UnwrapReaderFromStream(cx, unwrappedStream);
|
||||||
if (!iterResult) {
|
if (!unwrappedReader) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
val.setObject(*iterResult);
|
RootedObject readResult(cx,
|
||||||
|
ReadableStreamCreateReadResult(cx, val, false, unwrappedReader->forAuthorCode()));
|
||||||
|
if (!readResult) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
val.setObject(*readResult);
|
||||||
|
|
||||||
return PromiseObject::unforgeableResolve(cx, val);
|
return PromiseObject::unforgeableResolve(cx, val);
|
||||||
}
|
}
|
||||||
|
@ -3440,7 +3521,7 @@ ReadableByteStreamControllerPullSteps(JSContext* cx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6: Let promise be ! ReadableStreamAddReadRequest(stream).
|
// Step 6: Let promise be ! ReadableStreamAddReadRequest(stream, forAuthorCode).
|
||||||
RootedObject promise(cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream));
|
RootedObject promise(cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream));
|
||||||
if (!promise) {
|
if (!promise) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -3458,9 +3539,9 @@ ReadableByteStreamControllerPullSteps(JSContext* cx,
|
||||||
/**
|
/**
|
||||||
* Unified implementation of ReadableStream controllers' [[PullSteps]] internal
|
* Unified implementation of ReadableStream controllers' [[PullSteps]] internal
|
||||||
* methods.
|
* methods.
|
||||||
* Streams spec, 3.8.5.2. [[PullSteps]] ()
|
* Streams spec, 3.8.5.2. [[PullSteps]] ( forAuthorCode )
|
||||||
* and
|
* and
|
||||||
* Streams spec, 3.10.5.2. [[PullSteps]] ()
|
* Streams spec, 3.10.5.2. [[PullSteps]] ( forAuthorCode )
|
||||||
*/
|
*/
|
||||||
static MOZ_MUST_USE JSObject*
|
static MOZ_MUST_USE JSObject*
|
||||||
ReadableStreamControllerPullSteps(JSContext* cx, Handle<ReadableStreamController*> unwrappedController)
|
ReadableStreamControllerPullSteps(JSContext* cx, Handle<ReadableStreamController*> unwrappedController)
|
||||||
|
@ -4517,6 +4598,8 @@ JS::ReadableStreamReaderCancel(JSContext* cx, HandleObject readerObj, HandleValu
|
||||||
if (!unwrappedReader) {
|
if (!unwrappedReader) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
|
||||||
|
"C++ code should not touch readers created by scripts");
|
||||||
|
|
||||||
return ReadableStreamReaderGenericCancel(cx, unwrappedReader, reason);
|
return ReadableStreamReaderGenericCancel(cx, unwrappedReader, reason);
|
||||||
}
|
}
|
||||||
|
@ -4527,10 +4610,13 @@ JS::ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject readerObj)
|
||||||
AssertHeapIsIdle();
|
AssertHeapIsIdle();
|
||||||
CHECK_THREAD(cx);
|
CHECK_THREAD(cx);
|
||||||
|
|
||||||
Rooted<ReadableStreamReader*> unwrappedReader(cx, APIToUnwrapped<ReadableStreamReader>(cx, readerObj));
|
Rooted<ReadableStreamReader*> unwrappedReader(cx,
|
||||||
|
APIToUnwrapped<ReadableStreamReader>(cx, readerObj));
|
||||||
if (!unwrappedReader) {
|
if (!unwrappedReader) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
|
||||||
|
"C++ code should not touch readers created by scripts");
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
Rooted<ReadableStream*> unwrappedStream(cx, UnwrapStreamFromReader(cx, unwrappedReader));
|
Rooted<ReadableStream*> unwrappedStream(cx, UnwrapStreamFromReader(cx, unwrappedReader));
|
||||||
|
@ -4554,6 +4640,8 @@ JS::ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject readerObj)
|
||||||
if (!unwrappedReader) {
|
if (!unwrappedReader) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
|
||||||
|
"C++ code should not touch readers created by scripts");
|
||||||
|
|
||||||
return ::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
|
return ::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,13 @@ class ReadableStream : public NativeObject
|
||||||
static const Class protoClass_;
|
static const Class protoClass_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether or not read() result objects inherit from Object.prototype.
|
||||||
|
* Generally, they should do so only if the reader was created by author code.
|
||||||
|
* See <https://streams.spec.whatwg.org/#readable-stream-create-read-result>.
|
||||||
|
*/
|
||||||
|
enum class ForAuthorCodeBool { No, Yes };
|
||||||
|
|
||||||
class ReadableStreamReader : public NativeObject
|
class ReadableStreamReader : public NativeObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -143,6 +150,7 @@ class ReadableStreamReader : public NativeObject
|
||||||
Slot_Stream,
|
Slot_Stream,
|
||||||
Slot_Requests,
|
Slot_Requests,
|
||||||
Slot_ClosedPromise,
|
Slot_ClosedPromise,
|
||||||
|
Slot_ForAuthorCode,
|
||||||
SlotCount,
|
SlotCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -151,6 +159,28 @@ class ReadableStreamReader : public NativeObject
|
||||||
void clearStream() { setFixedSlot(Slot_Stream, UndefinedValue()); }
|
void clearStream() { setFixedSlot(Slot_Stream, UndefinedValue()); }
|
||||||
bool isClosed() { return !hasStream(); }
|
bool isClosed() { return !hasStream(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether this reader was created by author code.
|
||||||
|
*
|
||||||
|
* This returns Yes for readers created using `stream.getReader()`, and No
|
||||||
|
* for readers created for the internal use of algorithms like
|
||||||
|
* `stream.tee()` and `new Response(stream)`.
|
||||||
|
*
|
||||||
|
* The standard does not have this field. Instead, eight algorithms take a
|
||||||
|
* forAuthorCode parameter, and a [[forAuthorCode]] field is part of each
|
||||||
|
* read request. But the behavior is always equivalent to treating readers
|
||||||
|
* created by author code as having a bit set on them. We implement it that
|
||||||
|
* way for simplicity.
|
||||||
|
*/
|
||||||
|
ForAuthorCodeBool forAuthorCode() const {
|
||||||
|
return getFixedSlot(Slot_ForAuthorCode).toBoolean()
|
||||||
|
? ForAuthorCodeBool::Yes
|
||||||
|
: ForAuthorCodeBool::No;
|
||||||
|
}
|
||||||
|
void setForAuthorCode(ForAuthorCodeBool value) {
|
||||||
|
setFixedSlot(Slot_ForAuthorCode, BooleanValue(value == ForAuthorCodeBool::Yes));
|
||||||
|
}
|
||||||
|
|
||||||
NativeObject* requests() const {
|
NativeObject* requests() const {
|
||||||
return &getFixedSlot(Slot_Requests).toObject().as<NativeObject>();
|
return &getFixedSlot(Slot_Requests).toObject().as<NativeObject>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1036,10 +1036,35 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
|
||||||
return iterResultTemplate_;
|
return iterResultTemplate_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NativeObject* templateObj = createIterResultTemplateObject(cx, WithObjectPrototype::Yes);
|
||||||
|
iterResultTemplate_.set(templateObj);
|
||||||
|
return iterResultTemplate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeObject*
|
||||||
|
Realm::getOrCreateIterResultWithoutPrototypeTemplateObject(JSContext* cx)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(cx->realm() == this);
|
||||||
|
|
||||||
|
if (iterResultWithoutPrototypeTemplate_) {
|
||||||
|
return iterResultWithoutPrototypeTemplate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeObject* templateObj = createIterResultTemplateObject(cx, WithObjectPrototype::No);
|
||||||
|
iterResultWithoutPrototypeTemplate_.set(templateObj);
|
||||||
|
return iterResultWithoutPrototypeTemplate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeObject*
|
||||||
|
Realm::createIterResultTemplateObject(JSContext* cx, WithObjectPrototype withProto)
|
||||||
|
{
|
||||||
// Create template plain object
|
// Create template plain object
|
||||||
RootedNativeObject templateObject(cx, NewBuiltinClassInstance<PlainObject>(cx, TenuredObject));
|
RootedNativeObject templateObject(cx,
|
||||||
|
withProto == WithObjectPrototype::Yes
|
||||||
|
? NewBuiltinClassInstance<PlainObject>(cx, TenuredObject)
|
||||||
|
: NewObjectWithNullTaggedProto<PlainObject>(cx));
|
||||||
if (!templateObject) {
|
if (!templateObject) {
|
||||||
return iterResultTemplate_; // = nullptr
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new group for the template.
|
// Create a new group for the template.
|
||||||
|
@ -1048,7 +1073,7 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
|
||||||
templateObject->getClass(),
|
templateObject->getClass(),
|
||||||
proto));
|
proto));
|
||||||
if (!group) {
|
if (!group) {
|
||||||
return iterResultTemplate_; // = nullptr
|
return nullptr;
|
||||||
}
|
}
|
||||||
templateObject->setGroup(group);
|
templateObject->setGroup(group);
|
||||||
|
|
||||||
|
@ -1056,14 +1081,14 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
|
||||||
if (!NativeDefineDataProperty(cx, templateObject, cx->names().value, UndefinedHandleValue,
|
if (!NativeDefineDataProperty(cx, templateObject, cx->names().value, UndefinedHandleValue,
|
||||||
JSPROP_ENUMERATE))
|
JSPROP_ENUMERATE))
|
||||||
{
|
{
|
||||||
return iterResultTemplate_; // = nullptr
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set dummy `done` property
|
// Set dummy `done` property
|
||||||
if (!NativeDefineDataProperty(cx, templateObject, cx->names().done, TrueHandleValue,
|
if (!NativeDefineDataProperty(cx, templateObject, cx->names().done, TrueHandleValue,
|
||||||
JSPROP_ENUMERATE))
|
JSPROP_ENUMERATE))
|
||||||
{
|
{
|
||||||
return iterResultTemplate_; // = nullptr
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoSweepObjectGroup sweep(group);
|
AutoSweepObjectGroup sweep(group);
|
||||||
|
@ -1084,9 +1109,7 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
|
||||||
MOZ_ASSERT(shape->slot() == Realm::IterResultObjectDoneSlot &&
|
MOZ_ASSERT(shape->slot() == Realm::IterResultObjectDoneSlot &&
|
||||||
shape->propidRef() == NameToId(cx->names().done));
|
shape->propidRef() == NameToId(cx->names().done));
|
||||||
|
|
||||||
iterResultTemplate_.set(templateObject);
|
return templateObject;
|
||||||
|
|
||||||
return iterResultTemplate_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Iterator objects ****************************************************************************/
|
/*** Iterator objects ****************************************************************************/
|
||||||
|
|
|
@ -530,6 +530,12 @@ Realm::sweepTemplateObjects()
|
||||||
if (iterResultTemplate_ && IsAboutToBeFinalized(&iterResultTemplate_)) {
|
if (iterResultTemplate_ && IsAboutToBeFinalized(&iterResultTemplate_)) {
|
||||||
iterResultTemplate_.set(nullptr);
|
iterResultTemplate_.set(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iterResultWithoutPrototypeTemplate_ &&
|
||||||
|
IsAboutToBeFinalized(&iterResultWithoutPrototypeTemplate_))
|
||||||
|
{
|
||||||
|
iterResultWithoutPrototypeTemplate_.set(nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -365,6 +365,7 @@ class JS::Realm : public JS::shadow::Realm
|
||||||
js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_ { nullptr };
|
js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_ { nullptr };
|
||||||
js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_ { nullptr };
|
js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_ { nullptr };
|
||||||
js::ReadBarriered<js::NativeObject*> iterResultTemplate_ { nullptr };
|
js::ReadBarriered<js::NativeObject*> iterResultTemplate_ { nullptr };
|
||||||
|
js::ReadBarriered<js::NativeObject*> iterResultWithoutPrototypeTemplate_ { nullptr };
|
||||||
|
|
||||||
// There are two ways to enter a realm:
|
// There are two ways to enter a realm:
|
||||||
//
|
//
|
||||||
|
@ -695,7 +696,13 @@ class JS::Realm : public JS::shadow::Realm
|
||||||
static const size_t IterResultObjectValueSlot = 0;
|
static const size_t IterResultObjectValueSlot = 0;
|
||||||
static const size_t IterResultObjectDoneSlot = 1;
|
static const size_t IterResultObjectDoneSlot = 1;
|
||||||
js::NativeObject* getOrCreateIterResultTemplateObject(JSContext* cx);
|
js::NativeObject* getOrCreateIterResultTemplateObject(JSContext* cx);
|
||||||
|
js::NativeObject* getOrCreateIterResultWithoutPrototypeTemplateObject(JSContext* cx);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class WithObjectPrototype { No, Yes };
|
||||||
|
js::NativeObject* createIterResultTemplateObject(JSContext* cx, WithObjectPrototype withProto);
|
||||||
|
|
||||||
|
public:
|
||||||
js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
|
js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
|
||||||
js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
|
js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,9 @@
|
||||||
[response-stream-with-broken-then.any.html]
|
[response-stream-with-broken-then.any.html]
|
||||||
[Attempt to inject undefined via Object.prototype.then.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Attempt to inject {done: false, value: bye} via Object.prototype.then.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
|
[intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Attempt to inject 8.2 via Object.prototype.then.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Attempt to inject value: undefined via Object.prototype.then.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[response-stream-with-broken-then.any.worker.html]
|
[response-stream-with-broken-then.any.worker.html]
|
||||||
[Attempt to inject undefined via Object.prototype.then.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Attempt to inject {done: false, value: bye} via Object.prototype.then.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
|
[intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Attempt to inject 8.2 via Object.prototype.then.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Attempt to inject value: undefined via Object.prototype.then.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче