Skip to content

Commit 80bae9a

Browse files
jgrahamwhimboo
authored andcommitted
Make JS value serialization more flexible (#385)
Instead of having a `maxDepth` field, have a `serializationOptions` field that allows specifying multiple constraints on what's serialized. The initial supported constraints are: `maxObjectDepth`: The depth to which to serialize plain js objects (i.e. not DOM nodes). Defaults to null i.e. unlimited depth (matching WebDriver classic). `maxDomDepth`: Depth to which to serialize DOM nodes. Defaults to 0 i.e. serialize a node without any children. `includeShadowTree`: Whether to descend into shadow trees when serializing DOM nodes. Values are "none", meaning don't serialize shdow trees, "open" meaning only serialize open shadow trees, and "all" meaning serialize any shadow tree. Serialization of the tree itself follows the `maxDomDepth` parameter (but in all cases where an element has a shadow root, the shadow root itself is serialized). Co-authored-by: Henrik Skupin <[email protected]>
1 parent 038ccfe commit 80bae9a

File tree

1 file changed

+131
-57
lines changed

1 file changed

+131
-57
lines changed

index.bs

Lines changed: 131 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,7 @@ NodeProperties = {
18671867
?attributes: {*text => text},
18681868
?children: [*NodeRemoteValue],
18691869
?localName: text,
1870+
?mode: "open" / "closed",
18701871
?namespaceURI: text,
18711872
?nodeValue: text,
18721873
?shadowRoot: NodeRemoteValue / null,
@@ -1895,7 +1896,7 @@ Issue: handle String / Number / etc. wrapper objects specially?
18951896

18961897
<div algorithm>
18971898

1898-
To <dfn>serialize as a remote value</dfn> given a |value|, a |max depth|,
1899+
To <dfn>serialize as a remote value</dfn> given |value|, |serialization options|,
18991900
an |ownership type|, a |serialization internal map|, a |realm| and a |session|:
19001901

19011902
1. Let |remote value| be a result of [=serialize primitive protocol value=]
@@ -1923,7 +1924,8 @@ an |ownership type|, a |serialization internal map|, a |realm| and a |session|:
19231924
<dt>[=IsArray=](|value|)
19241925
<dd>Let |remote value| be [=serialize an Array-like=] with |session|,
19251926
<code>ArrayRemoteValue</code>, |handle id|, |known object|, |value|,
1926-
|max depth|, |ownership type|, |serialization internal map|, and |realm|.
1927+
|serialization options|, |ownership type|, |serialization internal map|,
1928+
|realm|, and |session|.
19271929

19281930
<dt>[=IsRegExp=](|value|)
19291931
<dd>
@@ -1964,11 +1966,12 @@ an |ownership type|, a |serialization internal map|, a |realm| and a |session|:
19641966

19651967
1. Let |serialized| be null.
19661968

1967-
1. If |known object| is <code>false</code>, and |max depth| is not 0, run
1968-
the following steps:
1969+
1. If |known object| is <code>false</code>, and |serialization
1970+
options|["<code>maxObjectDepth</code>"] is not 0, run the following
1971+
steps:
19691972

19701973
1. Let |serialized| be the result of [=serialize as a mapping=] with
1971-
[=CreateMapIterator=](|value|, key+value), |max depth|,
1974+
[=CreateMapIterator=](|value|, key+value), |serialization options|,
19721975
|ownership type|, |serialization internal map|, |realm|, and |session|.
19731976

19741977
1. If |serialized| is not null, set field <code>value</code> of |remote value| to
@@ -1986,12 +1989,14 @@ an |ownership type|, a |serialization internal map|, a |realm| and a |session|:
19861989

19871990
1. Let |serialized| be null.
19881991

1989-
1. If |known object| is <code>false</code>, and |max depth| is not 0, run
1990-
the following steps:
1992+
1. If |known object| is <code>false</code>, and |serialization
1993+
options|["<code>maxObjectDepth</code>"] is not 0, run the following
1994+
steps:
19911995

19921996
1. Let |serialized| be the result of [=serialize as a list=] with
1993-
[=CreateSetIterator=](|value|, value), |max depth|, |ownership type|,
1994-
|serialization internal map|, |realm|, and |session|.
1997+
[=CreateSetIterator=](|value|, value), |serialization options|,
1998+
|ownership type|, |serialization internal map|, |realm|, and
1999+
|session|.
19952000

19962001
1. If |serialized| is not null, set field <code>value</code> of |remote value| to
19972002
|serialized|.
@@ -2029,13 +2034,14 @@ an |ownership type|, a |serialization internal map|, a |realm| and a |session|:
20292034
<dt>|value| is a [=platform object=] that implements {{NodeList}}
20302035
<dd>Let |remote value| be [=serialize an Array-like=] with
20312036
<code>NodeListRemoteValue</code>,|handle id|, |known object|, |value|,
2032-
|max depth|, |ownership type|, |serialization internal map|, and |realm|.
2037+
|serialization options|, |ownership type|, |serialization internal map|,
2038+
|realm|, and |session|.
20332039

20342040
<dt>|value| is a [=platform object=] that implements {{HTMLCollection}}
20352041
<dd>Let |remote value| be [=serialize an Array-like=] with
20362042
<code>HTMLCollectionRemoteValue</code>, |handle id|, |known object|, |value|,
2037-
|max depth|, |ownership type|, |known object|, |serialization internal map|,
2038-
and |realm|.
2043+
|serialization options|, |ownership type|, |known object|, |serialization internal map|,
2044+
|realm|, and |session|.
20392045

20402046
<dt>|value| is a [=platform object=] that implements {{Node}}
20412047
<dd>
@@ -2074,14 +2080,27 @@ an |ownership type|, a |serialization internal map|, a |realm| and a |session|:
20742080

20752081
1. Set |serialized|["<code>childNodeCount</code>"] to |child node count|.
20762082

2077-
1. If |max depth| is equal to 0 let |children| be null.
2078-
Otherwise, let |children| be an empty [=/list=] and, for each node
2079-
|child| in the [=children=] of |value|:
2083+
1. If |serialization options|["<code>maxDomDepth</code>"] is equal to
2084+
0, or if |value| implements {{ShadowRoot}} and |serialization
2085+
options|["<code>includeShadowTree</code>"] is "<code>none</code>",
2086+
or if |serialization options|["<code>includeShadowTree</code>"] is
2087+
"<code>open</code>" and |value|'s <a spec=dom for=ShadowRoot>mode</a> is
2088+
"<code>closed</code>", let |children| be null.
20802089

2081-
1. Let |child depth| be |max depth| - 1 if |max depth| is not null, or null otherwise.
2090+
Otherwise, let |children| be an empty
2091+
[=/list=] and, for each node |child| in the [=children=] of
2092+
|value|:
2093+
2094+
1. Let |child serialization options| be a [=map/clone=] of
2095+
|serialization options|.
2096+
2097+
1. If |child serialization options|["<code>maxDomDepth</code>"] is
2098+
not null, set |child serialization
2099+
options|["<code>maxDomDepth</code>"] to |child serialization
2100+
options|["<code>maxDomDepth</code>"] - 1.
20822101

20832102
1. Let |serialized| be the result of [=serialize as a remote value=]
2084-
with |child|, |child depth|, |ownership type|,
2103+
with |child|, |child serialization options|, |ownership type|,
20852104
|serialization internal map|, |realm|, and |session|.
20862105

20872106
1. Append |serialized| to |children|.
@@ -2107,16 +2126,16 @@ an |ownership type|, a |serialization internal map|, a |realm| and a |session|:
21072126
1. If |shadow root| is null, let |serialized shadow| be null.
21082127
Otherwise run the following substeps:
21092128

2110-
1. Let |child depth| be |max depth| - 1 if |max depth| is not
2111-
null, or null otherwise.
2112-
21132129
1. Let |serialized shadow| be the result of
2114-
[=serialize as a remote value=] with |shadow root|, |child depth|,
2115-
false, |ownership type|, |serialization internal map|,
2130+
[=serialize as a remote value=] with |shadow root|,
2131+
|serialization options|, |ownership type|, |serialization internal map|,
21162132
|realm|, and |session|.
21172133

21182134
1. Set |serialized|["<code>shadowRoot</code>"] to |serialized shadow|.
21192135

2136+
1. If |value| implements {{ShadowRoot}}, set |serialized|["<code>mode</code>"]
2137+
to |value|'s <a spec=dom for=ShadowRoot>mode</a>.
2138+
21202139
1. If |serialized| is not null, set field <code>value</code> of |remote value| to
21212140
|serialized|.
21222141

@@ -2148,11 +2167,12 @@ an |ownership type|, a |serialization internal map|, a |realm| and a |session|:
21482167

21492168
1. Let |serialized| be null.
21502169

2151-
1. If |known object| is <code>false</code>, and |max depth| is not 0, run
2152-
the following steps:
2170+
1. If |known object| is <code>false</code>, and |serialization
2171+
options|["<code>maxObjectDepth</code>"] is not 0, run the following
2172+
steps:
21532173

21542174
1. Let |serialized| be the result of [=serialize as a mapping=] with
2155-
[=EnumerableOwnPropertyNames=](|value|, key+value), |max depth|,
2175+
[=EnumerableOwnPropertyNames=](|value|, key+value), |serialization options|,
21562176
|ownership type|, |serialization internal map|,
21572177
|realm|, and |session|.
21582178

@@ -2179,7 +2199,8 @@ remove optional flag from the field.
21792199

21802200
<div algorithm>
21812201
To <dfn>serialize an Array-like</dfn> given |production|, |handle id|, |known object|,
2182-
|value|, |max depth|, |ownership type|, |serialization internal map|, |realm|, and |session|:
2202+
|value|, |serialization options|, |ownership type|, |serialization internal map|, |realm|,
2203+
and |session|:
21832204

21842205
1. Let |remote value| be a [=/map=] matching |production|, with the
21852206
<code>handle</code> property set to |handle id| if it's not null, or omitted
@@ -2188,10 +2209,11 @@ To <dfn>serialize an Array-like</dfn> given |production|, |handle id|, |known ob
21882209
1. [=Set internal ids if needed=] with |serialization internal map|,
21892210
|remote value| and |value|.
21902211

2191-
1. If |known object| is <code>false</code>, and |max depth| is not 0:
2212+
1. If |known object| is <code>false</code>, and |serialization
2213+
options|["<code>maxObjectDepth</code>"]| is not 0:
21922214

21932215
1. Let |serialized| be the result of [=serialize as a list=] with
2194-
[=CreateArrayIterator=](|value|, value), |max depth|, |ownership type|,
2216+
[=CreateArrayIterator=](|value|, value), |serialization options|, |ownership type|,
21952217
|serialization internal map|, |realm|, and |session|.
21962218

21972219
1. If |serialized| is not null, set field <code>value</code> of |remote value| to
@@ -2202,31 +2224,43 @@ To <dfn>serialize an Array-like</dfn> given |production|, |handle id|, |known ob
22022224
</div>
22032225

22042226
<div algorithm>
2205-
To <dfn>serialize as a list</dfn> given |iterable|, |max depth|, |ownership type|,
2227+
To <dfn>serialize as a list</dfn> given |iterable|, |serialization options|, |ownership type|,
22062228
|serialization internal map|, |realm|, and |session|:
22072229

2208-
1. Let |serialized| be a new list.
2230+
1. Assert: |serialization options|["<code>maxObjectDepth</code>"] is greater
2231+
than 0.
2232+
2233+
1. Let |serialized| be a new list.
22092234

2210-
1. For each |child value| in |iterable|:
2235+
1. For each |child value| in |iterable|:
22112236

2212-
1. Let |child depth| be |max depth| - 1 if |max depth| is not null, or null
2213-
otherwise.
2237+
1. Let |child serialization options| be a [=map/clone=] of
2238+
|serialization options|.
22142239

2215-
1. Let |serialized child| be the result of [=serialize as a remote value=]
2216-
with |child value|, |child depth|, |ownership type|,
2217-
|serialization internal map|, |realm|, and |session|.
2240+
1. If |child serialization options|["<code>maxObjectDepth</code>"] is
2241+
not null, set |child serialization
2242+
options|["<code>maxObjectDepth</code>"] to |child serialization
2243+
options|["<code>maxObjectDepth</code>"] - 1.
22182244

2219-
1. Append |serialized child| to |serialized|.
2245+
1. Let |serialized child| be the result of [=serialize as a remote value=]
2246+
with |child value|, |child serialization options|, |ownership type|,
2247+
|serialization internal map|, |realm|, and |session|.
2248+
2249+
1. Append |serialized child| to |serialized|.
2250+
2251+
1. Return |serialized|
22202252

2221-
1. Return |serialized|
22222253
</div>
22232254

22242255
Issue: this assumes for-in works on iterators
22252256

22262257
<div algorithm>
2227-
To <dfn>serialize as a mapping</dfn> given |iterable|, |max depth|,
2258+
To <dfn>serialize as a mapping</dfn> given |iterable|, |serialization options|,
22282259
|ownership type|, |serialization internal map|, |realm|, and |session|:
22292260

2261+
1. Assert: |serialization options|["<code>maxObjectDepth</code>"] is greater
2262+
than 0.
2263+
22302264
1. Let |serialized| be a new list.
22312265

22322266
1. For |item| in |iterable|:
@@ -2239,16 +2273,21 @@ To <dfn>serialize as a mapping</dfn> given |iterable|, |max depth|,
22392273

22402274
1. Let |key| be |property|[0] and let |value| be |property|[1]
22412275

2242-
1. Let |child depth| be |max depth| - 1 if |max depth| is not null, or null
2243-
otherwise.
2276+
1. Let |child serialization options| be a [=map/clone=] of
2277+
|serialization options|.
2278+
2279+
1. If |child serialization options|["<code>maxObjectDepth</code>"] is
2280+
not null, set |child serialization
2281+
options|["<code>maxObjectDepth</code>"] to |child serialization
2282+
options|["<code>maxObjectDepth</code>"] - 1.
22442283

22452284
1. If [=Type=](|key|) is String, let |serialized key| be |child key|,
22462285
otherwise let |serialized key| be the result of [=serialize as a remote value=]
2247-
with |child key|, |child depth|, |ownership type|,
2286+
with |child key|, |child serialization options|, |ownership type|,
22482287
|serialization internal map|, |realm|, and |session|.
22492288

22502289
1. Let |serialized value| be the result of [=serialize as a remote value=]
2251-
with |value|, |child depth|, |ownership type|,
2290+
with |value|, |child serialization options|, |ownership type|,
22522291
|serialization internal map|, |realm|, and |session|.
22532292

22542293
1. Let |serialized child| be («|serialized key|, |serialized value|»).
@@ -4237,8 +4276,12 @@ To <dfn>get exception details</dfn> given a |realm|, a [=completion record=]
42374276
TODO: Tighten up the requirements here; people will probably try to parse
42384277
this data with regex or something equally bad.
42394278

4279+
1. Let |serialization options| be a [=/map=] matching the
4280+
<code>script.SerializationOptions</code> production with the fields set to
4281+
their default values.
4282+
42404283
1. Let |exception| be the result of [=serialize as a remote value=] with
4241-
|record|.\[[Value]], <code>1</code> as max depth, |ownership type|,
4284+
|record|.\[[Value]], |serialization options|, |ownership type|,
42424285
a new [=/map=] as serialization internal map, |realm| and |session|.
42434286

42444287
1. Let |stack trace| be the [=stack trace for an exception=] given |record|.
@@ -4636,6 +4679,21 @@ script.ResultOwnership = "root" / "none"
46364679
The <code>script.ResultOwnership</code> specifies how the serialized value
46374680
ownership will be treated.
46384681

4682+
#### The script.SerializationOptions type #### {#type-script-SerializationOptions}
4683+
4684+
[=Remote end definition=]
4685+
4686+
<pre class="cddl remote-cddl">
4687+
script.SerializationOptions = {
4688+
?maxDomDepth: (js-uint / null) .default 0,
4689+
?maxObjectDepth: (js-uint / null) .default null,
4690+
?includeShadowTree: ("none" / "open" / "all") .default "none",
4691+
}
4692+
</pre>
4693+
4694+
The <code>script.SerializationOptions</code> allows specifying how ECMAScript
4695+
objects will be serialized.
4696+
46394697
#### The script.Source type #### {#type-script-Source}
46404698

46414699
[=Local end definition=]
@@ -4860,12 +4918,13 @@ Note: In case of an arrow function in <code>functionDeclaration</code>, the
48604918
}
48614919

48624920
script.CallFunctionParameters = {
4863-
functionDeclaration: text;
4864-
awaitPromise: bool;
4865-
target: script.Target;
4866-
?arguments: [*script.ArgumentValue];
4867-
?this: script.ArgumentValue;
4868-
?resultOwnership: script.ResultOwnership;
4921+
functionDeclaration: text,
4922+
awaitPromise: bool,
4923+
target: script.Target,
4924+
?arguments: [*script.ArgumentValue],
4925+
?resultOwnership: script.ResultOwnership,
4926+
?serializationOptions: script.SerializationOptions
4927+
?this: script.ArgumentValue,
48694928
}
48704929

48714930
script.ArgumentValue = (
@@ -4958,6 +5017,11 @@ The [=remote end steps=] with |session| and |command parameters| are:
49585017
1. Let |await promise| be the value of the <code>awaitPromise</code> field of
49595018
|command parameters|.
49605019

5020+
1. Let |serialization options| be the value of the
5021+
<code>serializationOptions</code> field of |command parameters|, if present,
5022+
or otherwise a [=/map=] matching the <code>script.SerializationOptions</code>
5023+
production with the fields set to their default values.
5024+
49615025
1. Let |result ownership| be the value of the <code>resultOwnership</code> field of
49625026
|command parameters|, if present, or <code>none</code> otherwise.
49635027

@@ -5004,7 +5068,7 @@ The [=remote end steps=] with |session| and |command parameters| are:
50045068
1. Assert: |evaluation status|.\[[Type]] is <code>normal</code>.
50055069

50065070
1. Let |result| be the result of [=serialize as a remote value=] with
5007-
|evaluation status|.\[[Value]], <code>1</code> as |max depth|, |result ownership|,
5071+
|evaluation status|.\[[Value]], |serialization options|, |result ownership|,
50085072
a new [=/map=] as serialization internal map, |realm| and |session|.
50095073

50105074
1. Return a new [=/map=] matching the <code>script.EvaluateResultSuccess</code>
@@ -5034,10 +5098,11 @@ the promise is returned.
50345098
}
50355099

50365100
script.EvaluateParameters = {
5037-
expression: text;
5038-
target: script.Target;
5039-
awaitPromise: bool;
5040-
?resultOwnership: script.ResultOwnership;
5101+
expression: text,
5102+
target: script.Target,
5103+
awaitPromise: bool,
5104+
?resultOwnership: script.ResultOwnership,
5105+
?serializationOptions: script.SerializationOptions,
50415106
}
50425107
</pre>
50435108
</dd>
@@ -5069,6 +5134,11 @@ The [=remote end steps=] with |session| and |command parameters| are:
50695134
1. Let |await promise| be the value of the <code>awaitPromise</code> field of
50705135
|command parameters|.
50715136

5137+
1. Let |serialization options| be the value of the
5138+
<code>serializationOptions</code> field of |command parameters|, if present,
5139+
or otherwise a [=/map=] matching the <code>script.SerializationOptions</code>
5140+
production with the fields set to their default values.
5141+
50725142
1. Let |result ownership| be the value of the <code>resultOwnership</code> field of
50735143
|command parameters|, if present, or <code>none</code> otherwise.
50745144

@@ -5102,7 +5172,7 @@ The [=remote end steps=] with |session| and |command parameters| are:
51025172
1. Assert: |evaluation status|.\[[Type]] is <code>normal</code>.
51035173

51045174
1. Let |result| be the result of [=serialize as a remote value=] with
5105-
|evaluation status|.\[[Value]], <code>1</code> as |max depth|, |result ownership|,
5175+
|evaluation status|.\[[Value]], |serialization options|, |result ownership|,
51065176
a new [=/map=] as serialization internal map, |realm| and |session|.
51075177

51085178
1. Return a new [=/map=] matching the <code>script.EvaluateResultSuccess</code>
@@ -5575,10 +5645,14 @@ Define the following [=console steps=] with |method|, |args|, and
55755645

55765646
1. Let |serialized args| be a new list.
55775647

5648+
1. Let |serialization options| be a [=/map=] matching the
5649+
<code>script.SerializationOptions</code> production with the fields set to
5650+
their default values.
5651+
55785652
1. For each |arg| of |args|:
55795653

55805654
1. Let |serialized arg| be the result of [=serialize as a remote value=] with
5581-
|arg| as value, <code>1</code> as max depth, <code>none</code> as
5655+
|arg| as value, |serialization options|, <code>none</code> as
55825656
ownership type, a new [=/map=] as serialization internal map, |realm| and
55835657
|session|.
55845658

0 commit comments

Comments
 (0)