Class: BiDiGenerate::Schema Private

Inherits:
Object
  • Object
show all
Defined in:
rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Instance Method Summary collapse

Constructor Details

#initialize(schema) ⇒ Schema

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Vendor content (schema vendor.<namespace>) is folded in beside the spec's: a vendor module's commands and events become a domain of their own, and its types resolve like spec types and are emitted in that module (see owned_types).



581
582
583
584
585
586
587
588
589
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 581

def initialize(schema)
  @vendor = schema['vendor'] || {}
  @vendor_types = @vendor.values.map { |section| section['types'] || {} }.reduce({}, :merge)
  @types = schema['types'].merge(@vendor_types)
  @commands = schema['commands'] + vendor_messages('commands').select { |c| vendor_domain?(c['domain']) }
  @events = schema['events'] + vendor_events
  @domains = schema['domains'] || {}
  promote_command_params_records!
end

Instance Method Details

#build_added_command(cmd, namespace, domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The vendor variant already scopes the command, so its name drops the namespace (moz:listExtensions → list_extensions); a name that would silently override a spec command fails generation instead.



707
708
709
710
711
712
713
714
715
716
717
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 707

def build_added_command(cmd, namespace, domain)
  command = BiDiGenerate.build_command(self, cmd.merge('name' => cmd['name'].delete_prefix("#{namespace}:")))
  spec_names = commands_for(domain).map do |c|
    BiDiGenerate.safe_method_name(BiDiGenerate.camel_to_snake(c['name']))
  end
  if spec_names.include?(command.method_name)
    raise "vendor command #{cmd['method']} would override spec command #{domain}.#{command.method_name}"
  end

  command
end

#build_vendor_command(cmd, type_name, entry, namespace) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



719
720
721
722
723
724
725
726
727
728
729
730
731
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 719

def build_vendor_command(cmd, type_name, entry, namespace)
  shared = record_params(@types[type_name]['fields'], extensible_record?(type_name))
  taken = shared.map(&:ruby_name)
  VendorCommand.new(
    method_name: BiDiGenerate.safe_method_name(BiDiGenerate.camel_to_snake(cmd['name'])),
    wire_name: cmd['method'],
    result_ref: cmd['result'] && structured_ref(cmd['result']['ref']),
    params_class: BiDiGenerate.type_class_name(type_name),
    shared_params: shared,
    vendor_params: entry['fields'].map { |field| vendor_param(field, namespace, taken) },
    spec_href: cmd['specHref']
  )
end

#command_wrapper_refs(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The domain's command param/result wrapper type names — the classes a command constructs (params) or parses its result into. They are reachable (so tagged outbound/inbound) but are the message wrappers a command method already builds, not data a caller composes, so they are excluded from the type accessors.



661
662
663
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 661

def command_wrapper_refs(domain)
  commands_for(domain).flat_map { |c| [c.dig('params', 'ref'), c.dig('result', 'ref')] }.compact.to_set
end

#commands_for(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



653
654
655
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 653

def commands_for(domain)
  @commands.select { |c| c['domain'] == domain }
end

#domain_href(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The domain's #module-<domain> spec link, or nil when the schema has none.



617
618
619
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 617

def domain_href(domain)
  @domains.dig(domain, 'specHref')
end

#domain_relative_path(name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The class path to a type relative to its domain class (an accessor body resolves in the Domain subclass scope): "ExtensionData", or "AccessibilityLocator::Value" for a synthetic nested under its owner.



679
680
681
682
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 679

def domain_relative_path(name)
  prefix = "#{BiDiGenerate.snake_to_class_name(BiDiGenerate.camel_to_snake(name.split('.', 2).first))}::"
  ruby_path(name).sub(/\A#{Regexp.escape(prefix)}/, '')
end

#domains ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Domains that carry a command or event each become one generated module.



649
650
651
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 649

def domains
  (@commands + @events).map { |entry| entry['domain'] }.uniq
end

#enums_for(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Enum types declared under "." become nested constant modules.



775
776
777
778
779
780
781
782
783
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 775

def enums_for(domain)
  owned_types(domain).filter_map do |name, type|
    next unless type['kind'] == 'enum'

    pairs = type['values'].map { |v| [BiDiGenerate.enum_key(v), v] }
    Enum.new(constant_name: BiDiGenerate.screaming_snake(name.split('.', 2).last), pairs: pairs,
             primitive: type['primitive'], spec_href: type['specHref'])
  end
end

#envelope_synthetic?(type) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A synthetic record lifted out as an envelope's params (its owner is an envelope).

Returns:

  • (Boolean)


860
861
862
863
864
865
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 860

def envelope_synthetic?(type)
  return false unless type['synthetic']

  owner = @types[type['owner']]
  owner && owner['kind'] == 'record' && message_envelope?(owner)
end

#error_codes ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The protocol-root ErrorCode enum's wire values (e.g. "no such frame"), in schema order. Used to generate the BiDi-specific Error subclasses. [] when the schema has no ErrorCode.



826
827
828
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 826

def error_codes
  @types.dig('ErrorCode', 'values') || []
end

#events_for(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



754
755
756
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 754

def events_for(domain)
  @events.select { |e| e['domain'] == domain }
end

#message_envelope?(type) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A protocol message envelope is a record with a baked method discriminator ({method: <const>, params: …}) — the wire shape of a command/event message. No value type carries a const method field, so this is unambiguous.

Returns:

  • (Boolean)


855
856
857
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 855

def message_envelope?(type)
  type['fields'].any? { |f| f['wire'] == 'method' && f['type'].key?('const') }
end

#owned_types(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The types a domain's module declares. A spec domain's are the "." types the spec defines; a vendor's helper types under that prefix are not emitted until a generated command needs them. A vendor module (moz:debugging) owns the vendor types its commands and events name (Mozilla prefixes them mozDebugging.), read off their params and result refs.



789
790
791
792
793
794
795
796
797
798
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 789

def owned_types(domain)
  vendor = vendor_domain?(domain)
  prefixes = type_prefixes(domain).map { |prefix| "#{prefix}." }
  added = vendor ? Set.new : vendor_types_reached(domain)
  @types.select do |name, _|
    next false unless prefixes.any? { |p| name.start_with?(p) }

    @vendor_types.key?(name) ? vendor || added.include?(name) : !vendor
  end
end

#params_for(params_ref) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Flat params for a command: the record's fields, or — for a union of records — the merged superset of variant fields. Returns [] for commands with no params, or nil when params can't be flattened (alias, or a union whose variants aren't all records) so the caller forwards verbatim.



762
763
764
765
766
767
768
769
770
771
772
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 762

def params_for(params_ref)
  return [] unless params_ref

  type = @types[params_ref['ref']]
  return nil unless type

  case type['kind']
  when 'record' then record_params(type['fields'], extensible_record?(params_ref['ref']))
  when 'union' then union_params(type, params_ref['ref'])
  end
end

#plainly_reached_types ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Type names reached by at least one non-union-arm reference: used as a record field, list element, map value, or alias target somewhere — not solely as a named union's variant. A type reached only as a union arm is built through its union (a variant factory or the command's flattened dispatch), so a nested one needs no accessor; one reached as a plain field ref (browsingContext.AccessibilityLocator's value) does.



670
671
672
673
674
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 670

def plainly_reached_types
  @plainly_reached_types ||= @types.each_value.with_object(Set.new) do |node, reached|
    plain_refs(node).each { |ref| reached << ref }
  end
end

#promote_command_params_records! ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A command written in CDDL map form carries its params as an inline object (rather than the usual group form referencing a named params type). The projector links the command to those params, but hoists them into a synthetic record owned by the command's message envelope. That envelope is suppressed (Transport forms it), so the synthetic params record would never be emitted even though the command's params ref points straight at it. Promote it to a top-level domain record so the generator emits and references it like any other params type. Today this is exactly userAgentClientHints.setClientHintsOverride.



629
630
631
632
633
634
635
636
637
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 629

def promote_command_params_records!
  @commands.each do |cmd|
    ref = cmd.dig('params', 'ref')
    next unless ref

    type = @types[ref]
    promote_to_domain_type!(ref) if type && envelope_synthetic?(type)
  end
end

#promote_to_domain_type!(name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Strip the synthetic/owner/label tags so a lifted-out type emits as a top-level domain record instead of nesting under its (suppressed) envelope.



641
642
643
644
645
646
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 641

def promote_to_domain_type!(name)
  type = @types[name]
  type&.delete('synthetic')
  type&.delete('owner')
  type&.delete('label')
end

#ruby_path_for(name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Public ruby-path resolver (Owner::Label for a synthetic), matching how a variant's ref is emitted — so a caller can map a variant ref back to its emitted record.



876
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 876

def ruby_path_for(name) = ruby_path(name)

#structured_ref(name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The Protocol-relative class path a command result parses into, or nil when it is non-structured (or a bare list, returned raw).



869
870
871
872
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 869

def structured_ref(name)
  resolved = resolve_named(name)
  resolved[:list] ? nil : resolved[:ref]
end

#suppressed_record?(type) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Records the generator deliberately does not emit: a message envelope, or a synthetic params record lifted out of one. Both are reachable only through the envelope, which Transport replaces — so nothing else references them.

Returns:

  • (Boolean)


848
849
850
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 848

def suppressed_record?(type)
  message_envelope?(type) || envelope_synthetic?(type)
end

#type_kind(ref) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



750
751
752
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 750

def type_kind(ref)
  @types[ref]&.fetch('kind', nil)
end

#type_prefixes(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



816
817
818
819
820
821
822
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 816

def type_prefixes(domain)
  return [domain] unless vendor_domain?(domain)

  messages = commands_for(domain) + events_for(domain)
  refs = messages.flat_map { |m| [m.dig('params', 'ref'), m.dig('result', 'ref')] }
  refs.compact.select { |ref| ref.include?('.') }.map { |ref| ref.split('.', 2).first }.uniq
end

#types_for(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Structured value classes (records + discriminated unions) declared under "." Empty records are projector artifacts with nothing to carry, so they stay opaque hashes; only non-empty records and unions become classes. Command/event message envelopes (the {method, params} wire wrapper) are skipped — Transport forms that envelope, so nothing references them.



835
836
837
838
839
840
841
842
843
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 835

def types_for(domain)
  owned_types(domain).filter_map do |name, type|
    case type['kind']
    when 'record' then record_class(name, type) unless type['fields'].empty? || suppressed_record?(type)
    when 'union' then union_class(name)
    when 'alias' then union_class(name) if type['type'].key?('union')
    end
  end
end

#vendor_added_commands(domain, namespace) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The commands a vendor defines in a spec domain (webExtension.moz:listExtensions). They are emitted on that domain's vendor variant, never on the shared class (see vendor_modules_for).



610
611
612
613
614
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 610

def vendor_added_commands(domain, namespace)
  return [] if vendor_domain?(domain)

  (@vendor.dig(namespace, 'commands') || []).select { |c| c['domain'] == domain }
end

#vendor_domain?(domain) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A vendor module's domain keeps its wire prefix (moz:debugging); a spec domain has none.

Returns:

  • (Boolean)


592
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 592

def vendor_domain?(domain) = domain.include?(':')

#vendor_events ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A vendor event in a spec domain would need the same vendor-variant routing as a command; none exists yet, so fail generation rather than leak it into the shared class.



600
601
602
603
604
605
606
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 600

def vendor_events
  events = vendor_messages('events')
  stray = events.reject { |e| vendor_domain?(e['domain']) }
  raise "vendor event #{stray.first['method']} in spec domain #{stray.first['domain']} is unsupported" if stray.any?

  events
end

#vendor_messages(kind) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



594
595
596
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 594

def vendor_messages(kind)
  @vendor.values.flat_map { |section| section[kind] || [] }
end

#vendor_modules_for(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The vendor modules a domain carries, one per namespace (moz → module Moz). The schema's vendor section names, per namespace, which shared type each vendor extends; we map that type back to the command that sends it, so the vendor method mirrors the base command's wire method and result while adding the typed vendor fields. Empty for any domain (or schema) with no vendor extensions, so non-vendor output is unaffected.



689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 689

def vendor_modules_for(domain)
  parent = BiDiGenerate.snake_to_class_name(BiDiGenerate.camel_to_snake(domain))
  @vendor.filter_map do |namespace, spec|
    overrides = (spec['extends'] || {}).filter_map do |type_name, entry|
      cmd = @commands.find { |c| c.dig('params', 'ref') == type_name }
      build_vendor_command(cmd, type_name, entry, namespace) if cmd && cmd['domain'] == domain
    end
    added = vendor_added_commands(domain, namespace).map { |cmd| build_added_command(cmd, namespace, domain) }
    commands = overrides + added
    next if commands.empty?

    VendorModule.new(name: BiDiGenerate.snake_to_class_name(namespace), namespace: namespace, parent: parent,
                     commands: commands)
  end
end

#vendor_param(field, namespace, taken) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A vendor field's ruby name drops its namespace prefix (moz:permanent → permanent): the module already scopes it, so re-encoding the namespace in every identifier is redundant. The wire key is untouched. Falls back to the prefixed name only if stripping would collide with a shared param on the same command.



737
738
739
740
741
742
743
744
745
746
747
748
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 737

def vendor_param(field, namespace, taken)
  stripped = field['name'].sub(/\A#{Regexp.escape(namespace)}:/, '')
  ruby_name = BiDiGenerate.safe_field_name(BiDiGenerate.camel_to_snake(stripped))
  ruby_name = BiDiGenerate.safe_field_name(BiDiGenerate.camel_to_snake(field['name'])) if taken.include?(ruby_name)
  Param.new(
    ruby_name: ruby_name,
    wire_name: field['wire'],
    required: field['required'],
    enum: enum_const(field['type']),
    rbs: rbs_type(field['type'])
  )
end

#vendor_types_reached(domain) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The vendor types a vendor-added command in this spec domain reaches through its params and result, so they are emitted in the domain's module beside the command that returns them.



802
803
804
805
806
807
808
809
810
811
812
813
814
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 802

def vendor_types_reached(domain)
  commands = @vendor.keys.flat_map { |namespace| vendor_added_commands(domain, namespace) }
  pending = commands.flat_map { |c| [c.dig('params', 'ref'), c.dig('result', 'ref')] }.compact
  reached = Set.new
  until pending.empty?
    name = pending.pop
    next if reached.include?(name) || !@vendor_types.key?(name)

    reached << name
    pending.concat(plain_refs(@types[name]) + (@types[name]['variants'] || []))
  end
  reached
end