forked from w3c/webdriver-bidi
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.bs
4554 lines (3222 loc) · 149 KB
/
index.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<pre class=metadata>
Title: WebDriver BiDi
Shortname: webdriver-bidi
Level: 1
Status: ED
Group: browser-testing-tools
URL: https://w3c.github.io/webdriver-bidi/
Repository: w3c/webdriver-bidi
No Editor: true
Abstract: This document defines the BiDirectional WebDriver Protocol, a mechanism for remote control of user agents.
Boilerplate: conformance no
Complain About: accidental-2119 yes, missing-example-ids yes
Default Ref Status: current
Indent: 2
</pre>
<pre class=anchors>
spec: RFC6455; urlPrefix: https://tools.ietf.org/html/rfc6455
type: dfn
text: WebSocket URI; url: section-3
text: Establishes a WebSocket Connection; url: section-4.1
text: Server-Side Requirements; url: section-4.2
text: Reading the Client's Opening Handshake; url: section-4.2.1
text: %x1 denotes a text frame; url: section-5.2
text: Send a WebSocket Message; url: section-6.1
text: A WebSocket Message Has Been Received; url: section-6.2
text: The WebSocket Closing Handshake is Started; url: section-7.1.3
text: The WebSocket Connection is Closed; url: section-7.1.4
text: Fail the WebSocket Connection; url: section-7.1.7
text: Status Codes; url: section-7.4
text: Handling Errors in UTF-8-Encoded Data; url: section-8.1
spec: RFC8610; urlPrefix: https://tools.ietf.org/html/rfc8610
type: dfn
text: match a CDDL specification; url: appendix-C
spec: WEBDRIVER; urlPrefix: https://w3c.github.io/webdriver/
type: dfn
text: WebDriver new session algorithm; url: dfn-webdriver-new-session-algorithm
text: active sessions; url: dfn-active-session
text: additional WebDriver capability; url: dfn-additional-webdriver-capability
text: additional capability deserialization algorithm; url: dfn-additional-capability-deserialization-algorithm
text: capability name; url: dfn-capability-name
text: create a session; url: dfn-create-a-session
text: endpoint node; url: dfn-endpoint-node
text: error code; url: dfn-error-code
text: error; url: dfn-errors
text: getting a property; url: dfn-get-a-property
text: http session; url: dfn-http-session
text: intermediary node; url: dfn-intermediary-node
text: invalid argument; url: dfn-invalid-argument
text: invalid session id; url: dfn-invalid-session-id
text: local end; url: dfn-local-ends
text: matched capability serialization algorithm; url: dfn-matched-capability-serialization-algorithm
text: maximum active sessions; url: dfn-maximum-active-sessions
text: no such element; url: dfn-no-such-element
text: no such frame; url: dfn-no-such-frame
text: process capabilities; url: dfn-processing-capabilities
text: readiness state; url: dfn-readiness-state
text: remote end steps; url: dfn-remote-end-steps
text: remote end; url: dfn-remote-ends
text: session ID; url: dfn-session-id
text: session not created; url: dfn-session-not-created
text: session; url: dfn-sessions
text: set a property; url: dfn-set-a-property
text: success; url: dfn-success
text: try; url: dfn-try
text: trying; url: dfn-try
text: unknown command; url: dfn-unknown-command
text: unknown error; url: dfn-unknown-error
text: web element reference; url: dfn-web-element-reference
text: webdriver-active flag; url: dfn-webdriver-active-flag
text: window handle; url: dfn-window-handle
spec: CONSOLE; urlPrefix: https://console.spec.whatwg.org
type: dfn
text: formatter; url: formatter
text: formatting specifier; url: formatting-specifiers
text: printer; url: printer
spec: ECMASCRIPT; urlPrefix: https://tc39.es/ecma262/
type: dfn
text: Array; url: sec-array-objects
text: Await; url: await
text: BigInt; url: sec-bigint-constructor
text: boolean; url: sec-terms-and-definitions-boolean-value
text: Call; url: sec-call
text: Completion Record; url: sec-completion-record-specification-type
text: CreateArrayFromList; url: sec-createarrayfromlist
text: CreateArrayIterator; url: sec-createarrayiterator
text: CreateListFromArrayLike; url: sec-createlistfromarraylike
text: CreateMapIterator; url: sec-createmapiterator
text: CreateSetIterator; url: sec-createsetiterator
text: Date Time String Format; url: sec-date-time-string-format
text: Date.ToDateString; url: sec-todatestring
text: Date.parse; url: sec-date.parse
text: EnumerableOwnPropertyNames; url: sec-enumerableownpropertynames
text: Get; url: sec-get
text: HasProperty; url: sec-hasproperty
text: IsArray; url: sec-isarray
text: IsCallable; url: sec-iscallable
text: IsPromise; url: sec-ispromise
text: IsRegExp; url: sec-isregexp
text: LengthOfArrayLike; url: sec-lengthofarraylike
text: Map; url: sec-map-objects
text: Number; url: sec-number-constructor
text: null; url: sec-null-value
text: Object; url: sec-object-objects
text: Object.fromEntries; url: sec-object.fromentries
text: ScriptEvaluation; url: sec-runtime-semantics-scriptevaluation
text: Set object; url: sec-set-objects
text: String; url: sec-string-constructor
text: StringToBigInt; url: sec-stringtobigint
text: StringToNumber; url: sec-stringtonumber
text: RegExp; url: sec-regexp-pattern-flags
text: ThisTimeValue; url: thistimevalue
text: ToString; url: sec-tostring
text: Type; url: sec-ecmascript-data-types-and-values
text: undefined; url: sec-undefined-value
text: current realm record; url: current-realm
text: internal slot; url: sec-object-internal-methods-and-internal-slots
text: primitive ECMAScript value; url: sec-primitive-value
text: realm; url: sec-code-realms
text: running execution context; url: running-execution-context
text: time value; url: sec-time-values-and-time-range
spec: HTML; urlPrefix: https://html.spec.whatwg.org/multipage/
type: dfn
text: a browsing context is discarded; url: window-object.html#a-browsing-context-is-discarded
text: close; url: window-object.html#close-a-browsing-context
text: create a classic script; url: webappapis.html#creating-a-classic-script
text: create a new browsing context; url: browsers.html#creating-a-new-browsing-context
text: default classic script fetch options; url: webappapis.html#default-classic-script-fetch-options
text: environment settings object's Realm; url: webappapis.html#environment-settings-object's-realm
text: handled; url: webappapis.html#concept-error-handled
text: history handling behavior; url: browsing-the-web.html#history-handling-behavior
text: navigation id; url: browsing-the-web.html#navigation-id
text: report an error; url: webappapis.html#report-the-error
text: remove a browsing context; url: browsers.html#bcg-remove
text: session history; url: history.html#session-history
text: set up a window environment settings object; url: window-object.html#set-up-a-window-environment-settings-object
text: set up a worker environment settings object; url: workers.html#set-up-a-worker-environment-settings-object
text: set up a worklet environment settings object; url: worklets.html#set-up-a-worklet-environment-settings-object
text: window open steps; url: multipage/window-object.html#window-open-steps
text: worker event loop; url: webappapis.html#worker-event-loop-2
text: worklet global scopes; url: worklets.html#concept-document-worklet-global-scopes
</pre>
<pre class="link-defaults">
spec:infra; type:dfn; for:/; text:set
</pre>
<style>
var {
color: #cd5c5c
}
</style>
# Introduction # {#intro}
<em>This section is non-normative.</em>
[[WEBDRIVER|WebDriver]] defines a protocol for introspection and
remote control of user agents. This specification extends WebDriver by
introducing bidirectional communication. In place of the strict
command/response format of WebDriver, this permits events to stream
from the user agent to the controlling software, better matching the
evented nature of the browser DOM.
# Infrastructure # {#infrastructure}
This specification depends on the Infra Standard. [[!INFRA]]
Network protocol messages are defined using CDDL. [[!RFC8610]]
This specification defines a <dfn>wait queue</dfn> which is a map.
Issue: Surely there's a better mechanism for doing this "wait for an event" thing.
<div algorithm>
When an algorithm |algorithm| running [=in parallel=] <dfn>awaits</dfn> a set of
events |events|, and |resume id|:
1. Pause the execution of |algorithm|.
1. Assert: [=wait queue=] does not contain |resume id|.
1. Set [=wait queue=][|resume id|] to (|events|, |algorithm|).
</div>
<div algorithm>
To <dfn>resume</dfn> given |name|, |id| and |parameters|:
1. If [=wait queue=] does not contain |id|, return.
1. Let (|events|, |algorithm|) be [=wait queue=][|id|]
1. For each |event| in |events|:
1. If |event| equals |name|:
1. Remove |id| from [=wait queue=].
1. Resume running the steps in |algorithm| from the
point at which they were paused, passing |name| and |parameters| as the
result of the [=await=].
Issue: Should we have something like microtasks to ensure this runs
before any other tasks on the event loop?
</div>
# Protocol # {#protocol}
This section defines the basic concepts of the WebDriver BiDi
protocol. These terms are distinct from their representation at the
<a href=#transport>transport</a> layer.
The protocol is defined using a [[!RFC8610|CDDL]] definition. For the
convenience of implementors two seperate CDDL definitions are defined; the
<dfn>remote end definition</dfn> which defines the format of messages produced
on the [=local end=] and consumed on the [=remote end=], and the <dfn>local end
definition</dfn> which defines the format of messages produced on the [=remote
end=] and consumed on the [=local end=]
## Definition ## {#protocol-definition}
Issue: Should this be an appendix?
This section gives the initial contents of the [=remote end definition=] and
[=local end definition=]. These are augmented by the definition fragments defined in
the remainder of the specification.
[=Remote end definition=]
<pre class="cddl remote-cddl">
Command = {
id: uint,
CommandData,
*text => any,
}
CommandData = (
SessionCommand //
BrowsingContextCommand
)
EmptyParams = { *text }
</pre>
[=Local end definition=]
<pre class="cddl local-cddl">
Message = (
CommandResponse //
ErrorResponse //
Event
)
CommandResponse = {
id: uint,
result: ResultData,
*text => any
}
ErrorResponse = {
id: uint / null,
error: "unknown error" / "unknown command" / "invalid argument",
message: text,
?stacktrace: text,
*text => any
}
ResultData = (
EmptyResult //
SessionResult //
BrowsingContextResult //
ScriptResult
)
EmptyResult = {}
Event = {
EventData,
*text => any
}
EventData = (
BrowsingContextEvent //
ScriptEvent
)
</pre>
## Session ## {#session}
WebDriver BiDi extends the [=/session=] concept from [[WEBDRIVER|WebDriver]].
A [=/session=] has a <dfn>BiDi flag</dfn>, which is false unless otherwise
stated.
A <dfn>BiDi session</dfn> is a [=/session=] which has the [=BiDi flag=]
set to true.
<div algorithm>
The set of <dfn>active BiDi sessions</dfn> is given by:
1. Let |BiDi sessions| be a new set.
1. For each |session| in [=active sessions=]:
1. If |session| is a [=BiDi session=] append |session| to |BiDi sessions|.
1. Return |BiDi sessions|
</div>
## Modules ## {#protocol-modules}
The WebDriver BiDi protocol is organized into modules.
Each <dfn export>module</dfn> represents a collection of related
[=commands=] and [=events=] pertaining to a certain aspect of the user
agent. For example, a module might contain functionality for inspecting and
manipulating the DOM, or for script execution.
Each module has a <dfn>module name</dfn> which is a string. The
[=command name=] and [=event name=] for commands and events defined in the
module start with the [=module name=] followed by a period "<code>.</code>".
Modules which contain [=commands=] define [=remote end definition=]
fragments. These provide choices in the <code>CommandData</code> group for the
module's [=commands=], and can also define additional definition properties. They
can also define [=local end definition=] fragments that provide additional choices
in the <code>ResultData</code> group for the results of commands in the module.
Modules which contain events define [=local end definition=] fragments that are
choices in the <code>Event</code> group for the module's [=events=].
An implementation may define <dfn>extension modules</dfn>. These must have a
[=module name=] that contains a single colon "<code>:</code>" character. The
part before the colon is the prefix; this is typically the same for all
extension modules specific to a given implementation and should be unique for a
given implementation. Such modules extend the [=local end definition=] and [=remote
end definition=] providing additional groups as choices for the defined
[=commands=] and [=events=].
## Commands ## {#commands}
A <dfn export>command</dfn> is an asynchronous operation, requested by
the [=local end=] and run on the [=remote end=], resulting in either a
result or an error being returned to the [=local end=]. Multiple
commands can run at the same time, and commands can potentially be
long-running. As a consequence, commands can finish out-of-order.
Each [=command=] is defined by:
- A <dfn export for=command>command type</dfn> which is defined by a [=remote
end definition=] fragment containing a group. Each such group has two fields:
- <code>method</code> which is a string literal of the form <code>[module
name].[method name]</code>. This is the <dfn export for=command>command
name</dfn>.
- <code>params</code> which defines a mapping containing data that to be passed into
the command. The populated value of this map is the
<dfn export for=command>command parameters</dfn>.
- A <dfn export for=command>result type</dfn>, which is defined by a [=local
end definition=] fragment.
- A set of [=remote end steps=] which define the actions to take for a command
given a [=BiDi session=] and [=command parameters=] and return an
instance of the command [=return type=].
A command that can run without an active session is a <dfn>static
command</dfn>. Commands are not static commands unless stated in their
definition.
When commands are send from the [=local end=] they have a command id. This is an
identifier used by the [=local end=] to identify the response from a particular
command. From the point of view of the [=remote end=] this identifier is opaque
and cannot be used internally to identify the command.
Note: This is because the command id is entirely controlled by the [=local end=]
and isn't necessarily unique over the course of a session. For example a [=local
end=] which ignores all responses could use the same command id for each command.
The <dfn export for=command>set of all command names</dfn> is a set containing
all the defined [=command names=], including any belonging to [=extension
modules=].
## Events ## {#events}
An <dfn export>event</dfn> is a notification, sent by the [=remote
end=] to the [=local end=], signaling that something of interest has
occurred on the [=remote end=].
- An <dfn export for=event>event type</dfn> is defined by a [=local
end definition=] fragment containing a group. Each such group has two fields:
- <code>method</code> which is a string literal of the form <code>[module
name].[event name]</code>. This is the <dfn export for=event>event
name</dfn>.
- <code>params</code> which defines a mapping containing event data. The
populated value of this map is the <dfn export for=command>event
parameters</code>.
- A <dfn export>remote end event trigger</dfn> which defines when the event is
triggered and steps to construct the [=event type=] data.
- Optionally, a set of <dfn>remote end subscribe steps</dfn>, which define
steps to take when a local end subscribes to an event. Where defined these
steps have an associated <dfn>subscribe priority</dfn> which is an integer
controlling the order in which the steps are run when multiple events are
enabled at once, with lower integers indicating steps that run earlier.
A [=BiDi session=] has a <dfn export for=event>global event set</dfn>
which is a set containing the event names for events that are enabled for all
browsing contexts. This initially contains the [=event name=] for events that
are <dfn export for=event>in the default event set</dfn>.
A [=BiDi session=] has a <dfn export for=event>browsing context event
map</dfn>, which is a map with [=/top-level browsing context=] keys and values
that are a set of [=event name=]s for events that are enabled in the given
browsing context.
<div algorithm>
To obtain a list of <dfn>event enabled browsing contexts</dfn> given
|session| and |event name|:
1. Let |contexts| be an empty set.
1. For each |context| → |events| of |session|'s [=browsing context event map=]:
1. If |events| contains |event name|, append |context| to |contexts|
1. Return |contexts|.
</div>
<div algorithm>
The <dfn>set of sessions for which an event is enabled</dfn> given |event name| and
|browsing contexts| is:
1. Let |sessions| be a new set.
1. For each |session| in [=active BiDI sessions=]:
1. If [=event is enabled=] with |session|, |event name| and |browsing
contexts|, append |session| to |sessions|.
1. Return |sessions|
</div>
<div algorithm>
To determine if an <dfn>event is enabled</dfn> given |session|,
|event name| and |browsing contexts|:
Note: |browsing contexts| is a set because a [=shared worker=] can be associated
with multiple contexts.
1. Let |top-level browsing contexts| be an empty set.
1. For each |browsing context| of |browsing contexts|, append |browsing
context|'s [=top-level browsing context=] to |top-level browsing contexts|.
1. Let |event map| be the [=browsing context event map=] for |session|.
1. For each |browsing context| of |top-level browsing contexts|:
1. If |event map| [=contains=] |browsing context|, let |browsing context
events| be |event map|[|browsing context|]. Otherwise let |browsing
context events| be null.
1. If |browsing context events| is not null, and |browsing context events|
[=contains=] |event name|, return true.
1. If the [=global event set=] for |session| [=contains=] |event name| return
true.
1. Return false.
</div>
<div algorithm>
To <dfn>obtain a set of event names</dfn> given an |name|:
1. Let |events| be an empty set.
1. If |name| contains a U+002E (period):
1. If |name| is the [=event name=] for an event, append |name| to |events|
and return [=success=] with data |events|.
1. Return an [=error=] with [=error code=] [=Invalid Argument=]
1. Otherwise |name| is interpreted as representing all the events in a
module. If |name| is not a [=module name=] return an [=error=] with
[=error code=] [=Invalid Argument=].
1. Append the [=event name=] for each [=event=] in the module with name |name| to
|events|.
1. Return [=success=] with data |events|.
</div>
# Transport # {#transport}
Message transport is provided using the WebSocket protocol.
[[!RFC6455]]
Note: In the terms of the WebSocket protocol, the [=local end=] is the
client and the [=remote end=] is the server / remote host.
Note: The encoding of [=commands=] and [=events=] as messages is
similar to JSON-RPC, but this specification does not normatively
reference it. [[JSON-RPC]] The normative requirements on [=remote
ends=] are instead given as a precise processing model, while no
normative requirements are given for [=local ends=].
A <dfn>WebSocket listener</dfn> is a network endpoint that is able
to accept incoming [[!RFC6455|WebSocket]] connections.
A [=WebSocket listener=] has a <dfn for=listener>host</dfn>, a <dfn
for=listener>port</dfn>, a <dfn for=listener>secure flag</dfn>, and a
<dfn>list of WebSocket resources</dfn>.
When a [=WebSocket listener=] |listener| is created, a [=remote end=]
must start to listen for WebSocket connections on the host and port
given by |listener|'s [=listener/host=] and [=listener/port=]. If
|listener|'s [=listener/secure flag=] is set, then connections
established from |listener| must be TLS encrypted.
A [=remote end=] has a [=set=] of [=WebSocket listeners=] <dfn>active
listeners</dfn>, which is initially empty.
A [=remote end=] has a [=set=] of <dfn>WebSocket connections not associated with a
session</dfn>, which is initially empty.
A <dfn>WebSocket connection</dfn> is a network connection that follows the
requirements of the [[!RFC6455|WebSocket protocol]]
A [=BiDi session=] has a set of <dfn>session WebSocket
connections</dfn> whose elements are [=WebSocket connections=]. This is
initially empty.
A [=BiDi session=] |session| is <dfn>associated with connection</dfn>
|connection| if |session|'s [=session WebSocket connections=] contains |connection|.
Note: Each [=WebSocket connection=] is associated with at most one [=BiDi
session=].
<div>
When a client [=establishes a WebSocket connection=] |connection| by
connecting to one of the set of [=active listeners=] |listener|, the
implementation must proceed according to the WebSocket [=server-side
requirements=], with the following steps run when deciding whether to
accept the incoming connection:
1. Let |resource name| be the resource name from [=reading the
client's opening handshake=]. If |resource name| is not in
|listener|'s [=list of WebSocket resources=], then stop
running these steps and act as if the requested service is not
available.
1. If |resource name| is the byte string "<code>/session</code>",
and the implementation [=supports BiDi-only sessions=]:
1. Run any other implementation-defined steps to decide if the
connection should be accepted, and if it is not stop running these
steps and act as if the requested service is not available.
1. Add the connection to the set of [=WebSocket connections not associated
with a session=].
1. Return.
1. [=Get a session ID for a WebSocket resource=] with |resource name|
and let |session id| be that value. If |session id| is null then
stop running these steps and act as if the requested service is not
available.
1. If there is a [=/session=] in the list of [=active sessions=] with
|session id| as its [=session ID=] then let |session| be that
session. Otherwise stop running these steps and act as if the
requested service is not available.
1. Run any other implementation-defined steps to decide if the
connection should be accepted, and if it is not stop running these
steps and act as if the requested service is not available.
1. Otherwise append |connection| to |session|'s [=session WebSocket
connections=], and proceed with the WebSocket [=server-side requirements=]
when a server chooses to accept an incoming connection.
Issue: Do we support > 1 connection for a single session?
</div>
When [=a WebSocket message has been received=] for a [=WebSocket
connection=] |connection| with type |type| and data |data|, a [=remote
end=] must [=handle an incoming message=] given |connection|, |type|
and |data|.
When [=the WebSocket closing handshake is started=] or when [=the
WebSocket connection is closed=] for a [=WebSocket connection=]
|connection|, a [=remote end=] must [=handle a connection closing=]
given |connection|.
Note: Both conditions are needed because it is possible for a
WebSocket connection to be closed without a closing handshake.
<div algorithm>
To <dfn lt="construct a WebSocket resource name|constructing a
WebSocket resource name">construct a WebSocket resource name</dfn>
given a [=/session=] |session|:
1. If |session| is null, return "<code>/session</code>"
1. Return the result of concatenating the string "<code>/session/</code>"
with |session|'s [=session ID=].
</div>
<div algorithm>
To <dfn lt="construct a WebSocket URL|constructing a WebSocket
URL">construct a WebSocket URL</dfn> given a [=WebSocket listener=]
|listener| and [=/session=] |session|:
1. Let |resource name| be the result of [=constructing a WebSocket
resource name=] given |session|.
1. Return a [=WebSocket URI=] constructed with host set to
|listener|'s [=listener/host=], port set to |listener|'s
[=listener/port=], path set to |resource name|, following the wss-URI
construct if |listener|'s [=listener/secure flag=] is set and the ws-URL
construct otherwise.
</div>
<div algorithm>
To <dfn>get a session ID for a WebSocket resource</dfn>
given |resource name|:
1. If |resource name| doesn't begin with the byte string
"<code>/session/</code>", return null.
1. Let |session id| be the bytes in |resource name| following the
"<code>/session/</code>" prefix.
1. If |session id| is not the string representation of a
[[!RFC4122|UUID]], return null.
1. Return |session id|.
</div>
<div algorithm>
To <dfn>start listening for a WebSocket connection</dfn> given a
[=/session=] |session|:
1. If there is an existing [=WebSocket listener=] in the set of
[=active listeners=] which the [=remote end=] would like to reuse,
let |listener| be that listener. Otherwise let |listener| be a new
[=WebSocket listener=] with [=implementation-defined=]
[=listener/host=], [=listener/port=], [=listener/secure flag=],
and an empty [=list of WebSocket resources=].
1. Let |resource name| be the result of [=constructing a WebSocket
resource name=] given |session|.
1. Append |resource name| to the [=list of WebSocket resources=] for
|listener|.
1. [=set/Append=] |listener| to the [=remote end=]'s [=active
listeners=].
1. Return |listener|.
</div>
Note: An [=intermediary node=] handling multiple sessions can use one
or many WebSocket listeners. [[!WEBDRIVER|WebDriver]] defines that
an [=endpoint node=] supports at most one session at a time, so it's
expected to only have a single listener.
Note: For an [=endpoint node=] the [=listener/host=] in the above steps will
typically be "<code>localhost</code>".
<div algorithm>
To <dfn>handle an incoming message</dfn> given a [=WebSocket connection=]
|connection|, type |type| and data |data|:
1. If |type| is not [=%x1 denotes a text frame|text=], [=respond with an
error=] given |connection|, null, and [=invalid argument=], and finally
return.
1. [=Assert=]: |data| is a [=scalar value string=], because the
WebSocket [=handling errors in UTF-8-encoded data=] would already
have [=fail the WebSocket connection|failed the WebSocket
connection=] otherwise.
Issue: Nothing seems to define what [=status codes|status code=]
is used for UTF-8 errors.
1. If there is a [=BiDi Session=] [=associated with connection=] |connection|,
let |session| be that session. Otherwise if |connection| is in the set of
[=WebSocket connections not associated with a session=], let |session| be
null. Otherwise, return.
1. Let |parsed| be the result of [=parse JSON into Infra values|parsing JSON
into Infra values=] given |data|. If this throws an exception, then [=respond
with an error=] given |connection|, null, and [=invalid argument=], and
finally return.
1. Match |parsed| against the [=remote end definition=]. If this results in a
match:
1. Let |matched| be the map representing the matched data.
1. Assert: |matched| [=contains=] "<code>id</code>", "<code>method</code>", and
"<code>params</code>".
1. Let |command id| be |matched|["<code>id</code>"].
1. Let |method| be |matched|["<code>method</code>"]
1. Let |command| be the command with [=command name=] |method|.
1. If |session| is null and |command| is not a [=static command=], then
[=respond with an error=] given |connection|, |command id|, and [=invalid
session id=], and return.
1. Run the following steps in parallel:
1. Let |result| be the result of running the [=remote end steps=] for
|command| given |session| and [=command parameters=]
|matched|["<code>params</code>"]
1. If |result| is an [=error=], then [=respond with an error=] given
|connection|, |command id|, and |result|'s [=error code=], and finally
return.
1. Let |value| be |result|'s data.
1. Assert: |value| matches the definition for the [=result type=]
corresponding to the command with [=command name=] |method|.
1. If |method| is "<code>session.new</code>", let |session| be the entry in
the list of [=active sessions=] whose [=session ID=] is equal to the
"<code>sessionId</code>" property of |value|, let |session|'s
[=WebSocket connection=] be |connection|, and remove |connection| from
the set of [=WebSocket connections not associated with a session=].
1. Let |response| be a new map matching the <code>CommandResponse</code>
production in the [=local end definition=] with the <code>id</code>
field set to |command id| and the <code>value</code> field set to
|value|.
1. Let |serialized| be the result of [=serialize an infra value to JSON
bytes=] given |response|.
1. [=Send a WebSocket message=] comprised of |serialized| over
|connection|.
1. Otherwise:
1. Let |command id| be null.
1. If |parsed| is a map and |parsed|["<code>id</code>"] exists and is an
integer greater than or equal to zero, set |command id| to that integer.
1. Let |error code| be [=invalid argument=].
1. If |parsed| is a map and |parsed|["<code>method</code>"] exists and is a
string, but |parsed|["<code>method</code>"] is not in the [=set of all
command names=], set |error code| to [=unknown command=].
1. [=Respond with an error=] given |connection|, |command id|, and
|error code|.
</div>
<div algorithm>
To <dfn>get related browsing contexts</dfn> given an [=script/settings object=]
|settings|:
1. Let |related browsing contexts| be an empty set
1. If the [=responsible document=] of |settings| is a [=Document=], append the
[=responsible document=]'s [=Document/browsing context=] to |related browsing
contexts|.
Otherwise if the [=Realm/global object=] specified by |settings| is a
{{WorkerGlobalScope}}, for each |owner| in the [=Realm/global object=]'s
[=owner set=], if |owner| is a [=Document=], append |owner|'s
[=Document/browsing context=] to |related browsing contexts|.
1. Return |related browsing contexts|.
</div>
<div algorithm> To <dfn export>emit an event</dfn> given |session|, and |body|:
1. [=Assert=]: |body| has [=map/size=] 2 and [=contains=] "<code>method</code>"
and "<code>params</code>".
1. Let |connection| be |session|'s [=WebSocket connection=].
1. If |connection| is null, return.
1. Let |serialized| be the result of [=serialize an infra value to JSON
bytes=] given |body|.
1. [=Send a WebSocket message=] comprised of |serialized| over |connection|.
</div>
<div algorithm>
To <dfn>respond with an error</dfn> given a [=WebSocket connection=]
|connection|, |command id|, and |error code|:
1. Let |error data| be a new map matching the <code>ErrorResponse</code>
production in the [=local end definition=], with the <code>id</code> field
set to |command id|, the <code>error</code> field set to |error code|, the
<code>message</code> field set to an implementation-defined string
containing a human-readable definition of the error that occurred and the
<code>stacktrace</code> field optionally set to an implementation-defined
string containing a stack trace report of the active stack frames at the
time when the error occurred.
1. Let |response| be the result of [=serialize an infra value to JSON bytes=]
given |error data|.
Note: |command id| can be null, in which case the <code>id</code> field will
also be set to null, not omitted from |response|.
1. [=Send a WebSocket message=] comprised of |response| over |connection|.
</div>
<div algorithm>
To <dfn>handle a connection closing</dfn> given a [=WebSocket connection=]
|connection|:
1. If there is a [=BiDi session=] [=associated with connection=] |connection|:
1. Let |session| be the [=BiDi session=] [=associated with connection=]
|connection|.
1. Remove |connection| from |session|'s [=session WebSocket
connections=].
1. Otherwise, if the set of [=WebSocket connections not associated with a session=]
contains |connection|, remove |connection| from that set.
</div>
Note: This does not end any [=/session=].
Issue: Need to hook in to the session ending to allow the UA to close
the listener if it wants.
## Establishing a Connection ## {#establishing}
WebDriver clients opt in to a bidirectional connection by requesting a
capability with the name "<code>webSocketUrl</code>" and value
true.
This specification defines an
[=additional webdriver capability=] with the [=capability name=] "<code>webSocketUrl</code>".
<div algorithm="webSocketUrl capability deserialization algorithm">
The [=additional capability deserialization algorithm=] for the
"<code>webSocketUrl</code>" capability, with parameter |value| is:
1. If |value| is not a boolean, return [=error=] with [=error code|code=]
[=invalid argument=].
1. Return [=success=] with data |value|.
</div>
<div algorithm="webSocketUrl capability serialization algorithm">
The [=matched capability serialization algorithm=] for the "<code>webSocketUrl</code>" capability,
with parameter |value| is:
1. If |value| is false, return [=success=] with data null.
1. Return [=success=] with data true.
</div>
<div algorithm="webSocketUrl new session algorithm">
The [=WebDriver new session algorithm=] defined by this specification,
with parameters |session|, |capabilities|, and |flags| is:
1. If |flags| contains "<code>bidi</code>", return.
1. Let |webSocketUrl| be the result of [=getting a property=] named
"<code>webSocketUrl</code>" from |capabilities|.
1. If |webSocketUrl| is undefined or false, return.
1. [=Assert=]: |webSocketUrl| is true.
1. Let |listener| be the result of [=start listening for a WebSocket
connection=] given |session|.
1. Set |webSocketUrl| to the result of [=constructing a WebSocket
URL=] given |listener| and |session|.
1. [=Set a property=] on |capabilities| named
"<code>webSocketUrl</code>" to |webSocketUrl|.
1. Set |session|'s [=BiDi flag=] to true.
1. Append "<code>bidi</code>" to flags.
</div>
<div algorithm="no HTTP new session">
Implementations should also allow clients to establish a [=BiDi Session=] which
is not a [=HTTP Session=]. In this case the URL to the WebSocket server is
communicated out-of-band. An implementation that allows this <dfn>supports
BiDi-only sessions</dfn>. At the time such an implementation is ready to accept
requests to start a WebDriver session, it must:
1. [=Start listening for a WebSocket connection=] given null.
</div>
# Sandboxed Script Execution # {#sandbox}
A common requirements for automation tools is to execute scripts which have
access to the DOM of a document, but don't have information about any changes to
the DOM APIs made by scripts running in the browsing context containing the
document.
A [=BiDi session=] has a <dfn>context sandbox map</dfn> which is a weak map
in which the keys are [=/browsing contexts=] and the values are weak maps between
strings and {{SandboxWindowProxy}} objects.
Note: The definition of sandboxes here is an attempt to codify the behaviour of
existing implementations. It exposes parts of the implementations that have
previously been considered internal by specifications, in particular the
distinction between the internal state of platform objects (which is typically
implemented as native objects in the main implementation language of the browser
engine) and the ECMAScript-visible state. Because existing sandbox
implementations happen at a low level in the engine, implementations converging
toward the specficication in all details might be a slow process. In the
meantime, implementors are encouraged to provide detailed documentation on any
differences with the specification, and users of this feature are encouraged to
explicitly test that scripts running in sandboxes work in all implementations.
## Sandbox Realms ## {#sandbox-realm}
Each sandbox is a unique EMCAScript [=Realm=]. However the sandbox realm
provides access to platform objects in an existing {{Window}} realm via
{{SandboxProxy}} objects.
<div algorithm>
To <dfn>get or create a sandbox realm</dfn> given |name| and |browsing context|:
1. If [=context sandbox map=] does not contain |browsing context|, set [=context
sandbox map=][|source browsing context|] to a new map.
1. Let |sandboxes| be [=context sandbox map=][|source browsing context|].
1. If |sandboxes| does not contain |name|, set |sandboxes|[|name|] to [=create
a sandbox realm=] with |source browsing context|.
1. Return |sandboxes|[|name|].
</div>
<div algorithm>
To <dfn>create a sandbox realm</dfn> with |source browsing context|:
Issue: Define creation of sandbox realm. This is going to return a
{{SandboxWindowProxy}} wrapping the {{WindowProxy}} of |source browsing
context|.
</div>
## Sandbox Proxy Objects ## {#sandbox-proxy}
A <dfn interface>SandboxProxy</dfn> object is an exotic object that mediates sandboxed access to
objects from another realm. Sandbox proxy objects are designed to enforce the
following restrictions:
* Platform objects are accessible, but property access returns only
Web IDL-defined properties and not ECMAScript-defined properties (either
"expando" properties that are not present in the underlying interface, or
ECMAScript-defined properties that shadow a property in the underlying
interface).