Skip to content

[java] Prevent accessing BiDi internals from driver#17745

Closed
pujagani wants to merge 1 commit into
SeleniumHQ:trunkfrom
pujagani:java-bidi-connect-factory
Closed

[java] Prevent accessing BiDi internals from driver#17745
pujagani wants to merge 1 commit into
SeleniumHQ:trunkfrom
pujagani:java-bidi-connect-factory

Conversation

@pujagani

@pujagani pujagani commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

🔗 Related Issues

Aligning the code to the ADR #17670.

💥 What does this PR do?

It deprecates the method that allow public access to BiDi internals and adds a method that be used by passing a driver object.

🔧 Implementation Notes

🤖 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

Follow-up

  • Remove deprecated method after 2 versions.

Considerations

  • Hand-written BiDi module constructors are not updated because of ongoing BiDi generator work, that will soon replace those classes.

🔄 Types of changes

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

@selenium-ci selenium-ci added C-java Java Bindings B-devtools Includes everything BiDi or Chrome DevTools related labels Jul 3, 2026
@pujagani pujagani marked this pull request as ready for review July 3, 2026 06:34
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Java: add BiDi.from(WebDriver) and deprecate HasBiDi#getBiDi access

✨ Enhancement 🧪 Tests 🕐 10-20 Minutes

Grey Divider

AI Description

• Add a BiDi factory method that accepts a WebDriver and validates BiDi support.
• Deprecate HasBiDi#getBiDi to discourage direct access to BiDi internals.
• Migrate remote code and tests to the new BiDi.from(driver) access pattern.
Diagram

graph TD
  A["Client code"] --> B["BiDi.from(driver)"] --> C{"driver is HasBiDi?"} -->|"Yes"| D["HasBiDi.maybeGetBiDi()"] --> E{"BiDi present?"} -->|"Yes"| F["BiDi instance"]
  C -->|"No"| X["BiDiException: unsupported"]
  E -->|"No"| Y["BiDiException: cannot connect"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Hide HasBiDi behind internal API / module boundary
  • ➕ Stronger encapsulation: prevents external casts to HasBiDi entirely
  • ➕ Clearer public surface: only the supported factory is visible
  • ➖ Potentially breaking for downstream users relying on HasBiDi
  • ➖ More invasive refactor across drivers/augmenters and JPMS packaging
2. Expose a capability-based getter on WebDriver (new public interface)
  • ➕ Avoids casts and centralizes capability discovery in one place
  • ➕ Could offer a more future-proof contract than HasBiDi
  • ➖ Still expands public WebDriver surface area
  • ➖ Requires broader API design/approval and multi-language parity considerations

Recommendation: The PR’s approach (introduce BiDi.from(WebDriver) and deprecate HasBiDi#getBiDi) is a good incremental step aligned with the ADR: it centralizes validation and nudges callers away from direct BiDi-internal access without an immediate breaking change. A follow-up to further reduce/relocate HasBiDi exposure would improve encapsulation, but is appropriately deferred given compatibility constraints.

Files changed (8) +29 / -15

Enhancement (2) +17 / -0
BiDi.javaAdd BiDi.from(WebDriver) factory for safe BiDi acquisition +11/-0

Add BiDi.from(WebDriver) factory for safe BiDi acquisition

• Introduces a static factory method that accepts a WebDriver, validates BiDi support, and returns a BiDi instance or throws a BiDiException with a clearer failure reason.

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

HasBiDi.javaDeprecate HasBiDi#getBiDi in favor of BiDi.from(driver) +6/-0

Deprecate HasBiDi#getBiDi in favor of BiDi.from(driver)

• Marks the default getBiDi() method as deprecated (since 4.24, for removal) and documents the preferred replacement API.

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

Refactor (1) +1 / -2
RemoteScript.javaUse BiDi.from(driver) instead of casting to HasBiDi +1/-2

Use BiDi.from(driver) instead of casting to HasBiDi

• Replaces the direct HasBiDi cast/getBiDi call with the new BiDi.from(WebDriver) factory when initializing RemoteScript.

java/src/org/openqa/selenium/remote/RemoteScript.java

Tests (5) +11 / -13
BiDiSessionCleanUpTest.javaMigrate BiDi session cleanup tests to BiDi.from(driver) +2/-2

Migrate BiDi session cleanup tests to BiDi.from(driver)

• Updates test setup to obtain BiDi via BiDi.from(localDriver) rather than casting to HasBiDi.

java/test/org/openqa/selenium/bidi/BiDiSessionCleanUpTest.java

BiDiSessionTest.javaMigrate BiDi session creation test to BiDi.from(driver) +1/-1

Migrate BiDi session creation test to BiDi.from(driver)

• Updates the session creation test to use the new factory method for BiDi acquisition.

java/test/org/openqa/selenium/bidi/BiDiSessionTest.java

NetworkCommandsTest.javaUse BiDi.from(driver) for listener cleanup in network tests +2/-2

Use BiDi.from(driver) for listener cleanup in network tests

• Removes HasBiDi usage and switches listener removal to BiDi.from(driver).removeListener(callbackId).

java/test/org/openqa/selenium/bidi/network/NetworkCommandsTest.java

FirefoxDriverConcurrentTest.javaStop using FirefoxDriver#getBiDi directly in concurrency test +2/-2

Stop using FirefoxDriver#getBiDi directly in concurrency test

• Replaces FirefoxDriver casts with BiDi.from(driver) to align with the new access pattern.

java/test/org/openqa/selenium/firefox/FirefoxDriverConcurrentTest.java

RemoteWebDriverBiDiTest.javaMigrate Grid BiDi smoke test to BiDi.from(localDriver) +4/-6

Migrate Grid BiDi smoke test to BiDi.from(localDriver)

• Removes HasBiDi import and updates the BiDi session creation test to use BiDi.from(localDriver) (also drops try-with-resources for BiDi).

java/test/org/openqa/selenium/grid/router/RemoteWebDriverBiDiTest.java

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (2) 📎 Requirement gaps (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 18 rules

Grey Divider


Action required

1. BiDi.from() missing Javadoc 📘 Rule violation ✧ Quality
Description
The new public API method BiDi.from(WebDriver) lacks a Javadoc block, and therefore does not
document its parameter and return value as required. This reduces API clarity and violates the
project’s public API documentation requirements.
Code

java/src/org/openqa/selenium/bidi/BiDi.java[R44-52]

+  public static BiDi from(WebDriver driver) {
+    Require.nonNull("WebDriver", driver);
+    if (!(driver instanceof HasBiDi)) {
+      throw new BiDiException("WebDriver does not support BiDi protocol");
+    }
+    return ((HasBiDi) driver)
+        .maybeGetBiDi()
+        .orElseThrow(() -> new BiDiException("Unable to create a BiDi connection"));
+  }
Evidence
PR Compliance ID 330200 and 330201 require Javadoc for public API methods, including @param tags
for parameters and @return for non-void returns. The added public method from(WebDriver driver)
has no Javadoc at all in the changed lines.

Rule 330200: Require Javadoc for all public API types and methods
Rule 330201: Require complete Javadoc on public API methods
java/src/org/openqa/selenium/bidi/BiDi.java[44-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
`BiDi.from(WebDriver)` is a newly added public API method but has no Javadoc, so it does not document purpose, `@param driver`, and `@return`.

## Issue Context
Public API methods must have Javadoc and include tags for parameters and non-void returns.

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

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


2. HasBiDi.getBiDi() Javadoc incomplete 📘 Rule violation ✧ Quality
Description
The modified (deprecated) public method HasBiDi.getBiDi() has Javadoc consisting only of an
@deprecated tag and omits an @return tag. This violates the requirement for complete Javadoc on
touched public methods.
Code

java/src/org/openqa/selenium/bidi/HasBiDi.java[R27-33]

+  /**
+   * @deprecated Use {@link BiDi#from(WebDriver)} instead.
+   */
+  @Deprecated(since = "4.24", forRemoval = true)
  default BiDi getBiDi() {
    return maybeGetBiDi()
        .orElseThrow(() -> new BiDiException("Unable to create a BiDi connection"));
Evidence
PR Compliance ID 330201 requires that each changed public method has a Javadoc block with at least
one free-text sentence and includes an @return tag for non-void methods. The updated Javadoc for
getBiDi() contains only an @deprecated tag and provides no @return documentation.

Rule 330201: Require complete Javadoc on public API methods
java/src/org/openqa/selenium/bidi/HasBiDi.java[27-34]

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 touched public method `HasBiDi.getBiDi()` has tag-only Javadoc (`@deprecated` only) and does not include an `@return` tag even though it returns `BiDi`.

## Issue Context
Public methods changed in this PR must have complete Javadoc including at least one free-text sentence and `@return` for non-void returns.

## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/HasBiDi.java[27-34]

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


3. BiDi.from skips initialization 🐞 Bug ≡ Correctness
Description
BiDi.from(WebDriver) calls HasBiDi.maybeGetBiDi() and throws when it’s empty, but the BiDi augmenter
(BiDiProvider) intentionally keeps BiDi lazy and returns Optional.empty until getBiDi() is called.
This makes BiDi.from() fail on freshly augmented RemoteWebDriver instances, breaking
RemoteWebDriver.script()/RemoteScript and the updated RemoteWebDriverBiDiTest path.
Code

java/src/org/openqa/selenium/bidi/BiDi.java[R44-52]

+  public static BiDi from(WebDriver driver) {
+    Require.nonNull("WebDriver", driver);
+    if (!(driver instanceof HasBiDi)) {
+      throw new BiDiException("WebDriver does not support BiDi protocol");
+    }
+    return ((HasBiDi) driver)
+        .maybeGetBiDi()
+        .orElseThrow(() -> new BiDiException("Unable to create a BiDi connection"));
+  }
Evidence
BiDi.from relies on maybeGetBiDi(), but BiDiProvider’s HasBiDi implementation returns
biDi.getIfInitialized() from maybeGetBiDi (empty until initialized) and only initializes in
getBiDi(). The updated RemoteWebDriverBiDiTest constructs an augmented RemoteWebDriver and
immediately calls BiDi.from(localDriver), which will therefore throw. RemoteWebDriver.script()
constructs RemoteScript(this), and RemoteScript now calls BiDi.from(driver) during construction, so
the same failure impacts the Script API.

java/src/org/openqa/selenium/bidi/BiDi.java[44-52]
java/src/org/openqa/selenium/bidi/BiDiProvider.java[55-73]
java/test/org/openqa/selenium/grid/router/RemoteWebDriverBiDiTest.java[77-102]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[531-536]
java/src/org/openqa/selenium/remote/RemoteScript.java[56-61]

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

### Issue description
`BiDi.from(WebDriver)` currently uses `HasBiDi.maybeGetBiDi()` which may legitimately be empty for augmented drivers (BiDi is lazily created). This causes `BiDi.from()` to throw even when BiDi is supported, breaking BiDi-based features on `Augmenter`-enhanced `RemoteWebDriver`.

### Issue Context
- The `BiDiProvider` augmentation returns `Optional.empty()` from `maybeGetBiDi()` until `getBiDi()` is invoked (lazy initialization).
- The PR updates production code (`RemoteScript`) and tests to call `BiDi.from(driver)`, so this is a real runtime path.

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

### Suggested fix approach
Update `BiDi.from(WebDriver)` to *initialize* the BiDi instance when needed, e.g.:
- Prefer calling `((HasBiDi) driver).getBiDi()` (with a local `@SuppressWarnings("deprecation")` if necessary) so lazy augmenters initialize.
- Or: `hasBiDi.maybeGetBiDi().orElseGet(() -> hasBiDi.getBiDi())` to keep the “already initialized” fast path.

This ensures `BiDi.from()` works with `Augmenter`-provided `HasBiDi` implementations and fixes the failing `RemoteScript`/remote BiDi flows.

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


Grey Divider

Qodo Logo

Comment on lines +44 to +52
public static BiDi from(WebDriver driver) {
Require.nonNull("WebDriver", driver);
if (!(driver instanceof HasBiDi)) {
throw new BiDiException("WebDriver does not support BiDi protocol");
}
return ((HasBiDi) driver)
.maybeGetBiDi()
.orElseThrow(() -> new BiDiException("Unable to create a BiDi connection"));
}

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.

Action required

1. bidi.from() missing javadoc 📘 Rule violation ✧ Quality

The new public API method BiDi.from(WebDriver) lacks a Javadoc block, and therefore does not
document its parameter and return value as required. This reduces API clarity and violates the
project’s public API documentation requirements.
Agent Prompt
## Issue description
`BiDi.from(WebDriver)` is a newly added public API method but has no Javadoc, so it does not document purpose, `@param driver`, and `@return`.

## Issue Context
Public API methods must have Javadoc and include tags for parameters and non-void returns.

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

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

Comment on lines +27 to 33
/**
* @deprecated Use {@link BiDi#from(WebDriver)} instead.
*/
@Deprecated(since = "4.24", forRemoval = true)
default BiDi getBiDi() {
return maybeGetBiDi()
.orElseThrow(() -> new BiDiException("Unable to create a BiDi connection"));

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.

Action required

2. hasbidi.getbidi() javadoc incomplete 📘 Rule violation ✧ Quality

The modified (deprecated) public method HasBiDi.getBiDi() has Javadoc consisting only of an
@deprecated tag and omits an @return tag. This violates the requirement for complete Javadoc on
touched public methods.
Agent Prompt
## Issue description
The touched public method `HasBiDi.getBiDi()` has tag-only Javadoc (`@deprecated` only) and does not include an `@return` tag even though it returns `BiDi`.

## Issue Context
Public methods changed in this PR must have complete Javadoc including at least one free-text sentence and `@return` for non-void returns.

## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/HasBiDi.java[27-34]

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

Comment on lines +44 to +52
public static BiDi from(WebDriver driver) {
Require.nonNull("WebDriver", driver);
if (!(driver instanceof HasBiDi)) {
throw new BiDiException("WebDriver does not support BiDi protocol");
}
return ((HasBiDi) driver)
.maybeGetBiDi()
.orElseThrow(() -> new BiDiException("Unable to create a BiDi connection"));
}

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.

Action required

3. Bidi.from skips initialization 🐞 Bug ≡ Correctness

BiDi.from(WebDriver) calls HasBiDi.maybeGetBiDi() and throws when it’s empty, but the BiDi augmenter
(BiDiProvider) intentionally keeps BiDi lazy and returns Optional.empty until getBiDi() is called.
This makes BiDi.from() fail on freshly augmented RemoteWebDriver instances, breaking
RemoteWebDriver.script()/RemoteScript and the updated RemoteWebDriverBiDiTest path.
Agent Prompt
### Issue description
`BiDi.from(WebDriver)` currently uses `HasBiDi.maybeGetBiDi()` which may legitimately be empty for augmented drivers (BiDi is lazily created). This causes `BiDi.from()` to throw even when BiDi is supported, breaking BiDi-based features on `Augmenter`-enhanced `RemoteWebDriver`.

### Issue Context
- The `BiDiProvider` augmentation returns `Optional.empty()` from `maybeGetBiDi()` until `getBiDi()` is invoked (lazy initialization).
- The PR updates production code (`RemoteScript`) and tests to call `BiDi.from(driver)`, so this is a real runtime path.

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

### Suggested fix approach
Update `BiDi.from(WebDriver)` to *initialize* the BiDi instance when needed, e.g.:
- Prefer calling `((HasBiDi) driver).getBiDi()` (with a local `@SuppressWarnings("deprecation")` if necessary) so lazy augmenters initialize.
- Or: `hasBiDi.maybeGetBiDi().orElseGet(() -> hasBiDi.getBiDi())` to keep the “already initialized” fast path.

This ensures `BiDi.from()` works with `Augmenter`-provided `HasBiDi` implementations and fixes the failing `RemoteScript`/remote BiDi flows.

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

@pujagani

pujagani commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Resolving the AI bot comments

@pujagani

pujagani commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

This approach needs a rethink.

@pujagani pujagani closed this Jul 3, 2026
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 C-java Java Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants