@@ -2155,6 +2155,26 @@ The following abstract operations operate on {{ReadableStream}} instances at a h
2155
2155
the operations below, the JavaScript-modifiable reader, writer, and stream APIs (i.e. methods
2156
2156
on the appropriate prototypes) must not be used. Instead, the streams must be manipulated
2157
2157
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.
2158
2178
* <strong> Backpressure must be enforced:</strong>
2159
2179
* While [$WritableStreamDefaultWriterGetDesiredSize$] (|writer|) is ≤ 0 or is null, the user
2160
2180
agent must not read from |reader|.
@@ -4004,13 +4024,14 @@ dictionary UnderlyingSink {
4004
4024
UnderlyingSinkWriteCallback write;
4005
4025
UnderlyingSinkCloseCallback close;
4006
4026
UnderlyingSinkAbortCallback abort;
4007
- any type;
4027
+ WritableStreamType type;
4008
4028
};
4009
4029
4010
4030
callback UnderlyingSinkStartCallback = any (WritableStreamDefaultController controller);
4011
4031
callback UnderlyingSinkWriteCallback = Promise<undefined> (any chunk, WritableStreamDefaultController controller);
4012
4032
callback UnderlyingSinkCloseCallback = Promise<undefined> ();
4013
4033
callback UnderlyingSinkAbortCallback = Promise<undefined> (optional any reason);
4034
+ enum WritableStreamType { "owning" };
4014
4035
</xmp>
4015
4036
4016
4037
<dl>
@@ -4096,8 +4117,15 @@ callback UnderlyingSinkAbortCallback = Promise<undefined> (optional any reason);
4096
4117
4097
4118
<dt> <dfn dict-member for="UnderlyingSink">type</dfn> </dt>
4098
4119
<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.
4101
4129
</dl>
4102
4130
4103
4131
The <code> controller</code> argument passed to {{UnderlyingSink/start|start()}} and
@@ -4166,10 +4194,6 @@ as seen for example in [[#example-ws-no-backpressure]].
4166
4194
<p class="note"> We cannot declare the |underlyingSink| argument as having the {{UnderlyingSink}}
4167
4195
type directly, because doing so would lose the reference to the original object. We need to
4168
4196
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.
4173
4197
1. Perform ! [$InitializeWritableStream$] ([=this=] ).
4174
4198
1. Let |sizeAlgorithm| be ! [$ExtractSizeAlgorithm$] (|strategy|).
4175
4199
1. Let |highWaterMark| be ? [$ExtractHighWaterMark$] (|strategy|, 1).
@@ -4264,7 +4288,7 @@ interface WritableStreamDefaultWriter {
4264
4288
Promise<undefined> abort(optional any reason);
4265
4289
Promise<undefined> close();
4266
4290
undefined releaseLock();
4267
- Promise<undefined> write(optional any chunk);
4291
+ Promise<undefined> write(optional any chunk, optional StructuredSerializeOptions options = { } );
4268
4292
};
4269
4293
</xmp>
4270
4294
@@ -4349,19 +4373,23 @@ following table:
4349
4373
lock on the writer for the duration of the write; the lock instead simply prevents other
4350
4374
[=producers=] from writing in an interleaved manner.
4351
4375
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>
4353
4377
<dd>
4354
4378
<p> Writes the given [=chunk=] to the writable stream, by waiting until any previous writes have
4355
4379
finished successfully, and then sending the [=chunk=] to the [=underlying sink=] 's
4356
4380
{{UnderlyingSink/write|write()}} method. It will return a promise that fulfills with undefined
4357
4381
upon a successful write, or rejects if the write fails or stream becomes errored before the
4358
4382
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.
4359
4386
4360
4387
<p> Note that what "success" means is up to the [=underlying sink=] ; it might indicate simply that
4361
4388
the [=chunk=] has been accepted, and not necessarily that it is safely saved to its ultimate
4362
4389
destination.
4363
4390
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
4365
4393
avoid mutating it after passing it to {{WritableStreamDefaultWriter/write()}} , until after the
4366
4394
promise returned by {{WritableStreamDefaultWriter/write()}} settles. This ensures that the
4367
4395
[=underlying sink=] receives and processes the same value that was passed in.
@@ -4429,12 +4457,13 @@ following table:
4429
4457
</div>
4430
4458
4431
4459
<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>
4433
4461
method steps are:
4434
4462
4463
+ 1. Let |transferList| be |options|["transfer"] .
4435
4464
1. If [=this=] .[=WritableStreamDefaultWriter/[[stream]]=] is undefined, return [=a promise rejected
4436
4465
with=] a {{TypeError}} exception.
4437
- 1. Return ! [$WritableStreamDefaultWriterWrite$] ([=this=] , |chunk|).
4466
+ 1. Return ! [$WritableStreamDefaultWriterWrite$] ([=this=] , |chunk|, |transferList| ).
4438
4467
</div>
4439
4468
4440
4469
<h3 id="ws-default-controller-class">The {{WritableStreamDefaultController}} class</h3>
@@ -4505,6 +4534,10 @@ the following table:
4505
4534
<td> <dfn>\[[writeAlgorithm]]</dfn>
4506
4535
<td class="non-normative"> A promise-returning algorithm, taking one argument (the [=chunk=] to
4507
4536
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.
4508
4541
</table>
4509
4542
4510
4543
The <dfn>close sentinel</dfn> is a unique value enqueued into
@@ -5069,7 +5102,7 @@ The following abstract operations support the implementation and manipulation of
5069
5102
5070
5103
<div algorithm>
5071
5104
<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>
5073
5106
performs the following steps:
5074
5107
5075
5108
1. Let |stream| be |writer|.[=WritableStreamDefaultWriter/[[stream]]=] .
@@ -5088,7 +5121,7 @@ The following abstract operations support the implementation and manipulation of
5088
5121
|stream|.[=WritableStream/[[storedError]]=] .
5089
5122
1. Assert: |state| is "`writable`".
5090
5123
1. Let |promise| be ! [$WritableStreamAddWriteRequest$] (|stream|).
5091
- 1. Perform ! [$WritableStreamDefaultControllerWrite$] (|controller|, |chunk|, |chunkSize|).
5124
+ 1. Perform ! [$WritableStreamDefaultControllerWrite$] (|controller|, |chunk|, |chunkSize|, |transferList| ).
5092
5125
1. Return |promise|.
5093
5126
</div>
5094
5127
@@ -5102,7 +5135,7 @@ The following abstract operations support the implementation of the
5102
5135
<dfn abstract-op lt="SetUpWritableStreamDefaultController"
5103
5136
id="set-up-writable-stream-default-controller"> SetUpWritableStreamDefaultController(|stream|,
5104
5137
|controller|, |startAlgorithm|, |writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|,
5105
- |highWaterMark|, |sizeAlgorithm|)</dfn> performs the following steps:
5138
+ |highWaterMark|, |sizeAlgorithm|, |isOwning| )</dfn> performs the following steps:
5106
5139
5107
5140
1. Assert: |stream| [=implements=] {{WritableStream}} .
5108
5141
1. Assert: |stream|.[=WritableStream/[[controller]]=] is undefined.
@@ -5113,6 +5146,7 @@ The following abstract operations support the implementation of the
5113
5146
1. Set |controller|.[=WritableStreamDefaultController/[[started]]=] to false.
5114
5147
1. Set |controller|.[=WritableStreamDefaultController/[[strategySizeAlgorithm]]=] to
5115
5148
|sizeAlgorithm|.
5149
+ 1. Set |controller|.[=WritableStreamDefaultController/[[isOwning]]=] to |isOwning|.
5116
5150
1. Set |controller|.[=WritableStreamDefaultController/[[strategyHWM]]=] to |highWaterMark|.
5117
5151
1. Set |controller|.[=WritableStreamDefaultController/[[writeAlgorithm]]=] to |writeAlgorithm|.
5118
5152
1. Set |controller|.[=WritableStreamDefaultController/[[closeAlgorithm]]=] to |closeAlgorithm|.
@@ -5142,6 +5176,8 @@ The following abstract operations support the implementation of the
5142
5176
1. Let |writeAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
5143
5177
1. Let |closeAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
5144
5178
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.
5145
5181
1. If |underlyingSinkDict|["{{UnderlyingSink/start}}"] [=map/exists=] , then set |startAlgorithm| to
5146
5182
an algorithm which returns the result of [=invoking=]
5147
5183
|underlyingSinkDict|["{{UnderlyingSink/start}}"] with argument list « |controller| »
@@ -5159,7 +5195,7 @@ The following abstract operations support the implementation of the
5159
5195
|underlyingSinkDict|["{{UnderlyingSink/abort}}"] with argument list « |reason| » and
5160
5196
[=callback this value=] |underlyingSink|.
5161
5197
1. Perform ? [$SetUpWritableStreamDefaultController$] (|stream|, |controller|, |startAlgorithm|,
5162
- |writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|, |highWaterMark|, |sizeAlgorithm|).
5198
+ |writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|, |highWaterMark|, |sizeAlgorithm|, |isOwning| ).
5163
5199
</div>
5164
5200
5165
5201
<div algorithm>
@@ -5313,9 +5349,9 @@ The following abstract operations support the implementation of the
5313
5349
<div algorithm>
5314
5350
<dfn abstract-op lt="WritableStreamDefaultControllerWrite"
5315
5351
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:
5317
5353
5318
- 1. Let |enqueueResult| be [$EnqueueValueWithSize$] (|controller|, |chunk|, |chunkSize|, undefined ).
5354
+ 1. Let |enqueueResult| be [$EnqueueValueWithSize$] (|controller|, |chunk|, |chunkSize|, |transferList| ).
5319
5355
1. If |enqueueResult| is an abrupt completion,
5320
5356
1. Perform ! [$WritableStreamDefaultControllerErrorIfNeeded$] (|controller|,
5321
5357
|enqueueResult|.\[[Value]] ).
0 commit comments