Skip to content

[rb] add objectOnly/preserveExtras/scalar-primitive BiDi schema signals#17784

Merged
titusfortner merged 3 commits into
SeleniumHQ:trunkfrom
titusfortner:bidi-schema-signals
Jul 16, 2026
Merged

[rb] add objectOnly/preserveExtras/scalar-primitive BiDi schema signals#17784
titusfortner merged 3 commits into
SeleniumHQ:trunkfrom
titusfortner:bidi-schema-signals

Conversation

@titusfortner

@titusfortner titusfortner commented Jul 15, 2026

Copy link
Copy Markdown
Member

🔗 Related Issues

Builds on #17781 (this branch is rebased onto it — merge that one first; the diff resolves to just the signal changes once #17781 lands on trunk).

Supports the low-level BiDi behavioral contract proposed in #17786: these changes make the Ruby layer exhibit the inbound-strictness items at runtime (object-only unions reject non-objects — item 5; strict primitive/shape field checks — item 6; preserve-extras scoped to extensible-and-re-sendable types — item 8), rather than merely type-checking. The derivations live in the shared projector, so the same signals let the other bindings comply too.

💥 What does this PR do?

Adds three derived signals to the shared BiDi schema projector and consumes them in the generated Ruby binding, so the wire boundary is validated instead of passing malformed payloads through opaque:

  • objectOnly — a union whose every arm is an object rejects a non-object payload instead of returning it unchanged. Unions with a real scalar arm (e.g. input.Origin) still pass scalars through.
  • preserveExtras — only an extensible type that is also re-sendable (reachable from a command's params) stores and echoes unknown wire properties; a received-only extensible type now drops them.
  • complete scalar primitives — an inline literal choice the normalizer left un-hoisted now carries the primitive its literals share, so a previously-opaque scalar is strictly typed.

Every derivation lives in the shared projector, so bindings stay in parity by construction; each Ruby change is a one-line consume plus runtime support.

🔧 Implementation Notes

  • The Ruby generator now follows alias chains to surface a field's leaf primitive (e.g. js-uint → integer), matching what the Python/Java generators already do — this is what lets Ruby strictly type ~280 previously-opaque scalar fields.
  • Map keys stay lenient without weakening objectOnly: the projector marks an inline union that carries a bare-scalar arm (RemoteValue / text) with scalar, set to that arm's primitive. A binding collapsing the union onto the object-only value union passes a non-object leaf through only when it matches that primitive — a wrong-typed scalar (a number where a string is expected) is still a wire error. The tolerance is derived in the projector, so bindings consume it rather than re-detecting the shape.

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s): Claude Code
    • What was generated: projector signal derivations + unit tests, Ruby generator/runtime/spec changes, and the regenerated protocol files
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

@selenium-ci selenium-ci added C-rb Ruby Bindings C-nodejs JavaScript Bindings B-devtools Includes everything BiDi or Chrome DevTools related B-support Issue or PR related to support classes labels Jul 15, 2026
@selenium-ci

Copy link
Copy Markdown
Member

Thank you, @titusfortner for this code suggestion.

The support packages contain example code that many users find helpful, but they do not necessarily represent
the best practices for using Selenium, and the Selenium team is not currently merging changes to them.

After reviewing the change, unless it is a critical fix or a feature that is needed for Selenium
to work, we will likely close the PR.

We actively encourage people to add the wrapper and helper code that makes sense for them to their own frameworks.
If you have any questions, please contact us

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

[rb] Add BiDi schema signals for object-only unions, preserve-extras, and scalar primitives

✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Derive objectOnly, preserveExtras, and scalar primitive signals in the shared BiDi schema
 projector.
• Consume signals in Ruby generator/runtime to reject malformed wire payloads earlier.
• Add unit/spec coverage for object-only validation, extras round-tripping, and primitive typing.
Diagram

graph TD
  A["CDDL AST + model"] --> B["JS BiDi schema projector"] --> C["Projected schema (types/refs/signals)"] --> D["Ruby BiDi generator"] --> E["Generated Ruby protocol types"] --> F["Ruby serialization runtime"]
  E --> F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Re-derive signals per binding (Ruby/Python/Java/etc.)
  • ➕ Avoids changes to the shared projector output format
  • ➕ Lets each binding tailor derivation to its runtime model
  • ➖ High parity risk: subtle drift across languages over time
  • ➖ Duplicate complex schema-walk logic (aliases/unions/reachability)
  • ➖ Harder to test/validate consistently across bindings
2. Runtime schema introspection + generic validator (no generator changes)
  • ➕ Centralizes validation behavior in one runtime layer
  • ➕ Potentially reduces generator surface area
  • ➖ Ruby runtime would need richer schema access and a generic validator
  • ➖ More runtime overhead and more complex error reporting
  • ➖ Still needs the same derivations somewhere (object-only, resendable closure)
3. Always preserve unknown extras for all extensible records
  • ➕ Simpler rule; fewer schema signals
  • ➕ Maximizes round-tripping safety
  • ➖ Changes semantics for received-only extensible types (potentially surprising)
  • ➖ Encourages silently carrying unknown data farther than intended
  • ➖ Increases memory footprint for high-volume event payloads

Recommendation: Keep the current approach: derive objectOnly/preserveExtras/primitive and scalar once in the shared projector and have bindings consume them. This minimizes cross-language drift, keeps generator/runtime changes small (mostly one-line consumes plus targeted runtime support), and provides explicit, testable guarantees at the wire boundary without duplicating schema-walk logic per binding.

Files changed (22) +698 / -322

Enhancement (20) +540 / -322
project_bidi_schema.mjsDerive 'objectOnly', 'preserveExtras', and primitive/scalar signals in projected schema +123/-6

Derive 'objectOnly', 'preserveExtras', and primitive/scalar signals in projected schema

• Adds projector-side derivations for object-only unions, resendable-only extras preservation, and scalar typing signals. Inline literal choices now carry their shared primitive, and inline unions mark 'scalar: true' when a bare-scalar arm exists to support map-key passthrough in bindings.

javascript/selenium-webdriver/project_bidi_schema.mjs

bluetooth.rbConsume primitives and object-only unions in generated Bluetooth protocol +22/-21

Consume primitives and object-only unions in generated Bluetooth protocol

• Updates generated record fields to include explicit wire_key/primitive metadata for previously opaque scalars. Marks a discriminated union as 'object_only' so non-object payloads are rejected at parse time.

rb/lib/selenium/webdriver/bidi/protocol/bluetooth.rb

browser.rbTighten scalar typing and enforce object-only unions in Browser protocol +17/-13

Tighten scalar typing and enforce object-only unions in Browser protocol

• Annotates multiple fields with leaf primitives (e.g., integers/strings) rather than leaving them opaque. Marks relevant unions 'object_only' to prevent scalar passthrough when all variants are records.

rb/lib/selenium/webdriver/bidi/protocol/browser.rb

browsing_context.rbAdd primitives and object-only union enforcement across BrowsingContext types +73/-62

Add primitives and object-only union enforcement across BrowsingContext types

• Adds primitive metadata for many scalar fields (string/integer), improving wire validation. Marks unions like locators and clip rectangles as 'object_only' to reject malformed non-object payloads.

rb/lib/selenium/webdriver/bidi/protocol/browsing_context.rb

emulation.rbMark object-only union and type inline-enum primitive fields for Emulation +7/-3

Mark object-only union and type inline-enum primitive fields for Emulation

• Marks a union as 'object_only' and adds primitive annotations (e.g., ScreenArea integers, scrollbar_type string?). Enables strict primitive validation for an inline literal-choice field previously treated as untyped.

rb/lib/selenium/webdriver/bidi/protocol/emulation.rb

input.rbEnforce object-only unions and add integer primitives in Input actions +27/-20

Enforce object-only unions and add integer primitives in Input actions

• Marks action unions as 'object_only' and annotates multiple scalar fields (durations/buttons/coordinates) with integer primitives. Improves rejection of wrong-primitive payloads at deserialization.

rb/lib/selenium/webdriver/bidi/protocol/input.rb

log.rbReject scalar payloads for log entry union and type timestamps +5/-4

Reject scalar payloads for log entry union and type timestamps

• Marks the log Entry union as 'object_only' and annotates timestamps as integers. Prevents silently accepting scalars where only record variants are valid.

rb/lib/selenium/webdriver/bidi/protocol/log.rb

network.rbType more Network scalars and mark object-only unions +68/-62

Type more Network scalars and mark object-only unions

• Adds primitive annotations for many scalar fields (timestamps, sizes, status codes, ids) to enable strict validation. Marks unions like BytesValue/UrlPattern and auth continuation unions as 'object_only'.

rb/lib/selenium/webdriver/bidi/protocol/network.rb

script.rbEnforce object-only unions and support scalar-tolerant map entries in Script values +91/-82

Enforce object-only unions and support scalar-tolerant map entries in Script values

• Marks key unions (RemoteValue, LocalValue, PrimitiveProtocolValue, etc.) as 'object_only' so scalars are rejected when no scalar arm exists. For map/object entry lists, forwards 'scalar: true' on the 'value' field to keep bare-string map keys while still typing object values.

rb/lib/selenium/webdriver/bidi/protocol/script.rb

session.rbMark object-only unions and adjust extensible handling in Session protocol +4/-3

Mark object-only unions and adjust extensible handling in Session protocol

• Marks unions (e.g., ProxyConfiguration, UnsubscribeParameters) as 'object_only'. Removes generated extensible store for a type where preserve-extras is not enabled, aligning runtime behavior with the new 'preserveExtras' signal.

rb/lib/selenium/webdriver/bidi/protocol/session.rb

storage.rbApply preserve-extras gating and object-only unions in Storage protocol +6/-6

Apply preserve-extras gating and object-only unions in Storage protocol

• Removes unconditional extensible storage for records that are not resendable, and adds primitive annotations for integer fields like size/expiry. Marks PartitionDescriptor as 'object_only' for stricter wire validation.

rb/lib/selenium/webdriver/bidi/protocol/storage.rb

web_extension.rbMark extension data union as object-only and type extension ids +3/-2

Mark extension data union as object-only and type extension ids

• Marks ExtensionData as 'object_only' and adds string primitives to record fields previously emitted as shorthand. Improves validation and typing at the wire boundary.

rb/lib/selenium/webdriver/bidi/protocol/web_extension.rb

record.rbAdd 'scalar' passthrough support for ref-typed fields (map key tolerance) +27/-6

Add 'scalar' passthrough support for ref-typed fields (map key tolerance)

• Extends Record field metadata with a 'scalar' flag and threads it through list/ref parsing. When 'scalar' is set, non-Hash leaf values pass through instead of being handed to an 'object_only' union ref, preserving map string keys while still typing object values.

rb/lib/selenium/webdriver/bidi/serialization/record.rb

union.rbIntroduce 'object_only' unions that reject bare scalar payloads +12/-3

Introduce 'object_only' unions that reject bare scalar payloads

• Adds an 'object_only' declaration for unions derived from the schema's 'objectOnly' signal. Updates union deserialization so non-Hash payloads raise a wire error when all union arms are objects, while preserving legacy scalar passthrough for unions that genuinely have scalar arms.

rb/lib/selenium/webdriver/bidi/serialization/union.rb

bidi_generate.rbForward projector signals into Ruby output (object_only/preserveExtras/scalar/leaf primitive) +46/-20

Forward projector signals into Ruby output (object_only/preserveExtras/scalar/leaf primitive)

• Extends generator IR to carry 'scalar' on fields and 'object_only' on unions, and emits corresponding Ruby declarations. Gates record extensibility on 'preserveExtras' rather than raw 'extensible', and resolves leaf primitives by following alias chains so more scalar fields become strictly typed.

rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb

module.rb.erbEmit 'object_only' for generated union classes when signaled by schema +3/-0

Emit 'object_only' for generated union classes when signaled by schema

• Updates the Ruby protocol module template to add an 'object_only' declaration for unions that the generator marks as object-only. This activates strict wire validation in the Union runtime.

rb/lib/selenium/webdriver/bidi/support/templates/module.rb.erb

emulation.rbsUpdate Emulation RBS signatures to reflect typed scalar fields +3/-3

Update Emulation RBS signatures to reflect typed scalar fields

• Changes RBS types for fields like 'scrollbar_type' from untyped to 'String?', matching the new primitive typing. Updates method signatures accordingly.

rb/sig/lib/selenium/webdriver/bidi/protocol/emulation.rbs

network.rbsRemove extensions from received-only extensible Network types in RBS +1/-2

Remove extensions from received-only extensible Network types in RBS

• Drops 'extensions' accessors/params from RBS for types where 'preserveExtras' is not enabled, matching runtime behavior that no longer stores unknown keys. Keeps the constructor signature aligned with generated Ruby record definitions.

rb/sig/lib/selenium/webdriver/bidi/protocol/network.rbs

session.rbsAlign Session RBS with preserve-extras gating (remove extensions where not preserved) +1/-2

Align Session RBS with preserve-extras gating (remove extensions where not preserved)

• Removes 'extensions' from capabilities-like record signatures where unknown keys are no longer stored. Keeps RBS consistent with the generated Ruby protocol and serialization behavior.

rb/sig/lib/selenium/webdriver/bidi/protocol/session.rbs

storage.rbsUpdate Storage RBS to stop advertising extensions for non-preserving records +1/-2

Update Storage RBS to stop advertising extensions for non-preserving records

• Removes 'extensions' accessor and parameter from 'PartitionKey' in RBS where extras are not preserved. Keeps the signature consistent with preserve-extras gating semantics.

rb/sig/lib/selenium/webdriver/bidi/protocol/storage.rbs

Tests (2) +158 / -0
project_bidi_schema_test.mjsAdd unit tests for schema signal derivations +88/-0

Add unit tests for schema signal derivations

• Extends projector tests to assert correct 'objectOnly' behavior, 'preserveExtras' reachability gating, inline enum primitive inference, and inline union 'scalar' marking. Ensures projected schema remains internally consistent via 'checkSchema' assertions.

javascript/selenium-webdriver/project_bidi_schema_test.mjs

serialization_spec.rbAdd specs for object-only unions, preserve-extras gating, and primitive validation +70/-0

Add specs for object-only unions, preserve-extras gating, and primitive validation

• Adds Ruby specs asserting that object-only unions reject scalar payloads, alias-unions like input.Origin still pass scalars through, and scalar-tolerant map entries preserve string keys while typing values. Adds coverage for preserve-extras round-trips on resendable types and for primitive mismatch errors (inline enum primitive and alias-derived integer primitives).

rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb

@qodo-code-review

qodo-code-review Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 22 rules

Grey Divider


Action required

1. Scalar passthrough too broad ✓ Resolved 🐞 Bug ≡ Correctness
Description
Serialization::Record treats scalar: true refs as “pass through any non-Hash”, so inline unions
like Ref / text will accept invalid scalar payloads (e.g., integers/booleans) instead of rejecting
them. This undermines the PR’s goal of strict wire-boundary validation by allowing malformed values
to propagate unchanged.
Code

rb/lib/selenium/webdriver/bidi/serialization/record.rb[R199-207]

+            def read_list(raw, klass, scalar = false)
+              raw.map do |element|
+                if element.is_a?(::Array)
+                  read_list(element, klass, scalar)
+                elsif scalar && !element.is_a?(::Hash)
+                  element
+                else
+                  klass.from_json(element)
+                end
Evidence
The schema signal scalar: true is introduced specifically for unions like Ref / text where the
scalar arm is a string primitive, but the Ruby runtime’s passthrough check only verifies “not a
Hash”, so non-string scalars will incorrectly be accepted and returned unchanged. This occurs in
real generated protocol types (e.g., Script::ObjectRemoteValue.value).

javascript/selenium-webdriver/project_bidi_schema_test.mjs[451-462]
rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[552-566]
rb/lib/selenium/webdriver/bidi/serialization/record.rb[154-163]
rb/lib/selenium/webdriver/bidi/serialization/record.rb[195-209]
rb/lib/selenium/webdriver/bidi/protocol/script.rb[514-522]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Ruby runtime handling for `scalar: true` fields currently passes through **any** non-`Hash` leaf value. The schema signal is used to represent an inline union with a *specific* scalar arm (e.g., `Ref / text`), so the passthrough should validate the scalar’s primitive (typically `string`) rather than accepting arbitrary scalars.

### Issue Context
- The projector marks an inline union as `scalar: true` when it contains a bare scalar arm.
- Ruby generator forwards only a boolean `scalar` flag, losing the scalar arm’s primitive.
- Ruby runtime then permits any non-object leaf to bypass the object-only union parsing.

### Fix Focus Areas
- javascript/selenium-webdriver/project_bidi_schema.mjs[131-140]
- rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[552-566]
- rb/lib/selenium/webdriver/bidi/serialization/record.rb[154-163]
- rb/lib/selenium/webdriver/bidi/serialization/record.rb[195-209]

### Suggested fix direction
1. **Preserve scalar arm type** in the schema for inline unions, e.g. add `scalarPrimitive: 'string'` when the scalar arms’ primitive can be uniquely determined.
2. **Forward that metadata** through the Ruby generator into `Serialization::Record` field metadata (e.g. `scalar_primitive`).
3. In `Record#read_ref` / `read_list`, when `scalar` passthrough triggers, **validate the scalar value matches the declared primitive** before returning it; otherwise raise `Error::WebDriverError`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Map pair shape unchecked ✓ Resolved 🐞 Bug ≡ Correctness ⭐ New
Description
Serialization::Record::Deserializer#read_list treats scalar lists as map-encoded [key, value]
pairs but does not enforce that each element is a 2-item array, so malformed entries like ['k'] or
'k' can be accepted without raising. This weakens wire-boundary validation for scalar-tolerant map
fields (e.g., Script map/object values) and can propagate schema-invalid structures.
Code

rb/lib/selenium/webdriver/bidi/serialization/record.rb[R201-214]

+            def read_list(field, raw, klass)
+              raw.map do |element|
+                if field.scalar && element.is_a?(::Array) && element.size == 2
+                  key, value = element
+                  [read_map_key(field, key, klass), klass.from_json(value)]
+                elsif element.is_a?(::Array)
+                  read_list(field, element, klass)
+                elsif field.scalar && !element.is_a?(::Hash)
+                  scalar_value(field, element)
+                else
+                  klass.from_json(element)
+                end
+              end
+            end
Evidence
The runtime explicitly describes scalar lists as map-encoded [key, value] pairs, but the
implementation only special-cases the pair when element.size == 2 and otherwise silently
recurses/accepts scalars, allowing invalid shapes. The generated protocol marks BiDi map-like fields
with scalar: 'string', so this affects real wire parsing.

rb/lib/selenium/webdriver/bidi/serialization/record.rb[195-214]
rb/lib/selenium/webdriver/bidi/protocol/script.rb[190-204]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Serialization::Record::Deserializer#read_list` uses `field.scalar` to enable scalar-tolerant handling for map-encoded values, but it does not enforce that each list element is a `[key, value]` 2-tuple. As a result, malformed payloads (e.g. `[['k']]`, `['k']`, or other non-pair shapes) can deserialize without error.

### Issue Context
`field.scalar` is documented and generated specifically for map encoding as `[key, value]` pairs (with scalar tolerance applying only to the key). The runtime should reject any list elements that are not exactly a 2-element array when `field.scalar` is set.

### Fix Focus Areas
- rb/lib/selenium/webdriver/bidi/serialization/record.rb[195-214]

### Suggested fix
- In `read_list(field, raw, klass)`, when `field.scalar` is truthy:
 - Require every `element` to be an `Array` of length 2; otherwise raise `Error::WebDriverError` explaining the expected shape.
 - Remove/avoid the fallback branches that accept bare scalars or non-2-element arrays for `field.scalar` lists.
- Add/extend unit tests to assert that malformed map entry shapes raise (e.g. `Script::ObjectRemoteValue.from_json('type' => 'object', 'value' => [['k']])`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit e7ff8a5

Results up to commit b290135


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Scalar passthrough too broad ✓ Resolved 🐞 Bug ≡ Correctness
Description
Serialization::Record treats scalar: true refs as “pass through any non-Hash”, so inline unions
like Ref / text will accept invalid scalar payloads (e.g., integers/booleans) instead of rejecting
them. This undermines the PR’s goal of strict wire-boundary validation by allowing malformed values
to propagate unchanged.
Code

rb/lib/selenium/webdriver/bidi/serialization/record.rb[R199-207]

+            def read_list(raw, klass, scalar = false)
+              raw.map do |element|
+                if element.is_a?(::Array)
+                  read_list(element, klass, scalar)
+                elsif scalar && !element.is_a?(::Hash)
+                  element
+                else
+                  klass.from_json(element)
+                end
Evidence
The schema signal scalar: true is introduced specifically for unions like Ref / text where the
scalar arm is a string primitive, but the Ruby runtime’s passthrough check only verifies “not a
Hash”, so non-string scalars will incorrectly be accepted and returned unchanged. This occurs in
real generated protocol types (e.g., Script::ObjectRemoteValue.value).

javascript/selenium-webdriver/project_bidi_schema_test.mjs[451-462]
rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[552-566]
rb/lib/selenium/webdriver/bidi/serialization/record.rb[154-163]
rb/lib/selenium/webdriver/bidi/serialization/record.rb[195-209]
rb/lib/selenium/webdriver/bidi/protocol/script.rb[514-522]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Ruby runtime handling for `scalar: true` fields currently passes through **any** non-`Hash` leaf value. The schema signal is used to represent an inline union with a *specific* scalar arm (e.g., `Ref / text`), so the passthrough should validate the scalar’s primitive (typically `string`) rather than accepting arbitrary scalars.

### Issue Context
- The projector marks an inline union as `scalar: true` when it contains a bare scalar arm.
- Ruby generator forwards only a boolean `scalar` flag, losing the scalar arm’s primitive.
- Ruby runtime then permits any non-object leaf to bypass the object-only union parsing.

### Fix Focus Areas
- javascript/selenium-webdriver/project_bidi_schema.mjs[131-140]
- rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[552-566]
- rb/lib/selenium/webdriver/bidi/serialization/record.rb[154-163]
- rb/lib/selenium/webdriver/bidi/serialization/record.rb[195-209]

### Suggested fix direction
1. **Preserve scalar arm type** in the schema for inline unions, e.g. add `scalarPrimitive: 'string'` when the scalar arms’ primitive can be uniquely determined.
2. **Forward that metadata** through the Ruby generator into `Serialization::Record` field metadata (e.g. `scalar_primitive`).
3. In `Record#read_ref` / `read_list`, when `scalar` passthrough triggers, **validate the scalar value matches the declared primitive** before returning it; otherwise raise `Error::WebDriverError`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread rb/lib/selenium/webdriver/bidi/serialization/record.rb Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the shared WebDriver BiDi schema projector with three derived “signals” (objectOnly, preserveExtras, and improved scalar primitive typing) and updates the generated Ruby BiDi protocol layer + runtime to consume them, tightening wire-boundary validation and making unknown-key round-tripping behavior schema-driven.

Changes:

  • Add projector-derived schema signals (objectOnly, preserveExtras, and inline-enum primitive / scalar-tolerant inline unions) and unit tests for them in the JavaScript projector.
  • Update the Ruby BiDi generator and generated protocol to consume these signals (object-only unions reject scalars; only re-sendable extensible records preserve unknown keys; many previously-opaque scalar fields become strictly typed).
  • Extend Ruby serialization runtime and add/expand Ruby unit specs covering the new validation behaviors.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb Adds Ruby unit coverage for object-only unions, scalar-tolerant map-key behavior, preserve-extras gating, and stricter scalar primitive validation.
rb/sig/lib/selenium/webdriver/bidi/serialization.rbs Updates RBS for new serialization helper methods/signatures (read_ref, updated read_list, scalar_value, object_only).
rb/sig/lib/selenium/webdriver/bidi/protocol/storage.rbs Updates generated RBS to reflect preserve-extras gating (removes extensions where no longer stored).
rb/sig/lib/selenium/webdriver/bidi/protocol/session.rbs Updates generated RBS to reflect preserve-extras gating (removes extensions where no longer stored).
rb/sig/lib/selenium/webdriver/bidi/protocol/network.rbs Updates generated RBS to reflect preserve-extras gating (removes extensions where no longer stored).
rb/sig/lib/selenium/webdriver/bidi/protocol/emulation.rbs Tightens RBS types for newly-typed scalar fields (e.g., scrollbar_type: String?).
rb/lib/selenium/webdriver/bidi/support/templates/module.rb.erb Emits object_only on generated union classes when schema indicates unions are object-only.
rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb Generator consumes new schema signals (objectOnly, preserveExtras, scalar) and hoists leaf scalar primitives through alias chains.
rb/lib/selenium/webdriver/bidi/serialization/union.rb Implements object_only unions rejecting non-object inbound payloads.
rb/lib/selenium/webdriver/bidi/serialization/record.rb Adds scalar-tolerant union-position parsing support (scalar metadata) and wires it through list/ref reads.
rb/lib/selenium/webdriver/bidi/protocol/web_extension.rb Regenerated Ruby protocol with stricter primitive typing and object-only union flag adoption.
rb/lib/selenium/webdriver/bidi/protocol/storage.rb Regenerated Ruby protocol reflecting preserveExtras and stricter primitive typing; marks object-only unions.
rb/lib/selenium/webdriver/bidi/protocol/session.rb Regenerated Ruby protocol reflecting preserveExtras and object-only unions.
rb/lib/selenium/webdriver/bidi/protocol/script.rb Regenerated Ruby protocol adopting object-only unions and scalar-tolerant map-key parsing metadata.
rb/lib/selenium/webdriver/bidi/protocol/network.rb Regenerated Ruby protocol adopting object-only unions, preserve-extras gating, and stricter primitive typing.
rb/lib/selenium/webdriver/bidi/protocol/log.rb Regenerated Ruby protocol adopting object-only unions and stricter primitive typing.
rb/lib/selenium/webdriver/bidi/protocol/input.rb Regenerated Ruby protocol adopting object-only unions and stricter primitive typing.
rb/lib/selenium/webdriver/bidi/protocol/emulation.rb Regenerated Ruby protocol adopting object-only unions and inline-enum primitive typing.
rb/lib/selenium/webdriver/bidi/protocol/browsing_context.rb Regenerated Ruby protocol adopting object-only unions and stricter primitive typing.
rb/lib/selenium/webdriver/bidi/protocol/browser.rb Regenerated Ruby protocol adopting object-only unions and stricter primitive typing.
rb/lib/selenium/webdriver/bidi/protocol/bluetooth.rb Regenerated Ruby protocol with stricter primitive typing and object-only union flag adoption.
javascript/selenium-webdriver/project_bidi_schema.mjs Adds derived schema-signal computation and projector support for inline-enum primitives and scalar-tolerant inline unions.
javascript/selenium-webdriver/project_bidi_schema_test.mjs Adds projector unit tests covering the new derived schema signals.

Comment thread rb/lib/selenium/webdriver/bidi/serialization/record.rb
Comment thread rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb
Comment thread rb/lib/selenium/webdriver/bidi/serialization/record.rb
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 42d4bba

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit e7ff8a5

@titusfortner
titusfortner merged commit 2456cb5 into SeleniumHQ:trunk Jul 16, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-devtools Includes everything BiDi or Chrome DevTools related B-support Issue or PR related to support classes C-nodejs JavaScript Bindings C-rb Ruby Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants