Bug 1734886 - Part 8: Implement TransformStreamDefaultController methods r=mgaudet

Differential Revision: https://phabricator.services.mozilla.com/D142013
This commit is contained in:
Kagami Sascha Rosylight 2022-03-30 06:31:58 +00:00
Родитель aa604cdf0a
Коммит def205fdcd
12 изменённых файлов: 217 добавлений и 108 удалений

Просмотреть файл

@ -646,6 +646,10 @@ DOMInterfaces = {
'headerFile': 'TransceiverImpl.h'
},
'TransformStreamDefaultController': {
'implicitJSContext': ['terminate'],
},
'Plugin': {
'headerFile' : 'nsPluginArray.h',
'nativeType': 'nsPluginElement',

Просмотреть файл

@ -106,13 +106,11 @@ static bool ReadableStreamDefaultControllerCanCloseOrEnqueue(
state == ReadableStream::ReaderState::Readable;
}
enum class CloseOrEnqueue { Close, Enqueue };
// https://streams.spec.whatwg.org/#readable-stream-default-controller-can-close-or-enqueue
// This is a variant of ReadableStreamDefaultControllerCanCloseOrEnqueue
// that also throws when the function would return false to improve error
// messages.
static bool ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
bool ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
ReadableStreamDefaultController* aController,
CloseOrEnqueue aCloseOrEnqueue, ErrorResult& aRv) {
// Step 1. Let state be controller.[[stream]].[[state]].
@ -120,9 +118,9 @@ static bool ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
nsCString prefix;
if (aCloseOrEnqueue == CloseOrEnqueue::Close) {
prefix = "Cannot close a readable stream that "_ns;
prefix = "Cannot close a stream that "_ns;
} else {
prefix = "Cannot enqueue into a readable stream that "_ns;
prefix = "Cannot enqueue into a stream that "_ns;
}
switch (state) {
@ -348,7 +346,7 @@ void ReadableStreamDefaultController::Error(JSContext* aCx,
}
// https://streams.spec.whatwg.org/#readable-stream-default-controller-should-call-pull
static bool ReadableStreamDefaultControllerShouldCallPull(
bool ReadableStreamDefaultControllerShouldCallPull(
ReadableStreamDefaultController* aController) {
// Step 1.
ReadableStream* stream = aController->GetStream();

Просмотреть файл

@ -159,6 +159,15 @@ void ReadableStreamDefaultControllerClearAlgorithms(
Nullable<double> ReadableStreamDefaultControllerGetDesiredSize(
ReadableStreamDefaultController* aController);
enum class CloseOrEnqueue { Close, Enqueue };
bool ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
ReadableStreamDefaultController* aController,
CloseOrEnqueue aCloseOrEnqueue, ErrorResult& aRv);
bool ReadableStreamDefaultControllerShouldCallPull(
ReadableStreamDefaultController* aController);
} // namespace mozilla::dom
#endif // mozilla_dom_ReadableStreamDefaultController_h

Просмотреть файл

@ -40,6 +40,48 @@ JSObject* TransformStream::WrapObject(JSContext* aCx,
return TransformStream_Binding::Wrap(aCx, this, aGivenProto);
}
// https://streams.spec.whatwg.org/#transform-stream-error-writable-and-unblock-write
void TransformStreamErrorWritableAndUnblockWrite(JSContext* aCx,
TransformStream* aStream,
JS::HandleValue aError,
ErrorResult& aRv) {
// Step 1: Perform !
// TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]).
aStream->Controller()->SetAlgorithms(nullptr);
// Step 2: Perform !
// WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]],
// e).
// TODO: Remove MOZ_KnownLive (bug 1761577)
WritableStreamDefaultControllerErrorIfNeeded(
aCx, MOZ_KnownLive(aStream->Writable()->Controller()), aError, aRv);
if (aRv.Failed()) {
return;
}
// Step 3: If stream.[[backpressure]] is true, perform !
// TransformStreamSetBackpressure(stream, false).
if (aStream->Backpressure()) {
TransformStreamSetBackpressure(aStream, false, aRv);
}
}
// https://streams.spec.whatwg.org/#transform-stream-error
void TransformStreamError(JSContext* aCx, TransformStream* aStream,
JS::HandleValue aError, ErrorResult& aRv) {
// Step 1: Perform !
// ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]],
// e).
ReadableStreamDefaultControllerError(
aCx, aStream->Readable()->Controller()->AsDefault(), aError, aRv);
if (aRv.Failed()) {
return;
}
// Step 2: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, e).
TransformStreamErrorWritableAndUnblockWrite(aCx, aStream, aError, aRv);
}
// https://streams.spec.whatwg.org/#initialize-transform-stream
class TransformStreamUnderlyingSinkAlgorithms final
: public UnderlyingSinkAlgorithmsBase {
@ -178,9 +220,8 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
NS_INTERFACE_MAP_END_INHERITING(TransformStreamUnderlyingSourceAlgorithms)
// https://streams.spec.whatwg.org/#transform-stream-set-backpressure
static void TransformStreamSetBackpressure(TransformStream* aStream,
bool aBackpressure,
ErrorResult& aRv) {
void TransformStreamSetBackpressure(TransformStream* aStream,
bool aBackpressure, ErrorResult& aRv) {
// Step 1. Assert: stream.[[backpressure]] is not backpressure.
MOZ_ASSERT(aStream->Backpressure() != aBackpressure);
@ -238,6 +279,11 @@ void TransformStream::Initialize(JSContext* aCx, Promise* aStartPromise,
// Step 9. Set stream.[[backpressure]] and
// stream.[[backpressureChangePromise]] to undefined.
// Note(krosylight): The spec allows setting [[backpressure]] as undefined,
// but I don't see why it should be. Since the spec also allows strict boolean
// type, and this is only to not trigger assertion inside the setter, we just
// set it as false.
mBackpressure = false;
mBackpressureChangePromise = nullptr;
// Step 10. Perform ! TransformStreamSetBackpressure(stream, true).

Просмотреть файл

@ -36,12 +36,15 @@ class TransformStream final : public nsISupports, public nsWrapperCache {
void SetBackpressureChangePromise(Promise* aPromise) {
mBackpressureChangePromise = aPromise;
}
TransformStreamDefaultController* Controller() { return mController; }
void SetController(TransformStreamDefaultController* aController) {
mController = aController;
MOZ_KNOWN_LIVE TransformStreamDefaultController* Controller() {
return mController;
}
ReadableStream* Readable() { return mReadable; }
WritableStream* Writable() { return mWritable; }
void SetController(TransformStreamDefaultController& aController) {
MOZ_ASSERT(!mController);
mController = &aController;
}
MOZ_KNOWN_LIVE ReadableStream* Readable() { return mReadable; }
MOZ_KNOWN_LIVE WritableStream* Writable() { return mWritable; }
protected:
~TransformStream();
@ -73,13 +76,26 @@ class TransformStream final : public nsISupports, public nsWrapperCache {
nsCOMPtr<nsIGlobalObject> mGlobal;
// Internal slots
// MOZ_KNOWN_LIVE for slots that will never be reassigned
bool mBackpressure = false;
RefPtr<Promise> mBackpressureChangePromise;
RefPtr<TransformStreamDefaultController> mController;
RefPtr<ReadableStream> mReadable;
RefPtr<WritableStream> mWritable;
MOZ_KNOWN_LIVE RefPtr<TransformStreamDefaultController> mController;
MOZ_KNOWN_LIVE RefPtr<ReadableStream> mReadable;
MOZ_KNOWN_LIVE RefPtr<WritableStream> mWritable;
};
MOZ_CAN_RUN_SCRIPT void TransformStreamErrorWritableAndUnblockWrite(
JSContext* aCx, TransformStream* aStream, JS::HandleValue aError,
ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void TransformStreamError(JSContext* aCx,
TransformStream* aStream,
JS::HandleValue aError,
ErrorResult& aRv);
void TransformStreamSetBackpressure(TransformStream* aStream,
bool aBackpressure, ErrorResult& aRv);
} // namespace mozilla::dom
#endif // DOM_STREAMS_TRANSFORMSTREAM_H_

Просмотреть файл

@ -26,8 +26,9 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStreamDefaultController)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
void TransformStreamDefaultController::SetStream(TransformStream* aStream) {
mStream = aStream;
void TransformStreamDefaultController::SetStream(TransformStream& aStream) {
MOZ_ASSERT(!mStream);
mStream = &aStream;
}
void TransformStreamDefaultController::SetAlgorithms(
@ -62,20 +63,123 @@ Nullable<double> TransformStreamDefaultController::GetDesiredSize() const {
return ReadableStreamDefaultControllerGetDesiredSize(readableController);
}
// https://streams.spec.whatwg.org/#rs-default-controller-has-backpressure
// Looks like a readable stream thing but the spec explicitly says this is for
// TransformStream.
static bool ReadableStreamDefaultControllerHasBackpressure(
ReadableStreamDefaultController* aController) {
// Step 1: If ! ReadableStreamDefaultControllerShouldCallPull(controller) is
// true, return false.
// Step 2: Otherwise, return true.
return !ReadableStreamDefaultControllerShouldCallPull(aController);
}
void TransformStreamDefaultController::Enqueue(JSContext* aCx,
JS::Handle<JS::Value> aChunk,
ErrorResult& aRv) {
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
// Step 1: Perform ? TransformStreamDefaultControllerEnqueue(this, chunk).
// Inlining TransformStreamDefaultControllerEnqueue here.
// https://streams.spec.whatwg.org/#transform-stream-default-controller-enqueue
// Step 1: Let stream be controller.[[stream]].
RefPtr<TransformStream> stream = mStream;
// Step 2: Let readableController be stream.[[readable]].[[controller]].
RefPtr<ReadableStreamDefaultController> readableController =
stream->Readable()->Controller()->AsDefault();
// Step 3: If !
// ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController) is
// false, throw a TypeError exception.
if (!ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow(
readableController, CloseOrEnqueue::Enqueue, aRv)) {
return;
}
// Step 4: Let enqueueResult be
// ReadableStreamDefaultControllerEnqueue(readableController, chunk).
ErrorResult rv;
ReadableStreamDefaultControllerEnqueue(aCx, readableController, aChunk, rv);
// Step 5: If enqueueResult is an abrupt completion,
if (rv.MaybeSetPendingException(aCx)) {
JS::Rooted<JS::Value> error(aCx);
if (!JS_GetPendingException(aCx, &error)) {
// Uncatchable exception; we should mark aRv and return.
aRv.StealExceptionFromJSContext(aCx);
return;
}
JS_ClearPendingException(aCx);
// Step 5.1: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream,
// enqueueResult.[[Value]]).
TransformStreamErrorWritableAndUnblockWrite(aCx, stream, error, aRv);
// Step 5.2: Throw stream.[[readable]].[[storedError]].
JS::RootedValue storedError(aCx, stream->Readable()->StoredError());
aRv.MightThrowJSException();
aRv.ThrowJSException(aCx, storedError);
return;
}
// Step 6: Let backpressure be !
// ReadableStreamDefaultControllerHasBackpressure(readableController).
bool backpressure =
ReadableStreamDefaultControllerHasBackpressure(readableController);
// Step 7: If backpressure is not stream.[[backpressure]],
if (backpressure != stream->Backpressure()) {
// Step 7.1: Assert: backpressure is true.
MOZ_ASSERT(backpressure);
// Step 7.2: Perform ! TransformStreamSetBackpressure(stream, true).
TransformStreamSetBackpressure(stream, true, aRv);
}
}
// https://streams.spec.whatwg.org/#ts-default-controller-error
void TransformStreamDefaultController::Error(JSContext* aCx,
JS::Handle<JS::Value> aError,
ErrorResult& aRv) {
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
// Step 1: Perform ? TransformStreamDefaultControllerError(this, e).
// Inlining TransformStreamDefaultControllerError here.
// https://streams.spec.whatwg.org/#transform-stream-default-controller-error
// Perform ! TransformStreamError(controller.[[stream]], e).
TransformStreamError(aCx, mStream, aError, aRv);
}
void TransformStreamDefaultController::Terminate(ErrorResult& aRv) {
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
// https://streams.spec.whatwg.org/#ts-default-controller-terminate
void TransformStreamDefaultController::Terminate(JSContext* aCx,
ErrorResult& aRv) {
// Step 1: Perform ? TransformStreamDefaultControllerTerminate(this).
// Inlining TransformStreamDefaultControllerTerminate here.
// https://streams.spec.whatwg.org/#transform-stream-default-controller-terminate
// Step 1: Let stream be controller.[[stream]].
RefPtr<TransformStream> stream = mStream;
// Step 2: Let readableController be stream.[[readable]].[[controller]].
RefPtr<ReadableStreamDefaultController> readableController =
stream->Readable()->Controller()->AsDefault();
// Step 3: Perform ! ReadableStreamDefaultControllerClose(readableController).
ReadableStreamDefaultControllerClose(aCx, readableController, aRv);
// Step 4: Let error be a TypeError exception indicating that the stream has
// been terminated.
ErrorResult rv;
rv.ThrowTypeError("Terminating the stream");
JS::Rooted<JS::Value> error(aCx);
MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), &error));
// Step 5: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream,
// error).
TransformStreamErrorWritableAndUnblockWrite(aCx, stream, error, aRv);
}
// https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller
@ -88,10 +192,10 @@ void SetUpTransformStreamDefaultController(
MOZ_ASSERT(!aStream.Controller());
// Step 3. Set controller.[[stream]] to stream.
aController.SetStream(&aStream);
aController.SetStream(aStream);
// Step 4. Set stream.[[controller]] to controller.
aStream.SetController(&aController);
aStream.SetController(aController);
// Step 5. Set controller.[[transformAlgorithm]] to transformAlgorithm.
// Step 6. Set controller.[[flushAlgorithm]] to flushAlgorithm.

Просмотреть файл

@ -31,7 +31,7 @@ class TransformStreamDefaultController final : public nsISupports,
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TransformStreamDefaultController)
void SetStream(TransformStream* aStream);
void SetStream(TransformStream& aStream);
void SetAlgorithms(TransformerAlgorithms* aTransformerAlgorithms);
explicit TransformStreamDefaultController(nsIGlobalObject* aGlobal);
@ -46,15 +46,17 @@ class TransformStreamDefaultController final : public nsISupports,
Nullable<double> GetDesiredSize() const;
void Enqueue(JSContext* aCx, JS::Handle<JS::Value> aChunk, ErrorResult& aRv);
void Error(JSContext* aCx, JS::Handle<JS::Value> aError, ErrorResult& aRv);
void Terminate(ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void Enqueue(JSContext* aCx, JS::Handle<JS::Value> aChunk,
ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void Error(JSContext* aCx, JS::Handle<JS::Value> aError,
ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void Terminate(JSContext* aCx, ErrorResult& aRv);
private:
nsCOMPtr<nsIGlobalObject> mGlobal;
// Internal slots
RefPtr<TransformStream> mStream;
MOZ_KNOWN_LIVE RefPtr<TransformStream> mStream;
RefPtr<TransformerAlgorithms> mTransformerAlgorithms;
};

Просмотреть файл

@ -52,10 +52,12 @@ class WritableStream : public nsISupports, public nsWrapperCache {
Promise* GetCloseRequest() { return mCloseRequest; }
void SetCloseRequest(Promise* aRequest) { mCloseRequest = aRequest; }
WritableStreamDefaultController* Controller() { return mController; }
void SetController(WritableStreamDefaultController* aController) {
MOZ_ASSERT(aController);
mController = aController;
MOZ_KNOWN_LIVE WritableStreamDefaultController* Controller() {
return mController;
}
void SetController(WritableStreamDefaultController& aController) {
MOZ_ASSERT(!mController);
mController = &aController;
}
Promise* GetInFlightWriteRequest() const { return mInFlightWriteRequest; }
@ -162,7 +164,7 @@ class WritableStream : public nsISupports, public nsWrapperCache {
private:
bool mBackpressure = false;
RefPtr<Promise> mCloseRequest;
RefPtr<WritableStreamDefaultController> mController;
MOZ_KNOWN_LIVE RefPtr<WritableStreamDefaultController> mController;
RefPtr<Promise> mInFlightWriteRequest;
RefPtr<Promise> mInFlightCloseRequest;

Просмотреть файл

@ -138,7 +138,7 @@ void SetUpWritableStreamDefaultController(
MOZ_ASSERT(aController->Stream() == aStream);
// Step 4. Set stream.[[controller]] to controller.
aStream->SetController(aController);
aStream->SetController(*aController);
// Step 5. Perform ! ResetQueue(controller).
ResetQueue(aController);

Просмотреть файл

@ -11,12 +11,6 @@
[when controller.error is followed by a rejection, the error reason should come from controller.error]
expected: FAIL
[when strategy.size throws inside start(), the constructor should throw the same error]
expected: FAIL
[when strategy.size calls controller.error() then throws, the constructor should throw the first error]
expected: FAIL
[cancelling the readable side should error the writable]
expected: FAIL
@ -53,9 +47,6 @@
[the readable should be errored with the reason passed to the writable abort() method]
expected: FAIL
[errored TransformStream should not enqueue new chunks]
expected: FAIL
[errors.any.serviceworker.html]
[TransformStream errors thrown in transform put the writable and readable in an errored state]
@ -70,12 +61,6 @@
[when controller.error is followed by a rejection, the error reason should come from controller.error]
expected: FAIL
[when strategy.size throws inside start(), the constructor should throw the same error]
expected: FAIL
[when strategy.size calls controller.error() then throws, the constructor should throw the first error]
expected: FAIL
[cancelling the readable side should error the writable]
expected: FAIL
@ -112,9 +97,6 @@
[the readable should be errored with the reason passed to the writable abort() method]
expected: FAIL
[errored TransformStream should not enqueue new chunks]
expected: FAIL
[errors.any.worker.html]
[TransformStream errors thrown in transform put the writable and readable in an errored state]
@ -129,12 +111,6 @@
[when controller.error is followed by a rejection, the error reason should come from controller.error]
expected: FAIL
[when strategy.size throws inside start(), the constructor should throw the same error]
expected: FAIL
[when strategy.size calls controller.error() then throws, the constructor should throw the first error]
expected: FAIL
[cancelling the readable side should error the writable]
expected: FAIL
@ -171,9 +147,6 @@
[the readable should be errored with the reason passed to the writable abort() method]
expected: FAIL
[errored TransformStream should not enqueue new chunks]
expected: FAIL
[errors.any.html]
[TransformStream errors thrown in transform put the writable and readable in an errored state]
@ -188,12 +161,6 @@
[when controller.error is followed by a rejection, the error reason should come from controller.error]
expected: FAIL
[when strategy.size throws inside start(), the constructor should throw the same error]
expected: FAIL
[when strategy.size calls controller.error() then throws, the constructor should throw the first error]
expected: FAIL
[cancelling the readable side should error the writable]
expected: FAIL
@ -229,6 +196,3 @@
[the readable should be errored with the reason passed to the writable abort() method]
expected: FAIL
[errored TransformStream should not enqueue new chunks]
expected: FAIL

Просмотреть файл

@ -53,12 +53,6 @@
[Subclassing TransformStream should work]
expected: FAIL
[enqueue() should throw after controller.terminate()]
expected: FAIL
[controller.terminate() should do nothing the second time it is called]
expected: FAIL
[general.any.html]
[TransformStream writable starts in the writable state]
@ -115,12 +109,6 @@
[Subclassing TransformStream should work]
expected: FAIL
[enqueue() should throw after controller.terminate()]
expected: FAIL
[controller.terminate() should do nothing the second time it is called]
expected: FAIL
[general.any.worker.html]
[TransformStream writable starts in the writable state]
@ -177,12 +165,6 @@
[Subclassing TransformStream should work]
expected: FAIL
[enqueue() should throw after controller.terminate()]
expected: FAIL
[controller.terminate() should do nothing the second time it is called]
expected: FAIL
[general.any.serviceworker.html]
[TransformStream writable starts in the writable state]
@ -238,9 +220,3 @@
[Subclassing TransformStream should work]
expected: FAIL
[enqueue() should throw after controller.terminate()]
expected: FAIL
[controller.terminate() should do nothing the second time it is called]
expected: FAIL

Просмотреть файл

@ -14,9 +14,6 @@
[controller.terminate() inside flush() should not prevent writer.close() from succeeding]
expected: FAIL
[controller.enqueue() should throw after controller.terminate()]
expected: FAIL
[terminate.any.worker.html]
[controller.terminate() should error pipeTo()]
@ -34,9 +31,6 @@
[controller.terminate() inside flush() should not prevent writer.close() from succeeding]
expected: FAIL
[controller.enqueue() should throw after controller.terminate()]
expected: FAIL
[terminate.any.serviceworker.html]
[controller.terminate() should error pipeTo()]
@ -54,9 +48,6 @@
[controller.terminate() inside flush() should not prevent writer.close() from succeeding]
expected: FAIL
[controller.enqueue() should throw after controller.terminate()]
expected: FAIL
[terminate.any.html]
[controller.terminate() should error pipeTo()]
@ -73,6 +64,3 @@
[controller.terminate() inside flush() should not prevent writer.close() from succeeding]
expected: FAIL
[controller.enqueue() should throw after controller.terminate()]
expected: FAIL