@@ -2155,6 +2155,26 @@ The following abstract operations operate on {{ReadableStream}} instances at a h
21552155 the operations below, the JavaScript-modifiable reader, writer, and stream APIs (i.e. methods
21562156 on the appropriate prototypes) must not be used. Instead, the streams must be manipulated
21572157 directly.
2158+ <p class="note">
2159+ {{WritableStream}} "{{WritableStreamType/owning}} " and {{ReadableStream}} "{{ReadableStreamType/owning}} "
2160+ has some impact on the piping operation:
2161+ - Piping a not owning {{ReadableStream}} to a not owning {{WritableStream}} will enqueue
2162+ the exact samne JavaScript values.
2163+ - Piping an owning {{ReadableStream}} to an owning {{WritableStream}} is similar to the above case,
2164+ as ownership will be transferred from the {{ReadableStream}} to the {{WritableStream}} .
2165+ In case of error during the piping operation, the user Agent is responsible to call the
2166+ necessary [=closing steps=] of the JavaScript values that were dequeud from the
2167+ {{ReadableStream}} but not successfully enqueued in the {{WritableStream}} .
2168+ - Piping an owning {{ReadableStream}} to a not owning {{WritableStream}} will enqueue
2169+ the serialized/transfered JavaScript values.
2170+ In case of error during the piping operation, the user Agent is responsible to call the
2171+ necessary [=closing steps=] of the JavaScript values that were dequeud from the
2172+ {{ReadableStream}} but not successfully enqueued in the {{WritableStream}} .
2173+ Note that [=closing steps=] of the enqueued JavaScript values in the {{WritableStream}}
2174+ will not be called automatically. It is up to the application to handle this.
2175+ - Piping a not owning {{ReadableStream}} to an owning {{WritableStream}} will trigger
2176+ serialization of the {{ReadableStream}} chunks when enqueued in {{WritableStream}} .
2177+ Piping may fail if the serialization fails, say in case of transferable-only JavaScript values.
21582178 * <strong> Backpressure must be enforced:</strong>
21592179 * While [$WritableStreamDefaultWriterGetDesiredSize$] (|writer|) is ≤ 0 or is null, the user
21602180 agent must not read from |reader|.
@@ -4004,13 +4024,14 @@ dictionary UnderlyingSink {
40044024 UnderlyingSinkWriteCallback write;
40054025 UnderlyingSinkCloseCallback close;
40064026 UnderlyingSinkAbortCallback abort;
4007- any type;
4027+ WritableStreamType type;
40084028};
40094029
40104030callback UnderlyingSinkStartCallback = any (WritableStreamDefaultController controller);
40114031callback UnderlyingSinkWriteCallback = Promise<undefined> (any chunk, WritableStreamDefaultController controller);
40124032callback UnderlyingSinkCloseCallback = Promise<undefined> ();
40134033callback UnderlyingSinkAbortCallback = Promise<undefined> (optional any reason);
4034+ enum WritableStreamType { "owning" };
40144035</xmp>
40154036
40164037<dl>
@@ -4096,8 +4117,15 @@ callback UnderlyingSinkAbortCallback = Promise<undefined> (optional any reason);
40964117
40974118 <dt> <dfn dict-member for="UnderlyingSink">type</dfn> </dt>
40984119 <dd>
4099- <p> This property is reserved for future use, so any attempts to supply a value will throw an
4100- exception.
4120+ <p> Can be set to "<dfn enum-value for="WritaleStreamType">owning</dfn> " to signal that the
4121+ constructed {{WritableStream}} will own chunks (via transfer or serialization) before enqueuing them.
4122+ This ensures that enqueued chunks are not mutable by the source.
4123+ Transferred or serialized chunks may have [=closing steps=] ] which are executed if
4124+ enqueued chunks are dequeued without being provided to the application, for instance when
4125+ a {{WritableStream}} is errored.
4126+
4127+ <p> Setting any value other than "{{WritableStreamType/owning}} " or undefined will cause the
4128+ {{WritableStream()}} constructor to throw an exception.
41014129</dl>
41024130
41034131The <code> controller</code> argument passed to {{UnderlyingSink/start|start()}} and
@@ -4166,10 +4194,6 @@ as seen for example in [[#example-ws-no-backpressure]].
41664194 <p class="note"> We cannot declare the |underlyingSink| argument as having the {{UnderlyingSink}}
41674195 type directly, because doing so would lose the reference to the original object. We need to
41684196 retain the object so we can [=invoke=] the various methods on it.
4169- 1. If |underlyingSinkDict|["{{UnderlyingSink/type}}"] [=map/exists=] , throw a {{RangeError}}
4170- exception.
4171- <p class="note"> This is to allow us to add new potential types in the future, without
4172- backward-compatibility concerns.
41734197 1. Perform ! [$InitializeWritableStream$] ([=this=] ).
41744198 1. Let |sizeAlgorithm| be ! [$ExtractSizeAlgorithm$] (|strategy|).
41754199 1. Let |highWaterMark| be ? [$ExtractHighWaterMark$] (|strategy|, 1).
@@ -4264,7 +4288,7 @@ interface WritableStreamDefaultWriter {
42644288 Promise<undefined> abort(optional any reason);
42654289 Promise<undefined> close();
42664290 undefined releaseLock();
4267- Promise<undefined> write(optional any chunk);
4291+ Promise<undefined> write(optional any chunk, optional StructuredSerializeOptions options = { } );
42684292};
42694293</xmp>
42704294
@@ -4349,19 +4373,23 @@ following table:
43494373 lock on the writer for the duration of the write; the lock instead simply prevents other
43504374 [=producers=] from writing in an interleaved manner.
43514375
4352- <dt><code> await <var ignore> writer</var> .{{WritableStreamDefaultWriter/write(chunk)|write}} (<var ignore> chunk</var> )</code>
4376+ <dt><code> await <var ignore> writer</var> .{{WritableStreamDefaultWriter/write(chunk, options )|write}} (<var ignore> chunk</var> , <var ignore> options </var> )</code>
43534377 <dd>
43544378 <p> Writes the given [=chunk=] to the writable stream, by waiting until any previous writes have
43554379 finished successfully, and then sending the [=chunk=] to the [=underlying sink=] 's
43564380 {{UnderlyingSink/write|write()}} method. It will return a promise that fulfills with undefined
43574381 upon a successful write, or rejects if the write fails or stream becomes errored before the
43584382 writing process is initiated.
4383+ <p> If the writable stream has its type set to "{{WritableStreamType/owning}} ", the given [=chunk=]
4384+ is serialized with [=options=] , which ensures that mutating [=chunk=] will have no impact on what
4385+ is written by the writable stream.
43594386
43604387 <p> Note that what "success" means is up to the [=underlying sink=] ; it might indicate simply that
43614388 the [=chunk=] has been accepted, and not necessarily that it is safely saved to its ultimate
43624389 destination.
43634390
4364- <p id="write-mutable-chunks"> If <var ignore> chunk</var> is mutable, [=producers=] are advised to
4391+ <p id="write-mutable-chunks"> If <var ignore> chunk</var> is mutable and the writable stream type is
4392+ not "{{WritableStreamType/owning}} ", [=producers=] are advised to
43654393 avoid mutating it after passing it to {{WritableStreamDefaultWriter/write()}} , until after the
43664394 promise returned by {{WritableStreamDefaultWriter/write()}} settles. This ensures that the
43674395 [=underlying sink=] receives and processes the same value that was passed in.
@@ -4429,12 +4457,13 @@ following table:
44294457</div>
44304458
44314459<div algorithm>
4432- The <dfn id="default-writer-write" method for="WritableStreamDefaultWriter">write(|chunk|)</dfn>
4460+ The <dfn id="default-writer-write" method for="WritableStreamDefaultWriter">write(|chunk|, |options| )</dfn>
44334461 method steps are:
44344462
4463+ 1. Let |transferList| be |options|["transfer"] .
44354464 1. If [=this=] .[=WritableStreamDefaultWriter/[[stream]]=] is undefined, return [=a promise rejected
44364465 with=] a {{TypeError}} exception.
4437- 1. Return ! [$WritableStreamDefaultWriterWrite$] ([=this=] , |chunk|).
4466+ 1. Return ! [$WritableStreamDefaultWriterWrite$] ([=this=] , |chunk|, |transferList| ).
44384467</div>
44394468
44404469<h3 id="ws-default-controller-class">The {{WritableStreamDefaultController}} class</h3>
@@ -4505,6 +4534,10 @@ the following table:
45054534 <td> <dfn>\[[writeAlgorithm]]</dfn>
45064535 <td class="non-normative"> A promise-returning algorithm, taking one argument (the [=chunk=] to
45074536 write), which writes data to the [=underlying sink=]
4537+ <tr>
4538+ <td> <dfn>\[[isOwning]]</dfn>
4539+ <td class="non-normative"> A boolean flag indicating whether to take ownership of enqueued chunks
4540+ via transfer or serialization.
45084541</table>
45094542
45104543The <dfn>close sentinel</dfn> is a unique value enqueued into
@@ -5069,7 +5102,7 @@ The following abstract operations support the implementation and manipulation of
50695102
50705103<div algorithm>
50715104 <dfn abstract-op lt="WritableStreamDefaultWriterWrite"
5072- id="writable-stream-default-writer-write"> WritableStreamDefaultWriterWrite(|writer|, |chunk|)</dfn>
5105+ id="writable-stream-default-writer-write"> WritableStreamDefaultWriterWrite(|writer|, |chunk|, |transferList| )</dfn>
50735106 performs the following steps:
50745107
50755108 1. Let |stream| be |writer|.[=WritableStreamDefaultWriter/[[stream]]=] .
@@ -5088,7 +5121,7 @@ The following abstract operations support the implementation and manipulation of
50885121 |stream|.[=WritableStream/[[storedError]]=] .
50895122 1. Assert: |state| is "`writable`".
50905123 1. Let |promise| be ! [$WritableStreamAddWriteRequest$] (|stream|).
5091- 1. Perform ! [$WritableStreamDefaultControllerWrite$] (|controller|, |chunk|, |chunkSize|).
5124+ 1. Perform ! [$WritableStreamDefaultControllerWrite$] (|controller|, |chunk|, |chunkSize|, |transferList| ).
50925125 1. Return |promise|.
50935126</div>
50945127
@@ -5102,7 +5135,7 @@ The following abstract operations support the implementation of the
51025135 <dfn abstract-op lt="SetUpWritableStreamDefaultController"
51035136 id="set-up-writable-stream-default-controller"> SetUpWritableStreamDefaultController(|stream|,
51045137 |controller|, |startAlgorithm|, |writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|,
5105- |highWaterMark|, |sizeAlgorithm|)</dfn> performs the following steps:
5138+ |highWaterMark|, |sizeAlgorithm|, |isOwning| )</dfn> performs the following steps:
51065139
51075140 1. Assert: |stream| [=implements=] {{WritableStream}} .
51085141 1. Assert: |stream|.[=WritableStream/[[controller]]=] is undefined.
@@ -5113,6 +5146,7 @@ The following abstract operations support the implementation of the
51135146 1. Set |controller|.[=WritableStreamDefaultController/[[started]]=] to false.
51145147 1. Set |controller|.[=WritableStreamDefaultController/[[strategySizeAlgorithm]]=] to
51155148 |sizeAlgorithm|.
5149+ 1. Set |controller|.[=WritableStreamDefaultController/[[isOwning]]=] to |isOwning|.
51165150 1. Set |controller|.[=WritableStreamDefaultController/[[strategyHWM]]=] to |highWaterMark|.
51175151 1. Set |controller|.[=WritableStreamDefaultController/[[writeAlgorithm]]=] to |writeAlgorithm|.
51185152 1. Set |controller|.[=WritableStreamDefaultController/[[closeAlgorithm]]=] to |closeAlgorithm|.
@@ -5142,6 +5176,8 @@ The following abstract operations support the implementation of the
51425176 1. Let |writeAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
51435177 1. Let |closeAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
51445178 1. Let |abortAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
5179+ 1. Let |isOwning| be true if |underlyingSinkDict|["{{UnderlyingSink/type}}"] is
5180+ "{{WritableStreamType/owning}} " and false otherwise.
51455181 1. If |underlyingSinkDict|["{{UnderlyingSink/start}}"] [=map/exists=] , then set |startAlgorithm| to
51465182 an algorithm which returns the result of [=invoking=]
51475183 |underlyingSinkDict|["{{UnderlyingSink/start}}"] with argument list « |controller| »
@@ -5159,7 +5195,7 @@ The following abstract operations support the implementation of the
51595195 |underlyingSinkDict|["{{UnderlyingSink/abort}}"] with argument list « |reason| » and
51605196 [=callback this value=] |underlyingSink|.
51615197 1. Perform ? [$SetUpWritableStreamDefaultController$] (|stream|, |controller|, |startAlgorithm|,
5162- |writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|, |highWaterMark|, |sizeAlgorithm|).
5198+ |writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|, |highWaterMark|, |sizeAlgorithm|, |isOwning| ).
51635199</div>
51645200
51655201<div algorithm>
@@ -5313,9 +5349,9 @@ The following abstract operations support the implementation of the
53135349<div algorithm>
53145350 <dfn abstract-op lt="WritableStreamDefaultControllerWrite"
53155351 id="writable-stream-default-controller-write"> WritableStreamDefaultControllerWrite(|controller|,
5316- |chunk|, |chunkSize|)</dfn> performs the following steps:
5352+ |chunk|, |chunkSize|, |transferList| )</dfn> performs the following steps:
53175353
5318- 1. Let |enqueueResult| be [$EnqueueValueWithSize$] (|controller|, |chunk|, |chunkSize|, undefined ).
5354+ 1. Let |enqueueResult| be [$EnqueueValueWithSize$] (|controller|, |chunk|, |chunkSize|, |transferList| ).
53195355 1. If |enqueueResult| is an abrupt completion,
53205356 1. Perform ! [$WritableStreamDefaultControllerErrorIfNeeded$] (|controller|,
53215357 |enqueueResult|.\[[Value]] ).
0 commit comments