[java][bidi] Add BiDi code generator#17777
Conversation
PR Summary by QodoAdd Bazel-based Java BiDi protocol code generator
AI Description
Diagram
High-Level Assessment
Files changed (21)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
18 rules 1.
|
|
I started reviewing this and ended up creating #17786 |
Code Review by Qodo
Context used✅ Compliance rules (platform):
18 rules 1. Void events never dispatched
|
|
Code review by qodo was updated up to the latest commit 7b9799f |
|
Code review by qodo was updated up to the latest commit 1760de4 |
|
Code review by qodo was updated up to the latest commit 1f4ec87 |
| ], | ||
| ) | ||
|
|
||
| genrule( |
There was a problem hiding this comment.
1. Unquoted genrule arguments 🐞 Bug ☼ Reliability
The new :generate-bidi genrule builds a shell command without quoting the tool path, schema path, or output path, so the action can fail when any of those paths contain spaces or shell-special characters. This makes BiDi generation brittle and can break Bazel builds depending on workspace/execroot location.
Agent Prompt
### Issue description
The `genrule(name = "generate-bidi")` command concatenates `$(execpath ...)`, `$(location ...)`, and `$@` into a shell command without quoting. If any expanded path contains whitespace or shell-special characters, the generator will receive split/incorrect arguments and the build will fail.
### Issue Context
This genrule is new in this PR and is used to generate `bidi-generated.srcjar` during the build.
### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BUILD.bazel[59-65]
### Suggested fix
Update `cmd` to quote each argument as a single shell token, e.g.:
```bzl
cmd = "\"$(execpath :bidi-client-generator)\" \"$(location //javascript/selenium-webdriver:create-bidi-src_schema)\" \"$@\"",
```
(or equivalent quoting per the repo’s Bazel shell conventions), so paths with spaces are handled correctly.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| } | ||
|
|
||
| protected final void unsubscribe(String subscriptionId) { | ||
| /** |
There was a problem hiding this comment.
2. Ambiguous javadoc link 🐞 Bug ⚙ Maintainability
Module.unsubscribe’s Javadoc uses {@link #subscribe} even though Module declares two overloaded
subscribe methods, which can cause ambiguous/unresolved Javadoc links under doclint. This can break
strict Javadoc generation or produce incorrect API docs.
Agent Prompt
### Issue description
`Module` now has two overloaded `subscribe(...)` methods, but `unsubscribe`’s Javadoc links to `{@link #subscribe}` without a signature, which is ambiguous for Javadoc resolution.
### Issue Context
This PR introduces public `subscribe` overloads and adds Javadoc.
### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/Module.java[56-80]
### Suggested fix
Change the `unsubscribe` Javadoc to reference the specific overload(s), for example:
```java
@param subscriptionId a subscription id previously returned by
{@link #subscribe(Event, Consumer)} or {@link #subscribe(Event, Consumer, SubscriptionScope)}
```
(or remove the link and refer to “subscribe methods” in plain text).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 3d4a143 |
|
Good progress. Heads up in case you missed it, #17784 added the preserveExtras signal to the projected schema (alongside the existing extensible), so extensibility is now fully derivable per-type: extensible marks the open types, and preserveExtras is pre-scoped to the ones that are both extensible and re-sendable so you don't have to work out that scoping in the generator. |
🔗 Related Issues
💥 What does this PR do?
Adds the BiDi Java client generator: reads the shared, binding-neutral BiDi schema and emits the full typed Java BiDi protocol layer — module classes (one per domain, with commands and events) plus supporting POJOs, enums, and discriminated unions — across all 14 domains (~79 commands, 27 events, 298 generated classes total), on top of the hand-written seam (Module base class + Handle) that makes it usable.
Shape of a generated module class (real output, network.Network trimmed to 2 events + 2 commands — the full file has 5 events and 13 commands):
Usage is symmetric regardless of whether a command has a result:
🔧 Implementation Notes
Autogenerated code and not checked in
The build compiles the genrule's srcjar directly:
No generated
.javais ever committed.Trade-off accepted knowingly: less reviewable diff surface for generator changes. Given Java has a lot of generated classes, strict typing means one
.javafile per record/enum/union, not a handful of dynamically-typed modules like the JS or Python bindings. The actual generated code never appears in this PR's diff, only the generator that produces it.How to see the generated output since it's not in the diff, pull it out locally:
bazel build //java/src/org/openqa/selenium/bidi:bidi-generated mkdir -p /tmp/bidi-generated-src unzip -o -q bazel-bin/java/src/org/openqa/selenium/bidi/bidi-generated.srcjar -d /tmp/bidi-generated-src open /tmp/bidi-generated-src # or just browse/grep it298 files across 14 domains will land there. A few worth spot-checking first, each demonstrating a specific decision from the Implementation Notes above:
org/openqa/selenium/bidi/protocol/module/Network.java— a representative module class (commands + events)org/openqa/selenium/bidi/protocol/browsingcontext/SetViewportParameters.java— explicit-null vs. omitted-key handling side by side (viewport/devicePixelRatioare nullable,context/userContextsaren't)org/openqa/selenium/bidi/protocol/emulation/SetTimezoneOverrideParameters.java— required-but-nullable field on a sender typeorg/openqa/selenium/bidi/protocol/script/PrimitiveProtocolValue.javaandStringValue.java— theinterface extends/implementschain for multi-parent unionsIf you'd rather regenerate directly without the Bazel packaging step (useful for diffing output after a generator change):
bazel build //java/src/org/openqa/selenium/bidi:bidi-client-generator bazel run //java/src/org/openqa/selenium/bidi:bidi-client-generator -- \ "$(bazel info bazel-genfiles)/javascript/selenium-webdriver/create-bidi-src_schema.json" \ /tmp/bidi-out.srcjar unzip -o -q /tmp/bidi-out.srcjar -d /tmp/bidi-generated-src(Direct invocation of the built binary without
bazel runfails withCannot locate runfiles directory— it needsbazel runto set up its runfiles tree.)Explicit-null vs. omitted-key tracking
Tracked per-field for fields the schema marks nullable, so
toMap()can distinguish:"field": null→ clear itThis is gated strictly on the schema's own
nullableflag, not applied blanket to every optional field.Required-but-nullable fields
@Nullableon their constructor parameter (boxed, if the field would otherwise be a Java primitive).ConstructorCoercerwas extended to honor jspecify's@Nullable(previously onlyOptional<T>was treated as nullable).🤖 AI assistance
💡 Additional Considerations
Test coverage is not uniform across domains.
Only network, log, script, and browsingContext have dedicated generated-code tests (unit and/or browser-integration) added in this PR.
session, browser, emulation, storage, input, webExtension, permissions, speculation, bluetooth, userAgentClientHints have none yet.
Essentially, the modules that are needed for implementing high-level APIs for Selenium 5 have tests.
Extensible/unknown-key round-tripping (unrecognized JSON keys silently dropped) is a known, scoped gap, not implemented in this PR. Not implemented because ConstructorCoercer maps JSON keys 1:1 to constructor parameters and silently drops anything unmatched — supporting round-trip needs a genuinely different deserialization path (capturing unrecognized keys into a side bag alongside the typed fields), which is new mechanism work, and fairly big. So this is something we might not support in Java at all. We can see how much this is required and decide later.
Rest, will be added in a follow up PR.
🔄 Types of changes