Skip to content

[java][bidi] Add BiDi code generator#17777

Open
pujagani wants to merge 6 commits into
SeleniumHQ:trunkfrom
pujagani:java-bidi-codegen
Open

[java][bidi] Add BiDi code generator#17777
pujagani wants to merge 6 commits into
SeleniumHQ:trunkfrom
pujagani:java-bidi-codegen

Conversation

@pujagani

@pujagani pujagani commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

🔗 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):


// This file is generated. Do not edit — regenerate via BiDiGenerator.

package org.openqa.selenium.bidi.protocol.module;

import org.openqa.selenium.Beta;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.bidi.Command;
import org.openqa.selenium.bidi.ConverterFunctions;
import org.openqa.selenium.bidi.Event;
import org.openqa.selenium.bidi.Module;

/**
 * This is an unsupported API. No compatibility guarantees are provided.
 * It tracks the W3C WebDriver BiDi specification directly. As the specification
 * evolves, this API will change or be removed without prior notice.
 */
@Beta
@SuppressWarnings("unchecked")
public class Network extends Module {

  public static final Event<AuthRequiredParameters> AUTH_REQUIRED =
      new Event<>("network.authRequired", ConverterFunctions.fromMap(AuthRequiredParameters.class));

  public static final Event<BeforeRequestSentParameters> BEFORE_REQUEST_SENT =
      new Event<>("network.beforeRequestSent", ConverterFunctions.fromMap(BeforeRequestSentParameters.class));

  // ... 3 more events

  public Network(WebDriver driver) {
    super(driver);
  }

  public AddInterceptResult addIntercept(AddInterceptParameters params) {
    return send(new Command<>("network.addIntercept", params.toMap(), AddInterceptResult.class));
  }

  public void continueRequest(ContinueRequestParameters params) {
    send(new Command<>("network.continueRequest", params.toMap()));
  }

  // ... 11 more commands
}

Usage is symmetric regardless of whether a command has a result:

Network network = new Network(driver);

// command with a result
AddInterceptResult result = network.addIntercept(new AddInterceptParameters(List.of(InterceptPhase.BEFOREREQUESTSENT)));

// command with no result
network.removeIntercept(new RemoveInterceptParameters(result.getIntercept()));

// event: subscribe returns the browser-issued subscription id; unsubscribe takes it back
String subscriptionId = network.subscribe(Network.BEFORE_REQUEST_SENT, event -> { ... });
network.unsubscribe(subscriptionId);

🔧 Implementation Notes

Autogenerated code and not checked in

The build compiles the genrule's srcjar directly:

bazel build //java/src/org/openqa/selenium/bidi:bidi-generated

No generated .java is 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 .java file 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 it

298 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/devicePixelRatio are nullable, context/userContexts aren't)
  • org/openqa/selenium/bidi/protocol/emulation/SetTimezoneOverrideParameters.java — required-but-nullable field on a sender type
  • org/openqa/selenium/bidi/protocol/script/PrimitiveProtocolValue.java and StringValue.java — the interface extends/implements chain for multi-parent unions
    If 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 run fails with Cannot locate runfiles directory — it needs bazel run to 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 it
  • key omitted → never touched
    This is gated strictly on the schema's own nullable flag, not applied blanket to every optional field.

Required-but-nullable fields

  • Get @Nullable on their constructor parameter (boxed, if the field would otherwise be a Java primitive).
  • The shared ConstructorCoercer was extended to honor jspecify's @Nullable (previously only Optional<T> was treated as nullable).

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s):
    • What was generated:
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

  1. 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.

  2. 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

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

@selenium-ci selenium-ci added C-java Java Bindings B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related labels Jul 14, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add Bazel-based Java BiDi protocol code generator

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add a Java generator that emits typed BiDi modules/POJOs from the shared schema.
• Compile generated sources from a build-time srcjar; do not commit generated .java.
• Add unit and browser-integration tests for unions, nullability, and module events/commands.
Diagram

graph TD
  A["BiDi schema (JSON)"] --> B["Bazel genrule"] --> C["BiDiGenerator (java_binary)"] --> D[("bidi-generated.srcjar")]
  D --> E["bidi-generated (java_library)"] --> F["Generated protocol modules/POJOs"] --> G["Unit + integration tests"]
  E --> H["BiDi runtime (Command/Event/Module)"]
  subgraph Legend
    direction LR
    _file["Build input/output"] ~~~ _proc["Build step"] ~~~ _jar[("Artifact")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Check in generated sources + verification test
  • ➕ Much more reviewable diffs when generator changes
  • ➕ Easier for downstream users to browse protocol surface without building
  • ➖ Large churn in repo history and PR diffs (hundreds of files)
  • ➖ Requires CI enforcement to keep generated sources in sync
  • ➖ Adds friction when schema changes frequently
2. Use a codegen library (e.g., JavaPoet)
  • ➕ Reduces hand-rolled string-concatenation and formatting risk
  • ➕ Potentially simpler future refactors (imports, indentation, comments)
  • ➖ Adds/expands dependencies in the build
  • ➖ Does not remove the hard parts (schema modeling: unions, nullability, reachability)
  • ➖ May be harder to keep output stable if library formatting changes
3. Generate fewer files via runtime reflection / dynamic maps
  • ➕ Less generated surface area; smaller compile impact
  • ➕ Potentially simpler generator
  • ➖ Loses strong typing, IDE support, and compile-time safety
  • ➖ Harder to evolve safely as schema grows; more runtime errors

Recommendation: The PR’s build-time srcjar generation approach is appropriate for Selenium’s Bazel setup (and aligns with existing CDP/devtools precedent), given the very large Java type surface. Keep the generator-in-repo (not the output), but consider adding a lightweight CI job/target that regenerates and sanity-checks the srcjar contents (e.g., file count, package layout, compilation) to mitigate the reduced diff visibility without committing generated code.

Files changed (21) +2882 / -6

Enhancement (3) +1458 / -4
BiDiGenerator.javaAdd schema-driven Java BiDi protocol generator +1435/-0

Add schema-driven Java BiDi protocol generator

• Implements a CLI generator that reads the binding-neutral BiDi schema JSON, computes reachable and sender-only types, and emits Java module classes plus record/enum/union types into a srcjar. Handles multi-parent unions via interfaces, discriminated/structural union dispatch via fromMap(), nullable-vs-omitted outbound semantics for optional nullable fields, nested synthetic types as static inner classes, and reserved-word escaping.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java

ConverterFunctions.javaAdd Map→POJO deserializer helper for event payloads +20/-0

Add Map→POJO deserializer helper for event payloads

• Introduces ConverterFunctions.fromMap(Class<T>) to deserialize Map<String,Object> event payloads into typed POJOs using Selenium’s JSON coercion. This becomes the default mapping strategy for generated Event constants (with special handling for union types).

java/src/org/openqa/selenium/bidi/ConverterFunctions.java

Module.javaExpose subscribe/unsubscribe publicly for generated modules +3/-4

Expose subscribe/unsubscribe publicly for generated modules

• Changes Module.subscribe(...) and Module.unsubscribe(...) from protected to public so generated protocol modules can expose event subscription APIs directly through the shared Module seam. Keeps command sending protected/final while making event lifecycle accessible to consumers.

java/src/org/openqa/selenium/bidi/Module.java

Tests (16) +1375 / -0
BUILD.bazelAdd unit test suite for generated browsingcontext types +14/-0

Add unit test suite for generated browsingcontext types

• Creates a small JUnit5 test suite target that depends on bidi-generated and json/assertj to validate generated browsingcontext POJOs and serialization/deserialization semantics.

java/test/org/openqa/selenium/bidi/protocol/browsingcontext/BUILD.bazel

InfoTest.javaTest required+nullable vs optional field deserialization semantics +94/-0

Test required+nullable vs optional field deserialization semantics

• Validates that required nullable fields accept explicit null but still require key presence, while optional nullable fields deserialize to Optional.empty() whether null or absent. Exercises Json/ConstructorCoercer behavior expected by the generated POJOs.

java/test/org/openqa/selenium/bidi/protocol/browsingcontext/InfoTest.java

SetViewportParametersTest.javaTest explicit-null vs omitted-key behavior in generated toMap() +84/-0

Test explicit-null vs omitted-key behavior in generated toMap()

• Asserts that unset optional fields are omitted from the wire payload, while nullable optional fields support explicit clearing by emitting a key with null. Also confirms that non-nullable optional fields cannot represent an explicit-null wire state and thus remain omitted when set to null.

java/test/org/openqa/selenium/bidi/protocol/browsingcontext/SetViewportParametersTest.java

BUILD.bazelAdd unit test suite for generated emulation types +14/-0

Add unit test suite for generated emulation types

• Adds a small JUnit5 test suite for generated emulation protocol types, depending on bidi-generated and JSON tooling.

java/test/org/openqa/selenium/bidi/protocol/emulation/BUILD.bazel

SetTimezoneOverrideParametersTest.javaTest required-but-nullable field handling in construction and toMap() +50/-0

Test required-but-nullable field handling in construction and toMap()

• Ensures required nullable fields can be constructed with null and still serialize as an explicit key with null, not omitted. Confirms generator’s boxed/@Nullable behavior for required nullable types.

java/test/org/openqa/selenium/bidi/protocol/emulation/SetTimezoneOverrideParametersTest.java

SetTouchOverrideParametersTest.javaTest required nullable primitive boxing and JSON round-trip +62/-0

Test required nullable primitive boxing and JSON round-trip

• Verifies required nullable primitive fields are represented as boxed types, serialize null explicitly, and deserialize explicit null without throwing. Confirms real-value deserialization works as expected.

java/test/org/openqa/selenium/bidi/protocol/emulation/SetTouchOverrideParametersTest.java

BUILD.bazelAdd unit test suite for generated log types +14/-0

Add unit test suite for generated log types

• Creates a small JUnit5 suite target for generated log protocol classes, pulling in bidi-generated and JSON/assertj.

java/test/org/openqa/selenium/bidi/protocol/log/BUILD.bazel

EntryTest.javaTest union dispatch for log.Entry fromMap() +47/-0

Test union dispatch for log.Entry fromMap()

• Parses a representative console log entry payload into a Map and asserts Entry.fromMap dispatches to the correct generated subtype (ConsoleLogEntry). Exercises generated union deserialization paths.

java/test/org/openqa/selenium/bidi/protocol/log/EntryTest.java

BUILD.bazelAdd browser integration suite for generated module classes +27/-0

Add browser integration suite for generated module classes

• Defines a large java_selenium_test_suite that runs against BiDi-capable browsers and depends on bidi-generated plus Selenium remote/test infrastructure. Provides end-to-end coverage for generated command and event wiring through WebDriver/BiDi.

java/test/org/openqa/selenium/bidi/protocol/module/BUILD.bazel

BrowsingContextModuleTest.javaIntegration tests for generated BrowsingContext commands and events +205/-0

Integration tests for generated BrowsingContext commands and events

• Exercises generated browsing context module commands (create, navigate, getTree, close) and verifies event subscriptions (context created/destroyed, navigation events) function correctly. Uses CompletableFuture synchronization to validate actual browser-driven payloads.

java/test/org/openqa/selenium/bidi/protocol/module/BrowsingContextModuleTest.java

LogModuleTest.javaIntegration tests for generated Log module event subscriptions +170/-0

Integration tests for generated Log module event subscriptions

• Validates receipt and typing of console/javascript log entries, stack trace presence, multiple subscriptions, and unsubscribe behavior. Confirms module-level subscribe/unsubscribe lifecycle works against real browser traffic.

java/test/org/openqa/selenium/bidi/protocol/module/LogModuleTest.java

NetworkEventsTest.javaIntegration tests for generated Network event payloads +179/-0

Integration tests for generated Network event payloads

• Subscribes to multiple network events and validates key fields (context, request/response metadata, cookies) against real navigation. Marks known-not-working auth/error events as NotYetImplemented for specific browsers.

java/test/org/openqa/selenium/bidi/protocol/module/NetworkEventsTest.java

NetworkModuleTest.javaIntegration tests for generated Network commands and event lifecycle +72/-0

Integration tests for generated Network commands and event lifecycle

• Verifies a representative command round-trip (addIntercept/removeIntercept) and confirms subscribe→receive→unsubscribe works for a generated event. Separates interception logic from event lifecycle to keep tests deterministic.

java/test/org/openqa/selenium/bidi/protocol/module/NetworkModuleTest.java

ScriptModuleTest.javaIntegration tests for generated Script commands and events +228/-0

Integration tests for generated Script commands and events

• Covers callFunction/evaluate success and exception results, preload script add/remove, realm queries, channel message events, and realm lifecycle events with browser-specific NotYetImplemented annotations. Validates union result types (EvaluateResult) and typed RemoteValue variants in practice.

java/test/org/openqa/selenium/bidi/protocol/module/ScriptModuleTest.java

BUILD.bazelAdd unit test suite for generated network types +14/-0

Add unit test suite for generated network types

• Creates a small JUnit5 suite for generated network protocol classes, depending on bidi-generated and JSON/assertj.

java/test/org/openqa/selenium/bidi/protocol/network/BUILD.bazel

ContinueWithAuthParametersTest.javaTest discriminated union defaulting and serialization for ContinueWithAuthParameters +101/-0

Test discriminated union defaulting and serialization for ContinueWithAuthParameters

• Validates selector-based union dispatch to the correct nested variant type, including schema-defined default handling for unrecognized discriminator values. Also asserts nested enum wire-value round-tripping and variant toMap() serialization shape.

java/test/org/openqa/selenium/bidi/protocol/network/ContinueWithAuthParametersTest.java

Other (2) +49 / -2
BUILD.bazelWire up BiDi codegen binary + genrule-produced generated library +48/-2

Wire up BiDi codegen binary + genrule-produced generated library

• Adds a Bazel java_binary for BiDiGenerator and a genrule that runs it over the shared JS-produced schema to emit a bidi-generated.srcjar. Introduces a bidi-generated java_library that compiles the srcjar directly (no generated .java checked in) and updates exclusions so generator sources aren’t compiled into the main bidi library.

java/src/org/openqa/selenium/bidi/BUILD.bazel

BUILD.bazelAdd generated BiDi library as a test dependency +1/-0

Add generated BiDi library as a test dependency

• Extends the BiDi test suite deps to include the new //java/src/org/openqa/selenium/bidi:bidi-generated target so tests can compile against generated protocol classes.

java/test/org/openqa/selenium/bidi/BUILD.bazel

@qodo-code-review

qodo-code-review Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 18 rules

Grey Divider


Action required

1. Module.subscribe() missing Javadoc ✓ Resolved 📘 Rule violation ✧ Quality
Description
Module.subscribe(...) and Module.unsubscribe(...) are public methods but have no Javadoc
comments. This violates the requirement that public API methods include Javadoc documentation.
Code

java/src/org/openqa/selenium/bidi/Module.java[R48-57]

+  public final <X> String subscribe(Event<X> event, Consumer<X> handler) {
    return handle.subscribe(event, handler);
  }

-  protected final <X> String subscribe(
-      Event<X> event, Consumer<X> handler, SubscriptionScope scope) {
+  public final <X> String subscribe(Event<X> event, Consumer<X> handler, SubscriptionScope scope) {
    return handle.subscribe(event, handler, scope);
  }

-  protected final void unsubscribe(String subscriptionId) {
+  public final void unsubscribe(String subscriptionId) {
    handle.unsubscribe(subscriptionId);
Evidence
PR Compliance ID 330200 requires Javadoc for public API methods. In Module.java, the methods
subscribe(...) and unsubscribe(...) are public and have no preceding /** ... */ Javadoc
blocks.

Rule 330200: Require Javadoc for all public API types and methods
java/src/org/openqa/selenium/bidi/Module.java[48-57]

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

## Issue description
`Module.subscribe(...)` and `Module.unsubscribe(...)` are public but lack required Javadoc.

## Issue Context
Compliance requires Javadoc for public API methods.

## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/Module.java[48-57]

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


2. fromMap() Javadoc missing tags ✓ Resolved 📘 Rule violation ✧ Quality
Description
ConverterFunctions.fromMap(Class<T> type) has a Javadoc block but it lacks required @param and
@return tags. This violates the requirement for complete Javadoc on public methods.
Code

java/src/org/openqa/selenium/bidi/ConverterFunctions.java[R39-43]

+  /**
+   * Returns a function that deserializes a {@code Map<String, Object>} event payload into an
+   * instance of {@code type} via the Selenium JSON library (ConstructorCoercer).
+   */
+  public static <T> Function<Map<String, Object>, T> fromMap(Class<T> type) {
Evidence
PR Compliance ID 330201 requires public methods to have complete Javadoc, including @param for
each parameter and @return for non-void returns. fromMap(Class<T> type) has no @param or
@return tags in its Javadoc.

Rule 330201: Require complete Javadoc on public API methods
java/src/org/openqa/selenium/bidi/ConverterFunctions.java[39-52]

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

## Issue description
`ConverterFunctions.fromMap(...)` is public and has Javadoc, but the Javadoc is missing required tags (`@param type` and `@return`).

## Issue Context
Compliance requires complete Javadoc on public methods including parameter/return tags.

## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/ConverterFunctions.java[39-52]

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


3. Reserved-key deserialization breaks ✓ Resolved 🐞 Bug ≡ Correctness
Description
BiDiGenerator escapes reserved JSON field names (e.g., wire key "this" becomes Java identifier
"this_"), but record deserialization uses ConstructorCoercer which matches JSON keys to constructor
parameter names. This causes required fields to fail deserialization (missing key) or optional
fields to be silently ignored whenever wire name != Java parameter name.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R1266-1276]

+    private FieldInfo parseField(Map<String, Object> raw) {
+      String rawName = str(raw, "name");
+      String wire = str(raw, "wire");
+      // Escape Java reserved words used as field names in the spec (e.g. "this").
+      String name = escapeReserved(rawName);
+      boolean required = Boolean.TRUE.equals(raw.get("required"));
+      @SuppressWarnings("unchecked")
+      Map<String, Object> type = (Map<String, Object>) raw.get("type");
+      // wire key stays as the original spec name for JSON serialization
+      return new FieldInfo(name, wire != null ? wire : rawName, required, type);
+    }
Evidence
The generator explicitly changes the Java name while preserving the wire key, but ConstructorCoercer
requires constructor parameter names to match JSON keys; protocol payloads include a reserved key
("this"), demonstrating this mismatch is reachable.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1266-1276]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1108-1113]
java/src/org/openqa/selenium/json/ConstructorCoercer.java[191-207]
java/src/org/openqa/selenium/json/Json.java[46-51]
javascript/selenium-webdriver/bidi/scriptManager.js[335-369]

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

## Issue description
Generated record classes escape reserved words in Java identifiers (e.g. `this` -> `this_`) while keeping the JSON wire key as the original (`"this"`). Selenium JSON deserialization via `ConstructorCoercer` requires JSON property keys to match constructor parameter names, so any field where `wire != name` will not deserialize correctly.

## Issue Context
- `ConstructorCoercer` uses `Parameter.getName()` and checks `properties.containsKey(parameter.getName())`, meaning it cannot map JSON key `"this"` to a parameter named `this_`.
- The BiDi protocol payloads do include a `"this"` key (e.g. Script callFunction params).

## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1266-1276]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1108-1113]

## Suggested fix
In the generator, for any **record** type that has at least one field where `f.wire` differs from `f.name` (currently happens via `escapeReserved`), emit a `static fromJson(Map<String,Object>)` factory for that record.
- The method name must be exactly `fromJson` (so `StaticInitializerCoercer` picks it up).
- Inside `fromJson`, read values by **wire key** (`f.wire`), coerce each value to the correct Java type (recursing into nested records/unions/enums/lists/maps as needed), and invoke the all-fields constructor.
- Do **not** call `ConverterFunctions.fromMap(<ThisClass>.class)` inside this method (would recurse back into `fromJson`).
- Keep `toMap()` using wire keys as-is.

This preserves the escaped Java identifiers for API ergonomics while keeping JSON deserialization correct.

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



Remediation recommended

4. Map fields serialize wrong ✓ Resolved 🐞 Bug ≡ Correctness
Description
serializeExpr handles primitives/refs/lists but not map type refs, so toMap() will emit
Map<String,T> values without converting nested record/union/enum values to their wire form. Any
sender-side schema field with map values of complex types will generate malformed command payloads.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R1164-1199]

+    private String serializeExpr(String varName, Map<String, Object> typeRef, String domain) {
+      if (typeRef == null) return varName;
+      if (typeRef.containsKey("primitive") || typeRef.containsKey("const")) return varName;
+      if (typeRef.containsKey("ref")) {
+        String resolvedKind = resolvedKindOf(str(typeRef, "ref"));
+        if ("enum".equals(resolvedKind)) return varName + ".toString()";
+        if ("record".equals(resolvedKind) || "union".equals(resolvedKind)) {
+          return varName + ".toMap()";
+        }
+        // alias: recurse through
+        Map<String, Object> aliasNode = types.get(str(typeRef, "ref"));
+        if (aliasNode != null && "alias".equals(str(aliasNode, "kind"))) {
+          @SuppressWarnings("unchecked")
+          Map<String, Object> inner = (Map<String, Object>) aliasNode.get("type");
+          return serializeExpr(varName, inner, domain);
+        }
+        return varName;
+      }
+      if (typeRef.containsKey("list")) {
+        @SuppressWarnings("unchecked")
+        Map<String, Object> elem = (Map<String, Object>) typeRef.get("list");
+        String elemKind = resolvedKindFromTypeRef(elem);
+        if ("enum".equals(elemKind)) {
+          return varName
+              + ".stream().map(Object::toString)"
+              + ".collect(java.util.stream.Collectors.toList())";
+        }
+        if ("record".equals(elemKind) || "union".equals(elemKind)) {
+          return varName
+              + ".stream().map(e -> e.toMap())"
+              + ".collect(java.util.stream.Collectors.toList())";
+        }
+        return varName;
+      }
+      return varName;
+    }
Evidence
The generator can resolve map type refs into Java Map types, and the schema vocabulary includes map
refs, but serializeExpr has no corresponding logic to convert map values to wire-compatible forms.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1164-1199]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1053-1057]
javascript/selenium-webdriver/project_bidi_schema.mjs[24-35]

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

## Issue description
`serializeExpr` is used by generated `toMap()` methods to convert nested values into wire-compatible shapes. It supports primitives/const, refs (enum/record/union), and lists, but it does not handle `map` typeRefs.
As a result, fields typed as `Map<String, SomeRecord>` / `Map<String, SomeUnion>` / `Map<String, SomeEnum>` will be put on the wire as Java objects rather than `Map<String,Object>` / strings, leading to malformed payloads.

## Issue Context
The shared schema vocabulary explicitly includes `map` type refs, and the generator already resolves them to `java.util.Map<String, ...>` types.

## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1164-1199]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1053-1057]

## Suggested fix
Extend `serializeExpr` with a `typeRef.containsKey("map")` branch:
- Determine the resolved kind of the map value type.
- For enum values: map `v -> v.toString()`.
- For record/union values: map `v -> v.toMap()`.
- For nested list/map values: recurse similarly.
- Preserve key strings unchanged.

Example emitted expression shape (use LinkedHashMap to preserve order):
`varName.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> <serializedValueExpr>, (a,b)->b, LinkedHashMap::new))`

This makes `toMap()` correct for map-typed schema fields.

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


5. Srcjar not reproducible ✓ Resolved 🐞 Bug ☼ Reliability
Description
packToJar writes bidi-generated.srcjar using Files.walkFileTree traversal order and default JarEntry
metadata, which can vary across filesystems/runs. This makes the generated srcjar non-deterministic,
reducing Bazel cache hits and harming reproducible builds.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R1365-1396]

+  private static void packToJar(Path tempDir, Path outputJar) throws IOException {
+    Files.createDirectories(outputJar.getParent());
+    try (OutputStream os = Files.newOutputStream(outputJar);
+        JarOutputStream jos = new JarOutputStream(os)) {
+      Files.walkFileTree(
+          tempDir,
+          new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+                throws IOException {
+              String rel = tempDir.relativize(dir).toString().replace('\\', '/');
+              if (!rel.isEmpty()) {
+                JarEntry e = new JarEntry(rel + "/");
+                jos.putNextEntry(e);
+                jos.closeEntry();
+              }
+              return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                throws IOException {
+              String rel = tempDir.relativize(file).toString().replace('\\', '/');
+              JarEntry e = new JarEntry(rel);
+              jos.putNextEntry(e);
+              try (InputStream is = Files.newInputStream(file)) {
+                is.transferTo(jos);
+              }
+              jos.closeEntry();
+              return FileVisitResult.CONTINUE;
+            }
+          });
Evidence
The generator currently walks the filesystem and emits JarEntry objects without ordering or
timestamp normalization, and the Bazel genrule uses this jar as the build output artifact.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1365-1396]
java/src/org/openqa/selenium/bidi/BUILD.bazel[59-65]

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

## Issue description
The generator packs generated sources into a srcjar in a potentially non-deterministic way:
- `Files.walkFileTree` does not guarantee a stable traversal order.
- `JarEntry` is created without normalizing timestamps/metadata.
This can cause `bidi-generated.srcjar` to differ between builds even when input schema is unchanged.

## Issue Context
The srcjar is produced by a Bazel `genrule` and consumed directly as compilation input, so nondeterminism impacts caching and reproducibility.

## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1365-1396]
- java/src/org/openqa/selenium/bidi/BUILD.bazel[59-65]

## Suggested fix
Update `packToJar` to:
1. Collect all files under `tempDir` with `Files.walk(tempDir)`.
2. Sort paths by their normalized jar entry name (`tempDir.relativize(p).toString().replace('\\','/')`).
3. For each entry, set deterministic metadata (at minimum `entry.setTime(0L)`; optionally also set method/extra fields deterministically).
4. Write entries in sorted order.

This yields stable srcjar bytes for the same generated content.

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


Grey Divider

Qodo Logo

Comment thread java/src/org/openqa/selenium/bidi/Module.java
Comment thread java/src/org/openqa/selenium/bidi/ConverterFunctions.java
Comment thread java/src/org/openqa/selenium/bidi/BiDiGenerator.java
Comment thread java/src/org/openqa/selenium/bidi/BiDiGenerator.java Outdated
Comment thread java/src/org/openqa/selenium/bidi/BiDiGenerator.java
@titusfortner

Copy link
Copy Markdown
Member

I started reviewing this and ended up creating #17786
It's slightly different from what I did and Ruby and Python as well so I need to go back and fix those if we agree to each of these

@qodo-code-review

qodo-code-review Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 18 rules

Grey Divider


Action required

1. Void events never dispatched 🐞 Bug ≡ Correctness
Description
BiDiGenerator emits Event<Void> constants with a mapper that always returns null for events without
params, but Connection.handleEventResponse returns early when the mapper result is null. This
prevents any parameterless BiDi events from ever invoking subscribed handlers.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R532-538]

+        } else {
+          sb.append("  public static final Event<Void> ")
+              .append(evtConstant)
+              .append(" =\n")
+              .append("      new Event<>(\"")
+              .append(method)
+              .append("\", map -> null);\n\n");
Evidence
The generator explicitly creates Event<Void> with a mapper that returns null for no-params events,
and the runtime event dispatch loop returns early on null mapped values, so such events cannot reach
callbacks.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[512-539]
java/src/org/openqa/selenium/bidi/Connection.java[347-395]

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

### Issue description
Parameterless BiDi events are generated as `Event<Void>` with a mapper `map -> null`. The current event dispatch loop treats a `null` mapped value as a signal to skip dispatch entirely, so handlers for such events never fire.

### Issue Context
- Generator emits `new Event<>(..., map -> null)` when an event has no `params` schema.
- Runtime dispatch (`Connection.handleEventResponse`) currently does `if (value == null) return;` before invoking callbacks.

### Fix
Update event dispatch to still invoke callbacks even when the mapped value is `null` (this is required for `Event<Void>`). The simplest fix is to remove the `if (value == null) return;` guard, since `Consumer.accept(null)` is valid and preserves delivery semantics for void events.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/Connection.java[375-395]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[519-539]

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


2. Builder loses explicit null ✓ Resolved 🐞 Bug ≡ Correctness
Description
For immutable bidirectional record types, the generated nested Builder cannot represent “explicitly
set to null” for nullable optional fields, so toMap() will omit the key instead of sending an
explicit null. This breaks commands where nullable optional fields use explicit null as a distinct
wire state from omission (the generator explicitly documents this distinction via the <field>Set
flag).
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R905-912]

+      sb.append(bm).append("public ").append(cls).append(" build() {\n");
+      sb.append(bm).append("  return new ").append(cls).append("(");
+      sb.append(
+          fields.stream()
+              .map(f -> f.required ? f.name : "Optional.ofNullable(" + f.name + ")")
+              .collect(Collectors.joining(", ")));
+      sb.append(");\n");
+      sb.append(bm).append("}\n");
Evidence
The generator explicitly relies on a separate <field>Set boolean to distinguish “unset” from
“explicit null” for nullable optional fields, but the Builder path never sets that boolean when a
caller passes null, so toMap() will omit the field rather than emit an explicit null.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[679-705]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[799-815]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[905-912]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[982-998]

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

### Issue description
Generated immutable record Builders lose the “explicitly set to null” signal for nullable optional fields. The outer record uses a boolean `<field>Set` to decide whether to omit the key or send an explicit `null`, but Builder.build() always passes `Optional.ofNullable(field)` and the constructor derives `<field>Set` solely from `Optional.isPresent()`, making explicit-null indistinguishable from “never set”.

### Issue Context
This only affects records that are both receivable and sendable (immutable + has Builder) and have optional fields whose schema type is nullable.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[679-836]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[845-914]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[960-999]

### Suggested fix approach
- In appendBuilder, for each optional field where `isNullable(f.typeRef)` is true, generate an additional boolean flag in the Builder (e.g., `private boolean <name>Set;`).
- In the Builder setter for that field, set `<name>Set = true` regardless of whether the value is null.
- Update the generated outer-class constructor for immutable types so that, for nullable optional fields, it can accept and store the explicit-set flag (either as an extra constructor parameter or by generating a dedicated private constructor used only by Builder).
- Ensure toMap() for immutable types uses the propagated flag so `setX(null)` results in a payload containing `"x": null`.

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



Remediation recommended

3. Unquoted genrule arguments 🐞 Bug ☼ Reliability ⭐ New
Description
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.
Code

java/src/org/openqa/selenium/bidi/BUILD.bazel[R59-65]

+genrule(
+    name = "generate-bidi",
+    srcs = ["//javascript/selenium-webdriver:create-bidi-src_schema"],
+    outs = ["bidi-generated.srcjar"],
+    cmd = "$(execpath :bidi-client-generator) $(location //javascript/selenium-webdriver:create-bidi-src_schema) $@",
+    tools = [":bidi-client-generator"],
+)
Evidence
The genrule currently constructs an unquoted shell command string, and the repo’s JS BiDi generation
macro explicitly calls out that avoiding shell command strings prevents quoting/escaping
issues—highlighting this as a known concern in-repo.

java/src/org/openqa/selenium/bidi/BUILD.bazel[59-65]
javascript/selenium-webdriver/private/generate_bidi.bzl[150-154]

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

### 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


4. Numeric discriminator misdispatch ✓ Resolved 🐞 Bug ≡ Correctness
Description
BiDiGenerator emits discriminated-union comparisons as string equality for any non-null, non-boolean
selector value, so numeric discriminator literals will never match the Long/Double values produced
by JSON parsing. This causes union fromMap() to fall through to the default variant (if present) or
throw BiDiException despite a valid payload.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R1253-1268]

+            Object value = sv.get("value");
+            String variantRef = str(sv, "ref");
+            String variantClass = resolveRefToJavaClass(variantRef, domain);
+            // A discriminator value isn't always a string (e.g.
+            // bluetooth.HandleRequestDevicePromptParameters dispatches on a boolean "accept"
+            // field) — the deserialized map value is a real Boolean there, so comparing it
+            // against a quoted string literal via String#equals would never match. Emit a literal
+            // of the value's own type instead of always quoting it.
+            String test;
+            if (value == null) {
+              test = "discriminator == null";
+            } else if (value instanceof Boolean) {
+              test = "Objects.equals(discriminator, " + value + ")";
+            } else {
+              test = "\"" + value + "\".equals(discriminator)";
+            }
Evidence
The generator currently treats all non-Boolean discriminator literals as strings, but the schema
projector can create const values from literals, and Selenium JSON parsing produces numeric objects
for JSON numbers—so numeric discriminators can’t match the generated string comparison.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1242-1270]
javascript/selenium-webdriver/project_bidi_schema.mjs[105-113]
java/src/org/openqa/selenium/json/JsonInput.java[227-298]

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

### Issue description
`BiDiGenerator` generates discriminated-union dispatch code that only special-cases `null` and `Boolean` discriminator values. For any other discriminator literal, it generates a quoted string comparison (`"<value>".equals(discriminator)`), which fails for numeric discriminators because Selenium’s JSON parsing materializes numbers as `Long`/`Double`.

### Issue Context
- Schema projection can emit selector variant `value` from CDDL literals (`{ const: e.Value }`), which is not constrained to strings.
- Selenium JSON parsing reads integer numbers as `Long` and decimals as `Double`.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1253-1268]

### Suggested fix
Add an explicit `value instanceof Number` branch when computing `test`, emitting a typed Java numeric literal that matches the runtime discriminator type:
- If `value` is a `Long`, generate `Objects.equals(discriminator, 123L)`.
- If `value` is a `Double`, generate `Objects.equals(discriminator, 1.23)` (or `1.23d`).
This keeps dispatch behavior correct for schemas that use numeric discriminator tags.

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


5. Event payload double-parsed 🐞 Bug ➹ Performance
Description
ConverterFunctions.fromMap() re-serializes an event params Map back into JSON and reparses it to
build the typed payload object, adding avoidable CPU/allocation overhead for every event. Connection
already parses each websocket message into a Map before invoking the event mapper, so event handling
currently does extra encode/decode work per event.
Code

java/src/org/openqa/selenium/bidi/ConverterFunctions.java[R47-55]

+  public static <T> Function<Map<String, Object>, T> fromMap(Class<T> type) {
+    Require.nonNull("Type", type);
+    return map -> {
+      String json = JSON.toJson(map);
+      try (StringReader reader = new StringReader(json);
+          JsonInput input = JSON.newInput(reader)) {
+        return input.readNonNull(type);
+      }
+    };
Evidence
Connection already parses the incoming frame into a Map and passes the params map to the event
mapper; ConverterFunctions.fromMap then serializes that map back to JSON and reparses it, creating
redundant encode/decode work.

java/src/org/openqa/selenium/bidi/Connection.java[299-313]
java/src/org/openqa/selenium/bidi/Connection.java[374-379]
java/src/org/openqa/selenium/bidi/ConverterFunctions.java[47-55]

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

### Issue description
Event handling currently does redundant work:
1) `Connection.handle(...)` parses the websocket frame into a `Map<String,Object>`.
2) The event mapper produced by `ConverterFunctions.fromMap(...)` converts that map back to a JSON string and reparses it to construct the typed object.

This adds extra CPU and allocations on event-heavy paths.

### Issue Context
`Event` mappers are `Function<Map<String,Object>, X>`, so the current approach uses a JSON round-trip to reuse ConstructorCoercer-based deserialization.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/ConverterFunctions.java[47-55]
- java/src/org/openqa/selenium/bidi/Connection.java[299-313]
- java/src/org/openqa/selenium/bidi/Connection.java[374-379]

### Suggested fix
Prefer a single-parse event path by introducing a `JsonInput`-based event-mapping option:
- Add a new mapper shape (e.g., `Function<JsonInput, X>`) for `Event` (or add a parallel `Event2`/overload), similar to `Command` mapping.
- Update `Connection.handleEventResponse` to parse the *raw websocket message* once with `JsonInput`, position the reader at `params`, and invoke the `JsonInput` mapper.
- Update `BiDiGenerator` to generate event constants using the `JsonInput` mapper where possible.

This eliminates the per-event map→JSON→parse conversion while keeping deserialization behavior consistent.

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


View more (3)
6. Locale-dependent identifier casing ✓ Resolved 🐞 Bug ☼ Reliability
Description
BiDiGenerator derives Java identifiers using String#toUpperCase()/toLowerCase() without Locale.ROOT,
so builds under locales like Turkish can generate different enum/event constant names and fail
compilation for code expecting ASCII identifiers (e.g., Level.INFO). Use locale-stable casing to
make generated sources reproducible across environments.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R1659-1685]

+  static String domainPackage(String domain) {
+    return BASE_PKG + ".protocol." + domain.toLowerCase();
+  }
+
+  static String domainOf(String typeName) {
+    int dot = typeName.indexOf('.');
+    return dot >= 0 ? typeName.substring(0, dot) : typeName;
+  }
+
+  static String simpleNameOf(String typeName) {
+    int dot = typeName.indexOf('.');
+    return dot >= 0 ? typeName.substring(dot + 1) : typeName;
+  }
+
+  static String capitalize(String s) {
+    if (s == null || s.isEmpty()) return s;
+    return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+  }
+
+  static String toConstantName(String camel) {
+    // Insert underscore before each uppercase letter, then upper-case the whole string
+    return camel.replaceAll("([A-Z])", "_$1").toUpperCase();
+  }
+
+  static String toEnumConstant(String wireValue) {
+    return wireValue.toUpperCase().replace('-', '_').replace('.', '_').replace(' ', '_');
+  }
Evidence
The generator currently uses locale-sensitive case conversions for generated identifiers, and the
test suite already references ASCII enum constants like Level.INFO; under Turkish locale the
generator could emit a different identifier (e.g., İNFO), causing compilation failures.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1659-1685]
java/test/org/openqa/selenium/bidi/protocol/module/LogModuleTest.java[56-65]

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

### Issue description
`BiDiGenerator` uses locale-sensitive case conversions when generating Java identifiers (package names, event constants, enum constants). In non-English locales (notably Turkish), `toUpperCase()`/`toLowerCase()` can produce different characters (e.g., `i -> İ`), causing generated identifiers to differ and potentially breaking compilation of downstream code that references expected ASCII names.

### Issue Context
This affects `domainPackage`, `toConstantName`, and `toEnumConstant`, which all currently call `toLowerCase()`/`toUpperCase()` without specifying a locale.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1659-1685]

### Suggested fix
- Add `import java.util.Locale;`
- Change:
 - `domain.toLowerCase()` -> `domain.toLowerCase(Locale.ROOT)`
 - `.toUpperCase()` -> `.toUpperCase(Locale.ROOT)`
 - `wireValue.toUpperCase()` -> `wireValue.toUpperCase(Locale.ROOT)`

(Optionally) add a small unit test that temporarily sets the default locale to Turkish and asserts `toEnumConstant("info")` returns `INFO`.

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


7. BiDiGenerator.main() missing Javadoc 📘 Rule violation ✧ Quality
Description
BiDiGenerator.main(String[] args) is a new public method without any Javadoc. This violates the
requirement that changed public methods have complete Javadoc.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R136-140]

+  public static void main(String[] args) throws IOException {
+    if (args.length != 2) {
+      System.err.println("Usage: BiDiGenerator <schema.json> <output.srcjar>");
+      System.exit(1);
+    }
Evidence
PR Compliance ID 330201 requires a Javadoc block immediately above each changed public method.
BiDiGenerator.main(...) is public and appears without any preceding Javadoc.

Rule 330201: Require complete Javadoc on public API methods
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[136-140]

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

## Issue description
`BiDiGenerator.main(...)` is `public` but has no Javadoc.

## Issue Context
PR Compliance requires a Javadoc block for each changed public method, including tags for parameters/returns/throws as applicable.

## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[136-140]

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


8. Container results typed wrong ✓ Resolved 🐞 Bug ≡ Correctness
Description
For command results that are top-level list/map types, the generated module method return type is
parameterized (e.g., List<T>/Map<String,T>) but resolveCommandResultArg forces deserialization with
Object.class. This loses element/value coercion and yields raw Map/List structures, so callers can
get runtime type mismatches when they use the declared generic types.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R1444-1449]

+    private String resolveCommandResultArg(Map<String, Object> resultRef, String contextDomain) {
+      if (resultRef == null) return null;
+      if (resultRef.containsKey("list") || resultRef.containsKey("map")) {
+        // Generic container types: use Object.class and rely on caller to cast
+        return "Object.class";
+      }
Evidence
resolveJavaType generates parameterized java.util.List<...>/java.util.Map<String,...> types, but
resolveCommandResultArg explicitly returns Object.class for list/map results, which feeds into
Command’s readNonNull(typeOfX) mapper and prevents typed coercion for the declared generic return
type.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1237-1264]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1444-1449]
java/src/org/openqa/selenium/bidi/Command.java[43-46]

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

### Issue description
BiDiGenerator emits parameterized Java return types for `list` and `map` result schema refs, but `resolveCommandResultArg` returns `Object.class` for those shapes. That causes `Command` to deserialize using `JsonInput.readNonNull(Object.class)` which cannot preserve/carry generic element/value types, so the generated API promises `List<Foo>` but returns a raw `List` of uncoerced values.

### Issue Context
This affects any BiDi command whose `result` is directly a container (not wrapped in a record result type).

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1237-1264]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1444-1472]

### Suggested fix approach
Option A (preferred): generate a proper reflective `Type` for container results:
- For a list result, emit `new org.openqa.selenium.json.TypeToken<java.util.List<Elem>>() {}.getType()`.
- For a map result, emit `new org.openqa.selenium.json.TypeToken<java.util.Map<String, Val>>() {}.getType()`.
- Pass that `Type` into the `Command` constructor instead of `Object.class`.

Option B: if full generic coercion is not supported/desired, downgrade the generated method return type for top-level container results to `Object` (or `List<Object>` / `Map<String,Object>`) to match the actual deserialization behavior and avoid a misleading API contract.

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



Informational

9. Ambiguous Javadoc link 🐞 Bug ⚙ Maintainability ⭐ New
Description
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.
Code

java/src/org/openqa/selenium/bidi/Module.java[R73-79]

+  /**
+   * Cancels a previously registered event subscription.
+   *
+   * @param subscriptionId a subscription id previously returned by {@link #subscribe}
+   */
+  public final void unsubscribe(String subscriptionId) {
    handle.unsubscribe(subscriptionId);
Evidence
The file defines two subscribe overloads, and the unsubscribe Javadoc references #subscribe
without parameter types, making the link ambiguous.

java/src/org/openqa/selenium/bidi/Module.java[56-71]
java/src/org/openqa/selenium/bidi/Module.java[73-79]

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

### 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


Grey Divider

Previous review results

Review updated until commit 3d4a143

Results up to commit 9e68827 ⚖️ Balanced


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


Action required
1. Builder loses explicit null ✓ Resolved 🐞 Bug ≡ Correctness
Description
For immutable bidirectional record types, the generated nested Builder cannot represent “explicitly
set to null” for nullable optional fields, so toMap() will omit the key instead of sending an
explicit null. This breaks commands where nullable optional fields use explicit null as a distinct
wire state from omission (the generator explicitly documents this distinction via the <field>Set
flag).
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R905-912]

+      sb.append(bm).append("public ").append(cls).append(" build() {\n");
+      sb.append(bm).append("  return new ").append(cls).append("(");
+      sb.append(
+          fields.stream()
+              .map(f -> f.required ? f.name : "Optional.ofNullable(" + f.name + ")")
+              .collect(Collectors.joining(", ")));
+      sb.append(");\n");
+      sb.append(bm).append("}\n");
Evidence
The generator explicitly relies on a separate <field>Set boolean to distinguish “unset” from
“explicit null” for nullable optional fields, but the Builder path never sets that boolean when a
caller passes null, so toMap() will omit the field rather than emit an explicit null.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[679-705]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[799-815]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[905-912]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[982-998]

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

### Issue description
Generated immutable record Builders lose the “explicitly set to null” signal for nullable optional fields. The outer record uses a boolean `<field>Set` to decide whether to omit the key or send an explicit `null`, but Builder.build() always passes `Optional.ofNullable(field)` and the constructor derives `<field>Set` solely from `Optional.isPresent()`, making explicit-null indistinguishable from “never set”.

### Issue Context
This only affects records that are both receivable and sendable (immutable + has Builder) and have optional fields whose schema type is nullable.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[679-836]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[845-914]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[960-999]

### Suggested fix approach
- In appendBuilder, for each optional field where `isNullable(f.typeRef)` is true, generate an additional boolean flag in the Builder (e.g., `private boolean <name>Set;`).
- In the Builder setter for that field, set `<name>Set = true` regardless of whether the value is null.
- Update the generated outer-class constructor for immutable types so that, for nullable optional fields, it can accept and store the explicit-set flag (either as an extra constructor parameter or by generating a dedicated private constructor used only by Builder).
- Ensure toMap() for immutable types uses the propagated flag so `setX(null)` results in a payload containing `"x": null`.

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



Remediation recommended
2. BiDiGenerator.main() missing Javadoc 📘 Rule violation ✧ Quality
Description
BiDiGenerator.main(String[] args) is a new public method without any Javadoc. This violates the
requirement that changed public methods have complete Javadoc.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R136-140]

+  public static void main(String[] args) throws IOException {
+    if (args.length != 2) {
+      System.err.println("Usage: BiDiGenerator <schema.json> <output.srcjar>");
+      System.exit(1);
+    }
Evidence
PR Compliance ID 330201 requires a Javadoc block immediately above each changed public method.
BiDiGenerator.main(...) is public and appears without any preceding Javadoc.

Rule 330201: Require complete Javadoc on public API methods
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[136-140]

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

## Issue description
`BiDiGenerator.main(...)` is `public` but has no Javadoc.

## Issue Context
PR Compliance requires a Javadoc block for each changed public method, including tags for parameters/returns/throws as applicable.

## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[136-140]

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


3. Container results typed wrong ✓ Resolved 🐞 Bug ≡ Correctness
Description
For command results that are top-level list/map types, the generated module method return type is
parameterized (e.g., List<T>/Map<String,T>) but resolveCommandResultArg forces deserialization with
Object.class. This loses element/value coercion and yields raw Map/List structures, so callers can
get runtime type mismatches when they use the declared generic types.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R1444-1449]

+    private String resolveCommandResultArg(Map<String, Object> resultRef, String contextDomain) {
+      if (resultRef == null) return null;
+      if (resultRef.containsKey("list") || resultRef.containsKey("map")) {
+        // Generic container types: use Object.class and rely on caller to cast
+        return "Object.class";
+      }
Evidence
resolveJavaType generates parameterized java.util.List<...>/java.util.Map<String,...> types, but
resolveCommandResultArg explicitly returns Object.class for list/map results, which feeds into
Command’s readNonNull(typeOfX) mapper and prevents typed coercion for the declared generic return
type.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1237-1264]
java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1444-1449]
java/src/org/openqa/selenium/bidi/Command.java[43-46]

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

### Issue description
BiDiGenerator emits parameterized Java return types for `list` and `map` result schema refs, but `resolveCommandResultArg` returns `Object.class` for those shapes. That causes `Command` to deserialize using `JsonInput.readNonNull(Object.class)` which cannot preserve/carry generic element/value types, so the generated API promises `List<Foo>` but returns a raw `List` of uncoerced values.

### Issue Context
This affects any BiDi command whose `result` is directly a container (not wrapped in a record result type).

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1237-1264]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1444-1472]

### Suggested fix approach
Option A (preferred): generate a proper reflective `Type` for container results:
- For a list result, emit `new org.openqa.selenium.json.TypeToken<java.util.List<Elem>>() {}.getType()`.
- For a map result, emit `new org.openqa.selenium.json.TypeToken<java.util.Map<String, Val>>() {}.getType()`.
- Pass that `Type` into the `Command` constructor instead of `Object.class`.

Option B: if full generic coercion is not supported/desired, downgrade the generated method return type for top-level container results to `Object` (or `List<Object>` / `Map<String,Object>`) to match the actual deserialization behavior and avoid a misleading API contract.

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


Results up to commit 7b9799f ⚖️ Balanced


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


Remediation recommended
1. Locale-dependent identifier casing ✓ Resolved 🐞 Bug ☼ Reliability
Description
BiDiGenerator derives Java identifiers using String#toUpperCase()/toLowerCase() without Locale.ROOT,
so builds under locales like Turkish can generate different enum/event constant names and fail
compilation for code expecting ASCII identifiers (e.g., Level.INFO). Use locale-stable casing to
make generated sources reproducible across environments.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R1659-1685]

+  static String domainPackage(String domain) {
+    return BASE_PKG + ".protocol." + domain.toLowerCase();
+  }
+
+  static String domainOf(String typeName) {
+    int dot = typeName.indexOf('.');
+    return dot >= 0 ? typeName.substring(0, dot) : typeName;
+  }
+
+  static String simpleNameOf(String typeName) {
+    int dot = typeName.indexOf('.');
+    return dot >= 0 ? typeName.substring(dot + 1) : typeName;
+  }
+
+  static String capitalize(String s) {
+    if (s == null || s.isEmpty()) return s;
+    return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+  }
+
+  static String toConstantName(String camel) {
+    // Insert underscore before each uppercase letter, then upper-case the whole string
+    return camel.replaceAll("([A-Z])", "_$1").toUpperCase();
+  }
+
+  static String toEnumConstant(String wireValue) {
+    return wireValue.toUpperCase().replace('-', '_').replace('.', '_').replace(' ', '_');
+  }
Evidence
The generator currently uses locale-sensitive case conversions for generated identifiers, and the
test suite already references ASCII enum constants like Level.INFO; under Turkish locale the
generator could emit a different identifier (e.g., İNFO), causing compilation failures.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1659-1685]
java/test/org/openqa/selenium/bidi/protocol/module/LogModuleTest.java[56-65]

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

### Issue description
`BiDiGenerator` uses locale-sensitive case conversions when generating Java identifiers (package names, event constants, enum constants). In non-English locales (notably Turkish), `toUpperCase()`/`toLowerCase()` can produce different characters (e.g., `i -> İ`), causing generated identifiers to differ and potentially breaking compilation of downstream code that references expected ASCII names.

### Issue Context
This affects `domainPackage`, `toConstantName`, and `toEnumConstant`, which all currently call `toLowerCase()`/`toUpperCase()` without specifying a locale.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1659-1685]

### Suggested fix
- Add `import java.util.Locale;`
- Change:
 - `domain.toLowerCase()` -> `domain.toLowerCase(Locale.ROOT)`
 - `.toUpperCase()` -> `.toUpperCase(Locale.ROOT)`
 - `wireValue.toUpperCase()` -> `wireValue.toUpperCase(Locale.ROOT)`

(Optionally) add a small unit test that temporarily sets the default locale to Turkish and asserts `toEnumConstant("info")` returns `INFO`.

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


Results up to commit 1760de4 ⚖️ Balanced


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


Action required
1. Void events never dispatched 🐞 Bug ≡ Correctness
Description
BiDiGenerator emits Event<Void> constants with a mapper that always returns null for events without
params, but Connection.handleEventResponse returns early when the mapper result is null. This
prevents any parameterless BiDi events from ever invoking subscribed handlers.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R532-538]

+        } else {
+          sb.append("  public static final Event<Void> ")
+              .append(evtConstant)
+              .append(" =\n")
+              .append("      new Event<>(\"")
+              .append(method)
+              .append("\", map -> null);\n\n");
Evidence
The generator explicitly creates Event<Void> with a mapper that returns null for no-params events,
and the runtime event dispatch loop returns early on null mapped values, so such events cannot reach
callbacks.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[512-539]
java/src/org/openqa/selenium/bidi/Connection.java[347-395]

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

### Issue description
Parameterless BiDi events are generated as `Event<Void>` with a mapper `map -> null`. The current event dispatch loop treats a `null` mapped value as a signal to skip dispatch entirely, so handlers for such events never fire.

### Issue Context
- Generator emits `new Event<>(..., map -> null)` when an event has no `params` schema.
- Runtime dispatch (`Connection.handleEventResponse`) currently does `if (value == null) return;` before invoking callbacks.

### Fix
Update event dispatch to still invoke callbacks even when the mapped value is `null` (this is required for `Event<Void>`). The simplest fix is to remove the `if (value == null) return;` guard, since `Consumer.accept(null)` is valid and preserves delivery semantics for void events.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/Connection.java[375-395]
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[519-539]

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


Results up to commit 1f4ec87 ⚖️ Balanced


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


Remediation recommended
1. Numeric discriminator misdispatch 🐞 Bug ≡ Correctness
Description
BiDiGenerator emits discriminated-union comparisons as string equality for any non-null, non-boolean
selector value, so numeric discriminator literals will never match the Long/Double values produced
by JSON parsing. This causes union fromMap() to fall through to the default variant (if present) or
throw BiDiException despite a valid payload.
Code

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[R1253-1268]

+            Object value = sv.get("value");
+            String variantRef = str(sv, "ref");
+            String variantClass = resolveRefToJavaClass(variantRef, domain);
+            // A discriminator value isn't always a string (e.g.
+            // bluetooth.HandleRequestDevicePromptParameters dispatches on a boolean "accept"
+            // field) — the deserialized map value is a real Boolean there, so comparing it
+            // against a quoted string literal via String#equals would never match. Emit a literal
+            // of the value's own type instead of always quoting it.
+            String test;
+            if (value == null) {
+              test = "discriminator == null";
+            } else if (value instanceof Boolean) {
+              test = "Objects.equals(discriminator, " + value + ")";
+            } else {
+              test = "\"" + value + "\".equals(discriminator)";
+            }
Evidence
The generator currently treats all non-Boolean discriminator literals as strings, but the schema
projector can create const values from literals, and Selenium JSON parsing produces numeric objects
for JSON numbers—so numeric discriminators can’t match the generated string comparison.

java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1242-1270]
javascript/selenium-webdriver/project_bidi_schema.mjs[105-113]
java/src/org/openqa/selenium/json/JsonInput.java[227-298]

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

### Issue description
`BiDiGenerator` generates discriminated-union dispatch code that only special-cases `null` and `Boolean` discriminator values. For any other discriminator literal, it generates a quoted string comparison (`"<value>".equals(discriminator)`), which fails for numeric discriminators because Selenium’s JSON parsing materializes numbers as `Long`/`Double`.

### Issue Context
- Schema projection can emit selector variant `value` from CDDL literals (`{ const: e.Value }`), which is not constrained to strings.
- Selenium JSON parsing reads integer numbers as `Long` and decimals as `Double`.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDiGenerator.java[1253-1268]

### Suggested fix
Add an explicit `value instanceof Number` branch when computing `test`, emitting a typed Java numeric literal that matches the runtime discriminator type:
- If `value` is a `Long`, generate `Objects.equals(discriminator, 123L)`.
- If `value` is a `Double`, generate `Objects.equals(discriminator, 1.23)` (or `1.23d`).
This keeps dispatch behavior correct for schemas that use numeric discriminator tags.

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


2. Event payload double-parsed 🐞 Bug ➹ Performance
Description
ConverterFunctions.fromMap() re-serializes an event params Map back into JSON and reparses it to
build the typed payload object, adding avoidable CPU/allocation overhead for every event. Connection
already parses each websocket message into a Map before invoking the event mapper, so event handling
currently does extra encode/decode work per event.
Code

java/src/org/openqa/selenium/bidi/ConverterFunctions.java[R47-55]

+  public static <T> Function<Map<String, Object>, T> fromMap(Class<T> type) {
+    Require.nonNull("Type", type);
+    return map -> {
+      String json = JSON.toJson(map);
+      try (StringReader reader = new StringReader(json);
+          JsonInput input = JSON.newInput(reader)) {
+        return input.readNonNull(type);
+      }
+    };
Evidence
Connection already parses the incoming frame into a Map and passes the params map to the event
mapper; ConverterFunctions.fromMap then serializes that map back to JSON and reparses it, creating
redundant encode/decode work.

java/src/org/openqa/selenium/bidi/Connection.java[299-313]
java/src/org/openqa/selenium/bidi/Connection.java[374-379]
java/src/org/openqa/selenium/bidi/ConverterFunctions.java[47-55]

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

### Issue description
Event handling currently does redundant work:
1) `Connection.handle(...)` parses the websocket frame into a `Map<String,Object>`.
2) The event mapper produced by `ConverterFunctions.fromMap(...)` converts that map back to a JSON string and reparses it to construct the typed object.

This adds extra CPU and allocations on event-heavy paths.

### Issue Context
`Event` mappers are `Function<Map<String,Object>, X>`, so the current approach uses a JSON round-trip to reuse ConstructorCoercer-based deserialization.

### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/ConverterFunctions.java[47-55]
- java/src/org/openqa/selenium/bidi/Connection.java[299-313]
- java/src/org/openqa/selenium/bidi/Connection.java[374-379]

### Suggested fix
Prefer a single-parse event path by introducing a `JsonInput`-based event-mapping option:
- Add a new mapper shape (e.g., `Function<JsonInput, X>`) for `Event` (or add a parallel `Event2`/overload), similar to `Command` mapping.
- Update `Connection.handleEventResponse` to parse the *raw websocket message* once with `JsonInput`, position the reader at `params`, and invoke the `JsonInput` mapper.
- Update `BiDiGenerator` to generate event constants using the `JsonInput` mapper where possible.

This eliminates the per-event map→JSON→parse conversion while keeping deserialization behavior consistent.

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


Qodo Logo

Comment thread java/src/org/openqa/selenium/bidi/BiDiGenerator.java
Comment thread java/src/org/openqa/selenium/bidi/BiDiGenerator.java
Comment thread java/src/org/openqa/selenium/bidi/BiDiGenerator.java
Comment thread java/src/org/openqa/selenium/bidi/BiDiGenerator.java
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 7b9799f

Comment thread java/src/org/openqa/selenium/bidi/BiDiGenerator.java
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 1760de4

Comment thread java/src/org/openqa/selenium/bidi/BiDiGenerator.java
Comment thread java/src/org/openqa/selenium/bidi/ConverterFunctions.java
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 1f4ec87

],
)

genrule(

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.

Remediation recommended

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) {
/**

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.

Informational

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

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 3d4a143

@titusfortner

Copy link
Copy Markdown
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related C-java Java Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants