@@ -15,6 +15,9 @@ Markup Shorthands: markdown yes
15
15
spec:webidl; type:dfn; text:resolve
16
16
spec:webidl; type:dfn; text:new
17
17
spec:infra; type:dfn; text:list
18
+ spec:html; type:dfn; text:entangle
19
+ spec:html; type:dfn; text:message port post message steps
20
+ spec:html; type:dfn; text:port message queue
18
21
</pre>
19
22
20
23
<pre class="anchors">
@@ -28,10 +31,12 @@ urlPrefix: https://tc39.es/ecma262/; spec: ECMASCRIPT
28
31
text: DataView; url: #sec-dataview-objects
29
32
text: Number; url: #sec-ecmascript-language-types-number-type
30
33
text: Uint8Array; url: #sec-typedarray-objects
34
+ text: %Object.prototype%; url: #sec-properties-of-the-object-prototype-object
31
35
type: dfn
32
36
text: abstract operation; url: #sec-algorithm-conventions-abstract-operations
33
37
text: completion record; url: #sec-completion-record-specification-type
34
38
text: internal slot; url: #sec-object-internal-methods-and-internal-slots
39
+ text: realm; url: #sec-code-realms
35
40
text: the current Realm; url: #current-realm
36
41
text: the typed array constructors table; url: #table-49
37
42
text: typed array; url: #sec-typedarray-objects
@@ -41,11 +46,14 @@ urlPrefix: https://tc39.es/ecma262/; spec: ECMASCRIPT
41
46
text: CopyDataBlockBytes; url: #sec-copydatablockbytes
42
47
text: CreateArrayFromList; url: #sec-createarrayfromlist
43
48
text: CreateBuiltinFunction; url: #sec-createbuiltinfunction
49
+ text: CreateDataProperty; url: #sec-createdataproperty
44
50
text: Construct; url: #sec-construct
45
51
text: DetachArrayBuffer; url: #sec-detacharraybuffer
52
+ text: Get; url: #sec-get-o-p
46
53
text: GetV; url: #sec-getv
47
54
text: IsDetachedBuffer; url: #sec-isdetachedbuffer
48
55
text: IsInteger; url: #sec-isinteger
56
+ text: OrdinaryObjectCreate; url: #sec-ordinaryobjectcreate
49
57
text: SetFunctionLength; url: #sec-setfunctionlength
50
58
text: SetFunctionName; url: #sec-setfunctionname
51
59
text: Type; url: #sec-ecmascript-data-types-and-values
@@ -451,7 +459,7 @@ by the [=underlying source=] but not yet read by any consumer.
451
459
The Web IDL definition for the {{ReadableStream}} class is given as follows:
452
460
453
461
<xmp class="idl">
454
- [Exposed=(Window,Worker,Worklet)]
462
+ [Exposed=(Window,Worker,Worklet), Transferable ]
455
463
interface ReadableStream {
456
464
constructor(optional object underlyingSource, optional QueuingStrategy strategy = {});
457
465
@@ -507,6 +515,10 @@ table:
507
515
<td class="non-normative"> A {{ReadableStreamDefaultController}} or
508
516
{{ReadableByteStreamController}} created with the ability to control the state and queue of this
509
517
stream
518
+ <tr>
519
+ <td> <!-- TODO(ricea): Style this as <dfn unused> when that is supported.
520
+ See https://github.com/tabatkins/bikeshed/issues/1747 --><b>\[[detached]]</b>
521
+ <td class="non-normative">A boolean flag set to true when the stream is transferred
510
522
<tr>
511
523
<td><dfn>\[[disturbed]]</dfn>
512
524
<td class="non-normative"> A boolean flag set to true when the stream has been read from or
@@ -1010,6 +1022,43 @@ default-reader-asynciterator-prototype-internal-slots">Asynchronous iteration</h
1010
1022
1. Return [=a promise resolved with=] undefined.
1011
1023
</div>
1012
1024
1025
+ <h4 id="rs-transfer">Transfer via `postMessage()`</h4>
1026
+
1027
+ <dl class="domintro">
1028
+ <dt><code> destination.postMessage(rs, { transfer: [rs] });</code>
1029
+ <dd>
1030
+ <p> Sends a {{ReadableStream}} to another frame, window, or worker.
1031
+
1032
+ <p> The transferred stream can be used exactly like the original. The original will become
1033
+ [=locked to a reader|locked=] and no longer directly usable.
1034
+ </dd>
1035
+ </dl>
1036
+
1037
+ <div algorithm="ReadableStream transfer steps">
1038
+ {{ReadableStream}} objects are [=transferable objects=] . Their [=transfer steps=] , given |value|
1039
+ and |dataHolder|, are:
1040
+
1041
+ 1. If ! [$IsReadableStreamLocked$] (|value|) is true, throw a "{{DataCloneError}} " {{DOMException}} .
1042
+ 1. Let |port1| be a [=new=] {{MessagePort}} in [=the current Realm=] .
1043
+ 1. Let |port2| be a [=new=] {{MessagePort}} in [=the current Realm=] .
1044
+ 1. [=Entangle=] |port1| and |port2|.
1045
+ 1. Let |writable| be a [=new=] {{WritableStream}} in [=the current Realm=] .
1046
+ 1. Perform ! [$SetUpCrossRealmTransformWritable$] (|writable|, |port1|).
1047
+ 1. Let |promise| be ! [$ReadableStreamPipeTo$] (|value|, |writable|, false, false, false).
1048
+ 1. Set |promise|.\[[PromiseIsHandled]] to true.
1049
+ 1. Set |dataHolder|.\[[port]] to ! [$StructuredSerializeWithTransfer$] (|port2|, « |port2| »).
1050
+ </div>
1051
+
1052
+ <div algorithm="ReadableStream transfer-receiving steps">
1053
+ Their [=transfer-receiving steps=] , given |dataHolder| and |value|, are:
1054
+
1055
+ 1. Let |deserializedRecord| be ! [$StructuredDeserializeWithTransfer$] (|dataHolder|.\[[port]] ,
1056
+ [=the current Realm=] ).
1057
+ 1. Let |port| be |deserializedRecord|.\[[Deserialized]] .
1058
+ 1. Perform ! [$SetUpCrossRealmTransformReadable$] (|value|, |port|).
1059
+
1060
+ </div>
1061
+
1013
1062
<h3 id="generic-reader-mixin">The {{ReadableStreamGenericReader}} mixin</h3>
1014
1063
1015
1064
The {{ReadableStreamGenericReader}} mixin defines common internal slots, getters and methods that
@@ -3457,7 +3506,7 @@ The {{WritableStream}} represents a [=writable stream=].
3457
3506
The Web IDL definition for the {{WritableStream}} class is given as follows:
3458
3507
3459
3508
<xmp class="idl">
3460
- [Exposed=(Window,Worker,Worklet)]
3509
+ [Exposed=(Window,Worker,Worklet), Transferable ]
3461
3510
interface WritableStream {
3462
3511
constructor(optional object underlyingSink, optional QueuingStrategy strategy = {});
3463
3512
@@ -3491,6 +3540,9 @@ table:
3491
3540
<td> <dfn>\[[controller]]</dfn>
3492
3541
<td class="non-normative"> A {{WritableStreamDefaultController}} created with the ability to
3493
3542
control the state and queue of this stream
3543
+ <tr>
3544
+ <td> <!-- TODO(ricea): Style this as <dfn unused> when that is supported --><b>\[[detached]]</b>
3545
+ <td class="non-normative">A boolean flag set to true when the stream is transferred
3494
3546
<tr>
3495
3547
<td><dfn>\[[inFlightWriteRequest]]</dfn>
3496
3548
<td class="non-normative"> A slot set to the promise for the current in-flight write operation
@@ -3746,6 +3798,42 @@ as seen for example in [[#example-ws-no-backpressure]].
3746
3798
1. Return ? [$AcquireWritableStreamDefaultWriter$] ([=this=] ).
3747
3799
</div>
3748
3800
3801
+ <h4 id="ws-transfer">Transfer via `postMessage()`</h4>
3802
+
3803
+ <dl class="domintro">
3804
+ <dt><code> destination.postMessage(ws, { transfer: [ws] });</code>
3805
+ <dd>
3806
+ <p> Sends a {{WritableStream}} to another frame, window, or worker.
3807
+
3808
+ <p> The transferred stream can be used exactly like the original. The original will become
3809
+ [=locked to a writer|locked=] and no longer directly usable.
3810
+ </dd>
3811
+ </dl>
3812
+
3813
+ <div algorithm="WritableStream transfer steps">
3814
+ {{WritableStream}} objects are [=transferable objects=] . Their [=transfer steps=] , given |value|
3815
+ and |dataHolder|, are:
3816
+
3817
+ 1. If ! [$IsWritableStreamLocked$] (|value|) is true, throw a "{{DataCloneError}} " {{DOMException}} .
3818
+ 1. Let |port1| be a [=new=] {{MessagePort}} in [=the current Realm=] .
3819
+ 1. Let |port2| be a [=new=] {{MessagePort}} in [=the current Realm=] .
3820
+ 1. [=Entangle=] |port1| and |port2|.
3821
+ 1. Let |readable| be a [=new=] {{ReadableStream}} in [=the current Realm=] .
3822
+ 1. Perform ! [$SetUpCrossRealmTransformReadable$] (|readable|, |port1|).
3823
+ 1. Let |promise| be ! [$ReadableStreamPipeTo$] (|readable|, |value|, false, false, false).
3824
+ 1. Set |promise|.\[[PromiseIsHandled]] to true.
3825
+ 1. Set |dataHolder|.\[[port]] to ! [$StructuredSerializeWithTransfer$] (|port2|, « |port2| »).
3826
+ </div>
3827
+
3828
+ <div algorithm="WritableStream transfer-receiving steps">
3829
+ Their [=transfer-receiving steps=] , given |dataHolder| and |value|, are:
3830
+
3831
+ 1. Let |deserializedRecord| be ! [$StructuredDeserializeWithTransfer$] (|dataHolder|.\[[port]] ,
3832
+ [=the current Realm=] ).
3833
+ 1. Let |port| be a |deserializedRecord|.\[[Deserialized]] .
3834
+ 1. Perform ! [$SetUpCrossRealmTransformWritable$] (|value|, |port|).
3835
+ </div>
3836
+
3749
3837
<h3 id="default-writer-class">The {{WritableStreamDefaultWriter}} class</h3>
3750
3838
3751
3839
The {{WritableStreamDefaultWriter}} class represents a [=writable stream writer=] designed to be
@@ -4884,7 +4972,7 @@ The {{TransformStream}} class is a concrete instance of the general [=transform
4884
4972
The Web IDL definition for the {{TransformStream}} class is given as follows:
4885
4973
4886
4974
<xmp class="idl">
4887
- [Exposed=(Window,Worker,Worklet)]
4975
+ [Exposed=(Window,Worker,Worklet), Transferable ]
4888
4976
interface TransformStream {
4889
4977
constructor(optional object transformer,
4890
4978
optional QueuingStrategy writableStrategy = {},
@@ -4918,6 +5006,9 @@ table:
4918
5006
<td> <dfn>\[[controller]]</dfn>
4919
5007
<td class="non-normative"> A {{TransformStreamDefaultController}} created with the ability to
4920
5008
control [=TransformStream/[[readable]]=] and [=TransformStream/[[writable]]=]
5009
+ <tr>
5010
+ <td> <!-- TODO(ricea): Style this as <dfn unused> when that is supported --><b>\[[detached]]</b>
5011
+ <td class="non-normative">A boolean flag set to true when the stream is transferred
4921
5012
<tr>
4922
5013
<td><dfn>\[[readable]]</dfn>
4923
5014
<td class="non-normative"> The {{ReadableStream}} instance controlled by this object
@@ -5090,6 +5181,52 @@ side=], or to terminate or error the stream.
5090
5181
1. Return [=this=] .[=TransformStream/[[writable]]=] .
5091
5182
</div>
5092
5183
5184
+ <h4 id="ts-transfer">Transfer via `postMessage()`</h4>
5185
+
5186
+ <dl class="domintro">
5187
+ <dt><code> destination.postMessage(ts, { transfer: [ts] });</code>
5188
+ <dd>
5189
+ <p> Sends a {{TransformStream}} to another frame, window, or worker.
5190
+
5191
+ <p> The transferred stream can be used exactly like the original. Its [=readable side|readable=]
5192
+ and [=writable sides=] will become locked and no longer directly usable.
5193
+ </dd>
5194
+ </dl>
5195
+
5196
+ <div algorithm="TransformStream transfer steps">
5197
+ {{TransformStream}} objects are [=transferable objects=] . Their [=transfer steps=] , given |value|
5198
+ and |dataHolder|, are:
5199
+
5200
+ 1. Let |readable| be |value|.[=TransformStream/[[readable]]=] .
5201
+ 1. Let |writable| be |value|.[=TransformStream/[[writable]]=] .
5202
+ 1. If ! [$IsReadableStreamLocked$] (|readable|) is true, throw a "{{DataCloneError}} "
5203
+ {{DOMException}} .
5204
+ 1. If ! [$IsWritableStreamLocked$] (|writable|) is true, throw a "{{DataCloneError}} "
5205
+ {{DOMException}} .
5206
+ 1. Set |dataHolder|.\[[readable]] to ! [$StructuredSerializeWithTransfer$] (|readable|,
5207
+ « |readable| »).
5208
+ 1. Set |dataHolder|.\[[writable]] to ! [$StructuredSerializeWithTransfer$] (|writable|,
5209
+ « |writable| »).
5210
+ </div>
5211
+
5212
+ <div algorithm="TransformStream transfer-receiving steps">
5213
+ Their [=transfer-receiving steps=] , given |dataHolder| and |value|, are:
5214
+
5215
+ 1. Let |readableRecord| be ! [$StructuredDeserializeWithTransfer$] (|dataHolder|.\[[readable]] ,
5216
+ [=the current Realm=] ).
5217
+ 1. Let |writableRecord| be ! [$StructuredDeserializeWithTransfer$] (|dataHolder|.\[[writable]] ,
5218
+ [=the current Realm=] ).
5219
+ 1. Set |value|.[=TransformStream/[[readable]]=] to |readableRecord|.\[[Deserialized]] .
5220
+ 1. Set |value|.[=TransformStream/[[writable]]=] to |writableRecord|.\[[Deserialized]] .
5221
+ 1. Set |value|.[=TransformStream/[[backpressure]]=] ,
5222
+ |value|.[=TransformStream/[[backpressureChangePromise]]=] , and
5223
+ |value|.[=TransformStream/[[controller]]=] to undefined.
5224
+
5225
+ <p class="note"> The [=TransformStream/[[backpressure]]=] ,
5226
+ [=TransformStream/[[backpressureChangePromise]]=] , and [=TransformStream/[[controller]]=] slots are
5227
+ not used in a transferred {{TransformStream}} .</p>
5228
+ </div>
5229
+
5093
5230
<h3 id="ts-default-controller-class">The {{TransformStreamDefaultController}} class</h3>
5094
5231
5095
5232
The {{TransformStreamDefaultController}} class has methods that allow manipulation of the
@@ -5900,6 +6037,150 @@ for="value-with-size">value</dfn> and <dfn for="value-with-size">size</dfn>.
5900
6037
1. Set |container|.\[[queueTotalSize]] to 0.
5901
6038
</div>
5902
6039
6040
+ <h3 id="transferrable-streams">Transferable streams</h3>
6041
+
6042
+ Transferable streams are implemented using a special kind of identity transform which has the
6043
+ [=writable side=] in one [=realm=] and the [=readable side=] in another realm. The following
6044
+ abstract operations are used to implement these "cross-realm transforms".
6045
+
6046
+ <div algorithm>
6047
+ <dfn abstract-op lt="CrossRealmTransformSendError">CrossRealmTransformSendError(|port|,
6048
+ |error|)</dfn> performs the following steps:
6049
+
6050
+ 1. Perform [$PackAndPostMessage$] (|port|, "`error`", |error|), discarding the result.
6051
+
6052
+ <p class="note"> As we are already in an errored state when this abstract operation is performed, we
6053
+ cannot handle further errors, so we just discard them.</p>
6054
+ </div>
6055
+
6056
+ <div algorithm>
6057
+ <dfn abstract-op lt="PackAndPostMessage">PackAndPostMessage(|port|, |type|, |value|)</dfn> performs
6058
+ the following steps:
6059
+
6060
+ 1. Let |message| be [$OrdinaryObjectCreate$] (null).
6061
+ 1. Perform ! [$CreateDataProperty$] (|message|, "`type`", |type|).
6062
+ 1. Perform ! [$CreateDataProperty$] (|message|, "`value`", |value|).
6063
+ 1. Let |targetPort| be the port with which |port| is entangled, if any; otherwise let it be null.
6064
+ 1. Let |options| be «[ "`transfer`" → « » ] ».
6065
+ 1. Run the [=message port post message steps=] providing |targetPort|, |message|, and |options|.
6066
+
6067
+ <p class="note"> A JavaScript object is used for transfer to avoid having to duplicate the [=message
6068
+ port post message steps=] . The prototype of the object is set to null to avoid interference from
6069
+ {{%Object.prototype%}} .</p>
6070
+ </div>
6071
+
6072
+ <div algorithm>
6073
+ <dfn abstract-op lt="PackAndPostMessageHandlingError">PackAndPostMessageHandlingError(|port|,
6074
+ |type|, |value|)</dfn> performs the following steps:
6075
+
6076
+ 1. Let |result| be [$PackAndPostMessage$] (|port|, |type|, |value|).
6077
+ 1. If |result| is an abrupt completion,
6078
+ 1. Perform ! [$CrossRealmTransformSendError$] (|port|, |result|.\[[Value]] ).
6079
+ 1. Return |result| as a completion record.
6080
+ </div>
6081
+
6082
+ <div algorithm>
6083
+ <dfn abstract-op lt="SetUpCrossRealmTransformReadable">SetUpCrossRealmTransformReadable(|stream|,
6084
+ |port|)</dfn> performs the following steps:
6085
+
6086
+ 1. Perform ! [$InitializeReadableStream$] (|stream|).
6087
+ 1. Let |controller| be a [=new=] {{ReadableStreamDefaultController}} .
6088
+ 1. Add a handler for |port|'s {{MessagePort/message}} event with the following steps:
6089
+ 1. Let |data| be the data of the message.
6090
+ 1. Assert: [$Type$] (|data|) is Object.
6091
+ 1. Let |type| be ! [$Get$] (|data|, "`type`").
6092
+ 1. Let |value| be ! [$Get$] (|data|, "`value`").
6093
+ 1. Assert: [$Type$] (|type|) is String.
6094
+ 1. If |type| is "`chunk`",
6095
+ 1. Perform ! [$ReadableStreamDefaultControllerEnqueue$] (|controller|, |value|).
6096
+ 1. Otherwise, if |type| is "`close`",
6097
+ 1. Perform ! [$ReadableStreamDefaultControllerClose$] (|controller|).
6098
+ 1. Disentangle |port|.
6099
+ 1. Otherwise, if |type| is "`error`",
6100
+ 1. Perform ! [$ReadableStreamDefaultControllerError$] (|controller|, |value|).
6101
+ 1. Disentangle |port|.
6102
+ 1. Add a handler for |port|'s {{MessagePort/messageerror}} event with the following steps:
6103
+ 1. Let |error| be a new "{{DataCloneError}} " {{DOMException}} .
6104
+ 1. Perform ! [$CrossRealmTransformSendError$] (|port|, |error|).
6105
+ 1. Perform ! [$ReadableStreamDefaultControllerError$] (|controller|, |error|).
6106
+ 1. Disentangle |port|.
6107
+ 1. Enable |port|'s [=port message queue=] .
6108
+ 1. Let |startAlgorithm| be an algorithm that returns undefined.
6109
+ 1. Let |pullAlgorithm| be the following steps:
6110
+ 1. Perform ! [$PackAndPostMessage$] (|port|, "`pull`", undefined).
6111
+ 1. Return [=a promise resolved with=] undefined.
6112
+ 1. Let |cancelAlgorithm| be the following steps, taking a |reason| argument:
6113
+ 1. Let |result| be [$PackAndPostMessageHandlingError$] (|port|, "`error`", |reason|).
6114
+ 1. Disentangle |port|.
6115
+ 1. If |result| is an abrupt completion, return [=a promise rejected with=] |result|.\[[Value]] .
6116
+ 1. Otherwise, return [=a promise resolved with=] undefined.
6117
+ 1. Let |sizeAlgorithm| be an algorithm that returns 1.
6118
+ 1. Perform ! [$SetUpReadableStreamDefaultController$] (|stream|, |controller|, |startAlgorithm|,
6119
+ |pullAlgorithm|, |cancelAlgorithm|, 0, |sizeAlgorithm|).
6120
+
6121
+ <p class="note"> Implementations are encouraged to explicitly handle failures from the asserts in
6122
+ this algorithm, as the input might come from an untrusted context. Failure to do so could lead to
6123
+ security issues.</p>
6124
+ </div>
6125
+
6126
+ <div algorithm>
6127
+ <dfn abstract-op lt="SetUpCrossRealmTransformWritable">SetUpCrossRealmTransformWritable(|stream|,
6128
+ |port|)</dfn> performs the following steps:
6129
+
6130
+ 1. Perform ! [$InitializeWritableStream$] (|stream|).
6131
+ 1. Let |controller| be a [=new=] {{WritableStreamDefaultController}} .
6132
+ 1. Let |backpressurePromise| be [=a new promise=] .
6133
+ 1. Add a handler for |port|'s {{MessagePort/message}} event with the following steps:
6134
+ 1. Let |data| be the data of the message.
6135
+ 1. Assert: [$Type$] (|data|) is Object.
6136
+ 1. Let |type| be ! [$Get$] (|data|, "`type`").
6137
+ 1. Let |value| be ! [$Get$] (|data|, "`value`").
6138
+ 1. Assert: [$Type$] (|type|) is String.
6139
+ 1. If |type| is "`pull`",
6140
+ 1. If |backpressurePromise| is not undefined,
6141
+ 1. [=Resolve=] |backpressurePromise| with undefined.
6142
+ 1. Set |backpressurePromise| to undefined.
6143
+ 1. Otherwise, if |type| is "`error`",
6144
+ 1. Perform ! [$WritableStreamDefaultControllerErrorIfNeeded$] (|controller|, |value|).
6145
+ 1. If |backpressurePromise| is not undefined,
6146
+ 1. [=Resolve=] |backpressurePromise| with undefined.
6147
+ 1. Set |backpressurePromise| to undefined.
6148
+ 1. Add a handler for |port|'s {{MessagePort/messageerror}} event with the following steps:
6149
+ 1. Let |error| be a new "{{DataCloneError}} " {{DOMException}} .
6150
+ 1. Perform ! [$CrossRealmTransformSendError$] (|port|, |error|).
6151
+ 1. Perform ! [$WritableStreamDefaultControllerError$] (|controller|, |error|).
6152
+ 1. Disentangle |port|.
6153
+ 1. Enable |port|'s [=port message queue=] .
6154
+ 1. Let |startAlgorithm| be an algorithm that returns undefined.
6155
+ 1. Let |writeAlgorithm| be the following steps, taking a |chunk| argument:
6156
+ 1. If |backpressurePromise| is undefined, set |backpressurePromise| to
6157
+ [=a promise resolved with=] undefined.
6158
+ 1. Return the result of [=reacting=] to |backpressurePromise| with the following
6159
+ fulfillment steps:
6160
+ 1. Set |backpressurePromise| to [=a new promise=] .
6161
+ 1. Let |result| be [$PackAndPostMessageHandlingError$] (|port|, "`chunk`", |chunk|).
6162
+ 1. If |result| is an abrupt completion,
6163
+ 1. Disentangle |port|.
6164
+ 1. Return [=a promise rejected with=] |result|.\[[Value]] .
6165
+ 1. Otherwise, return [=a promise resolved with=] undefined.
6166
+ 1. Let |closeAlgorithm| be the folowing steps:
6167
+ 1. Perform ! [$PackAndPostMessage$] (|port|, "`close`", undefined).
6168
+ 1. Disentangle |port|.
6169
+ 1. Return [=a promise resolved with=] undefined.
6170
+ 1. Let |abortAlgorithm| be the following steps, taking a |reason| argument:
6171
+ 1. Let |result| be [$PackAndPostMessageHandlingError$] (|port|, "`error`", |reason|).
6172
+ 1. Disentangle |port|.
6173
+ 1. If |result| is an abrupt completion, return [=a promise rejected with=] |result|.\[[Value]] .
6174
+ 1. Otherwise, return [=a promise resolved with=] undefined.
6175
+ 1. Let |sizeAlgorithm| be an algorithm that returns 1.
6176
+ 1. Perform ! [$SetUpWritableStreamDefaultController$] (|stream|, |controller|, |startAlgorithm|,
6177
+ |writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|, 1, |sizeAlgorithm|).
6178
+
6179
+ <p class="note"> Implementations are encouraged to explicitly handle failures from the asserts in
6180
+ this algorithm, as the input might come from an untrusted context. Failure to do so could lead to
6181
+ security issues.</p>
6182
+ </div>
6183
+
5903
6184
<h3 id="misc-abstract-ops">Miscellaneous</h4>
5904
6185
5905
6186
The following abstract operations are a grab-bag of utilities.
0 commit comments