@@ -19,6 +19,7 @@ spec:infra; type:dfn; text:list
1919spec:html; type:dfn; text:entangle
2020spec:html; type:dfn; text:message port post message steps
2121spec:html; type:dfn; text:port message queue
22+ spec:html; type:dfn; text:transferable objects
2223</pre>
2324
2425<pre class="anchors">
@@ -571,7 +572,7 @@ callback UnderlyingSourceStartCallback = any (ReadableStreamController controlle
571572callback UnderlyingSourcePullCallback = Promise<undefined> (ReadableStreamController controller);
572573callback UnderlyingSourceCancelCallback = Promise<undefined> (optional any reason);
573574
574- enum ReadableStreamType { "bytes" };
575+ enum ReadableStreamType { "bytes", "transfer" };
575576</xmp>
576577
577578<dl>
@@ -639,8 +640,7 @@ enum ReadableStreamType { "bytes" };
639640 something more persistently wrong.
640641 </div>
641642
642- <dt> <dfn dict-member for="UnderlyingSource" lt="type"><code>type</code></dfn> (byte streams
643- only)</dt>
643+ <dt> <dfn dict-member for="UnderlyingSource" lt="type"><code>type</code></dfn> </dt>
644644 <dd>
645645 <p> Can be set to "<dfn enum-value for="ReadableStreamType">bytes</dfn> " to signal that the
646646 constructed {{ReadableStream}} is a <a>readable byte stream</a> . This ensures that the resulting
@@ -651,6 +651,14 @@ enum ReadableStreamType { "bytes" };
651651 <p> For an example of how to set up a readable byte stream, including using the different
652652 controller interface, see [[#example-rbs-push]] .
653653
654+ <p> Can be set to "<dfn enum-value for="ReadableStreamType">transfer</dfn> " to signal that the
655+ constructed {{ReadableStream}} will transfer or serialize chunks before enqueuing them.
656+ This ensures that enqueued chunks are not mutable by the source.
657+ Transferred or serialized chunks may have <dfn>closing steps</dfn> which are executed if
658+ enqueued chunks are dequeued without being provided to the application, for instance when
659+ a {{ReadableStream}} is errored.
660+ </p>
661+
654662 <p> Setting any value other than "{{ReadableStreamType/bytes}} " or undefined will cause the
655663 {{ReadableStream()}} constructor to throw an exception.
656664
@@ -801,7 +809,8 @@ option. If {{UnderlyingSource/type}} is set to undefined (including via omission
801809 1. Perform ? [$SetUpReadableByteStreamControllerFromUnderlyingSource$] ([=this=] ,
802810 |underlyingSource|, |underlyingSourceDict|, |highWaterMark|).
803811 1. Otherwise,
804- 1. Assert: |underlyingSourceDict|["{{UnderlyingSource/type}}"] does not [=map/exist=] .
812+ 1. Assert: |underlyingSourceDict|["{{UnderlyingSource/type}}"] does not [=map/exist=] or
813+ is "{{ReadableStreamType/transfer}} ".
805814 1. Let |sizeAlgorithm| be ! [$ExtractSizeAlgorithm$] (|strategy|).
806815 1. Let |highWaterMark| be ? [$ExtractHighWaterMark$] (|strategy|, 1).
807816 1. Perform ? [$SetUpReadableStreamDefaultControllerFromUnderlyingSource$] ([=this=] ,
@@ -1523,6 +1532,9 @@ the following table:
15231532 <tr>
15241533 <td> <dfn>\[[stream]]</dfn>
15251534 <td class="non-normative"> The {{ReadableStream}} instance controlled
1535+ <tr>
1536+ <td> <dfn>\[[isTransferring]]</dfn>
1537+ <td class="non-normative"> A boolean flag indicating whether to transfer enqueued chunks
15261538</table>
15271539
15281540<h4 id="rs-default-controller-prototype">Methods and properties</h4>
@@ -2235,8 +2247,8 @@ create them does not matter.
22352247 objects|transferring=] their [=chunks=] . However, it does introduce a noticeable asymmetry between
22362248 the two branches, and limits the possible [=chunks=] to serializable ones. [[!HTML]]
22372249
2238- If |stream| is a [=readable byte stream=] , then |cloneForBranch2| is ignored and chunks are cloned
2239- unconditionally.
2250+ If |stream| is a [=readable byte stream=] , or if |stream| type is " {{ReadableStreamType/transfer}} ",
2251+ then |cloneForBranch2| is ignored and chunks are cloned unconditionally.
22402252
22412253 <p class="note"> In this standard ReadableStreamTee is always called with |cloneForBranch2| set to
22422254 false; other specifications pass true via the [=ReadableStream/tee=] wrapper algorithm.
@@ -2247,6 +2259,8 @@ create them does not matter.
22472259 1. Assert: |cloneForBranch2| is a boolean.
22482260 1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableByteStreamController}} ,
22492261 return ? [$ReadableByteStreamTee$] (|stream|).
2262+ 1. If |stream|.[=ReadableStream/[[controller]]=] .[=ReadableStreamDefaultController/[[isTransferring]]=]
2263+ is true, return ? [$ReadableStreamDefaultTee$] (|stream|, true).
22502264 1. Return ? [$ReadableStreamDefaultTee$] (|stream|, |cloneForBranch2|).
22512265</div>
22522266
@@ -2955,8 +2969,12 @@ The following abstract operations support the implementation of the
29552969 1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$] (|controller|) is false, return.
29562970 1. Let |stream| be |controller|.[=ReadableStreamDefaultController/[[stream]]=] .
29572971 1. If ! [$IsReadableStreamLocked$] (|stream|) is true and !
2958- [$ReadableStreamGetNumReadRequests$] (|stream|) > 0, perform !
2959- [$ReadableStreamFulfillReadRequest$] (|stream|, |chunk|, false).
2972+ [$ReadableStreamGetNumReadRequests$] (|stream|) > 0, perform the following steps:
2973+ 1. Let |internalChunk| be |chunk|.
2974+ 1. If |controller|.[=ReadableStreamDefaultController/[[isTransferring]]=] is true, perform the following steps:
2975+ 1. Set |internalChunk| to [$StructuredTransferOrSerialize$] (|chunk|).
2976+ 1. If |internalChunk| is an abrupt completion, return.
2977+ 1. ![$ReadableStreamFulfillReadRequest$] (|stream|, |internalChunk|, false).
29602978 1. Otherwise,
29612979 1. Let |result| be the result of performing
29622980 |controller|.[=ReadableStreamDefaultController/[[strategySizeAlgorithm]]=] , passing in |chunk|,
@@ -3029,7 +3047,7 @@ The following abstract operations support the implementation of the
30293047 <dfn abstract-op lt="SetUpReadableStreamDefaultController"
30303048 id="set-up-readable-stream-default-controller"> SetUpReadableStreamDefaultController(|stream|,
30313049 |controller|, |startAlgorithm|, |pullAlgorithm|, |cancelAlgorithm|, |highWaterMark|,
3032- |sizeAlgorithm|)</dfn> performs the following steps:
3050+ |sizeAlgorithm|, |isTransferring| )</dfn> performs the following steps:
30333051
30343052 1. Assert: |stream|.[=ReadableStream/[[controller]]=] is undefined.
30353053 1. Set |controller|.[=ReadableStreamDefaultController/[[stream]]=] to |stream|.
@@ -3039,8 +3057,9 @@ The following abstract operations support the implementation of the
30393057 |controller|.[=ReadableStreamDefaultController/[[pullAgain]]=] , and
30403058 |controller|.[=ReadableStreamDefaultController/[[pulling]]=] to false.
30413059 1. Set |controller|.[=ReadableStreamDefaultController/[[strategySizeAlgorithm]]=] to
3042- |sizeAlgorithm| and |controller|.[=ReadableStreamDefaultController/[[strategyHWM]]=] to
3043- |highWaterMark|.
3060+ |sizeAlgorithm|, |controller|.[=ReadableStreamDefaultController/[[strategyHWM]]=] to
3061+ |highWaterMark| and |controller|.[=ReadableStreamDefaultController/[[isTransferring]]=] to
3062+ |isTransferring|.
30443063 1. Set |controller|.[=ReadableStreamDefaultController/[[pullAlgorithm]]=] to |pullAlgorithm|.
30453064 1. Set |controller|.[=ReadableStreamDefaultController/[[cancelAlgorithm]]=] to |cancelAlgorithm|.
30463065 1. Set |stream|.[=ReadableStream/[[controller]]=] to |controller|.
@@ -3065,6 +3084,9 @@ The following abstract operations support the implementation of the
30653084 1. Let |startAlgorithm| be an algorithm that returns undefined.
30663085 1. Let |pullAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
30673086 1. Let |cancelAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
3087+ 1. Let |cancelAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
3088+ 1. Let |isTransferring| be true if |underlyingSourceDict|["{{UnderlyingSource/pull}}"] is
3089+ "{{ReadableStreamType/transfer}} " and false otherwise.
30683090 1. If |underlyingSourceDict|["{{UnderlyingSource/start}}"] [=map/exists=] , then set
30693091 |startAlgorithm| to an algorithm which returns the result of [=invoking=]
30703092 |underlyingSourceDict|["{{UnderlyingSource/start}}"] with argument list
@@ -3078,7 +3100,7 @@ The following abstract operations support the implementation of the
30783100 [=invoking=] |underlyingSourceDict|["{{UnderlyingSource/cancel}}"] with argument list
30793101 « |reason| » and [=callback this value=] |underlyingSource|.
30803102 1. Perform ? [$SetUpReadableStreamDefaultController$] (|stream|, |controller|, |startAlgorithm|,
3081- |pullAlgorithm|, |cancelAlgorithm|, |highWaterMark|, |sizeAlgorithm|).
3103+ |pullAlgorithm|, |cancelAlgorithm|, |highWaterMark|, |sizeAlgorithm|, |isTransferring| ).
30823104</div>
30833105
30843106<h4 id="rbs-controller-abstract-ops">Byte stream controllers</h4>
@@ -6346,8 +6368,8 @@ In what follows, a <dfn>value-with-size</dfn> is a [=struct=] with the two [=str
63466368for="value-with-size"> value</dfn> and <dfn for="value-with-size">size</dfn> .
63476369
63486370<div algorithm>
6349- <dfn abstract-op lt="DequeueValue" id="dequeue-value">DequeueValue(|container|)</dfn> performs the
6350- following steps:
6371+ <dfn abstract-op lt="DequeueValue" id="dequeue-value">DequeueValue(|container|)</dfn> performs
6372+ the following steps:
63516373
63526374 1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots.
63536375 1. Assert: |container|.\[[queue]] is not [=list/is empty|empty=] .
@@ -6368,7 +6390,11 @@ for="value-with-size">value</dfn> and <dfn for="value-with-size">size</dfn>.
63686390 1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots.
63696391 1. If ! [$IsNonNegativeNumber$] (|size|) is false, throw a {{RangeError}} exception.
63706392 1. If |size| is +∞, throw a {{RangeError}} exception.
6371- 1. [=list/Append=] a new [=value-with-size=] with [=value-with-size/value=] |value| and
6393+ 1. Let |enqueuedValue| be |value|.
6394+ 1. If |container|.\[[isTransferring]] is true, perform the following steps:
6395+ 1. Set |enqueuedValue| to [$StructuredTransferOrSerialize$] (|value|).
6396+ 1. If |enqueuedValue| is an abrupt completion, return.
6397+ 1. [=list/Append=] a new [=value-with-size=] with [=value-with-size/value=] |enqueuedValue| and
63726398 [=value-with-size/size=] |size| to |container|.\[[queue]] .
63736399 1. Set |container|.\[[queueTotalSize]] to |container|.\[[queueTotalSize]] + |size|.
63746400</div>
@@ -6388,6 +6414,10 @@ for="value-with-size">value</dfn> and <dfn for="value-with-size">size</dfn>.
63886414 performs the following steps:
63896415
63906416 1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots.
6417+ 1. If |container|.\[[isTransferring]] is true, perform the following steps until |container|.\[[queue]]
6418+ is [=list/is empty|empty=] :
6419+ 1. Let |chunk| be ! [$DequeueValue$] ([=this=] ).
6420+ 1. If |chunk| has [=closing steps=] , perform the [=closing steps=] given |chunk|.
63916421 1. Set |container|.\[[queue]] to a new empty [=list=] .
63926422 1. Set |container|.\[[queueTotalSize]] to 0.
63936423</div>
@@ -6596,6 +6626,18 @@ The following abstract operations are a grab-bag of utilities.
65966626 1. Return ? [$StructuredDeserialize$] (|serialized|, [=the current Realm=] ).
65976627</div>
65986628
6629+ <div algorithm>
6630+ <dfn abstract-op lt="StructuredTransferOrSerialize">StructuredTransferOrSerialize(|value|)</dfn>
6631+ performs the following steps:
6632+ 1. If |value| is [=transferable objects|transferable=] , perform the following steps:
6633+ // FIXME: We should probably handle other transferable types like ArrayBuffers.
6634+ 1. Let |serialized| be ! [$StructuredSerializeWithTransfer$] (|value|, « |value| »).
6635+ 1. Let |deserialized| be ! [$StructuredDeserializeWithTransfer$] (|serialized|,
6636+ [=the current Realm=] ).
6637+ 1. return |deserialized|.\[[Deserialized]] .
6638+ 1. return [$StructuredClone$] (|value|).
6639+ </div>
6640+
65996641<h2 id="other-specs">Using streams in other specifications</h2>
66006642
66016643Much of this standard concerns itself with the internal machinery of streams. Other specifications
0 commit comments