diff --git a/index.bs b/index.bs
index 240002d2c..c31e4786d 100644
--- a/index.bs
+++ b/index.bs
@@ -2709,7 +2709,7 @@ WritableStream(underlyingSink = {}, { size, highWater
potential types in the future, without backward-compatibility concerns.
1. Set *this*.[[writableStreamController]] to ? Construct(`WritableStreamDefaultController`, « *this*,
_underlyingSink_, _size_, _highWaterMark_ »).
- 1. Perform ? WritableStreamDefaultControllerStart(*this*.[[writableStreamController]]).
+ 1. Perform ? *this*.[[writableStreamController]].[[StartSteps]]().
Properties of the {{WritableStream}} Prototype
@@ -2800,27 +2800,37 @@ writable stream is locked to a writer.
1. If _stream_.[[pendingAbortRequest]] is not *undefined*, return a promise rejected with _error_.
1. Let _controller_ be _stream_.[[writableStreamController]].
1. Assert: _controller_ is not *undefined*.
- 1. Let _writer_ be _stream_.[[writer]].
- 1. If _writer_ is not *undefined*,
- 1. If ! WritableStreamCloseQueuedOrInFlight(_stream_) is *false* and _stream_.[[backpressure]] is *true*,
- reject _writer_.[[readyPromise]] with _error_.
- 1. Otherwise, set _writer_.[[readyPromise]] to a promise rejected with _error_.
- 1. Set _writer_.[[readyPromise]].[[PromiseIsHandled]] to *true*.
1. If ! WritableStreamHasOperationMarkedInFlight(_stream_) is *false* and _controller_.[[started]] is *true*,
1. Perform ! WritableStreamFinishAbort(_stream_).
- 1. Return ! WritableStreamDefaultControllerAbort(_controller_, _reason_).
+ 1. Return ! _controller_.[[AbortSteps]](_reason_).
+ 1. Let _writer_ be _stream_.[[writer]].
+ 1. If _writer_ is not *undefined*,
+ 1. Perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(_writer_, _error_).
1. Let _promise_ be a new promise.
1. Set _stream_.[[pendingAbortRequest]] to Record {[[promise]]: _promise_, [[reason]]: _reason_}.
1. Return _promise_.
+WritableStreamError ( stream,
+error )
+
+
+ 1. Set _stream_.[[state]] to `"errored"`.
+ 1. Set _stream_.[[storedError]] to _error_.
+ 1. Perform ! _stream_.[[writableStreamController]].[[ErrorSteps]]().
+ 1. If _stream_.[[pendingAbortRequest]] is *undefined*,
+ 1. Let _writer_ be _stream_.[[writer]].
+ 1. If _writer_ is not *undefined*,
+ 1. Perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(_writer_, _error_).
+ 1. If ! WritableStreamHasOperationMarkedInFlight(_stream_) is *false*, perform !
+ WritableStreamRejectPromisesInReactionToError(_stream_).
+
+
WritableStreamFinishAbort (
stream )
1. Let _error_ be a new *TypeError* indicating that the stream has been aborted.
- 1. Set _stream_.[[state]] to `"errored"`.
- 1. Set _stream_.[[storedError]] to _error_.
1. Perform ! WritableStreamRejectPromisesInReactionToError(_stream_).
@@ -2871,30 +2881,19 @@ nothrow>WritableStreamFinishInFlightWriteInErroredState ( stream )
WritableStreamFinishInFlightWriteWithError ( stream, reason )
+nothrow>WritableStreamFinishInFlightWriteWithError ( stream, error )
1. Assert: _stream_.[[inFlightWriteRequest]] is not *undefined*.
- 1. Reject _stream_.[[inFlightWriteRequest]] with _reason_.
+ 1. Reject _stream_.[[inFlightWriteRequest]] with _error_.
1. Set _stream_.[[inFlightWriteRequest]] to *undefined*.
1. Let _state_ be _stream_.[[state]].
1. If _state_ is `"errored"`,
1. Perform ! WritableStreamFinishInFlightWriteInErroredState(_stream_).
1. Return.
1. Assert: _state_ is `"writable"`.
- 1. Set _stream_.[[state]] to `"errored"`.
- 1. Set _stream_.[[storedError]] to _reason_.
- 1. If _stream_.[[pendingAbortRequest]] is *undefined*,
- 1. Let _writer_ be _stream_.[[writer]].
- 1. If _writer_ is not *undefined*,
- 1. If ! WritableStreamCloseQueuedOrInFlight(_stream_) is *false* and _stream_.[[backpressure]] is *true*,
- reject _writer_.[[readyPromise]] with _reason_.
- 1. Otherwise, set _writer_.[[readyPromise]] to a promise rejected with _reason_.
- 1. Set _writer_.[[readyPromise]].[[PromiseIsHandled]] to *true*.
- 1. Otherwise,
- 1. Reject _stream_.[[pendingAbortRequest]] with _reason_.
- 1. Set _stream_.[[pendingAbortRequest]] to *undefined*.
- 1. Perform ! WritableStreamRejectPromisesInReactionToError(_stream_).
+ 1. Perform ! WritableStreamError(_stream_, _error_).
+ 1. Perform ! WritableStreamRejectAbortRequestIfPending(_stream_).
WritableStreamFinishInFlightClose ( stream )
1. Perform ! WritableStreamFinishInFlightCloseInErroredState(_stream_).
1. Return.
1. Assert: _state_ is `"writable"`.
- 1. If _stream_.[[pendingAbortRequest]] is *undefined*,
- 1. Set _stream_.[[state]] to `"closed"`.
- 1. Let _writer_ be _stream_.[[writer]].
- 1. If _writer_ is not *undefined*, resolve _writer_.[[closedPromise]] with *undefined*.
- 1. Return.
- 1. Resolve _stream_.[[pendingAbortRequest]] with *undefined*.
- 1. Set _stream_.[[pendingAbortRequest]] to *undefined*.
- 1. Let _error_ be a new *TypeError* indicating that the stream was requested to abort but has been closed.
- 1. Set _stream_.[[state]] to `"errored"`.
- 1. Set _stream_.[[storedError]] to _error_.
- 1. Perform ! WritableStreamRejectClosedPromiseInReactionToError(_stream_).
+ 1. Set _stream_.[[state]] to `"closed"`.
+ 1. Let _writer_ be _stream_.[[writer]].
+ 1. If _writer_ is not *undefined*, resolve _writer_.[[closedPromise]] with *undefined*.
+ 1. If _stream_.[[pendingAbortRequest]] is not *undefined*,
+ 1. Resolve _stream_.[[pendingAbortRequest]] with *undefined*.
+ 1. Set _stream_.[[pendingAbortRequest]] to *undefined*.
WritableStreamFinishInFlightCloseInErroredState ( stream )
WritableStreamFinishInFlightCloseWithError ( stream, reason )
+nothrow>WritableStreamFinishInFlightCloseWithError ( stream, error )
1. Assert: _stream_.[[inFlightCloseRequest]] is not *undefined*.
- 1. Reject _stream_.[[inFlightCloseRequest]] with _reason_.
+ 1. Reject _stream_.[[inFlightCloseRequest]] with _error_.
1. Set _stream_.[[inFlightCloseRequest]] to *undefined*.
1. Let _state_ be _stream_.[[state]].
1. If _state_ is `"errored"`,
1. Perform ! WritableStreamFinishInFlightCloseInErroredState(_stream_).
1. Return.
1. Assert: _state_ is `"writable"`.
- 1. Set _stream_.[[state]] to `"errored"`.
- 1. Set _stream_.[[storedError]] to _reason_.
- 1. If _stream_.[[pendingAbortRequest]] is *undefined*,
- 1. Let _writer_ be _stream_.[[writer]].
- 1. If _writer_ is not *undefined*,
- 1. Set _writer_.[[readyPromise]] to a promise rejected with _reason_.
- 1. Set _writer_.[[readyPromise]].[[PromiseIsHandled]] to *true*.
- 1. Otherwise,
- 1. Reject _stream_.[[pendingAbortRequest]] with _reason_.
- 1. Set _stream_.[[pendingAbortRequest]] to *undefined*.
- 1. Perform ! WritableStreamRejectClosedPromiseInReactionToError(_stream_).
+ 1. Perform ! WritableStreamError(_stream_, _error_).
+ 1. Perform ! WritableStreamRejectAbortRequestIfPending(_stream_).
@@ -2971,8 +2956,7 @@ nothrow>WritableStreamHandleAbortRequestIfPending ( stream )
1. Perform ! WritableStreamFinishAbort(_stream_).
1. Let _abortRequest_ be _stream_.[[pendingAbortRequest]].
1. Set _stream_.[[pendingAbortRequest]] to *undefined*.
- 1. Let _promise_ be ! WritableStreamDefaultControllerAbort(_stream_.[[writableStreamController]],
- _abortRequest_.[[reason]]).
+ 1. Let _promise_ be ! _stream_.[[writableStreamController]].[[AbortSteps]](_abortRequest_.[[reason]]).
1. Upon fulfillment of _promise_ with value _result_, resolve _abortRequest_.[[promise]] with _result_.
1. Upon rejection of _promise_ with reason _reason_, reject _abortRequest_.[[promise]] with _reason_.
@@ -3325,6 +3309,16 @@ nothrow>WritableStreamDefaultWriterCloseWithErrorPropagation ( writer
1. Return ! WritableStreamDefaultWriterClose(_writer_).
+WritableStreamDefaultWriterEnsureReadyPromiseRejected( writer, error )
+
+
+ 1. If _writer_.[[readyPromise]].[[PromiseState]] is `"pending"`, reject _writer.[[readyPromise]] with _error_.
+ 1. Otherwise, set _writer_.[[readyPromise]] to a promise rejected with _error_.
+ 1. Set _writer_.[[readyPromise]].[[PromiseIsHandled]] to *true*.
+
+
WritableStreamDefaultWriterGetDesiredSize ( writer )
@@ -3345,11 +3339,7 @@ nothrow>WritableStreamDefaultWriterRelease ( writer )
1. Assert: _stream_.[[writer]] is _writer_.
1. Let _releasedError_ be a new *TypeError*.
1. Let _state_ be _stream_.[[state]].
- 1. If _state_ is `"writable"` and ! WritableStreamCloseQueuedOrInFlight(_stream_) is *false* and
- _stream_.[[pendingAbortRequest]] is *undefined* and _stream_.[[backpressure]] is *true*, reject
- _writer_.[[readyPromise]] with _releasedError_.
- 1. Otherwise, set _writer_.[[readyPromise]] to a promise rejected with _releasedError_.
- 1. Set _writer_.[[readyPromise]].[[PromiseIsHandled]] to *true*.
+ 1. Perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(_writer_, _releasedError_).
1. If _state_ is `"writable"` or ! WritableStreamHasOperationMarkedInFlight(_stream_) is *true*,
reject _writer_.[[closedPromise]] with _releasedError_.
1. Otherwise, set _writer_.[[closedPromise]] to a promise rejected with _releasedError_.
@@ -3488,6 +3478,48 @@ WritableStreamDefaultController(stream, underlyingSink,
+Writable Stream Default Controller Internal Methods
+
+The following are additional internal methods implemented by each {{WritableStreamDefaultController}} instance. The
+writable stream implementation will call into these.
+
+The reason these are in method form, instead of as abstract operations, is to make it clear that the
+writable stream implementation is decoupled from the controller implementation, and could in the future be expanded with
+other controllers, as long as those controllers implemented such internal methods. A similar scenario is seen for
+readable streams, where there actually are multiple controller types and as such the counterpart internal methods are
+used polymorphically.
+
+
\[[AbortSteps]]()
+
+
+ 1. Let _sinkAbortPromise_ be ! PromiseInvokeOrNoop(*this*.[[underlyingSink]], `"abort"`, « _reason_ »).
+ 1. Return the result of transforming _sinkAbortPromise_ with a fulfillment handler that returns
+ *undefined*.
+
+
+\[[ErrorSteps]]()
+
+
+ 1. Perform ! ResetQueue(*this*).
+
+
+\[[StartSteps]]()
+
+
+ 1. Let _startResult_ be ? InvokeOrNoop(*this*.[[underlyingSink]], `"start"`, « *this* »).
+ 1. Let _stream_ be *this*.[[controlledWritableStream]].
+ 1. Let _startPromise_ be a promise resolved with _startResult_:
+ 1. Upon fulfillment of _startPromise_,
+ 1. Set *this*.[[started]] to *true*.
+ 1. If _stream_.[[state]] is `"errored"`, perform ! WritableStreamRejectAbortRequestIfPending(_stream_).
+ 1. Otherwise, perform ! WritableStreamHandleAbortRequestIfPending(_stream_).
+ 1. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(*this*).
+ 1. Upon rejection of _startPromise_ with reason _r_,
+ 1. Assert: _stream_.[[state]] is `"writable"` or `"errored"`.
+ 1. Perform ! WritableStreamDefaultControllerErrorIfNeeded(*this*, _r_).
+ 1. Perform ! WritableStreamRejectAbortRequestIfPending(_stream_).
+
+
Writable Stream Default Controller Abstract Operations
IsWritableStreamDefaultController ( x )
1. Return *true*.
-WritableStreamDefaultControllerAbort ( controller, reason )
-
-
- 1. Perform ! ResetQueue(_controller_).
- 1. Let _sinkAbortPromise_ be ! PromiseInvokeOrNoop(_controller_.[[underlyingSink]], `"abort"`, «
- _reason_ »).
- 1. Return the result of transforming _sinkAbortPromise_ with a fulfillment handler that returns
- *undefined*.
-
-
WritableStreamDefaultControllerClose ( controller )
@@ -3554,24 +3575,6 @@ nothrow>WritableStreamDefaultControllerWrite ( controller, chunk
1. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(_controller_).
-
-WritableStreamDefaultControllerStart ( controller )
-
-
- 1. Let _startResult_ be ? InvokeOrNoop(_controller_.[[underlyingSink]], `"start"`, « _controller_ »).
- 1. Let _stream_ be _controller_.[[controlledWritableStream]].
- 1. Let _startPromise_ be a promise resolved with _startResult_:
- 1. Upon fulfillment of _startPromise_,
- 1. Set _controller_.[[started]] to *true*.
- 1. If _stream_.[[state]] is `"errored"`, perform ! WritableStreamRejectAbortRequestIfPending(_stream_).
- 1. Otherwise, perform ! WritableStreamHandleAbortRequestIfPending(_stream_).
- 1. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(_controller_).
- 1. Upon rejection of _startPromise_ with reason _r_,
- 1. Assert: _stream_.[[state]] is `"writable"` or `"errored"`.
- 1. Perform ! WritableStreamDefaultControllerErrorIfNeeded(_controller_, _r_).
- 1. Perform ! WritableStreamRejectAbortRequestIfPending(_stream_).
-
-
WritableStreamDefaultControllerAdvanceQueueIfNeeded (
controller )
@@ -3589,11 +3592,11 @@ aoid="WritableStreamDefaultControllerAdvanceQueueIfNeeded" nothrow>WritableStrea
WritableStreamDefaultControllerErrorIfNeeded ( controller, e )
+nothrow>WritableStreamDefaultControllerErrorIfNeeded ( controller, error )
1. If _controller_.[[controlledWritableStream]].[[state]] is `"writable"`, perform !
- WritableStreamDefaultControllerError(_controller_, _e_).
+ WritableStreamDefaultControllerError(_controller_, _error_).
WritableStreamDefaultControllerGetBackpressure ( controller )
WritableStreamDefaultControllerError ( controller, e )
+nothrow>WritableStreamDefaultControllerError ( controller, error )
1. Let _stream_ be _controller_.[[controlledWritableStream]].
1. Assert: _stream_.[[state]] is `"writable"`.
- 1. Set _stream_.[[state]] to `"errored"`.
- 1. Set _stream_.[[storedError]] to _e_.
- 1. Let _writer_ be _stream_.[[writer]].
- 1. If _stream_.[[pendingAbortRequest]] is *undefined* and _writer_ is not *undefined*,
- 1. If ! WritableStreamCloseQueuedOrInFlight(_stream_) is *false* and _stream_.[[backpressure]] is *true*,
- reject _writer_.[[readyPromise]] with _e_.
- 1. Otherwise, set _writer_.[[readypromise]] to a promise rejected with _e_.
- 1. Set _writer_.[[readyPromise]].[[PromiseIsHandled]] to *true*.
- 1. Perform ! ResetQueue(_controller_).
- 1. If ! WritableStreamHasOperationMarkedInFlight(_stream_) is *false*,
- perform ! WritableStreamRejectPromisesInReactionToError(_stream_).
+ 1. Perform ! WritableStreamError(_stream_, _error_).
Transform Streams
diff --git a/reference-implementation/lib/writable-stream.js b/reference-implementation/lib/writable-stream.js
index b0e988ebf..fb507212c 100644
--- a/reference-implementation/lib/writable-stream.js
+++ b/reference-implementation/lib/writable-stream.js
@@ -5,6 +5,10 @@ const { InvokeOrNoop, PromiseInvokeOrNoop, ValidateAndNormalizeQueuingStrategy,
const { rethrowAssertionErrorRejection } = require('./utils.js');
const { DequeueValue, EnqueueValueWithSize, PeekQueueValue, ResetQueue } = require('./queue-with-sizes.js');
+const StartSteps = Symbol('[[StartSteps]]');
+const AbortSteps = Symbol('[[AbortSteps]]');
+const ErrorSteps = Symbol('[[ErrorSteps]]');
+
class WritableStream {
constructor(underlyingSink = {}, { size, highWaterMark = 1 } = {}) {
this._state = 'writable';
@@ -45,7 +49,7 @@ class WritableStream {
}
this._writableStreamController = new WritableStreamDefaultController(this, underlyingSink, size, highWaterMark);
- WritableStreamDefaultControllerStart(this._writableStreamController);
+ this._writableStreamController[StartSteps]();
}
get locked() {
@@ -137,19 +141,14 @@ function WritableStreamAbort(stream, reason) {
const controller = stream._writableStreamController;
assert(controller !== undefined, 'controller must not be undefined');
- const writer = stream._writer;
- if (writer !== undefined) {
- if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._backpressure === true) {
- defaultWriterReadyPromiseReject(writer, error);
- } else {
- defaultWriterReadyPromiseResetToRejected(writer, error);
- }
- writer._readyPromise.catch(() => {});
- }
-
if (WritableStreamHasOperationMarkedInFlight(stream) === false && controller._started === true) {
WritableStreamFinishAbort(stream);
- return WritableStreamDefaultControllerAbort(controller, reason);
+ return controller[AbortSteps](reason);
+ }
+
+ const writer = stream._writer;
+ if (writer !== undefined) {
+ WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error);
}
const promise = new Promise((resolve, reject) => {
@@ -163,13 +162,28 @@ function WritableStreamAbort(stream, reason) {
return promise;
}
-function WritableStreamFinishAbort(stream) {
- const error = new TypeError('Aborted');
-
+function WritableStreamError(stream, error) {
stream._state = 'errored';
stream._storedError = error;
- WritableStreamRejectPromisesInReactionToError(stream);
+ stream._writableStreamController[ErrorSteps]();
+
+ if (stream._pendingAbortRequest === undefined) {
+ const writer = stream._writer;
+ if (writer !== undefined) {
+ WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error);
+ }
+ }
+
+ if (WritableStreamHasOperationMarkedInFlight(stream) === false) {
+ WritableStreamRejectPromisesInReactionToError(stream);
+ }
+}
+
+function WritableStreamFinishAbort(stream) {
+ const error = new TypeError('Aborted');
+
+ WritableStreamError(stream, error);
}
// WritableStream API exposed for controllers.
@@ -212,9 +226,9 @@ function WritableStreamFinishInFlightWriteInErroredState(stream) {
WritableStreamRejectPromisesInReactionToError(stream);
}
-function WritableStreamFinishInFlightWriteWithError(stream, reason) {
+function WritableStreamFinishInFlightWriteWithError(stream, error) {
assert(stream._inFlightWriteRequest !== undefined);
- stream._inFlightWriteRequest._reject(reason);
+ stream._inFlightWriteRequest._reject(error);
stream._inFlightWriteRequest = undefined;
const state = stream._state;
@@ -226,25 +240,8 @@ function WritableStreamFinishInFlightWriteWithError(stream, reason) {
assert(state === 'writable');
- stream._state = 'errored';
- stream._storedError = reason;
-
- if (stream._pendingAbortRequest === undefined) {
- const writer = stream._writer;
- if (writer !== undefined) {
- if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._backpressure === true) {
- defaultWriterReadyPromiseReject(writer, reason);
- } else {
- defaultWriterReadyPromiseResetToRejected(writer, reason);
- }
- writer._readyPromise.catch(() => {});
- }
- } else {
- stream._pendingAbortRequest._reject(reason);
- stream._pendingAbortRequest = undefined;
- }
-
- WritableStreamRejectPromisesInReactionToError(stream);
+ WritableStreamError(stream, error);
+ WritableStreamRejectAbortRequestIfPending(stream);
}
function WritableStreamFinishInFlightClose(stream) {
@@ -261,26 +258,17 @@ function WritableStreamFinishInFlightClose(stream) {
assert(state === 'writable');
- if (stream._pendingAbortRequest === undefined) {
- stream._state = 'closed';
+ stream._state = 'closed';
- const writer = stream._writer;
- if (writer !== undefined) {
- defaultWriterClosedPromiseResolve(writer);
- }
-
- return;
+ const writer = stream._writer;
+ if (writer !== undefined) {
+ defaultWriterClosedPromiseResolve(writer);
}
- stream._pendingAbortRequest._resolve();
- stream._pendingAbortRequest = undefined;
-
- const error = new TypeError('Requested to abort but has been closed');
-
- stream._state = 'errored';
- stream._storedError = error;
-
- WritableStreamRejectClosedPromiseInReactionToError(stream);
+ if (stream._pendingAbortRequest !== undefined) {
+ stream._pendingAbortRequest._resolve();
+ stream._pendingAbortRequest = undefined;
+ }
}
function WritableStreamFinishInFlightCloseInErroredState(stream) {
@@ -288,9 +276,9 @@ function WritableStreamFinishInFlightCloseInErroredState(stream) {
WritableStreamRejectClosedPromiseInReactionToError(stream);
}
-function WritableStreamFinishInFlightCloseWithError(stream, reason) {
+function WritableStreamFinishInFlightCloseWithError(stream, error) {
assert(stream._inFlightCloseRequest !== undefined);
- stream._inFlightCloseRequest._reject(reason);
+ stream._inFlightCloseRequest._reject(error);
stream._inFlightCloseRequest = undefined;
const state = stream._state;
@@ -302,21 +290,8 @@ function WritableStreamFinishInFlightCloseWithError(stream, reason) {
assert(state === 'writable');
- stream._state = 'errored';
- stream._storedError = reason;
-
- if (stream._pendingAbortRequest === undefined) {
- const writer = stream._writer;
- if (writer !== undefined) {
- defaultWriterReadyPromiseResetToRejected(writer, reason);
- writer._readyPromise.catch(() => {});
- }
- } else {
- stream._pendingAbortRequest._reject(reason);
- stream._pendingAbortRequest = undefined;
- }
-
- WritableStreamRejectClosedPromiseInReactionToError(stream);
+ WritableStreamError(stream, error);
+ WritableStreamRejectAbortRequestIfPending(stream);
}
function WritableStreamCloseQueuedOrInFlight(stream) {
@@ -336,7 +311,7 @@ function WritableStreamHandleAbortRequestIfPending(stream) {
const abortRequest = stream._pendingAbortRequest;
stream._pendingAbortRequest = undefined;
- const promise = WritableStreamDefaultControllerAbort(stream._writableStreamController, abortRequest._reason);
+ const promise = stream._writableStreamController[AbortSteps](abortRequest._reason);
promise.then(
abortRequest._resolve,
abortRequest._reject
@@ -621,6 +596,15 @@ function WritableStreamDefaultWriterCloseWithErrorPropagation(writer) {
return WritableStreamDefaultWriterClose(writer);
}
+function WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error) {
+ if (writer._readyPromiseState === 'pending') {
+ defaultWriterReadyPromiseReject(writer, error);
+ } else {
+ defaultWriterReadyPromiseResetToRejected(writer, error);
+ }
+ writer._readyPromise.catch(() => {});
+}
+
function WritableStreamDefaultWriterGetDesiredSize(writer) {
const stream = writer._ownerWritableStream;
const state = stream._state;
@@ -645,13 +629,7 @@ function WritableStreamDefaultWriterRelease(writer) {
'Writer was released and can no longer be used to monitor the stream\'s closedness');
const state = stream._state;
- if (state === 'writable' && WritableStreamCloseQueuedOrInFlight(stream) === false &&
- stream._pendingAbortRequest === undefined && stream._backpressure === true) {
- defaultWriterReadyPromiseReject(writer, releasedError);
- } else {
- defaultWriterReadyPromiseResetToRejected(writer, releasedError);
- }
- writer._readyPromise.catch(() => {});
+ WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError);
if (state === 'writable' || WritableStreamHasOperationMarkedInFlight(stream) === true) {
defaultWriterClosedPromiseReject(writer, releasedError);
@@ -736,16 +714,45 @@ class WritableStreamDefaultController {
WritableStreamDefaultControllerError(this, e);
}
-}
-// Abstract operations implementing interface required by the WritableStream.
+ [AbortSteps](reason) {
+ const sinkAbortPromise = PromiseInvokeOrNoop(this._underlyingSink, 'abort', [reason]);
+ return sinkAbortPromise.then(() => undefined);
+ }
-function WritableStreamDefaultControllerAbort(controller, reason) {
- ResetQueue(controller);
- const sinkAbortPromise = PromiseInvokeOrNoop(controller._underlyingSink, 'abort', [reason]);
- return sinkAbortPromise.then(() => undefined);
+ [ErrorSteps]() {
+ ResetQueue(this);
+ }
+
+ [StartSteps]() {
+ const startResult = InvokeOrNoop(this._underlyingSink, 'start', [this]);
+ const stream = this._controlledWritableStream;
+
+ Promise.resolve(startResult).then(
+ () => {
+ this._started = true;
+
+ if (stream._state === 'errored') {
+ WritableStreamRejectAbortRequestIfPending(stream);
+ } else {
+ WritableStreamHandleAbortRequestIfPending(stream);
+ }
+
+ // This is a no-op if the stream was errored above.
+ WritableStreamDefaultControllerAdvanceQueueIfNeeded(this);
+ },
+ r => {
+ assert(stream._state === 'writable' || stream._state === 'errored');
+ WritableStreamDefaultControllerErrorIfNeeded(this, r);
+ WritableStreamRejectAbortRequestIfPending(stream);
+ }
+ )
+ .catch(rethrowAssertionErrorRejection);
+ }
}
+// Abstract operations implementing interface required by the WritableStream.
+
function WritableStreamDefaultControllerClose(controller) {
EnqueueValueWithSize(controller, 'close', 0);
WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
@@ -789,32 +796,6 @@ function WritableStreamDefaultControllerWrite(controller, chunk, chunkSize) {
WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
}
-function WritableStreamDefaultControllerStart(controller) {
- const startResult = InvokeOrNoop(controller._underlyingSink, 'start', [controller]);
- const stream = controller._controlledWritableStream;
-
- Promise.resolve(startResult).then(
- () => {
- controller._started = true;
-
- if (stream._state === 'errored') {
- WritableStreamRejectAbortRequestIfPending(stream);
- } else {
- WritableStreamHandleAbortRequestIfPending(stream);
- }
-
- // This is a no-op if the stream was errored above.
- WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
- },
- r => {
- assert(stream._state === 'writable' || stream._state === 'errored');
- WritableStreamDefaultControllerErrorIfNeeded(controller, r);
- WritableStreamRejectAbortRequestIfPending(stream);
- }
- )
- .catch(rethrowAssertionErrorRejection);
-}
-
// Abstract operations for the WritableStreamDefaultController.
function IsWritableStreamDefaultController(x) {
@@ -857,9 +838,9 @@ function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller) {
}
}
-function WritableStreamDefaultControllerErrorIfNeeded(controller, e) {
+function WritableStreamDefaultControllerErrorIfNeeded(controller, error) {
if (controller._controlledWritableStream._state === 'writable') {
- WritableStreamDefaultControllerError(controller, e);
+ WritableStreamDefaultControllerError(controller, error);
}
}
@@ -933,29 +914,12 @@ function WritableStreamDefaultControllerGetBackpressure(controller) {
// A client of WritableStreamDefaultController may use these functions directly to bypass state check.
-function WritableStreamDefaultControllerError(controller, e) {
+function WritableStreamDefaultControllerError(controller, error) {
const stream = controller._controlledWritableStream;
assert(stream._state === 'writable');
- stream._state = 'errored';
- stream._storedError = e;
-
- const writer = stream._writer;
- if (stream._pendingAbortRequest === undefined && writer !== undefined) {
- if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._backpressure === true) {
- defaultWriterReadyPromiseReject(writer, e);
- } else {
- defaultWriterReadyPromiseResetToRejected(writer, e);
- }
- writer._readyPromise.catch(() => {});
- }
-
- ResetQueue(controller);
-
- if (WritableStreamHasOperationMarkedInFlight(stream) === false) {
- WritableStreamRejectPromisesInReactionToError(stream);
- }
+ WritableStreamError(stream, error);
}
// Helper functions for the WritableStream.
@@ -1024,18 +988,21 @@ function defaultWriterReadyPromiseInitialize(writer) {
writer._readyPromise_resolve = resolve;
writer._readyPromise_reject = reject;
});
+ writer._readyPromiseState = 'pending';
}
function defaultWriterReadyPromiseInitializeAsRejected(writer, reason) {
writer._readyPromise = Promise.reject(reason);
writer._readyPromise_resolve = undefined;
writer._readyPromise_reject = undefined;
+ writer._readyPromiseState = 'rejected';
}
function defaultWriterReadyPromiseInitializeAsResolved(writer) {
writer._readyPromise = Promise.resolve(undefined);
writer._readyPromise_resolve = undefined;
writer._readyPromise_reject = undefined;
+ writer._readyPromiseState = 'fulfilled';
}
function defaultWriterReadyPromiseReject(writer, reason) {
@@ -1045,6 +1012,7 @@ function defaultWriterReadyPromiseReject(writer, reason) {
writer._readyPromise_reject(reason);
writer._readyPromise_resolve = undefined;
writer._readyPromise_reject = undefined;
+ writer._readyPromiseState = 'rejected';
}
function defaultWriterReadyPromiseReset(writer) {
@@ -1055,6 +1023,7 @@ function defaultWriterReadyPromiseReset(writer) {
writer._readyPromise_resolve = resolve;
writer._readyPromise_reject = reject;
});
+ writer._readyPromiseState = 'pending';
}
function defaultWriterReadyPromiseResetToRejected(writer, reason) {
@@ -1062,6 +1031,7 @@ function defaultWriterReadyPromiseResetToRejected(writer, reason) {
assert(writer._readyPromise_reject === undefined);
writer._readyPromise = Promise.reject(reason);
+ writer._readyPromiseState = 'rejected';
}
function defaultWriterReadyPromiseResolve(writer) {
@@ -1071,4 +1041,5 @@ function defaultWriterReadyPromiseResolve(writer) {
writer._readyPromise_resolve(undefined);
writer._readyPromise_resolve = undefined;
writer._readyPromise_reject = undefined;
+ writer._readyPromiseState = 'fulfilled';
}
diff --git a/reference-implementation/test.js b/reference-implementation/test.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/reference-implementation/web-platform-tests b/reference-implementation/web-platform-tests
index faf0d4284..9d97169bd 160000
--- a/reference-implementation/web-platform-tests
+++ b/reference-implementation/web-platform-tests
@@ -1 +1 @@
-Subproject commit faf0d42846a37b30d855902af7d316eaca17e379
+Subproject commit 9d97169bd491b8e243ff9094f7870c00fc539874