selenium-webdriver 4.47.0 → 4.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES +18 -0
- data/bin/linux-arm64/selenium-manager +0 -0
- data/bin/{linux → linux-x86_64}/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/selenium-manager-THIRD-PARTY-NOTICES.txt +343 -129
- data/bin/selenium-manager.cdx.json +947 -868
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/webdriver/bidi/error.rb +4 -0
- data/lib/selenium/webdriver/bidi/protocol/browsing_context.rb +20 -3
- data/lib/selenium/webdriver/bidi/protocol/emulation.rb +272 -6
- data/lib/selenium/webdriver/bidi/serialization/record.rb +38 -41
- data/lib/selenium/webdriver/bidi/serialization/union.rb +11 -7
- data/lib/selenium/webdriver/bidi/serialization.rb +2 -14
- data/lib/selenium/webdriver/bidi/support/bidi_generate.rb +21 -12
- data/lib/selenium/webdriver/common/selenium_manager.rb +6 -2
- data/lib/selenium/webdriver/common/websocket_connection.rb +64 -26
- data/lib/selenium/webdriver/version.rb +1 -1
- metadata +4 -3
|
Binary file
|
|
@@ -23,6 +23,10 @@ require 'selenium/webdriver/bidi/protocol/error_code'
|
|
|
23
23
|
module Selenium
|
|
24
24
|
module WebDriver
|
|
25
25
|
module Error
|
|
26
|
+
# Raised locally when a BiDi wire payload does not match this Selenium's generated
|
|
27
|
+
# schema. It is not a protocol error code; the (de)serialization layer raises it directly.
|
|
28
|
+
class SerializationError < WebDriverError; end
|
|
29
|
+
|
|
26
30
|
# Register each BiDi-only code as a WebDriverError subclass; shared codes keep their classic class.
|
|
27
31
|
BiDi::Protocol::ErrorCode::CLASS_NAMES.each_value do |name|
|
|
28
32
|
const_set(name, Class.new(WebDriverError)) unless const_defined?(name, false)
|
|
@@ -209,7 +209,8 @@ module Selenium
|
|
|
209
209
|
context: {wire_key: 'context', primitive: 'string'},
|
|
210
210
|
origin: {wire_key: 'origin', required: false, enum: 'BrowsingContext::CAPTURE_SCREENSHOT_PARAMETERS_ORIGIN'},
|
|
211
211
|
format: {wire_key: 'format', required: false, ref: 'BrowsingContext::ImageFormat'},
|
|
212
|
-
clip: {wire_key: 'clip', required: false, ref: 'BrowsingContext::ClipRectangle'}
|
|
212
|
+
clip: {wire_key: 'clip', required: false, ref: 'BrowsingContext::ClipRectangle'},
|
|
213
|
+
image_size: {wire_key: 'imageSize', required: false, ref: 'BrowsingContext::ImageSize'}
|
|
213
214
|
)
|
|
214
215
|
|
|
215
216
|
# @api private
|
|
@@ -220,6 +221,14 @@ module Selenium
|
|
|
220
221
|
quality: {wire_key: 'quality', required: false, primitive: 'number'}
|
|
221
222
|
)
|
|
222
223
|
|
|
224
|
+
# @api private
|
|
225
|
+
# @see https://www.selenium.dev/documentation/warnings/bidi-implementation/
|
|
226
|
+
# @see https://w3c.github.io/webdriver-bidi/#cddl-type-browsingcontextimagesize
|
|
227
|
+
ImageSize = Serialization::Record.define(
|
|
228
|
+
max_width: {wire_key: 'maxWidth', required: false, primitive: 'integer'},
|
|
229
|
+
max_height: {wire_key: 'maxHeight', required: false, primitive: 'integer'}
|
|
230
|
+
)
|
|
231
|
+
|
|
223
232
|
# @api private
|
|
224
233
|
# @see https://www.selenium.dev/documentation/warnings/bidi-implementation/
|
|
225
234
|
# @see https://w3c.github.io/webdriver-bidi/#cddl-type-browsingcontextcliprectangle
|
|
@@ -585,6 +594,7 @@ module Selenium
|
|
|
585
594
|
def inner_text_locator(**) = InnerTextLocator.new(**)
|
|
586
595
|
def x_path_locator(**) = XPathLocator.new(**)
|
|
587
596
|
def image_format(**) = ImageFormat.new(**)
|
|
597
|
+
def image_size(**) = ImageSize.new(**)
|
|
588
598
|
def clip_rectangle = ClipRectangle
|
|
589
599
|
def element_clip_rectangle(**) = ElementClipRectangle.new(**)
|
|
590
600
|
def box_clip_rectangle(**) = BoxClipRectangle.new(**)
|
|
@@ -610,10 +620,17 @@ module Selenium
|
|
|
610
620
|
context:,
|
|
611
621
|
origin: Serialization::UNSET,
|
|
612
622
|
format: Serialization::UNSET,
|
|
613
|
-
clip: Serialization::UNSET
|
|
623
|
+
clip: Serialization::UNSET,
|
|
624
|
+
image_size: Serialization::UNSET
|
|
614
625
|
)
|
|
615
626
|
Serialization.validate!('origin', origin, BrowsingContext::CAPTURE_SCREENSHOT_PARAMETERS_ORIGIN)
|
|
616
|
-
params = CaptureScreenshotParameters.new(
|
|
627
|
+
params = CaptureScreenshotParameters.new(
|
|
628
|
+
context: context,
|
|
629
|
+
origin: origin,
|
|
630
|
+
format: format,
|
|
631
|
+
clip: clip,
|
|
632
|
+
image_size: image_size
|
|
633
|
+
)
|
|
617
634
|
execute(
|
|
618
635
|
cmd: 'browsingContext.captureScreenshot',
|
|
619
636
|
params: params,
|
|
@@ -48,6 +48,140 @@ module Selenium
|
|
|
48
48
|
landscape_secondary: 'landscape-secondary'
|
|
49
49
|
}.freeze
|
|
50
50
|
|
|
51
|
+
MEDIA_FEATURES_ANY_HOVER = {
|
|
52
|
+
none: 'none',
|
|
53
|
+
hover: 'hover'
|
|
54
|
+
}.freeze
|
|
55
|
+
|
|
56
|
+
MEDIA_FEATURES_ANY_POINTER = {
|
|
57
|
+
none: 'none',
|
|
58
|
+
coarse: 'coarse',
|
|
59
|
+
fine: 'fine'
|
|
60
|
+
}.freeze
|
|
61
|
+
|
|
62
|
+
MEDIA_FEATURES_COLOR_GAMUT = {
|
|
63
|
+
srgb: 'srgb',
|
|
64
|
+
p3: 'p3',
|
|
65
|
+
rec2020: 'rec2020'
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
68
|
+
MEDIA_FEATURES_DISPLAY_MODE = {
|
|
69
|
+
fullscreen: 'fullscreen',
|
|
70
|
+
standalone: 'standalone',
|
|
71
|
+
minimal_ui: 'minimal-ui',
|
|
72
|
+
browser: 'browser',
|
|
73
|
+
picture_in_picture: 'picture-in-picture'
|
|
74
|
+
}.freeze
|
|
75
|
+
|
|
76
|
+
MEDIA_FEATURES_DYNAMIC_RANGE = {
|
|
77
|
+
standard: 'standard',
|
|
78
|
+
high: 'high'
|
|
79
|
+
}.freeze
|
|
80
|
+
|
|
81
|
+
MEDIA_FEATURES_ENVIRONMENT_BLENDING = {
|
|
82
|
+
opaque: 'opaque',
|
|
83
|
+
additive: 'additive',
|
|
84
|
+
subtractive: 'subtractive'
|
|
85
|
+
}.freeze
|
|
86
|
+
|
|
87
|
+
MEDIA_FEATURES_FORCED_COLORS = {
|
|
88
|
+
none: 'none',
|
|
89
|
+
active: 'active'
|
|
90
|
+
}.freeze
|
|
91
|
+
|
|
92
|
+
MEDIA_FEATURES_GRID = {
|
|
93
|
+
_0: 0,
|
|
94
|
+
_1: 1
|
|
95
|
+
}.freeze
|
|
96
|
+
|
|
97
|
+
MEDIA_FEATURES_HOVER = {
|
|
98
|
+
none: 'none',
|
|
99
|
+
hover: 'hover'
|
|
100
|
+
}.freeze
|
|
101
|
+
|
|
102
|
+
MEDIA_FEATURES_INVERTED_COLORS = {
|
|
103
|
+
none: 'none',
|
|
104
|
+
inverted: 'inverted'
|
|
105
|
+
}.freeze
|
|
106
|
+
|
|
107
|
+
MEDIA_FEATURES_NAV_CONTROLS = {
|
|
108
|
+
none: 'none',
|
|
109
|
+
back: 'back'
|
|
110
|
+
}.freeze
|
|
111
|
+
|
|
112
|
+
MEDIA_FEATURES_OVERFLOW_BLOCK = {
|
|
113
|
+
none: 'none',
|
|
114
|
+
scroll: 'scroll',
|
|
115
|
+
optional_paged: 'optional-paged',
|
|
116
|
+
paged: 'paged'
|
|
117
|
+
}.freeze
|
|
118
|
+
|
|
119
|
+
MEDIA_FEATURES_OVERFLOW_INLINE = {
|
|
120
|
+
none: 'none',
|
|
121
|
+
scroll: 'scroll'
|
|
122
|
+
}.freeze
|
|
123
|
+
|
|
124
|
+
MEDIA_FEATURES_POINTER = {
|
|
125
|
+
none: 'none',
|
|
126
|
+
coarse: 'coarse',
|
|
127
|
+
fine: 'fine'
|
|
128
|
+
}.freeze
|
|
129
|
+
|
|
130
|
+
MEDIA_FEATURES_PREFERS_COLOR_SCHEME = {
|
|
131
|
+
light: 'light',
|
|
132
|
+
dark: 'dark'
|
|
133
|
+
}.freeze
|
|
134
|
+
|
|
135
|
+
MEDIA_FEATURES_PREFERS_CONTRAST = {
|
|
136
|
+
no_preference: 'no-preference',
|
|
137
|
+
more: 'more',
|
|
138
|
+
less: 'less',
|
|
139
|
+
custom: 'custom'
|
|
140
|
+
}.freeze
|
|
141
|
+
|
|
142
|
+
MEDIA_FEATURES_PREFERS_REDUCED_DATA = {
|
|
143
|
+
no_preference: 'no-preference',
|
|
144
|
+
reduce: 'reduce'
|
|
145
|
+
}.freeze
|
|
146
|
+
|
|
147
|
+
MEDIA_FEATURES_PREFERS_REDUCED_MOTION = {
|
|
148
|
+
no_preference: 'no-preference',
|
|
149
|
+
reduce: 'reduce'
|
|
150
|
+
}.freeze
|
|
151
|
+
|
|
152
|
+
MEDIA_FEATURES_PREFERS_REDUCED_TRANSPARENCY = {
|
|
153
|
+
no_preference: 'no-preference',
|
|
154
|
+
reduce: 'reduce'
|
|
155
|
+
}.freeze
|
|
156
|
+
|
|
157
|
+
MEDIA_FEATURES_SCAN = {
|
|
158
|
+
interlace: 'interlace',
|
|
159
|
+
progressive: 'progressive'
|
|
160
|
+
}.freeze
|
|
161
|
+
|
|
162
|
+
MEDIA_FEATURES_SCRIPTING = {
|
|
163
|
+
none: 'none',
|
|
164
|
+
initial_only: 'initial-only',
|
|
165
|
+
enabled: 'enabled'
|
|
166
|
+
}.freeze
|
|
167
|
+
|
|
168
|
+
MEDIA_FEATURES_UPDATE = {
|
|
169
|
+
none: 'none',
|
|
170
|
+
slow: 'slow',
|
|
171
|
+
fast: 'fast'
|
|
172
|
+
}.freeze
|
|
173
|
+
|
|
174
|
+
MEDIA_FEATURES_VIDEO_COLOR_GAMUT = {
|
|
175
|
+
srgb: 'srgb',
|
|
176
|
+
p3: 'p3',
|
|
177
|
+
rec2020: 'rec2020'
|
|
178
|
+
}.freeze
|
|
179
|
+
|
|
180
|
+
MEDIA_FEATURES_VIDEO_DYNAMIC_RANGE = {
|
|
181
|
+
standard: 'standard',
|
|
182
|
+
high: 'high'
|
|
183
|
+
}.freeze
|
|
184
|
+
|
|
51
185
|
SET_SCROLLBAR_TYPE_OVERRIDE_PARAMETERS_SCROLLBAR_TYPE = {
|
|
52
186
|
classic: 'classic',
|
|
53
187
|
overlay: 'overlay'
|
|
@@ -120,17 +254,149 @@ module Selenium
|
|
|
120
254
|
# @see https://www.selenium.dev/documentation/warnings/bidi-implementation/
|
|
121
255
|
# @see https://w3c.github.io/webdriver-bidi/#cddl-type-emulationsetmediafeaturesoverrideparameters
|
|
122
256
|
SetMediaFeaturesOverrideParameters = Serialization::Record.define(
|
|
123
|
-
features: {wire_key: 'features', nullable: true, ref: 'Emulation::
|
|
257
|
+
features: {wire_key: 'features', nullable: true, ref: 'Emulation::MediaFeatures'},
|
|
124
258
|
contexts: {wire_key: 'contexts', required: false, list: true},
|
|
125
259
|
user_contexts: {wire_key: 'userContexts', required: false, list: true}
|
|
126
260
|
)
|
|
127
261
|
|
|
128
262
|
# @api private
|
|
129
263
|
# @see https://www.selenium.dev/documentation/warnings/bidi-implementation/
|
|
130
|
-
# @see https://w3c.github.io/webdriver-bidi/#cddl-type-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
264
|
+
# @see https://w3c.github.io/webdriver-bidi/#cddl-type-emulationmediafeatures
|
|
265
|
+
MediaFeatures = Serialization::Record.define(
|
|
266
|
+
any_hover: {
|
|
267
|
+
wire_key: 'any-hover',
|
|
268
|
+
required: false,
|
|
269
|
+
nullable: true,
|
|
270
|
+
enum: 'Emulation::MEDIA_FEATURES_ANY_HOVER'
|
|
271
|
+
},
|
|
272
|
+
any_pointer: {
|
|
273
|
+
wire_key: 'any-pointer',
|
|
274
|
+
required: false,
|
|
275
|
+
nullable: true,
|
|
276
|
+
enum: 'Emulation::MEDIA_FEATURES_ANY_POINTER'
|
|
277
|
+
},
|
|
278
|
+
color: {wire_key: 'color', required: false, nullable: true, primitive: 'integer'},
|
|
279
|
+
color_gamut: {
|
|
280
|
+
wire_key: 'color-gamut',
|
|
281
|
+
required: false,
|
|
282
|
+
nullable: true,
|
|
283
|
+
enum: 'Emulation::MEDIA_FEATURES_COLOR_GAMUT'
|
|
284
|
+
},
|
|
285
|
+
color_index: {wire_key: 'color-index', required: false, nullable: true, primitive: 'integer'},
|
|
286
|
+
display_mode: {
|
|
287
|
+
wire_key: 'display-mode',
|
|
288
|
+
required: false,
|
|
289
|
+
nullable: true,
|
|
290
|
+
enum: 'Emulation::MEDIA_FEATURES_DISPLAY_MODE'
|
|
291
|
+
},
|
|
292
|
+
dynamic_range: {
|
|
293
|
+
wire_key: 'dynamic-range',
|
|
294
|
+
required: false,
|
|
295
|
+
nullable: true,
|
|
296
|
+
enum: 'Emulation::MEDIA_FEATURES_DYNAMIC_RANGE'
|
|
297
|
+
},
|
|
298
|
+
environment_blending: {
|
|
299
|
+
wire_key: 'environment-blending',
|
|
300
|
+
required: false,
|
|
301
|
+
nullable: true,
|
|
302
|
+
enum: 'Emulation::MEDIA_FEATURES_ENVIRONMENT_BLENDING'
|
|
303
|
+
},
|
|
304
|
+
forced_colors: {
|
|
305
|
+
wire_key: 'forced-colors',
|
|
306
|
+
required: false,
|
|
307
|
+
nullable: true,
|
|
308
|
+
enum: 'Emulation::MEDIA_FEATURES_FORCED_COLORS'
|
|
309
|
+
},
|
|
310
|
+
grid: {wire_key: 'grid', required: false, nullable: true, enum: 'Emulation::MEDIA_FEATURES_GRID'},
|
|
311
|
+
horizontal_viewport_segments: {
|
|
312
|
+
wire_key: 'horizontal-viewport-segments',
|
|
313
|
+
required: false,
|
|
314
|
+
nullable: true,
|
|
315
|
+
primitive: 'integer'
|
|
316
|
+
},
|
|
317
|
+
hover: {wire_key: 'hover', required: false, nullable: true, enum: 'Emulation::MEDIA_FEATURES_HOVER'},
|
|
318
|
+
inverted_colors: {
|
|
319
|
+
wire_key: 'inverted-colors',
|
|
320
|
+
required: false,
|
|
321
|
+
nullable: true,
|
|
322
|
+
enum: 'Emulation::MEDIA_FEATURES_INVERTED_COLORS'
|
|
323
|
+
},
|
|
324
|
+
monochrome: {wire_key: 'monochrome', required: false, nullable: true, primitive: 'integer'},
|
|
325
|
+
nav_controls: {
|
|
326
|
+
wire_key: 'nav-controls',
|
|
327
|
+
required: false,
|
|
328
|
+
nullable: true,
|
|
329
|
+
enum: 'Emulation::MEDIA_FEATURES_NAV_CONTROLS'
|
|
330
|
+
},
|
|
331
|
+
overflow_block: {
|
|
332
|
+
wire_key: 'overflow-block',
|
|
333
|
+
required: false,
|
|
334
|
+
nullable: true,
|
|
335
|
+
enum: 'Emulation::MEDIA_FEATURES_OVERFLOW_BLOCK'
|
|
336
|
+
},
|
|
337
|
+
overflow_inline: {
|
|
338
|
+
wire_key: 'overflow-inline',
|
|
339
|
+
required: false,
|
|
340
|
+
nullable: true,
|
|
341
|
+
enum: 'Emulation::MEDIA_FEATURES_OVERFLOW_INLINE'
|
|
342
|
+
},
|
|
343
|
+
pointer: {wire_key: 'pointer', required: false, nullable: true, enum: 'Emulation::MEDIA_FEATURES_POINTER'},
|
|
344
|
+
prefers_color_scheme: {
|
|
345
|
+
wire_key: 'prefers-color-scheme',
|
|
346
|
+
required: false,
|
|
347
|
+
nullable: true,
|
|
348
|
+
enum: 'Emulation::MEDIA_FEATURES_PREFERS_COLOR_SCHEME'
|
|
349
|
+
},
|
|
350
|
+
prefers_contrast: {
|
|
351
|
+
wire_key: 'prefers-contrast',
|
|
352
|
+
required: false,
|
|
353
|
+
nullable: true,
|
|
354
|
+
enum: 'Emulation::MEDIA_FEATURES_PREFERS_CONTRAST'
|
|
355
|
+
},
|
|
356
|
+
prefers_reduced_data: {
|
|
357
|
+
wire_key: 'prefers-reduced-data',
|
|
358
|
+
required: false,
|
|
359
|
+
nullable: true,
|
|
360
|
+
enum: 'Emulation::MEDIA_FEATURES_PREFERS_REDUCED_DATA'
|
|
361
|
+
},
|
|
362
|
+
prefers_reduced_motion: {
|
|
363
|
+
wire_key: 'prefers-reduced-motion',
|
|
364
|
+
required: false,
|
|
365
|
+
nullable: true,
|
|
366
|
+
enum: 'Emulation::MEDIA_FEATURES_PREFERS_REDUCED_MOTION'
|
|
367
|
+
},
|
|
368
|
+
prefers_reduced_transparency: {
|
|
369
|
+
wire_key: 'prefers-reduced-transparency',
|
|
370
|
+
required: false,
|
|
371
|
+
nullable: true,
|
|
372
|
+
enum: 'Emulation::MEDIA_FEATURES_PREFERS_REDUCED_TRANSPARENCY'
|
|
373
|
+
},
|
|
374
|
+
scan: {wire_key: 'scan', required: false, nullable: true, enum: 'Emulation::MEDIA_FEATURES_SCAN'},
|
|
375
|
+
scripting: {
|
|
376
|
+
wire_key: 'scripting',
|
|
377
|
+
required: false,
|
|
378
|
+
nullable: true,
|
|
379
|
+
enum: 'Emulation::MEDIA_FEATURES_SCRIPTING'
|
|
380
|
+
},
|
|
381
|
+
update: {wire_key: 'update', required: false, nullable: true, enum: 'Emulation::MEDIA_FEATURES_UPDATE'},
|
|
382
|
+
vertical_viewport_segments: {
|
|
383
|
+
wire_key: 'vertical-viewport-segments',
|
|
384
|
+
required: false,
|
|
385
|
+
nullable: true,
|
|
386
|
+
primitive: 'integer'
|
|
387
|
+
},
|
|
388
|
+
video_color_gamut: {
|
|
389
|
+
wire_key: 'video-color-gamut',
|
|
390
|
+
required: false,
|
|
391
|
+
nullable: true,
|
|
392
|
+
enum: 'Emulation::MEDIA_FEATURES_VIDEO_COLOR_GAMUT'
|
|
393
|
+
},
|
|
394
|
+
video_dynamic_range: {
|
|
395
|
+
wire_key: 'video-dynamic-range',
|
|
396
|
+
required: false,
|
|
397
|
+
nullable: true,
|
|
398
|
+
enum: 'Emulation::MEDIA_FEATURES_VIDEO_DYNAMIC_RANGE'
|
|
399
|
+
}
|
|
134
400
|
)
|
|
135
401
|
|
|
136
402
|
# @api private
|
|
@@ -245,7 +511,7 @@ module Selenium
|
|
|
245
511
|
|
|
246
512
|
def geolocation_coordinates(**) = GeolocationCoordinates.new(**)
|
|
247
513
|
def geolocation_position_error(**) = GeolocationPositionError.new(**)
|
|
248
|
-
def
|
|
514
|
+
def media_features(**) = MediaFeatures.new(**)
|
|
249
515
|
def network_conditions_offline(**) = NetworkConditionsOffline.new(**)
|
|
250
516
|
def screen_area(**) = ScreenArea.new(**)
|
|
251
517
|
def screen_orientation(**) = ScreenOrientation.new(**)
|
|
@@ -79,13 +79,13 @@ module Selenium
|
|
|
79
79
|
construct(**attributes)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
# Inbound: builds from the wire. A missing required field
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
82
|
+
# Inbound: builds from the wire. A missing required field raises (in +wire_value+); enum
|
|
83
|
+
# tokens are mapped back to symbols and an unrecognized one raises (in +read+); an
|
|
84
|
+
# undeclared property is captured silently (extensible) or warned and dropped (closed)
|
|
85
|
+
# — strict on shape, lenient on extras.
|
|
86
86
|
def from_json(json_payload)
|
|
87
87
|
unless json_payload.is_a?(::Hash)
|
|
88
|
-
raise Error::
|
|
88
|
+
raise Error::SerializationError, "#{name} expected an object on the wire, got #{json_payload.inspect}"
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
attributes = fields.to_h do |f|
|
|
@@ -173,8 +173,8 @@ module Selenium
|
|
|
173
173
|
|
|
174
174
|
# Outbound mirror of scalar_value: a bare map key must match one of the arm's primitives.
|
|
175
175
|
def check_outbound_scalar(field, value)
|
|
176
|
-
|
|
177
|
-
return if
|
|
176
|
+
checks = Array(field.scalar).filter_map { |primitive| PRIMITIVE_CHECKS[primitive] }
|
|
177
|
+
return if checks.empty? || checks.any? { |check| check.call(value) }
|
|
178
178
|
|
|
179
179
|
raise ::ArgumentError,
|
|
180
180
|
"#{name}##{field.name} expected #{Array(field.scalar).join(' or ')}, got #{value.inspect}"
|
|
@@ -204,8 +204,8 @@ module Selenium
|
|
|
204
204
|
# ArgumentError here rather than a rejection the browser reports a round-trip later. A field
|
|
205
205
|
# with no primitive descriptor (enum, ref, opaque) passes; lists are skipped, as inbound does.
|
|
206
206
|
def check_outbound_primitive(field, value)
|
|
207
|
-
|
|
208
|
-
return if
|
|
207
|
+
check = PRIMITIVE_CHECKS[field.primitive]
|
|
208
|
+
return if check.nil? || check.call(value)
|
|
209
209
|
|
|
210
210
|
raise ::ArgumentError, "#{name}##{field.name} expected #{field.primitive}, got #{value.inspect}"
|
|
211
211
|
end
|
|
@@ -214,38 +214,32 @@ module Selenium
|
|
|
214
214
|
!UNSET.equal?(field.fixed)
|
|
215
215
|
end
|
|
216
216
|
|
|
217
|
+
# A required field absent from the response cannot yield a valid typed object, so it raises
|
|
218
|
+
# rather than substitute a placeholder or represent the field as omitted; a remote end that
|
|
219
|
+
# lags the schema is handled by a project schema override, not by runtime tolerance.
|
|
217
220
|
def wire_value(field, json_payload)
|
|
218
221
|
return field.fixed if fixed?(field)
|
|
219
222
|
return read(field, json_payload[field.wire_key]) if json_payload.key?(field.wire_key)
|
|
220
223
|
return UNSET unless field.required
|
|
221
224
|
|
|
222
|
-
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# A required field absent from the response is tolerated as omitted (UNSET) and warned, so a
|
|
226
|
-
# schema ahead of the browser does not block the caller; strict mode (SE_BIDI_STRICT) escalates
|
|
227
|
-
# to an error for callers who want it. Omitted (UNSET) stays distinct from an explicit null (nil),
|
|
228
|
-
# which matters for the required-and-nullable fields the schema flags.
|
|
229
|
-
def missing_required(field)
|
|
230
|
-
message = "#{name}##{field.name} is required but was missing from the response"
|
|
231
|
-
raise Error::WebDriverError, message if Serialization.strict?
|
|
232
|
-
|
|
233
|
-
WebDriver.logger.warn(message, id: :bidi_missing_required)
|
|
234
|
-
UNSET
|
|
225
|
+
raise Error::SerializationError, "#{name}##{field.name} is required but was missing from the response"
|
|
235
226
|
end
|
|
236
227
|
|
|
237
228
|
def read(field, raw)
|
|
238
229
|
if raw.nil?
|
|
239
230
|
return raw if field.nullable
|
|
240
231
|
|
|
241
|
-
raise Error::
|
|
232
|
+
raise Error::SerializationError, "#{name}##{field.name} received null but is not nullable"
|
|
242
233
|
end
|
|
243
234
|
check_shape(field, raw)
|
|
244
235
|
return Serialization.to_symbol("#{name}##{field.name}", raw, enum_hash(field)) if field.enum
|
|
245
236
|
|
|
246
237
|
if field.ref.nil?
|
|
247
|
-
|
|
248
|
-
|
|
238
|
+
return raw if field.list
|
|
239
|
+
|
|
240
|
+
check_primitive(field, raw)
|
|
241
|
+
# A whole number is exact in both types, so the declared type is held with nothing lost.
|
|
242
|
+
return field.primitive == 'integer' && raw.is_a?(::Float) ? raw.to_i : raw
|
|
249
243
|
end
|
|
250
244
|
|
|
251
245
|
read_ref(field, raw)
|
|
@@ -268,24 +262,27 @@ module Selenium
|
|
|
268
262
|
return if field.list == raw.is_a?(::Array)
|
|
269
263
|
return unless field.list || field.enum || field.ref
|
|
270
264
|
|
|
271
|
-
raise Error::
|
|
265
|
+
raise Error::SerializationError,
|
|
272
266
|
"#{name}##{field.name} expected #{field.list ? 'a list' : 'a single value'}, got #{raw.inspect}"
|
|
273
267
|
end
|
|
274
268
|
|
|
275
|
-
#
|
|
276
|
-
# number type)
|
|
277
|
-
#
|
|
278
|
-
#
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
'
|
|
269
|
+
# The check a schema primitive admits, by JSON kind rather than Ruby class: `number` is
|
|
270
|
+
# any Numeric (JSON has one number type), and `integer` is any whole one — a browser is
|
|
271
|
+
# free to send `5` or `5.0` (JS has no int/float split), while a fractional value like
|
|
272
|
+
# 1.5 is a real mismatch. A field with no primitive descriptor is left unchecked.
|
|
273
|
+
WHOLE_FLOAT = ->(value) { value.is_a?(::Float) && value.finite? && (value % 1).zero? }
|
|
274
|
+
PRIMITIVE_CHECKS = {
|
|
275
|
+
'string' => ->(value) { value.is_a?(::String) },
|
|
276
|
+
'boolean' => ->(value) { value.is_a?(::TrueClass) || value.is_a?(::FalseClass) },
|
|
277
|
+
'number' => ->(value) { value.is_a?(::Numeric) },
|
|
278
|
+
'integer' => ->(value) { value.is_a?(::Integer) || WHOLE_FLOAT.call(value) }
|
|
282
279
|
}.freeze
|
|
283
280
|
|
|
284
281
|
def check_primitive(field, raw)
|
|
285
|
-
|
|
286
|
-
return if
|
|
282
|
+
check = PRIMITIVE_CHECKS[field.primitive]
|
|
283
|
+
return if check.nil? || check.call(raw)
|
|
287
284
|
|
|
288
|
-
raise Error::
|
|
285
|
+
raise Error::SerializationError, "#{name}##{field.name} expected #{field.primitive}, got #{raw.inspect}"
|
|
289
286
|
end
|
|
290
287
|
|
|
291
288
|
def enum_hash(field)
|
|
@@ -314,7 +311,7 @@ module Selenium
|
|
|
314
311
|
# malformed entry and is rejected outright.
|
|
315
312
|
def read_map_entry(field, element, klass)
|
|
316
313
|
unless element.is_a?(::Array) && element.size == 2
|
|
317
|
-
raise Error::
|
|
314
|
+
raise Error::SerializationError,
|
|
318
315
|
"#{name}##{field.name} expected a [key, value] pair, got #{element.inspect}"
|
|
319
316
|
end
|
|
320
317
|
|
|
@@ -326,13 +323,13 @@ module Selenium
|
|
|
326
323
|
# A bare scalar at a scalar-tolerant union position must match one of the union's
|
|
327
324
|
# scalar-arm primitives (+scalar+ is a primitive name or an array of them); a
|
|
328
325
|
# wrong-typed scalar (a number where a string is expected) is a wire error, not
|
|
329
|
-
# something to pass through. An unrecognized primitive (none in
|
|
326
|
+
# something to pass through. An unrecognized primitive (none in PRIMITIVE_CHECKS) is
|
|
330
327
|
# left unchecked, matching the lenient default elsewhere.
|
|
331
328
|
def scalar_value(field, value)
|
|
332
|
-
|
|
333
|
-
return value if
|
|
329
|
+
checks = Array(field.scalar).filter_map { |primitive| PRIMITIVE_CHECKS[primitive] }
|
|
330
|
+
return value if checks.empty? || checks.any? { |check| check.call(value) }
|
|
334
331
|
|
|
335
|
-
raise Error::
|
|
332
|
+
raise Error::SerializationError,
|
|
336
333
|
"#{name}##{field.name} expected #{Array(field.scalar).join(' or ')}, got #{value.inspect}"
|
|
337
334
|
end
|
|
338
335
|
|
|
@@ -53,19 +53,23 @@ module Selenium
|
|
|
53
53
|
# An outbound scalar outside that set matches no arm, so it is a caller error.
|
|
54
54
|
def scalar_values(*values) = @scalar_values = values
|
|
55
55
|
|
|
56
|
-
# A non-Hash payload is a bare scalar arm (e.g. input.Origin's "viewport")
|
|
57
|
-
#
|
|
58
|
-
# (object_only), where a non-Hash cannot match any variant and is a wire error.
|
|
56
|
+
# A non-Hash payload is a bare scalar arm (e.g. input.Origin's "viewport"), valid only
|
|
57
|
+
# as a literal the schema pins; under object_only it cannot match any variant at all.
|
|
59
58
|
def from_json(json_payload)
|
|
60
59
|
unless json_payload.is_a?(::Hash)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
if @object_only
|
|
61
|
+
raise Error::SerializationError,
|
|
62
|
+
"#{name} expected an object on the wire, got #{json_payload.inspect}"
|
|
63
|
+
end
|
|
64
|
+
return json_payload if scalar_arm?(json_payload)
|
|
65
|
+
|
|
66
|
+
raise Error::SerializationError,
|
|
67
|
+
"#{name} received a scalar not in this Selenium's BiDi schema: #{json_payload.inspect}"
|
|
64
68
|
end
|
|
65
69
|
|
|
66
70
|
variant = select(json_payload)
|
|
67
71
|
unless variant
|
|
68
|
-
raise Error::
|
|
72
|
+
raise Error::SerializationError,
|
|
69
73
|
"#{name} received a variant not in this Selenium's BiDi schema: #{json_payload.inspect}"
|
|
70
74
|
end
|
|
71
75
|
Protocol.const_get(variant).from_json(json_payload)
|
|
@@ -21,8 +21,7 @@ module Selenium
|
|
|
21
21
|
module WebDriver
|
|
22
22
|
class BiDi
|
|
23
23
|
# Wire round-trip runtime for the generated protocol layer: the value-type bases
|
|
24
|
-
# (Record, Union), the omit sentinel (UNSET), outbound enum validation
|
|
25
|
-
# strict-inbound toggle.
|
|
24
|
+
# (Record, Union), the omit sentinel (UNSET), and outbound enum validation.
|
|
26
25
|
#
|
|
27
26
|
# @api private
|
|
28
27
|
module Serialization
|
|
@@ -34,17 +33,6 @@ module Selenium
|
|
|
34
33
|
def UNSET.inspect = 'UNSET'
|
|
35
34
|
UNSET.freeze
|
|
36
35
|
|
|
37
|
-
# Strict inbound mode. Off by default: a required field missing from a response is
|
|
38
|
-
# tolerated as omitted and warned, so a schema ahead of the browser does not block the
|
|
39
|
-
# caller. When SE_BIDI_STRICT is set to anything but 0/false, that same case escalates
|
|
40
|
-
# to an error for callers who want it.
|
|
41
|
-
#
|
|
42
|
-
# @api private
|
|
43
|
-
def self.strict?
|
|
44
|
-
value = ENV.fetch('SE_BIDI_STRICT', '').strip.downcase
|
|
45
|
-
!value.empty? && value != '0' && value != 'false'
|
|
46
|
-
end
|
|
47
|
-
|
|
48
36
|
# Validates an outbound enum argument: +value+ is a symbol (or list of symbols) that
|
|
49
37
|
# must be a key of the enum hash (+{symbol => wire_token}+), so a bad value fails
|
|
50
38
|
# locally with a clear error instead of a round-trip. Outbound only; inbound wire
|
|
@@ -79,7 +67,7 @@ module Selenium
|
|
|
79
67
|
return value if value.nil?
|
|
80
68
|
return value.map { |element| to_symbol(name, element, enum) } if value.is_a?(::Array)
|
|
81
69
|
|
|
82
|
-
enum.key(value) || raise(Error::
|
|
70
|
+
enum.key(value) || raise(Error::SerializationError, "#{name} received an unknown value: #{value.inspect}")
|
|
83
71
|
end
|
|
84
72
|
end
|
|
85
73
|
end # BiDi
|
|
@@ -137,14 +137,16 @@ module BiDiGenerate
|
|
|
137
137
|
# snake_case hash key for an enum value (only a label for the wire value it maps to).
|
|
138
138
|
# Preserves camelCase word boundaries (beforeRequestSent → before_request_sent), maps a
|
|
139
139
|
# leading minus to "neg" (-0 → neg0, -Infinity → neg_infinity; no underscore before a
|
|
140
|
-
# digit, so the key stays normalcase),
|
|
141
|
-
# (dedicated-worker → dedicated_worker)
|
|
140
|
+
# digit, so the key stays normalcase), collapses other punctuation
|
|
141
|
+
# (dedicated-worker → dedicated_worker), and prefixes a numeric value so the key
|
|
142
|
+
# is still a valid symbol (0 → _0).
|
|
142
143
|
def self.enum_key(value)
|
|
143
|
-
camel_to_snake(value.to_s)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
token = camel_to_snake(value.to_s)
|
|
145
|
+
.sub(/\A-(?=\d)/, 'neg')
|
|
146
|
+
.sub(/\A-/, 'neg_')
|
|
147
|
+
.gsub(/[^a-z0-9]+/, '_')
|
|
148
|
+
.gsub(/\A_+|_+\z/, '')
|
|
149
|
+
token.empty? || token.match?(/\A\d/) ? "_#{token}" : token
|
|
148
150
|
end
|
|
149
151
|
|
|
150
152
|
# ruby_name is the snake_case keyword argument; wire_name is the exact key the
|
|
@@ -308,9 +310,16 @@ module BiDiGenerate
|
|
|
308
310
|
def type_entry = "'#{wire_name}' => #{payload_ref || 'nil'}"
|
|
309
311
|
end
|
|
310
312
|
|
|
311
|
-
#
|
|
312
|
-
|
|
313
|
-
|
|
313
|
+
# The RBS type a schema value primitive maps to; an unrecognized one is untyped.
|
|
314
|
+
RBS_VALUE_TYPES = {'string' => 'String', 'integer' => 'Integer', 'number' => 'Float',
|
|
315
|
+
'boolean' => 'bool'}.freeze
|
|
316
|
+
|
|
317
|
+
# constant_name is the SCREAMING_SNAKE hash name; pairs are [symbol_key, wire_value] tuples;
|
|
318
|
+
# primitive is the schema's declared value type. spec_href links to the type's definition in
|
|
319
|
+
# the live spec (nil when the schema has none).
|
|
320
|
+
Enum = Struct.new(:constant_name, :pairs, :primitive, :spec_href, keyword_init: true) do
|
|
321
|
+
def rbs_value_type = RBS_VALUE_TYPES.fetch(primitive, 'untyped')
|
|
322
|
+
end
|
|
314
323
|
|
|
315
324
|
# The generated Protocol::ErrorCode module (filename 'error_code'): `codes` is the [wire, class_name]
|
|
316
325
|
# pairs in schema order (the full map); `new_classes` is the subset of class names the classic
|
|
@@ -694,9 +703,9 @@ module BiDiGenerate
|
|
|
694
703
|
next unless type['kind'] == 'enum'
|
|
695
704
|
next unless name.start_with?("#{domain}.")
|
|
696
705
|
|
|
697
|
-
pairs = type['values'].map { |v| [BiDiGenerate.enum_key(v), v
|
|
706
|
+
pairs = type['values'].map { |v| [BiDiGenerate.enum_key(v), v] }
|
|
698
707
|
Enum.new(constant_name: BiDiGenerate.screaming_snake(name.sub("#{domain}.", '')), pairs: pairs,
|
|
699
|
-
spec_href: type['specHref'])
|
|
708
|
+
primitive: type['primitive'], spec_href: type['specHref'])
|
|
700
709
|
end
|
|
701
710
|
end
|
|
702
711
|
|