Skip to content

[java][bidi] Route navigation commands through BiDi#17232

Open
krishnamohan-kothapalli wants to merge 5 commits intoSeleniumHQ:trunkfrom
krishnamohan-kothapalli:java-bidi-navigation
Open

[java][bidi] Route navigation commands through BiDi#17232
krishnamohan-kothapalli wants to merge 5 commits intoSeleniumHQ:trunkfrom
krishnamohan-kothapalli:java-bidi-navigation

Conversation

@krishnamohan-kothapalli
Copy link

@krishnamohan-kothapalli krishnamohan-kothapalli commented Mar 17, 2026

Implements get(), back(), forward(), and refresh() via BrowsingContext when webSocketUrl capability is present, falling back to Classic otherwise. Maps pageLoadStrategy to ReadinessState: normal->COMPLETE, eager->INTERACTIVE, none->NONE. Adds integration tests covering all navigation commands with BiDi enabled.

#13995

What does this PR do?

Routes get(), back(), forward(), and refresh() through the WebDriver BiDi
protocol when BiDi is enabled (webSocketUrl capability present), falling
back to Classic WebDriver commands otherwise.

This is the Java implementation of the navigation proof-of-concept agreed
at the Selenium Dev Summit — the minimum required for Selenium 5 and the
reference pattern for BiDi/Classic switching across all bindings.

Implementation Notes

  • Added getReadinessState() helper that maps pageLoadStrategy capability
    to BiDi ReadinessState: normal→COMPLETE, eager→INTERACTIVE, none→NONE
  • Used getWindowHandle() as the browsing context ID (per issue guidance,
    proper context tracking is deferred to a follow-up)
  • Added BrowsingContext as a dependency in BUILD.bazel for the :api target
  • Reuses existing BrowsingContext class from org.openqa.selenium.bidi.browsingcontext

Additional Considerations

Types of changes

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

@CLAassistant
Copy link

CLAassistant commented Mar 17, 2026

CLA assistant check
All committers have signed the CLA.

@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 Mar 17, 2026
@qodo-code-review
Copy link
Contributor

Review Summary by Qodo

Route Java navigation commands through WebDriver BiDi protocol

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Routes navigation commands through WebDriver BiDi protocol
• Maps pageLoadStrategy capability to BiDi ReadinessState enum
• Implements fallback to Classic WebDriver when BiDi unavailable
• Adds comprehensive integration tests for BiDi navigation

Grey Divider

File Changes

1. java/src/org/openqa/selenium/remote/RemoteWebDriver.java ✨ Enhancement +34/-4

Implement BiDi routing for navigation commands

• Added imports for BrowsingContext and ReadinessState from BiDi module
• Modified get() method to route through BiDi when webSocketUrl capability present
• Implemented getReadinessState() helper mapping pageLoadStrategy to ReadinessState
• Updated back(), forward(), and refresh() in RemoteNavigation class to use BiDi
• Maintained fallback to Classic WebDriver commands when BiDi unavailable

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


2. java/test/org/openqa/selenium/bidi/browsingcontext/BiDiNavigationTest.java 🧪 Tests +113/-0

Add BiDi navigation integration tests

• Created new integration test class for BiDi navigation functionality
• Added tests for driver.get() with single and multiple navigations
• Added tests for navigate().to() with String and URL parameters
• Added tests for history traversal via back() and forward()
• Added test for page reload via refresh()
• Marked history traversal tests with @NotYetImplemented(EDGE) annotation

java/test/org/openqa/selenium/bidi/browsingcontext/BiDiNavigationTest.java


3. java/src/org/openqa/selenium/remote/BUILD.bazel ⚙️ Configuration changes +1/-0

Add BrowsingContext dependency

• Added dependency on //java/src/org/openqa/selenium/bidi/browsingcontext module

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


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Mar 17, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0) 📐 Spec deviations (0)

Grey Divider


Action required

1. BiDi navigation crash path🐞 Bug ✓ Correctness
Description
RemoteWebDriver routes navigation through BrowsingContext when webSocketUrl != null, but
BrowsingContext throws unless the driver implements HasBiDi, so navigation can fail with
IllegalArgumentException instead of falling back to Classic. This also mis-detects BiDi support
when webSocketUrl is present but not a String (e.g., boolean), which is explicitly handled
elsewhere in the codebase.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R375-380]

+    if (getCapabilities().getCapability("webSocketUrl") != null) {
+      new BrowsingContext(this, getWindowHandle())
+          .navigate(url, getReadinessState());
+    } else {
+      execute(DriverCommand.GET(url));
+    }
Evidence
The new logic switches on getCapabilities().getCapability("webSocketUrl") != null and then
instantiates BrowsingContext(this, ...). However, RemoteWebDriver does not implement HasBiDi
by default, and BrowsingContext(WebDriver, String) enforces driver instanceof HasBiDi with an
exception. Additionally, BiDi augmentation in BiDiProvider only applies when webSocketUrl can be
cast to a String, and the Grid LocalNode code documents that webSocketUrl may be a boolean for
unsupported versions—making != null an unsafe predicate.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[373-381]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[110-120]
java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java[75-87]
java/src/org/openqa/selenium/bidi/BiDiProvider.java[42-45]
java/src/org/openqa/selenium/bidi/BiDiProvider.java[87-90]
java/src/org/openqa/selenium/grid/node/local/LocalNode.java[1159-1165]

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

## Issue description
Navigation commands (`get`, `back`, `forward`, `refresh`) switch to BiDi based on `webSocketUrl != null`, but the BiDi BrowsingContext API requires a `WebDriver` that implements `HasBiDi`. This can throw at runtime (breaking navigation) and prevents the intended Classic fallback.
### Issue Context
- `BrowsingContext(WebDriver, String)` throws when `driver` is not `HasBiDi`.
- BiDi augmentation is only applicable when `webSocketUrl` is a **String** (not merely present).
- The Grid codebase explicitly notes `webSocketUrl` can be boolean for unsupported versions.
### Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[373-381]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1232-1248]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1261-1268]
### Suggested change
- Introduce a small helper, e.g. `private boolean isBiDiEnabled()` returning something like:
- `return (this instanceof HasBiDi) && (getCapabilities().getCapability("webSocketUrl") instanceof String);`
- Use this helper to decide whether to call `BrowsingContext` or Classic `execute(...)`.
- Keep Classic fallback as the default when BiDi is not truly available.

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


2. Wrong readiness mapping type🐞 Bug ✓ Correctness
Description
getReadinessState() compares the pageLoadStrategy capability to string literals, but driver
options store a PageLoadStrategy enum in capabilities. When an enum value is present, the
comparisons fail and the method always returns ReadinessState.COMPLETE, silently breaking
eager/none behavior.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R383-390]

+  private ReadinessState getReadinessState() {
+    Object strategy = getCapabilities().getCapability(CapabilityType.PAGE_LOAD_STRATEGY);
+    if ("eager".equals(strategy)) {
+      return ReadinessState.INTERACTIVE;
+    } else if ("none".equals(strategy)) {
+      return ReadinessState.NONE;
+    }
+    return ReadinessState.COMPLETE;
Evidence
AbstractDriverOptions#setPageLoadStrategy stores a PageLoadStrategy enum as the capability
value. In RemoteWebDriver#getReadinessState, the code does "eager".equals(strategy) where
strategy is an Object—this returns false when the object is PageLoadStrategy.EAGER (even though
its toString() is "eager"), causing the method to incorrectly default to COMPLETE.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[383-390]
java/src/org/openqa/selenium/remote/AbstractDriverOptions.java[79-82]
java/src/org/openqa/selenium/PageLoadStrategy.java[22-36]

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

## Issue description
`RemoteWebDriver#getReadinessState()` compares `pageLoadStrategy` to string literals, but the capability is commonly stored as a `PageLoadStrategy` enum, causing eager/none strategies to be misinterpreted as COMPLETE.
### Issue Context
- `AbstractDriverOptions#setPageLoadStrategy` stores a `PageLoadStrategy` enum into capabilities.
- Comparing strings to the enum object using `"eager".equals(strategy)` will never match.
### Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[383-390]
### Suggested change
- Convert the capability to a normalized string:
- `String strategy = strategyObj == null ? "normal" : strategyObj.toString();`
- compare using `equalsIgnoreCase` (or a switch on the normalized value)
- Alternatively, parse via `PageLoadStrategy.fromString(...)` and branch on the enum value.

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



Remediation recommended

3. Duplicated webSocketUrl BiDi switch📘 Rule violation ⛯ Reliability
Description
The BiDi-vs-Classic switching rule (webSocketUrl capability check + BrowsingContext creation) is
duplicated across multiple navigation methods, increasing the chance of drift/inconsistent behavior.
This violates the requirement to centralize duplicated business rules into a single reusable
implementation.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R375-380]

+    if (getCapabilities().getCapability("webSocketUrl") != null) {
+      new BrowsingContext(this, getWindowHandle())
+          .navigate(url, getReadinessState());
+    } else {
+      execute(DriverCommand.GET(url));
+    }
Evidence
PR Compliance ID 14 requires consolidating duplicated business rules; the same webSocketUrl
capability gate (and similar BrowsingContext construction) is repeated in get(), back(),
forward(), and refresh().

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[375-380]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1234-1238]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1243-1247]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1262-1267]
Best Practice: Learned patterns

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 BiDi/Classic switching logic is duplicated across multiple navigation methods (`get`, `back`, `forward`, `refresh`). This duplication can drift over time (e.g., one method changes the enabling condition or context id logic while others do not), causing inconsistent user-visible behavior.
## Issue Context
The repeated pattern is: check `getCapabilities().getCapability("webSocketUrl") != null` and create a `new BrowsingContext(..., getWindowHandle())` to perform the operation. This is a business rule (feature gating + context selection) and should be centralized.
## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[375-390]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1234-1238]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1243-1247]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1262-1267]

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


4. No logging for BiDi routing 📘 Rule violation ✓ Correctness
Description
Navigation commands now conditionally route through BiDi based on webSocketUrl, but no log is
emitted to indicate which path was taken. This reduces operational insight when diagnosing
navigation issues in BiDi-enabled sessions.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R375-380]

+    if (getCapabilities().getCapability("webSocketUrl") != null) {
+      new BrowsingContext(this, getWindowHandle())
+          .navigate(url, getReadinessState());
+    } else {
+      execute(DriverCommand.GET(url));
+    }
Evidence
PR Compliance ID 7 calls for logging around user-impacting operations where it helps explain
actions/decisions; the newly introduced BiDi-vs-Classic routing decision is made without any
logging, even though it changes the execution path for core navigation calls.

AGENTS.md
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[375-380]

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 code now conditionally routes navigation via BiDi when `webSocketUrl` is present, but does not log this decision. When a user reports navigation failures or unexpected behavior, it is harder to determine whether BiDi or Classic execution was used.
## Issue Context
The routing decision is made in `get()`, `back()`, `forward()`, and `refresh()` based on `webSocketUrl`. Logging should prefer stable identifiers (e.g., chosen transport/path, browsing context id, readiness state) and avoid leaking potentially sensitive data.
## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[375-390]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1234-1238]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1243-1247]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1262-1267]

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@krishnamohan-kothapalli krishnamohan-kothapalli force-pushed the java-bidi-navigation branch 2 times, most recently from aae4c08 to 1eee757 Compare March 18, 2026 13:53
@VietND96
Copy link
Member

Could you please rebase the branch correctly? It is showing 3000+ file changes.

Krishna and others added 2 commits March 18, 2026 13:01
Implements get(), back(), forward(), and refresh() via
BrowsingContext when webSocketUrl capability is present,
falling back to Classic otherwise. Maps pageLoadStrategy
to ReadinessState: normal->COMPLETE, eager->INTERACTIVE,
none->NONE. Adds integration tests covering all navigation
commands with BiDi enabled.

Fixes SeleniumHQ#13995
- Guard BiDi path with instanceof HasBiDi check to prevent
  IllegalArgumentException on plain RemoteWebDriver instances
- Check webSocketUrl instanceof String (not just != null) to
  avoid false positive on Boolean.TRUE during session setup
- Handle PageLoadStrategy as both enum and String when mapping
  to ReadinessState to fix eager/none being silently ignored

Addresses review feedback on SeleniumHQ#13995
@krishnamohan-kothapalli
Copy link
Author

@VietND96 Fixed! The CLA assistant was treating my local machine email as a different user, so I had to clean up the commit history. The branch now has only the 2 relevant commits, and all committers have signed the CLA.

@cgoldberg
Copy link
Member

I thing the test in RBE is failing because your navigation isn't handling alerts that may be present during pageload. You need a listener and some logic to handle them, since the old unhandledPromptBehavior isn't replicated in BiDi.

The failing test is:
https://gypsum.cluster.engflow.com/invocations/default/83f2b019-771f-48e7-b19c-3e18206a5afd

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.

5 participants