selenium-webdriver 4.51.0 → 4.52.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 +15 -0
- data/Gemfile +14 -0
- data/README.md +20 -0
- data/bin/linux-arm64/selenium-manager +0 -0
- data/bin/linux-x86_64/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/selenium-manager-THIRD-PARTY-NOTICES.txt +543 -226
- data/bin/selenium-manager.cdx.json +804 -722
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/webdriver/atoms/findElements.js +960 -250
- data/lib/selenium/webdriver/atoms/getAttribute.js +153 -127
- data/lib/selenium/webdriver/atoms/isDisplayed.js +433 -364
- data/lib/selenium/webdriver/atoms.rb +4 -1
- data/lib/selenium/webdriver/bidi/protocol/bluetooth.rb +6 -6
- data/lib/selenium/webdriver/bidi/protocol/browser.rb +21 -9
- data/lib/selenium/webdriver/bidi/protocol/browsing_context.rb +109 -39
- data/lib/selenium/webdriver/bidi/protocol/emulation.rb +89 -44
- data/lib/selenium/webdriver/bidi/protocol/input.rb +95 -31
- data/lib/selenium/webdriver/bidi/protocol/log.rb +4 -4
- data/lib/selenium/webdriver/bidi/protocol/moz_debugging.rb +12 -10
- data/lib/selenium/webdriver/bidi/protocol/moz_profiler.rb +4 -4
- data/lib/selenium/webdriver/bidi/protocol/network.rb +82 -39
- data/lib/selenium/webdriver/bidi/protocol/script.rb +38 -24
- data/lib/selenium/webdriver/bidi/protocol/session.rb +61 -8
- data/lib/selenium/webdriver/bidi/protocol/storage.rb +3 -3
- data/lib/selenium/webdriver/bidi/protocol/user_agent_client_hints.rb +3 -3
- data/lib/selenium/webdriver/bidi/protocol/web_extension.rb +17 -9
- data/lib/selenium/webdriver/bidi/serialization/record.rb +66 -24
- data/lib/selenium/webdriver/bidi/serialization.rb +2 -2
- data/lib/selenium/webdriver/bidi/support/bidi_generate.rb +228 -150
- data/lib/selenium/webdriver/common/client_config.rb +4 -1
- data/lib/selenium/webdriver/common/driver_finder.rb +5 -2
- data/lib/selenium/webdriver/common/websocket_connection.rb +51 -13
- data/lib/selenium/webdriver/remote/http/default.rb +17 -1
- data/lib/selenium/webdriver/version.rb +1 -1
- data/selenium-webdriver.gemspec +0 -12
- metadata +6 -163
|
@@ -86,12 +86,22 @@ module BiDiGenerate
|
|
|
86
86
|
value.is_a?(String) ? "'#{value}'" : value.to_s
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
# Source literal for a bound (a number, or an exclusive flag's `true`), grouping a long integer's
|
|
90
|
+
# digits as RuboCop's NumericLiterals expects.
|
|
91
|
+
def self.numeric_literal(value)
|
|
92
|
+
return value.to_s unless value.is_a?(Integer) && value.abs >= 10_000
|
|
93
|
+
|
|
94
|
+
grouped = value.abs.to_s.reverse.scan(/\d{1,3}/).join('_').reverse
|
|
95
|
+
value.negative? ? "-#{grouped}" : grouped
|
|
96
|
+
end
|
|
97
|
+
|
|
89
98
|
# Renders `prefix(args)` on one line, or one argument per line when it would exceed
|
|
90
99
|
# LINE_LIMIT at the given indent. open/close default to parentheses (pass {} for a
|
|
91
|
-
# hash literal) so emitted calls and literals stay within RuboCop's length limit.
|
|
92
|
-
|
|
100
|
+
# hash literal) so emitted calls and literals stay within RuboCop's length limit. trailing
|
|
101
|
+
# counts what the caller appends to the last line (an entry's separating comma).
|
|
102
|
+
def self.wrap_call(prefix, args, indent, open: '(', close: ')', trailing: 0)
|
|
93
103
|
one_line = "#{prefix}#{open}#{args.join(', ')}#{close}"
|
|
94
|
-
return one_line if args.empty? || indent + one_line.length <= LINE_LIMIT
|
|
104
|
+
return one_line if args.empty? || indent + one_line.length + trailing <= LINE_LIMIT
|
|
95
105
|
|
|
96
106
|
pad = ' ' * (indent + 2)
|
|
97
107
|
"#{prefix}#{open}\n#{pad}#{args.join(",\n#{pad}")}\n#{' ' * indent}#{close}"
|
|
@@ -251,65 +261,14 @@ module BiDiGenerate
|
|
|
251
261
|
end
|
|
252
262
|
end
|
|
253
263
|
|
|
254
|
-
# A browser-specific extension to a command, kept out of the shared class so a
|
|
255
|
-
# non-matching browser never sees it. shared_params are the base command's own
|
|
256
|
-
# (required) params, forwarded verbatim; vendor_params are the typed extra fields,
|
|
257
|
-
# composed into the extensible params record's passthrough bag under their exact
|
|
258
|
-
# wire keys. params_class/result_ref/wire_name mirror the base command.
|
|
259
|
-
VendorCommand = Struct.new(:method_name, :wire_name, :result_ref, :params_class,
|
|
260
|
-
:shared_params, :vendor_params, :spec_href, keyword_init: true) do
|
|
261
|
-
def def_header(indent)
|
|
262
|
-
BiDiGenerate.wrap_call("def #{method_name}", shared_params.map(&:sig_part) + vendor_params.map(&:sig_part),
|
|
263
|
-
indent)
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
# The full `def … end` method block, fully indented from `indent`. Optional vendor
|
|
267
|
-
# fields are placed into the passthrough bag only when set (UNSET stays omitted), so
|
|
268
|
-
# they serialize exactly like a field on the extensible record.
|
|
269
|
-
def render_lines(indent)
|
|
270
|
-
body = ' ' * (indent + 2)
|
|
271
|
-
[*doc_lines(' ' * indent), "#{' ' * indent}#{def_header(indent)}", *extensions_lines(body, indent),
|
|
272
|
-
params_line(body, indent), execute_line(body, indent), "#{' ' * indent}end"]
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def doc_lines(pad)
|
|
276
|
-
lines = ["#{pad}# @api private", "#{pad}# @see #{BiDiGenerate::BIDI_DOC_URL}"]
|
|
277
|
-
lines << "#{pad}# @see #{spec_href}" if spec_href
|
|
278
|
-
lines
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
# The extensible passthrough bag, carrying each set vendor field under its exact wire key.
|
|
282
|
-
def extensions_lines(body, indent)
|
|
283
|
-
inner = ' ' * (indent + 4)
|
|
284
|
-
entries = vendor_params.map { |p| "#{inner}'#{p.wire_name}' => #{p.ruby_name}" }.join(",\n")
|
|
285
|
-
["#{body}extensions = {", entries, "#{body}}.reject { |_, value| Serialization::UNSET.equal?(value) }"]
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
def params_line(body, indent)
|
|
289
|
-
kwargs = shared_params.map { |p| "#{p.ruby_name}: #{p.ruby_name}" } + ['extensions: extensions']
|
|
290
|
-
"#{body}#{BiDiGenerate.wrap_call("params = #{params_class}.new", kwargs, indent + 2)}"
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
def execute_line(body, indent)
|
|
294
|
-
args = ["cmd: '#{wire_name}'", 'params: params']
|
|
295
|
-
args << "result: #{result_ref}" if result_ref
|
|
296
|
-
"#{body}#{BiDiGenerate.wrap_call('execute', args, indent + 2)}"
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def rbs_signature
|
|
300
|
-
params = (shared_params.map(&:rbs_part) + vendor_params.map(&:rbs_part)).join(', ')
|
|
301
|
-
ret = result_ref ? "::Selenium::WebDriver::BiDi::Protocol::#{result_ref}" : 'untyped'
|
|
302
|
-
"(#{params}) -> #{ret}"
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
|
|
306
264
|
# A namespaced group of browser-specific commands (e.g. Firefox's), emitted as a subclass of
|
|
307
265
|
# the domain: it overrides the commands a vendor extends with `moz:` fields and adds the
|
|
308
266
|
# commands a vendor defines in the domain (`webExtension.moz:listExtensions`).
|
|
309
267
|
# A subclass (rather than a runtime-mixed module) keeps the vendor signatures statically
|
|
310
268
|
# visible to type checkers, and is constructed directly (`<Name>.new(source)`) for a
|
|
311
|
-
# matching session — no factory or runtime mix-in.
|
|
312
|
-
|
|
269
|
+
# matching session — no factory or runtime mix-in. types are the variant's own params
|
|
270
|
+
# records, which shadow the shared ones so its overriding commands build them by bare name.
|
|
271
|
+
VendorModule = Struct.new(:name, :namespace, :parent, :types, :commands, keyword_init: true) do
|
|
313
272
|
def render(indent)
|
|
314
273
|
pad = ' ' * indent
|
|
315
274
|
lines = [
|
|
@@ -318,6 +277,7 @@ module BiDiGenerate
|
|
|
318
277
|
"#{pad}# Construct #{name}.new(source) for a matching session; other sessions use #{parent}.",
|
|
319
278
|
"#{pad}class #{name} < #{parent}"
|
|
320
279
|
]
|
|
280
|
+
types.each { |type| lines.concat(type_lines(type, indent + 2)) << '' }
|
|
321
281
|
commands.each_with_index do |cmd, index|
|
|
322
282
|
lines << '' unless index.zero?
|
|
323
283
|
lines.concat(cmd.render_lines(indent + 2))
|
|
@@ -325,6 +285,13 @@ module BiDiGenerate
|
|
|
325
285
|
lines << "#{pad}end"
|
|
326
286
|
lines.join("\n")
|
|
327
287
|
end
|
|
288
|
+
|
|
289
|
+
def type_lines(type, indent)
|
|
290
|
+
pad = ' ' * indent
|
|
291
|
+
lines = ["#{pad}# @api private", "#{pad}# @see #{BiDiGenerate::BIDI_DOC_URL}"]
|
|
292
|
+
lines << "#{pad}# @see #{type.spec_href}" if type.spec_href
|
|
293
|
+
lines << "#{pad}#{type.define_assignment(type.ruby_name, indent)}"
|
|
294
|
+
end
|
|
328
295
|
end
|
|
329
296
|
|
|
330
297
|
# payload_ref is the Protocol-relative class the event's params parse into (nil when
|
|
@@ -355,16 +322,16 @@ module BiDiGenerate
|
|
|
355
322
|
# for a scalar/opaque field); list wraps it in an array. wire_key is the exact
|
|
356
323
|
# JSON payload key (the schema's `wire` name, baked verbatim).
|
|
357
324
|
FieldIR = Struct.new(:ruby_name, :wire_key, :required, :nullable, :ref, :list, :enum, :primitive, :scalar, :const,
|
|
358
|
-
:map_of, :rbs, keyword_init: true) do
|
|
325
|
+
:map_of, :bounds, :rbs, keyword_init: true) do
|
|
359
326
|
# A `Serialization::Record.define` spec entry: `name: 'jsonKey'` shorthand, or
|
|
360
327
|
# `name: {wire_key:, …}` when the field carries JSON facts beyond its name.
|
|
361
328
|
# enum carries the allowed-values constant path, validated at construction.
|
|
362
|
-
def spec_entry(indent = 0)
|
|
329
|
+
def spec_entry(indent = 0, trailing: 0)
|
|
363
330
|
meta = value_facts
|
|
364
331
|
return "#{ruby_name}: '#{wire_key}'" if meta.empty?
|
|
365
332
|
|
|
366
333
|
meta.unshift("wire_key: '#{wire_key}'")
|
|
367
|
-
BiDiGenerate.wrap_call("#{ruby_name}: ", meta, indent, open: '{', close: '}')
|
|
334
|
+
BiDiGenerate.wrap_call("#{ruby_name}: ", meta, indent, open: '{', close: '}', trailing: trailing)
|
|
368
335
|
end
|
|
369
336
|
|
|
370
337
|
# The JSON facts beyond the field's name, in the order Record.define reads them. A
|
|
@@ -379,22 +346,28 @@ module BiDiGenerate
|
|
|
379
346
|
end
|
|
380
347
|
|
|
381
348
|
# The facts describing the value's type and shape: what the runtime reads it as and checks
|
|
382
|
-
# it against.
|
|
349
|
+
# it against. For a list, ref/scalar/enum/primitive describe each element. `map_of` is the
|
|
350
|
+
# value primitive(s) of a string-keyed map (`{*text => text}`).
|
|
383
351
|
def type_facts
|
|
384
352
|
facts = []
|
|
385
353
|
facts << "ref: '#{ref}'" if ref
|
|
386
354
|
facts << 'list: true' if list
|
|
387
|
-
facts << "scalar: #{
|
|
355
|
+
facts << "scalar: #{primitive_literal(scalar)}" if scalar
|
|
388
356
|
facts << "enum: '#{enum}'" if enum
|
|
389
|
-
facts << "primitive:
|
|
390
|
-
facts << "map:
|
|
391
|
-
facts
|
|
357
|
+
facts << "primitive: #{primitive_literal(primitive)}" if primitive
|
|
358
|
+
facts << "map: #{primitive_literal(map_of)}" if map_of
|
|
359
|
+
facts + bound_facts
|
|
392
360
|
end
|
|
393
361
|
|
|
394
|
-
# The
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
362
|
+
# The numeric bounds every value the field carries is held to (for a list or map, each numeric one).
|
|
363
|
+
def bound_facts
|
|
364
|
+
bounds.map { |key, value| "#{BiDiGenerate.camel_to_snake(key)}: #{BiDiGenerate.numeric_literal(value)}" }
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# A single primitive string, or an array when a value may match several: a union of scalars
|
|
368
|
+
# (`primitive`), a scalar-tolerant union position (`scalar`), or a map's union of values (`map`).
|
|
369
|
+
def primitive_literal(value)
|
|
370
|
+
value.is_a?(::Array) ? BiDiGenerate.word_array(value) : "'#{value}'"
|
|
398
371
|
end
|
|
399
372
|
|
|
400
373
|
# The `self.new` keyword for this field — a user-supplied input carrying the field's
|
|
@@ -425,7 +398,8 @@ module BiDiGenerate
|
|
|
425
398
|
def define_entries(entry_indent)
|
|
426
399
|
entries = []
|
|
427
400
|
entries << discriminator_entry if discriminator
|
|
428
|
-
|
|
401
|
+
last = fields.last unless extensible
|
|
402
|
+
entries.concat(fields.map { |f| f.spec_entry(entry_indent, trailing: f.equal?(last) ? 0 : 1) })
|
|
429
403
|
entries << 'extensible: true' if extensible
|
|
430
404
|
entries
|
|
431
405
|
end
|
|
@@ -586,11 +560,62 @@ module BiDiGenerate
|
|
|
586
560
|
@events = schema['events'] + vendor_events
|
|
587
561
|
@domains = schema['domains'] || {}
|
|
588
562
|
promote_command_params_records!
|
|
563
|
+
@vendor_params = fold_vendor_extensions!
|
|
589
564
|
end
|
|
590
565
|
|
|
591
566
|
# A vendor module's domain keeps its wire prefix (`moz:debugging`); a spec domain has none.
|
|
592
567
|
def vendor_domain?(domain) = domain.include?(':')
|
|
593
568
|
|
|
569
|
+
# A vendor's fields on a spec type are declared fields (ADR 17786), so they are typed and never ride
|
|
570
|
+
# the extensions map. Fields on a spec command's params go to a record of the vendor variant's own (see
|
|
571
|
+
# vendor_modules_for), returned per namespace; fields on any other type join it as optional fields.
|
|
572
|
+
def fold_vendor_extensions!
|
|
573
|
+
@vendor_field_refs = []
|
|
574
|
+
@vendor.to_h do |namespace, section|
|
|
575
|
+
params = {}
|
|
576
|
+
(section['extends'] || {}).each do |type_name, entry|
|
|
577
|
+
fields = vendor_extension_fields(namespace, type_name, entry)
|
|
578
|
+
if params_command(type_name)
|
|
579
|
+
params[type_name] = fields
|
|
580
|
+
else
|
|
581
|
+
optional = fields.map { |field| field.merge('required' => false) }
|
|
582
|
+
@types[type_name] = @types[type_name].merge('fields' => @types[type_name]['fields'] + optional)
|
|
583
|
+
end
|
|
584
|
+
end
|
|
585
|
+
[namespace, params]
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
# Also records the vendor types the fields reference, so vendor_types_reached emits them.
|
|
590
|
+
def vendor_extension_fields(namespace, type_name, entry)
|
|
591
|
+
type = @types[type_name] || raise("vendor #{namespace} extends unknown type #{type_name}")
|
|
592
|
+
raise "vendor #{namespace} extends #{type_name}, which is not a record" unless type['kind'] == 'record'
|
|
593
|
+
|
|
594
|
+
fields = entry['fields'].map { |field| hoist_vendor_enum(type_name, field) }
|
|
595
|
+
@vendor_field_refs.concat(fields.flat_map { |field| refs_in_type(field['type']) })
|
|
596
|
+
fields
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
# The projector leaves a vendor field's enum inline (`moz:scope`), so name it after the field in the
|
|
600
|
+
# extended type's domain (`browsingContext.MozScope`) to emit and validate it like a spec enum.
|
|
601
|
+
def hoist_vendor_enum(type_name, field)
|
|
602
|
+
values = field['type']['enum']
|
|
603
|
+
return field unless values
|
|
604
|
+
|
|
605
|
+
domain = type_name.split('.', 2).first
|
|
606
|
+
name = "#{domain}.#{BiDiGenerate.snake_to_class_name(BiDiGenerate.camel_to_snake(field['name']))}"
|
|
607
|
+
enum = {'kind' => 'enum', 'values' => values, 'primitive' => field['type']['primitive']}
|
|
608
|
+
raise "vendor enum #{name} conflicts with an existing type" if @types.key?(name) && @types[name] != enum
|
|
609
|
+
|
|
610
|
+
@types[name] = enum
|
|
611
|
+
field.merge('type' => field['type'].slice('nullable').merge('ref' => name))
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
# The spec command that sends a type as its params, or nil.
|
|
615
|
+
def params_command(type_name)
|
|
616
|
+
@commands.find { |c| c.dig('params', 'ref') == type_name && !vendor_domain?(c['domain']) }
|
|
617
|
+
end
|
|
618
|
+
|
|
594
619
|
def vendor_messages(kind)
|
|
595
620
|
@vendor.values.flat_map { |section| section[kind] || [] }
|
|
596
621
|
end
|
|
@@ -681,24 +706,23 @@ module BiDiGenerate
|
|
|
681
706
|
ruby_path(name).sub(/\A#{Regexp.escape(prefix)}/, '')
|
|
682
707
|
end
|
|
683
708
|
|
|
684
|
-
# The vendor modules a domain carries, one per namespace (`moz` → module `Moz`).
|
|
685
|
-
#
|
|
686
|
-
#
|
|
709
|
+
# The vendor modules a domain carries, one per namespace (`moz` → module `Moz`). Each
|
|
710
|
+
# command whose params a vendor extends is overridden there with a closed params record of
|
|
711
|
+
# the variant's own (the shared fields plus the vendor's), so the vendor method mirrors the
|
|
687
712
|
# base command's wire method and result while adding the typed vendor fields. Empty for
|
|
688
713
|
# any domain (or schema) with no vendor extensions, so non-vendor output is unaffected.
|
|
689
714
|
def vendor_modules_for(domain)
|
|
690
715
|
parent = BiDiGenerate.snake_to_class_name(BiDiGenerate.camel_to_snake(domain))
|
|
691
|
-
@
|
|
692
|
-
overrides =
|
|
693
|
-
cmd =
|
|
694
|
-
|
|
716
|
+
@vendor_params.filter_map do |namespace, extended|
|
|
717
|
+
overrides = extended.filter_map do |type_name, fields|
|
|
718
|
+
cmd = params_command(type_name)
|
|
719
|
+
build_vendor_override(cmd, type_name, fields, namespace) if cmd['domain'] == domain
|
|
695
720
|
end
|
|
696
721
|
added = vendor_added_commands(domain, namespace).map { |cmd| build_added_command(cmd, namespace, domain) }
|
|
697
|
-
|
|
698
|
-
next if commands.empty?
|
|
722
|
+
next if overrides.empty? && added.empty?
|
|
699
723
|
|
|
700
724
|
VendorModule.new(name: BiDiGenerate.snake_to_class_name(namespace), namespace: namespace, parent: parent,
|
|
701
|
-
commands:
|
|
725
|
+
types: overrides.map(&:first), commands: overrides.map(&:last) + added)
|
|
702
726
|
end
|
|
703
727
|
end
|
|
704
728
|
|
|
@@ -716,35 +740,27 @@ module BiDiGenerate
|
|
|
716
740
|
command
|
|
717
741
|
end
|
|
718
742
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
743
|
+
# The vendor variant's params record and the command that builds it, as [record, command].
|
|
744
|
+
def build_vendor_override(cmd, type_name, fields, namespace)
|
|
745
|
+
shared = @types[type_name]['fields']
|
|
746
|
+
taken = shared.map { |field| field_name(field, false) }
|
|
747
|
+
type = @types[type_name].merge('fields' => shared + fields.map { |f| vendor_field(f, namespace, taken) })
|
|
748
|
+
record = record_class(type_name, type)
|
|
749
|
+
command = Command.new(wire_name: cmd['method'],
|
|
750
|
+
method_name: BiDiGenerate.safe_method_name(BiDiGenerate.camel_to_snake(cmd['name'])),
|
|
751
|
+
params: record_params(type['fields'], false),
|
|
752
|
+
result_ref: cmd['result'] && structured_ref(cmd['result']['ref']),
|
|
753
|
+
params_class: record.ruby_name, union_params: false, spec_href: cmd['specHref'])
|
|
754
|
+
[record, command]
|
|
731
755
|
end
|
|
732
756
|
|
|
733
757
|
# A vendor field's ruby name drops its namespace prefix (`moz:permanent` → permanent): the
|
|
734
758
|
# module already scopes it, so re-encoding the namespace in every identifier is redundant. The
|
|
735
759
|
# wire key is untouched. Falls back to the prefixed name only if stripping would collide with a
|
|
736
|
-
# shared
|
|
737
|
-
def
|
|
738
|
-
stripped = field['name'].
|
|
739
|
-
|
|
740
|
-
ruby_name = BiDiGenerate.safe_field_name(BiDiGenerate.camel_to_snake(field['name'])) if taken.include?(ruby_name)
|
|
741
|
-
Param.new(
|
|
742
|
-
ruby_name: ruby_name,
|
|
743
|
-
wire_name: field['wire'],
|
|
744
|
-
required: field['required'],
|
|
745
|
-
enum: enum_const(field['type']),
|
|
746
|
-
rbs: rbs_type(field['type'])
|
|
747
|
-
)
|
|
760
|
+
# shared field on the same record.
|
|
761
|
+
def vendor_field(field, namespace, taken)
|
|
762
|
+
stripped = field.merge('name' => field['name'].delete_prefix("#{namespace}:"))
|
|
763
|
+
taken.include?(field_name(stripped, false)) ? field : stripped
|
|
748
764
|
end
|
|
749
765
|
|
|
750
766
|
def type_kind(ref)
|
|
@@ -798,10 +814,11 @@ module BiDiGenerate
|
|
|
798
814
|
end
|
|
799
815
|
|
|
800
816
|
# The vendor types a vendor-added command in this spec domain reaches through its params and
|
|
801
|
-
# result,
|
|
817
|
+
# result, or a vendor field folded onto a spec type references, so they are emitted in the
|
|
818
|
+
# domain's module beside what uses them.
|
|
802
819
|
def vendor_types_reached(domain)
|
|
803
820
|
commands = @vendor.keys.flat_map { |namespace| vendor_added_commands(domain, namespace) }
|
|
804
|
-
pending = commands.flat_map { |c| [c.dig('params', 'ref'), c.dig('result', 'ref')] }.compact
|
|
821
|
+
pending = commands.flat_map { |c| [c.dig('params', 'ref'), c.dig('result', 'ref')] }.compact + @vendor_field_refs
|
|
805
822
|
reached = Set.new
|
|
806
823
|
until pending.empty?
|
|
807
824
|
name = pending.pop
|
|
@@ -921,33 +938,61 @@ module BiDiGenerate
|
|
|
921
938
|
# Frozen because it is shared across callers.
|
|
922
939
|
OPAQUE = {ref: nil, list: false, rbs: 'untyped'}.freeze
|
|
923
940
|
|
|
924
|
-
# Projects a schema type node to {ref:, list:, nullable:, rbs:}. Deriving the
|
|
941
|
+
# Projects a schema type node to {ref:, list:, nullable:, primitive:, bounds:, rbs:}. Deriving the
|
|
925
942
|
# serialization facts and the RBS signature from one walk keeps them from drifting
|
|
926
943
|
# apart when the schema shape changes.
|
|
927
944
|
def resolve(node)
|
|
928
945
|
nullable = node['nullable'] ? true : false
|
|
929
946
|
if node.key?('list')
|
|
930
|
-
|
|
931
|
-
return
|
|
932
|
-
rbs: nilable("Array[#{element[:rbs]}]", nullable)}
|
|
947
|
+
list = resolve_list(node['list'])
|
|
948
|
+
return list.merge(nullable: nullable, rbs: nilable(list[:rbs], nullable))
|
|
933
949
|
end
|
|
934
950
|
if node.key?('ref')
|
|
935
|
-
named = resolve_named(node['ref'])
|
|
951
|
+
named = narrowed(resolve_named(node['ref']), node)
|
|
936
952
|
return {ref: named[:ref], list: named[:list], nullable: nullable, scalar: named[:scalar],
|
|
937
|
-
map: named[:map],
|
|
953
|
+
map: named[:map], primitive: named[:primitive], bounds: named[:bounds],
|
|
954
|
+
rbs: nilable(named[:rbs], nullable)}
|
|
938
955
|
end
|
|
939
956
|
return resolve_union(node, nullable) if node.key?('union')
|
|
940
957
|
|
|
941
|
-
{ref: nil, list: false, nullable: nullable,
|
|
958
|
+
{ref: nil, list: false, nullable: nullable, primitive: checkable_primitive(node),
|
|
959
|
+
bounds: node.slice(*BOUND_KEYS), rbs: nilable(scalar_rbs(node), nullable)}
|
|
960
|
+
end
|
|
961
|
+
|
|
962
|
+
# A ref's own bounds narrow those of the alias it names (`js-uint .ge 1` is 1..2**53-1).
|
|
963
|
+
def narrowed(named, node)
|
|
964
|
+
own = node.slice(*BOUND_KEYS)
|
|
965
|
+
own.empty? ? named : named.merge(bounds: narrow_bounds(named[:bounds] || {}, own))
|
|
966
|
+
end
|
|
967
|
+
|
|
968
|
+
# A list's facts describe its element, so `primitive` (like `ref`) is the element's. A nested
|
|
969
|
+
# list's elements are lists, so no primitive check applies to them.
|
|
970
|
+
def resolve_list(element_node)
|
|
971
|
+
element = resolve(element_node)
|
|
972
|
+
# Record checks every element as non-null, so a nullable one would reject valid wire data.
|
|
973
|
+
raise "nullable list element #{element_node.inspect} is not supported by Record" if element[:nullable]
|
|
974
|
+
|
|
975
|
+
primitive, bounds = element.values_at(:primitive, :bounds) unless element[:list]
|
|
976
|
+
{ref: element[:ref], list: true, scalar: element[:scalar], primitive: primitive, bounds: bounds,
|
|
977
|
+
rbs: "Array[#{element[:rbs]}]"}
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
# Reached through alias chains too (resolve_named), so js-uint is checked as an integer.
|
|
981
|
+
def checkable_primitive(node)
|
|
982
|
+
node['primitive'] if CHECKABLE_PRIMITIVES.include?(node['primitive'])
|
|
942
983
|
end
|
|
943
984
|
|
|
944
985
|
# An inline union of one union-typed arm plus scalars (e.g. a MappingRemoteValue entry,
|
|
945
986
|
# RemoteValue / string) is carried as that union ref so nested entries are typed. Because
|
|
946
987
|
# the union is object_only, a bare-scalar sibling would raise there — so the projector's
|
|
947
988
|
# `scalar` signal (a bare-scalar arm is present) is forwarded, and the runtime passes a
|
|
948
|
-
# non-object leaf through instead (the map's string keys).
|
|
949
|
-
# multiple structured arms,
|
|
989
|
+
# non-object leaf through instead (the map's string keys). A union of scalars carries their
|
|
990
|
+
# primitives; any other shape (a record arm, multiple structured arms, a scalar beside an
|
|
991
|
+
# enum) stays opaque.
|
|
950
992
|
def resolve_union(node, nullable)
|
|
993
|
+
primitives = union_primitives(node['union'])
|
|
994
|
+
return scalar_union(primitives, nullable).merge(bounds: union_bounds(node['union'])) if primitives
|
|
995
|
+
|
|
951
996
|
refs = node['union'].select { |arm| arm.key?('ref') }
|
|
952
997
|
opaque = {ref: nil, list: false, nullable: nullable, rbs: nilable('untyped', nullable)}
|
|
953
998
|
return opaque unless refs.one? && union_ref?(refs.first['ref'])
|
|
@@ -957,6 +1002,33 @@ module BiDiGenerate
|
|
|
957
1002
|
rbs: nilable('untyped', nullable)}
|
|
958
1003
|
end
|
|
959
1004
|
|
|
1005
|
+
# The primitives a union of scalars admits (pageRanges' `js-uint / text`), or nil when any arm
|
|
1006
|
+
# is something else (an enum, a const, a ref), which no primitive check can admit.
|
|
1007
|
+
def union_primitives(arms)
|
|
1008
|
+
primitives = arms.map do |arm|
|
|
1009
|
+
resolved = resolve(arm)
|
|
1010
|
+
resolved[:primitive] unless arm.key?('const') || resolved[:list] || resolved[:nullable]
|
|
1011
|
+
end
|
|
1012
|
+
primitives.flatten.uniq unless primitives.include?(nil)
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
# The runtime holds every numeric value a field carries to one set of bounds, so numeric arms
|
|
1016
|
+
# that disagree (`js-uint / float`) would be checked wrongly; fail generation instead.
|
|
1017
|
+
def union_bounds(arms)
|
|
1018
|
+
numeric = arms.map { |arm| resolve(arm) }.select { |arm| NUMERIC_PRIMITIVES.include?(arm[:primitive]) }
|
|
1019
|
+
bounds = numeric.map { |arm| arm[:bounds] || {} }.uniq
|
|
1020
|
+
raise "numeric union arms #{arms} have different bounds: #{bounds}" if bounds.size > 1
|
|
1021
|
+
|
|
1022
|
+
bounds.first
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
def scalar_union(primitives, nullable)
|
|
1026
|
+
rbs = primitives.map { |primitive| PRIMITIVE_RBS.fetch(primitive) }.join(' | ')
|
|
1027
|
+
rbs = "(#{rbs})" if nullable && primitives.size > 1
|
|
1028
|
+
{ref: nil, list: false, nullable: nullable, primitive: primitives.one? ? primitives.first : primitives,
|
|
1029
|
+
rbs: nilable(rbs, nullable)}
|
|
1030
|
+
end
|
|
1031
|
+
|
|
960
1032
|
# True when a ref (following aliases) is a union — the only arm whose from_json tolerates a
|
|
961
1033
|
# scalar sibling. A record arm would raise on one, so it is not carried.
|
|
962
1034
|
def union_ref?(name)
|
|
@@ -991,13 +1063,20 @@ module BiDiGenerate
|
|
|
991
1063
|
# A field-less record is either a genuinely empty one (EmptyParams, Extensible — opaque)
|
|
992
1064
|
# or the projector's stand-in for an inline map (`{*text => text}` is hoisted to a record
|
|
993
1065
|
# with no fields and the value type under `map`, e.g. script.NodePropertiesAttributes).
|
|
994
|
-
# A
|
|
995
|
-
# a
|
|
1066
|
+
# A map whose values are a primitive, or a union of primitives (Firefox `prefs`), becomes a
|
|
1067
|
+
# `map` descriptor the runtime checks and a typed Hash; a ref-valued one stays opaque for now.
|
|
996
1068
|
def map_record(type)
|
|
997
|
-
|
|
998
|
-
return OPAQUE unless
|
|
1069
|
+
primitives = map_value_primitives(type['map'])
|
|
1070
|
+
return OPAQUE unless primitives
|
|
1071
|
+
|
|
1072
|
+
rbs = primitives.map { |primitive| PRIMITIVE_RBS.fetch(primitive) }.join(' | ')
|
|
1073
|
+
{ref: nil, list: false, map: primitives.one? ? primitives.first : primitives,
|
|
1074
|
+
bounds: union_bounds(type['map']['union'] || [type['map']]), rbs: "Hash[String, #{rbs}]"}
|
|
1075
|
+
end
|
|
999
1076
|
|
|
1000
|
-
|
|
1077
|
+
# Each arm's checkable primitive, following aliases (js-int is an integer); nil if any arm has none.
|
|
1078
|
+
def map_value_primitives(node)
|
|
1079
|
+
union_primitives(node['union'] || [node]) if node
|
|
1001
1080
|
end
|
|
1002
1081
|
|
|
1003
1082
|
# A named structured type's serialization ref (nil for a dotless/global type, never
|
|
@@ -1025,14 +1104,11 @@ module BiDiGenerate
|
|
|
1025
1104
|
|
|
1026
1105
|
def resolve_named_alias(name, inner, seen)
|
|
1027
1106
|
return named_union(name) if inner.key?('union')
|
|
1028
|
-
return resolve_named(inner['ref'], seen) if inner.key?('ref')
|
|
1029
|
-
|
|
1030
|
-
if inner.key?('list')
|
|
1031
|
-
element = resolve(inner['list'])
|
|
1032
|
-
return {ref: element[:ref], list: true, scalar: element[:scalar], rbs: "Array[#{element[:rbs]}]"}
|
|
1033
|
-
end
|
|
1107
|
+
return narrowed(resolve_named(inner['ref'], seen), inner) if inner.key?('ref')
|
|
1108
|
+
return resolve_list(inner['list']) if inner.key?('list')
|
|
1034
1109
|
|
|
1035
|
-
{ref: nil, list: false,
|
|
1110
|
+
{ref: nil, list: false, primitive: checkable_primitive(inner), bounds: inner.slice(*BOUND_KEYS),
|
|
1111
|
+
rbs: scalar_rbs(inner)}
|
|
1036
1112
|
end
|
|
1037
1113
|
|
|
1038
1114
|
def nilable(type, flag)
|
|
@@ -1055,9 +1131,7 @@ module BiDiGenerate
|
|
|
1055
1131
|
fields = type['fields'].reject { |f| baked_discriminator?(f) }.map { |f| field_ir(f, extensible) }
|
|
1056
1132
|
# Every extensible type gets the extensions store: an undeclared wire key is preserved
|
|
1057
1133
|
# and echoed back on any type the spec marks extensible, whether or not it is re-sendable.
|
|
1058
|
-
# Extensibility alone is the signal; send-reachability does not enter into it.
|
|
1059
|
-
# vendor extends is open too: its vendor variant composes the vendor fields through that
|
|
1060
|
-
# store (see VendorCommand), which a closed record would reject.
|
|
1134
|
+
# Extensibility alone is the signal; send-reachability does not enter into it.
|
|
1061
1135
|
TypeClass.new(ruby_name: BiDiGenerate.type_class_name(name), fields: fields,
|
|
1062
1136
|
discriminator: discriminator, extensible: extensible,
|
|
1063
1137
|
schema_name: name, synthetic: type['synthetic'] ? true : false,
|
|
@@ -1065,12 +1139,8 @@ module BiDiGenerate
|
|
|
1065
1139
|
**directionality(name))
|
|
1066
1140
|
end
|
|
1067
1141
|
|
|
1068
|
-
def vendor_extended?(name)
|
|
1069
|
-
@vendor.values.any? { |spec| (spec['extends'] || {}).key?(name) }
|
|
1070
|
-
end
|
|
1071
|
-
|
|
1072
1142
|
def extensible_record?(name)
|
|
1073
|
-
@types.dig(name, 'extensible')
|
|
1143
|
+
@types.dig(name, 'extensible') ? true : false
|
|
1074
1144
|
end
|
|
1075
1145
|
|
|
1076
1146
|
def field_name(field, extensible)
|
|
@@ -1091,8 +1161,9 @@ module BiDiGenerate
|
|
|
1091
1161
|
FieldIR.new(ruby_name: ruby_name, wire_key: field['wire'],
|
|
1092
1162
|
required: field['required'], nullable: resolved[:nullable],
|
|
1093
1163
|
ref: resolved[:ref], list: resolved[:list], enum: enum_const(field['type']),
|
|
1094
|
-
primitive:
|
|
1095
|
-
const: leaf_const(field['type']), map_of: resolved[:map],
|
|
1164
|
+
primitive: resolved[:primitive], scalar: resolved[:scalar],
|
|
1165
|
+
const: leaf_const(field['type']), map_of: resolved[:map], bounds: resolved[:bounds] || {},
|
|
1166
|
+
rbs: resolved[:rbs])
|
|
1096
1167
|
end
|
|
1097
1168
|
|
|
1098
1169
|
# The literal value of a const field, following alias chains, so the runtime can reject a
|
|
@@ -1111,21 +1182,24 @@ module BiDiGenerate
|
|
|
1111
1182
|
leaf_const(type['type'], seen)
|
|
1112
1183
|
end
|
|
1113
1184
|
|
|
1114
|
-
#
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
def leaf_primitive(node, seen = {})
|
|
1120
|
-
return node['primitive'] if node.key?('primitive') && CHECKABLE_PRIMITIVES.include?(node['primitive'])
|
|
1121
|
-
return nil unless node.key?('ref')
|
|
1185
|
+
# A value must satisfy both, so each side keeps the tighter bound, and at an equal value the exclusive one.
|
|
1186
|
+
def narrow_bounds(bounds, narrowing)
|
|
1187
|
+
result = bounds.dup
|
|
1188
|
+
{'min' => 'exclusiveMin', 'max' => 'exclusiveMax'}.each do |side, flag|
|
|
1189
|
+
next unless narrowing.key?(side) && tighter_bound?(side, flag, narrowing, bounds)
|
|
1122
1190
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1191
|
+
result[side] = narrowing[side]
|
|
1192
|
+
result.delete(flag)
|
|
1193
|
+
result[flag] = true if narrowing[flag]
|
|
1194
|
+
end
|
|
1195
|
+
result.slice(*BOUND_KEYS)
|
|
1196
|
+
end
|
|
1126
1197
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1198
|
+
def tighter_bound?(side, flag, candidate, current)
|
|
1199
|
+
return true unless current.key?(side)
|
|
1200
|
+
return candidate[flag] && !current[flag] if candidate[side] == current[side]
|
|
1201
|
+
|
|
1202
|
+
side == 'min' ? candidate[side] > current[side] : candidate[side] < current[side]
|
|
1129
1203
|
end
|
|
1130
1204
|
|
|
1131
1205
|
def union_class(name)
|
|
@@ -1238,6 +1312,10 @@ module BiDiGenerate
|
|
|
1238
1312
|
# const, or opaque value) gets no descriptor and is left unchecked — lenient, so a missed
|
|
1239
1313
|
# check fails open rather than a wrong strict default rejecting valid data.
|
|
1240
1314
|
CHECKABLE_PRIMITIVES = %w[string number integer boolean].freeze
|
|
1315
|
+
NUMERIC_PRIMITIVES = %w[number integer].freeze
|
|
1316
|
+
|
|
1317
|
+
# The schema's numeric bound keys, in the order the field facts render them.
|
|
1318
|
+
BOUND_KEYS = %w[min max exclusiveMin exclusiveMax].freeze
|
|
1241
1319
|
|
|
1242
1320
|
# The leaf of +resolve+: the bare scalar type, before any nullable wrap. An alias's
|
|
1243
1321
|
# own nullable is intentionally left off — only the referencing node's is applied.
|
|
@@ -35,7 +35,7 @@ module Selenium
|
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
attr_accessor :open_timeout, :read_timeout, :max_redirects, :proxy
|
|
38
|
+
attr_accessor :open_timeout, :read_timeout, :max_redirects, :proxy, :ignore_certificates
|
|
39
39
|
attr_writer :extra_headers, :user_agent
|
|
40
40
|
attr_reader :server_url
|
|
41
41
|
|
|
@@ -44,6 +44,7 @@ module Selenium
|
|
|
44
44
|
# @param [Numeric] read_timeout Seconds to wait for a response.
|
|
45
45
|
# @param [Integer] max_redirects Maximum number of redirects to follow.
|
|
46
46
|
# @param [Proxy] proxy Proxy to use for the connection.
|
|
47
|
+
# @param [Boolean] ignore_certificates Skip verifying the server certificate (unsafe outside a trusted network).
|
|
47
48
|
# @param [Hash] extra_headers Additional headers to send with each request.
|
|
48
49
|
# @param [String] user_agent Value to send as the User-Agent header.
|
|
49
50
|
# @param [String, URI] server_url URL of the server to connect to.
|
|
@@ -52,6 +53,7 @@ module Selenium
|
|
|
52
53
|
read_timeout: 120,
|
|
53
54
|
max_redirects: 20,
|
|
54
55
|
proxy: nil,
|
|
56
|
+
ignore_certificates: false,
|
|
55
57
|
extra_headers: nil,
|
|
56
58
|
user_agent: nil,
|
|
57
59
|
server_url: nil)
|
|
@@ -59,6 +61,7 @@ module Selenium
|
|
|
59
61
|
@read_timeout = read_timeout
|
|
60
62
|
@max_redirects = max_redirects
|
|
61
63
|
@proxy = proxy || proxy_from_environment
|
|
64
|
+
@ignore_certificates = ignore_certificates
|
|
62
65
|
@extra_headers = extra_headers
|
|
63
66
|
@user_agent = user_agent
|
|
64
67
|
self.server_url = server_url
|
|
@@ -97,8 +97,11 @@ module Selenium
|
|
|
97
97
|
args << @options.binary.gsub('\\', '\\\\\\')
|
|
98
98
|
end
|
|
99
99
|
if @options.proxy
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
proxy = @options.proxy.ssl || @options.proxy.http
|
|
101
|
+
if proxy
|
|
102
|
+
args << '--proxy'
|
|
103
|
+
args << proxy
|
|
104
|
+
end
|
|
102
105
|
end
|
|
103
106
|
args
|
|
104
107
|
end
|