Skip to content

[java][bidi] Handle user prompts during BiDi navigation #17244

Open
krishnamohan-kothapalli wants to merge 13 commits into
SeleniumHQ:trunkfrom
krishnamohan-kothapalli:java-bidi-navigation-clean
Open

[java][bidi] Handle user prompts during BiDi navigation #17244
krishnamohan-kothapalli wants to merge 13 commits into
SeleniumHQ:trunkfrom
krishnamohan-kothapalli:java-bidi-navigation-clean

Conversation

@krishnamohan-kothapalli
Copy link
Copy Markdown

🔗 Related Issues

💥 What does this PR do?

When navigating via BiDi (get(), back(), forward(), refresh()), an
onload alert blocks the page from reaching its readiness state, causing
browsingContext.navigate/traverseHistory/reload to hang indefinitely.

This fix registers a browsingContext.userPromptOpened listener before each
BiDi navigation that automatically dismisses or accepts prompts according to
the unhandledPromptBehavior capability (defaulting to dismiss and notify).
The listener is removed in a finally block after navigation completes.

🔧 Implementation Notes

Classic WebDriver delegated prompt handling during navigation to the browser
via the unhandledPromptBehavior session capability. BiDi navigation commands
don't have that mechanism, so we replicate it by subscribing to
browsingContext.userPromptOpened for the duration of each navigation call.

The listener runs on the BiDi WebSocket receiver thread while sendAndWait
blocks the caller thread — the Connection implementation already handles
this pattern safely (see the tryLock comment in handleEventResponse).

IGNORE behaviour is preserved: navigation runs without a listener,
leaving the alert open for the caller to handle.

💡 Additional Considerations

  • Follow-up parity work may be needed in other bindings (Python, Ruby, .NET, JS)
    to replicate this behaviour for their BiDi navigation paths.

🔄 Types of changes

  • Bug fix (backwards compatible)

Krishna and others added 4 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
@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 21, 2026
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Handle user prompts during BiDi navigation with automatic dismissal

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Handle user prompts during BiDi navigation via listener
• Route navigation commands (get, back, forward, refresh) through BiDi
• Map pageLoadStrategy to ReadinessState for BiDi compatibility
• Automatically dismiss or accept alerts per unhandledPromptBehavior capability

Grey Divider

File Changes

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

Implement BiDi navigation with automatic prompt handling

• Added BiDi navigation support for get(), back(), forward(), and refresh() methods
• Implemented navigateViaBiDi() wrapper that registers a browsingContext.userPromptOpened
 listener to handle alerts according to unhandledPromptBehavior capability
• Added helper methods: isBiDiEnabled(), getReadinessState(), and
 getUnhandledPromptBehaviour() to manage BiDi navigation state
• Imported necessary BiDi classes and JSON utilities for event handling

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 comprehensive test suite for BiDi navigation functionality
• Tests cover driver.get(), navigate().to() with both String and URL objects
• Tests verify browser history traversal via back() and forward() methods
• Tests validate page reload via refresh() method

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


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

Add BiDi 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
Copy Markdown
Contributor

qodo-code-review Bot commented Mar 21, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0)

Grey Divider


Action required

1. biDiPromptListenerInstalled lacks rollback ✓ Resolved 📘 Rule violation ☼ Reliability
Description
ensureBiDiPromptListener() sets biDiPromptListenerInstalled to true before calling
getBiDi().addListener(...), but if addListener (including its underlying session.subscribe)
throws, the flag is never reset and the listener will never be installed or retried for the rest of
the session. This transient-failure path can permanently disable prompt handling, risking stuck BiDi
navigations (e.g., onload prompts) and inconsistent session behavior.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R452-474]

+  private void ensureBiDiPromptListener() {
+    if (!biDiPromptListenerInstalled.compareAndSet(false, true)) {
+      return;
+    }
+    ((HasBiDi) this)
+        .getBiDi()
+        .addListener(
+            USER_PROMPT_OPENED_EVENT,
+            prompt -> {
+              String contextId = biDiNavigatingContextId;
+              if (contextId == null || !contextId.equals(prompt.getBrowsingContextId())) {
+                return;
+              }
+              LOG.fine(
+                  () ->
+                      String.format(
+                          "Handling %s user prompt during BiDi navigation (%s)",
+                          prompt.getType(), biDiAcceptPrompt ? "accept" : "dismiss"));
+              if (biDiNotifyOnPrompt) {
+                biDiHandledPrompt.compareAndSet(null, prompt);
+              }
+              new BrowsingContext(this, contextId).handleUserPrompt(biDiAcceptPrompt);
+            });
Evidence
PR Compliance ID 11 requires rollback/cleanup on failure paths when registering async listeners, yet
the cited code path sets the “installed” flag before attempting listener registration and lacks any
try/catch rollback. Because BiDi.addListener performs a session.subscribe that can throw, an
exception during addListener leaves the system in a partially initialized state where
biDiPromptListenerInstalled remains true, so subsequent calls will skip installation and the
listener will not be retried.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[452-475]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[448-475]
java/src/org/openqa/selenium/bidi/BiDi.java[84-91]
java/src/org/openqa/selenium/bidi/Connection.java[159-175]
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
`ensureBiDiPromptListener()` marks `biDiPromptListenerInstalled` as installed (via `compareAndSet(false, true)`) *before* attempting `getBiDi().addListener(...)`. If `addListener` fails/throws (including failures from the underlying `session.subscribe`), the flag remains `true`, causing future navigations to skip listener installation and potentially leaving the session without BiDi prompt handling.

## Issue Context
This logic runs in an async/concurrent setting (e.g., BiDi receiver thread vs caller thread) and is intended to lazily install the listener once per session while remaining robust to transient failures. Per PR Compliance ID 11, failure paths during async listener registration should roll back/clean up so the system can retry rather than becoming permanently broken.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[452-475]

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


2. biDiPageLoadTimeout cache not synchronized ✓ Resolved 📘 Rule violation ☼ Reliability
Description
getPageLoadDuration() reads/writes biDiPageLoadTimeout using double-checked locking but without
volatile/consistent synchronization, and pageLoadTimeout() updates it without any lock. This can
lead to stale reads or missed updates when accessed from multiple threads (notably with BiDi event
handling occurring concurrently).
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R387-396]

+  private Duration getPageLoadDuration() {
+    if (biDiPageLoadTimeout == null) {
+      synchronized (this) {
+        if (biDiPageLoadTimeout == null) {
+          biDiPageLoadTimeout = manage().timeouts().getPageLoadTimeout();
+        }
+      }
+    }
+    return biDiPageLoadTimeout;
+  }
Evidence
PR Compliance ID 11 requires shared state updates in concurrent/async code to be
synchronized/visibility-safe. The new cache uses a double-checked pattern in getPageLoadDuration()
and an unsynchronized write in pageLoadTimeout(), which violates that requirement.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[387-396]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1241-1244]
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
`biDiPageLoadTimeout` is accessed with an unsafe double-checked-locking pattern and is also written outside any synchronization. Without `volatile` or a single consistent lock/atomic primitive, other threads may observe stale values.

## Issue Context
BiDi navigation introduces concurrent execution (BiDi receiver thread vs caller thread). Any shared cached state used by navigation should have correct memory-visibility guarantees.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[387-396]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1241-1244]

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


3. RemoteWebDriver compile failure ✓ Resolved 🐞 Bug ≡ Correctness
Description
RemoteWebDriver references biDiPageLoadTimeout and uses Json/JsonInput in the new BiDi navigation
path, but the class does not declare biDiPageLoadTimeout and does not import Json/JsonInput, causing
compilation to fail. This blocks building RemoteWebDriver and therefore blocks the PR from merging.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R385-409]

+  // Returns the effective page load timeout for BiDi navigation commands. The value is cached so
+  // repeated navigations don't incur an extra HTTP round-trip to GET_TIMEOUTS.
+  private Duration getPageLoadDuration() {
+    if (biDiPageLoadTimeout == null) {
+      synchronized (this) {
+        if (biDiPageLoadTimeout == null) {
+          biDiPageLoadTimeout = manage().timeouts().getPageLoadTimeout();
+        }
+      }
+    }
+    return biDiPageLoadTimeout;
+  }
+
+  private static final Json BIDI_JSON = new Json();
+
+  // Shared event definition for browsingContext.userPromptOpened used during navigation.
+  private static final Event<UserPromptOpened> USER_PROMPT_OPENED_EVENT =
+      new Event<>(
+          "browsingContext.userPromptOpened",
+          params -> {
+            try (StringReader reader = new StringReader(BIDI_JSON.toJson(params));
+                JsonInput input = BIDI_JSON.newInput(reader)) {
+              return input.readNonNull(UserPromptOpened.class);
+            }
+          });
Evidence
The import section does not include org.openqa.selenium.json.Json/JsonInput, yet the new code
uses Json/JsonInput for USER_PROMPT_OPENED_EVENT. The class field declarations also do not
include biDiPageLoadTimeout, yet getPageLoadDuration() and RemoteTimeouts.pageLoadTimeout()
reference it, which is a compile-time error.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[18-114]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[125-153]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[385-409]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1241-1244]

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` contains new BiDi navigation code that references `biDiPageLoadTimeout` and constructs/parses JSON with `Json`/`JsonInput`, but the class currently has no `biDiPageLoadTimeout` field and no imports for `org.openqa.selenium.json.Json` and `org.openqa.selenium.json.JsonInput`. This will fail compilation.

## Issue Context
The new methods `getPageLoadDuration()` and `RemoteTimeouts.pageLoadTimeout()` read/write `biDiPageLoadTimeout`. The new `USER_PROMPT_OPENED_EVENT` mapper uses `Json` and `JsonInput`.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[18-114]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[125-153]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[385-409]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1241-1244]

## What to change
1. Add missing imports:
  - `import org.openqa.selenium.json.Json;`
  - `import org.openqa.selenium.json.JsonInput;`
2. Declare a `biDiPageLoadTimeout` field in `RemoteWebDriver` (e.g., `@Nullable private volatile Duration biDiPageLoadTimeout;`) near other fields so `getPageLoadDuration()` and `pageLoadTimeout()` compile.
3. Ensure the field is safely published if you keep the double-checked locking pattern (use `volatile` or simplify synchronization).

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


View more (1)
4. Notify behavior ignored ✓ Resolved 🐞 Bug ≡ Correctness
Description
RemoteWebDriver.navigateViaBiDi treats ACCEPT_AND_NOTIFY/DISMISS_AND_NOTIFY exactly like
ACCEPT/DISMISS (only toggling accept vs dismiss), so the “notify” part is never surfaced to the
caller. With the default DISMISS_AND_NOTIFY, BiDi navigation (get/back/forward/refresh) will
silently succeed even though an unexpected prompt was opened.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R448-490]

+  private UnexpectedAlertBehaviour getUnhandledPromptBehaviour() {
+    Object raw = getCapabilities().getCapability(CapabilityType.UNHANDLED_PROMPT_BEHAVIOUR);
+    if (raw instanceof UnexpectedAlertBehaviour) {
+      return (UnexpectedAlertBehaviour) raw;
+    }
+    if (raw instanceof String) {
+      return UnexpectedAlertBehaviour.fromString((String) raw);
+    }
+    // W3C WebDriver spec default is "dismiss and notify"
+    return UnexpectedAlertBehaviour.DISMISS_AND_NOTIFY;
+  }
+
+  // Wraps a BiDi navigation call with prompt handling that replicates, for BiDi, the automatic
+  // unhandledPromptBehavior that classic WebDriver delegates to the browser. The listener is
+  // registered only for the duration of the navigation and removed in the finally block, so it
+  // cannot interfere with user-triggered alerts after the page loads.
+  private void navigateViaBiDi(String contextId, Runnable navigation) {
+    UnexpectedAlertBehaviour behaviour = getUnhandledPromptBehaviour();
+    if (behaviour == UnexpectedAlertBehaviour.IGNORE) {
+      navigation.run();
+      return;
+    }
+    boolean accept =
+        behaviour == UnexpectedAlertBehaviour.ACCEPT
+            || behaviour == UnexpectedAlertBehaviour.ACCEPT_AND_NOTIFY;
+    BiDi biDi = ((HasBiDi) this).getBiDi();
+    long listenerId =
+        biDi.addListener(
+            contextId,
+            USER_PROMPT_OPENED_EVENT,
+            prompt -> {
+              LOG.fine(
+                  () ->
+                      String.format(
+                          "Handling %s user prompt during BiDi navigation (%s)",
+                          prompt.getType(), accept ? "accept" : "dismiss"));
+              new BrowsingContext(this, contextId).handleUserPrompt(accept);
+            });
+    try {
+      navigation.run();
+    } finally {
+      biDi.removeListener(listenerId);
+    }
Evidence
The code explicitly defaults to DISMISS_AND_NOTIFY, but navigateViaBiDi only uses the enum to decide
accept vs dismiss, and never performs any additional action for the “notify” variants;
handleUserPrompt itself cannot notify because it is a void send-only call. The presence of
*_AND_NOTIFY enum values and UnhandledAlertException shows there is a distinct “notify the caller”
pathway that is currently not exercised.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[448-490]
java/src/org/openqa/selenium/UnexpectedAlertBehaviour.java[22-49]
java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java[219-225]
java/src/org/openqa/selenium/UnhandledAlertException.java[25-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
`navigateViaBiDi` collapses `ACCEPT_AND_NOTIFY`/`DISMISS_AND_NOTIFY` into the same behavior as `ACCEPT`/`DISMISS` (it only computes an `accept` boolean), and never notifies the caller when a prompt was handled under a `*_AND_NOTIFY` policy.

### Issue Context
- `getUnhandledPromptBehaviour()` defaults to `DISMISS_AND_NOTIFY`, so this is the common case.
- `BrowsingContext.handleUserPrompt(...)` only sends the BiDi command; it does not throw a user-visible exception.

### Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[448-490]

### Implementation sketch
- Compute two booleans: `accept` and `notify` (`notify = behaviour == ACCEPT_AND_NOTIFY || behaviour == DISMISS_AND_NOTIFY`).
- Track whether a prompt was seen/handled during the navigation (e.g., `AtomicReference<UserPromptOpened>` or at least `AtomicReference<String>` for `prompt.getMessage()`).
- In the event handler, if `notify` is true, store the prompt message/type (first one is fine) and still call `handleUserPrompt(accept)`.
- After `navigation.run()` completes successfully (and after listener cleanup), if `notify` is true and a prompt was seen, throw `UnhandledAlertException` (include the stored alert text/message if available).
- Ensure navigation exceptions still take precedence (only throw the notify exception when navigation itself succeeded).

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



Remediation recommended

5. Prompt callback exceptions leak ✓ Resolved 🐞 Bug ☼ Reliability
Description
The browsingContext.userPromptOpened callback calls handleUserPrompt() without catching exceptions;
if BiDi.send fails, the exception propagates through Connection.handleEventResponse and is rethrown
from the WebSocket onText task. This can make prompt handling flaky and produce hard-to-diagnose
BiDi event processing failures.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R460-474]

+            prompt -> {
+              String contextId = biDiNavigatingContextId;
+              if (contextId == null || !contextId.equals(prompt.getBrowsingContextId())) {
+                return;
+              }
+              LOG.fine(
+                  () ->
+                      String.format(
+                          "Handling %s user prompt during BiDi navigation (%s)",
+                          prompt.getType(), biDiAcceptPrompt ? "accept" : "dismiss"));
+              if (biDiNotifyOnPrompt) {
+                biDiHandledPrompt.compareAndSet(null, prompt);
+              }
+              new BrowsingContext(this, contextId).handleUserPrompt(biDiAcceptPrompt);
+            });
Evidence
The RemoteWebDriver listener executes BiDi commands inside the event callback; Connection invokes
callbacks without try/catch, and the onText handler rethrows runtime exceptions from event
processing tasks.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[456-474]
java/src/org/openqa/selenium/bidi/Connection.java[336-384]
java/src/org/openqa/selenium/bidi/Connection.java[265-275]
java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java[223-225]

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 `USER_PROMPT_OPENED_EVENT` listener invokes `new BrowsingContext(...).handleUserPrompt(...)` directly. If that call throws (e.g., BiDi command error), the exception escapes the callback and bubbles up through the BiDi event dispatch code.

### Issue Context
`Connection.handleEventResponse` calls listeners without guarding against exceptions, and `Listener.onText` wraps runtime exceptions and rethrows them.

### Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[460-474]

### Recommended fix
Wrap the body (or at least the `handleUserPrompt` call) in `try { ... } catch (RuntimeException e) { LOG.log(Level.WARNING, ... , e); }` to prevent exceptions from escaping the event callback. Optionally, consider recording the failure so the navigation thread can surface it if needed.

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


6. No BiDi unsubscribe ✓ Resolved 🐞 Bug ➹ Performance
Description
navigateViaBiDi calls BiDi.addListener for every navigation, and BiDi.addListener always executes a
session.subscribe call, but cleanup only calls BiDi.removeListener which does not send
session.unsubscribe. This adds an extra round-trip per navigation and can leave the browser
subscribed to browsingContext.userPromptOpened for the remainder of the session even when no local
handler exists.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R473-490]

+    BiDi biDi = ((HasBiDi) this).getBiDi();
+    long listenerId =
+        biDi.addListener(
+            contextId,
+            USER_PROMPT_OPENED_EVENT,
+            prompt -> {
+              LOG.fine(
+                  () ->
+                      String.format(
+                          "Handling %s user prompt during BiDi navigation (%s)",
+                          prompt.getType(), accept ? "accept" : "dismiss"));
+              new BrowsingContext(this, contextId).handleUserPrompt(accept);
+            });
+    try {
+      navigation.run();
+    } finally {
+      biDi.removeListener(listenerId);
+    }
Evidence
The RemoteWebDriver change adds a per-navigation BiDi.addListener call; in BiDi.java, addListener
always sends session.subscribe, while removeListener only removes the local listener without
unsubscribing. Connection only unsubscribes in clearListeners(), which is invoked on BiDi.close(),
not after each navigation.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[464-490]
java/src/org/openqa/selenium/bidi/BiDi.java[71-119]
java/src/org/openqa/selenium/bidi/Connection.java[232-251]

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

### Issue description
`navigateViaBiDi` performs `biDi.addListener(...)` on every navigation. In Selenium’s BiDi implementation, `addListener` always sends `session.subscribe`, while `removeListener` only removes the local callback and does not `session.unsubscribe`. This creates avoidable overhead and leaves the remote end subscribed longer than necessary.

### Issue Context
- `BiDi.addListener(String, Event, Consumer)` issues `session.subscribe` unconditionally.
- `BiDi.removeListener(long)` only removes the callback locally.

### Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[473-490]

### Implementation options
1) **Preferred (minimal API change):** Subscribe once per session and gate handling.
  - Lazily register a single `USER_PROMPT_OPENED_EVENT` listener once.
  - Use an `AtomicBoolean inNavigation` + `AtomicReference<String> activeContextId` + `AtomicBoolean accept` to only auto-handle prompts during navigation and only for the active context.
  - This removes the per-navigation `session.subscribe` round-trip and prevents accumulating remote subscriptions.

2) **API-level fix (more correct):** Extend BiDi to unsubscribe when the last listener is removed.
  - Update BiDi/Connection to track actual subscription state separately from local callback presence and call `session.unsubscribe` when appropriate.

Pick whichever aligns with project direction, but avoid issuing `session.subscribe` repeatedly without any corresponding unsubscribe behavior.

ⓘ 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 aa400a8

Results up to commit cfed07f


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)


Action required
1. Notify behavior ignored ✓ Resolved 🐞 Bug ≡ Correctness
Description
RemoteWebDriver.navigateViaBiDi treats ACCEPT_AND_NOTIFY/DISMISS_AND_NOTIFY exactly like
ACCEPT/DISMISS (only toggling accept vs dismiss), so the “notify” part is never surfaced to the
caller. With the default DISMISS_AND_NOTIFY, BiDi navigation (get/back/forward/refresh) will
silently succeed even though an unexpected prompt was opened.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R448-490]

+  private UnexpectedAlertBehaviour getUnhandledPromptBehaviour() {
+    Object raw = getCapabilities().getCapability(CapabilityType.UNHANDLED_PROMPT_BEHAVIOUR);
+    if (raw instanceof UnexpectedAlertBehaviour) {
+      return (UnexpectedAlertBehaviour) raw;
+    }
+    if (raw instanceof String) {
+      return UnexpectedAlertBehaviour.fromString((String) raw);
+    }
+    // W3C WebDriver spec default is "dismiss and notify"
+    return UnexpectedAlertBehaviour.DISMISS_AND_NOTIFY;
+  }
+
+  // Wraps a BiDi navigation call with prompt handling that replicates, for BiDi, the automatic
+  // unhandledPromptBehavior that classic WebDriver delegates to the browser. The listener is
+  // registered only for the duration of the navigation and removed in the finally block, so it
+  // cannot interfere with user-triggered alerts after the page loads.
+  private void navigateViaBiDi(String contextId, Runnable navigation) {
+    UnexpectedAlertBehaviour behaviour = getUnhandledPromptBehaviour();
+    if (behaviour == UnexpectedAlertBehaviour.IGNORE) {
+      navigation.run();
+      return;
+    }
+    boolean accept =
+        behaviour == UnexpectedAlertBehaviour.ACCEPT
+            || behaviour == UnexpectedAlertBehaviour.ACCEPT_AND_NOTIFY;
+    BiDi biDi = ((HasBiDi) this).getBiDi();
+    long listenerId =
+        biDi.addListener(
+            contextId,
+            USER_PROMPT_OPENED_EVENT,
+            prompt -> {
+              LOG.fine(
+                  () ->
+                      String.format(
+                          "Handling %s user prompt during BiDi navigation (%s)",
+                          prompt.getType(), accept ? "accept" : "dismiss"));
+              new BrowsingContext(this, contextId).handleUserPrompt(accept);
+            });
+    try {
+      navigation.run();
+    } finally {
+      biDi.removeListener(listenerId);
+    }
Evidence
The code explicitly defaults to DISMISS_AND_NOTIFY, but navigateViaBiDi only uses the enum to decide
accept vs dismiss, and never performs any additional action for the “notify” variants;
handleUserPrompt itself cannot notify because it is a void send-only call. The presence of
*_AND_NOTIFY enum values and UnhandledAlertException shows there is a distinct “notify the caller”
pathway that is currently not exercised.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[448-490]
java/src/org/openqa/selenium/UnexpectedAlertBehaviour.java[22-49]
java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java[219-225]
java/src/org/openqa/selenium/UnhandledAlertException.java[25-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
`navigateViaBiDi` collapses `ACCEPT_AND_NOTIFY`/`DISMISS_AND_NOTIFY` into the same behavior as `ACCEPT`/`DISMISS` (it only computes an `accept` boolean), and never notifies the caller when a prompt was handled under a `*_AND_NOTIFY` policy.

### Issue Context
- `getUnhandledPromptBehaviour()` defaults to `DISMISS_AND_NOTIFY`, so this is the common case.
- `BrowsingContext.handleUserPrompt(...)` only sends the BiDi command; it does not throw a user-visible exception.

### Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[448-490]

### Implementation sketch
- Compute two booleans: `accept` and `notify` (`notify = behaviour == ACCEPT_AND_NOTIFY || behaviour == DISMISS_AND_NOTIFY`).
- Track whether a prompt was seen/handled during the navigation (e.g., `AtomicReference<UserPromptOpened>` or at least `AtomicReference<String>` for `prompt.getMessage()`).
- In the event handler, if `notify` is true, store the prompt message/type (first one is fine) and still call `handleUserPrompt(accept)`.
- After `navigation.run()` completes successfully (and after listener cleanup), if `notify` is true and a prompt was seen, throw `UnhandledAlertException` (include the stored alert text/message if available).
- Ensure navigation exceptions still take precedence (only throw the notify exception when navigation itself succeeded).

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



Remediation recommended
2. No BiDi unsubscribe ✓ Resolved 🐞 Bug ➹ Performance
Description
navigateViaBiDi calls BiDi.addListener for every navigation, and BiDi.addListener always executes a
session.subscribe call, but cleanup only calls BiDi.removeListener which does not send
session.unsubscribe. This adds an extra round-trip per navigation and can leave the browser
subscribed to browsingContext.userPromptOpened for the remainder of the session even when no local
handler exists.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R473-490]

+    BiDi biDi = ((HasBiDi) this).getBiDi();
+    long listenerId =
+        biDi.addListener(
+            contextId,
+            USER_PROMPT_OPENED_EVENT,
+            prompt -> {
+              LOG.fine(
+                  () ->
+                      String.format(
+                          "Handling %s user prompt during BiDi navigation (%s)",
+                          prompt.getType(), accept ? "accept" : "dismiss"));
+              new BrowsingContext(this, contextId).handleUserPrompt(accept);
+            });
+    try {
+      navigation.run();
+    } finally {
+      biDi.removeListener(listenerId);
+    }
Evidence
The RemoteWebDriver change adds a per-navigation BiDi.addListener call; in BiDi.java, addListener
always sends session.subscribe, while removeListener only removes the local listener without
unsubscribing. Connection only unsubscribes in clearListeners(), which is invoked on BiDi.close(),
not after each navigation.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[464-490]
java/src/org/openqa/selenium/bidi/BiDi.java[71-119]
java/src/org/openqa/selenium/bidi/Connection.java[232-251]

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

### Issue description
`navigateViaBiDi` performs `biDi.addListener(...)` on every navigation. In Selenium’s BiDi implementation, `addListener` always sends `session.subscribe`, while `removeListener` only removes the local callback and does not `session.unsubscribe`. This creates avoidable overhead and leaves the remote end subscribed longer than necessary.

### Issue Context
- `BiDi.addListener(String, Event, Consumer)` issues `session.subscribe` unconditionally.
- `BiDi.removeListener(long)` only removes the callback locally.

### Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[473-490]

### Implementation options
1) **Preferred (minimal API change):** Subscribe once per session and gate handling.
  - Lazily register a single `USER_PROMPT_OPENED_EVENT` listener once.
  - Use an `AtomicBoolean inNavigation` + `AtomicReference<String> activeContextId` + `AtomicBoolean accept` to only auto-handle prompts during navigation and only for the active context.
  - This removes the per-navigation `session.subscribe` round-trip and prevents accumulating remote subscriptions.

2) **API-level fix (more correct):** Extend BiDi to unsubscribe when the last listener is removed.
  - Update BiDi/Connection to track actual subscription state separately from local callback presence and call `session.unsubscribe` when appropriate.

Pick whichever aligns with project direction, but avoid issuing `session.subscribe` repeatedly without any corresponding unsubscribe behavior.

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


Results up to commit 0f63055


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)


Action required
1. biDiPageLoadTimeout cache not synchronized ✓ Resolved 📘 Rule violation ☼ Reliability
Description
getPageLoadDuration() reads/writes biDiPageLoadTimeout using double-checked locking but without
volatile/consistent synchronization, and pageLoadTimeout() updates it without any lock. This can
lead to stale reads or missed updates when accessed from multiple threads (notably with BiDi event
handling occurring concurrently).
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R387-396]

+  private Duration getPageLoadDuration() {
+    if (biDiPageLoadTimeout == null) {
+      synchronized (this) {
+        if (biDiPageLoadTimeout == null) {
+          biDiPageLoadTimeout = manage().timeouts().getPageLoadTimeout();
+        }
+      }
+    }
+    return biDiPageLoadTimeout;
+  }
Evidence
PR Compliance ID 11 requires shared state updates in concurrent/async code to be
synchronized/visibility-safe. The new cache uses a double-checked pattern in getPageLoadDuration()
and an unsynchronized write in pageLoadTimeout(), which violates that requirement.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[387-396]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1241-1244]
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
`biDiPageLoadTimeout` is accessed with an unsafe double-checked-locking pattern and is also written outside any synchronization. Without `volatile` or a single consistent lock/atomic primitive, other threads may observe stale values.

## Issue Context
BiDi navigation introduces concurrent execution (BiDi receiver thread vs caller thread). Any shared cached state used by navigation should have correct memory-visibility guarantees.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[387-396]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1241-1244]

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


2. RemoteWebDriver compile failure ✓ Resolved 🐞 Bug ≡ Correctness
Description
RemoteWebDriver references biDiPageLoadTimeout and uses Json/JsonInput in the new BiDi navigation
path, but the class does not declare biDiPageLoadTimeout and does not import Json/JsonInput, causing
compilation to fail. This blocks building RemoteWebDriver and therefore blocks the PR from merging.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R385-409]

+  // Returns the effective page load timeout for BiDi navigation commands. The value is cached so
+  // repeated navigations don't incur an extra HTTP round-trip to GET_TIMEOUTS.
+  private Duration getPageLoadDuration() {
+    if (biDiPageLoadTimeout == null) {
+      synchronized (this) {
+        if (biDiPageLoadTimeout == null) {
+          biDiPageLoadTimeout = manage().timeouts().getPageLoadTimeout();
+        }
+      }
+    }
+    return biDiPageLoadTimeout;
+  }
+
+  private static final Json BIDI_JSON = new Json();
+
+  // Shared event definition for browsingContext.userPromptOpened used during navigation.
+  private static final Event<UserPromptOpened> USER_PROMPT_OPENED_EVENT =
+      new Event<>(
+          "browsingContext.userPromptOpened",
+          params -> {
+            try (StringReader reader = new StringReader(BIDI_JSON.toJson(params));
+                JsonInput input = BIDI_JSON.newInput(reader)) {
+              return input.readNonNull(UserPromptOpened.class);
+            }
+          });
Evidence
The import section does not include org.openqa.selenium.json.Json/JsonInput, yet the new code
uses Json/JsonInput for USER_PROMPT_OPENED_EVENT. The class field declarations also do not
include biDiPageLoadTimeout, yet getPageLoadDuration() and RemoteTimeouts.pageLoadTimeout()
reference it, which is a compile-time error.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[18-114]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[125-153]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[385-409]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1241-1244]

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` contains new BiDi navigation code that references `biDiPageLoadTimeout` and constructs/parses JSON with `Json`/`JsonInput`, but the class currently has no `biDiPageLoadTimeout` field and no imports for `org.openqa.selenium.json.Json` and `org.openqa.selenium.json.JsonInput`. This will fail compilation.

## Issue Context
The new methods `getPageLoadDuration()` and `RemoteTimeouts.pageLoadTimeout()` read/write `biDiPageLoadTimeout`. The new `USER_PROMPT_OPENED_EVENT` mapper uses `Json` and `JsonInput`.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[18-114]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[125-153]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[385-409]
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[1241-1244]

## What to change
1. Add missing imports:
  - `import org.openqa.selenium.json.Json;`
  - `import org.openqa.selenium.json.JsonInput;`
2. Declare a `biDiPageLoadTimeout` field in `RemoteWebDriver` (e.g., `@Nullable private volatile Duration biDiPageLoadTimeout;`) near other fields so `getPageLoadDuration()` and `pageLoadTimeout()` compile.
3. Ensure the field is safely published if you keep the double-checked locking pattern (use `volatile` or simplify synchronization).

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


Results up to commit b42bd10


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)


Action required
1. biDiPromptListenerInstalled lacks rollback ✓ Resolved 📘 Rule violation ☼ Reliability
Description
ensureBiDiPromptListener() sets biDiPromptListenerInstalled to true before calling
getBiDi().addListener(...), but if addListener (including its underlying session.subscribe)
throws, the flag is never reset and the listener will never be installed or retried for the rest of
the session. This transient-failure path can permanently disable prompt handling, risking stuck BiDi
navigations (e.g., onload prompts) and inconsistent session behavior.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R452-474]

+  private void ensureBiDiPromptListener() {
+    if (!biDiPromptListenerInstalled.compareAndSet(false, true)) {
+      return;
+    }
+    ((HasBiDi) this)
+        .getBiDi()
+        .addListener(
+            USER_PROMPT_OPENED_EVENT,
+            prompt -> {
+              String contextId = biDiNavigatingContextId;
+              if (contextId == null || !contextId.equals(prompt.getBrowsingContextId())) {
+                return;
+              }
+              LOG.fine(
+                  () ->
+                      String.format(
+                          "Handling %s user prompt during BiDi navigation (%s)",
+                          prompt.getType(), biDiAcceptPrompt ? "accept" : "dismiss"));
+              if (biDiNotifyOnPrompt) {
+                biDiHandledPrompt.compareAndSet(null, prompt);
+              }
+              new BrowsingContext(this, contextId).handleUserPrompt(biDiAcceptPrompt);
+            });
Evidence
PR Compliance ID 11 requires rollback/cleanup on failure paths when registering async listeners, yet
the cited code path sets the “installed” flag before attempting listener registration and lacks any
try/catch rollback. Because BiDi.addListener performs a session.subscribe that can throw, an
exception during addListener leaves the system in a partially initialized state where
biDiPromptListenerInstalled remains true, so subsequent calls will skip installation and the
listener will not be retried.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[452-475]
java/src/org/openqa/selenium/remote/RemoteWebDriver.java[448-475]
java/src/org/openqa/selenium/bidi/BiDi.java[84-91]
java/src/org/openqa/selenium/bidi/Connection.java[159-175]
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
`ensureBiDiPromptListener()` marks `biDiPromptListenerInstalled` as installed (via `compareAndSet(false, true)`) *before* attempting `getBiDi().addListener(...)`. If `addListener` fails/throws (including failures from the underlying `session.subscribe`), the flag remains `true`, causing future navigations to skip listener installation and potentially leaving the session without BiDi prompt handling.

## Issue Context
This logic runs in an async/concurrent setting (e.g., BiDi receiver thread vs caller thread) and is intended to lazily install the listener once per session while remaining robust to transient failures. Per PR Compliance ID 11, failure paths during async listener registration should roll back/clean up so the system can retry rather than becoming permanently broken.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[452-475]

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



Remediation recommended
2. Prompt callback exceptions leak ✓ Resolved 🐞 Bug ☼ Reliability
Description
The browsingContext.userPromptOpened callback calls handleUserPrompt() without catching exceptions;
if BiDi.send fails, the exception propagates through Connection.handleEventResponse and is rethrown
from the WebSocket onText task. This can make prompt handling flaky and produce hard-to-diagnose
BiDi event processing failures.
Code

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[R460-474]

+            prompt -> {
+              String contextId = biDiNavigatingContextId;
+              if (contextId == null || !contextId.equals(prompt.getBrowsingContextId())) {
+                return;
+              }
+              LOG.fine(
+                  () ->
+                      String.format(
+                          "Handling %s user prompt during BiDi navigation (%s)",
+                          prompt.getType(), biDiAcceptPrompt ? "accept" : "dismiss"));
+              if (biDiNotifyOnPrompt) {
+                biDiHandledPrompt.compareAndSet(null, prompt);
+              }
+              new BrowsingContext(this, contextId).handleUserPrompt(biDiAcceptPrompt);
+            });
Evidence
The RemoteWebDriver listener executes BiDi commands inside the event callback; Connection invokes
callbacks without try/catch, and the onText handler rethrows runtime exceptions from event
processing tasks.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java[456-474]
java/src/org/openqa/selenium/bidi/Connection.java[336-384]
java/src/org/openqa/selenium/bidi/Connection.java[265-275]
java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java[223-225]

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 `USER_PROMPT_OPENED_EVENT` listener invokes `new BrowsingContext(...).handleUserPrompt(...)` directly. If that call throws (e.g., BiDi command error), the exception escapes the callback and bubbles up through the BiDi event dispatch code.

### Issue Context
`Connection.handleEventResponse` calls listeners without guarding against exceptions, and `Listener.onText` wraps runtime exceptions and rethrows them.

### Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebDriver.java[460-474]

### Recommended fix
Wrap the body (or at least the `handleUserPrompt` call) in `try { ... } catch (RuntimeException e) { LOG.log(Level.WARNING, ... , e); }` to prevent exceptions from escaping the event callback. Optionally, consider recording the failure so the navigation thread can surface it if needed.

ⓘ 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/remote/RemoteWebDriver.java
krishnamohan-kothapalli and others added 3 commits March 21, 2026 20:44
…t interference

Replace the permanent session-scoped browsingContext.userPromptOpened listener
(biDiPromptListenerInstalled + biDiNavigatingContextId gate) with a per-navigation
register/deregister pattern. The listener is now installed via BiDi.addListener()
scoped to the navigating context immediately before navigation runs, then removed
via BiDi.removeListener(id) in a finally block. This ensures the listener cannot
fire for user-triggered alerts that appear after page load, which was causing
AlertsTest, FrameSwitchingTest, HistoryNavigationTest, and PageLoadingTest failures.

Also fix import ordering: move json imports after io (alphabetical), remove the
now-unused AtomicBoolean import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 17, 2026

Persistent review updated to latest commit cfed07f

Comment thread java/src/org/openqa/selenium/remote/RemoteWebDriver.java
krishnamohan-kothapalli and others added 2 commits May 16, 2026 22:15
…licies

navigateViaBiDi previously collapsed ACCEPT_AND_NOTIFY and DISMISS_AND_NOTIFY
into the same silent behaviour as ACCEPT/DISMISS. Since DISMISS_AND_NOTIFY is the
W3C spec default, callers were never notified when a prompt was auto-handled.

Fix: compute a `notify` flag alongside `accept`. When notify is true, capture the
first handled UserPromptOpened in an AtomicReference. After navigation succeeds
(listener already removed), throw UnhandledAlertException with the alert text so
callers observe the same behaviour as classic WebDriver. Navigation exceptions take
precedence — the notify throw only happens on a clean navigation run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 17, 2026

Persistent review updated to latest commit 0f63055

Comment thread java/src/org/openqa/selenium/remote/RemoteWebDriver.java
Comment thread java/src/org/openqa/selenium/remote/RemoteWebDriver.java
Two related issues:
1. The volatile field declaration was lost during the trunk merge rebase; re-add it.
   volatile is required so that the outer unsynchronized read in the double-checked
   locking pattern in getPageLoadDuration() always sees the latest value.
2. pageLoadTimeout() was writing biDiPageLoadTimeout outside any lock, creating a
   race: if getPageLoadDuration()'s lazy-init synchronized block ran concurrently
   and completed after the user's write, it would silently overwrite the user-set
   value with the GET_TIMEOUTS result. Fix by synchronizing the write on RemoteWebDriver.this,
   the same monitor used by getPageLoadDuration()'s inner block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 17, 2026

Persistent review updated to latest commit 30d2ffb

…on null safety

Two compile/nullability issues:

1. Json and JsonInput are used by USER_PROMPT_OPENED_EVENT but their imports were
   absent from the committed HEAD (lost in a prior rebase). Re-add them in
   alphabetical order (io → json → logging).

2. getPageLoadDuration() returned biDiPageLoadTimeout (@nullable Duration) directly
   as the non-nullable Duration return type, violating the package-level @NullMarked
   contract. Switch to the canonical local-variable DCL pattern: read the volatile
   field once into 'cached', initialize inside the synchronized block and assign back
   to both 'cached' and the field, then return 'cached'. This keeps the return type
   non-nullable and avoids the extra volatile read on the hot path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 17, 2026

Persistent review updated to latest commit c1aff62

…Di navigation

BiDi.addListener always sends session.subscribe, while removeListener only
removes the local callback without session.unsubscribe. The per-navigation
listener approach therefore accumulated remote subscriptions on every call
to get/back/forward/refresh with no corresponding unsubscribe.

Fix by switching to a single session-scoped listener installed lazily on the
first non-IGNORE navigation (ensureBiDiPromptListener). The listener is gated
on biDiNavigatingContextId (non-null only while a navigation is in progress)
so it has no effect on user-triggered alerts outside of navigation — preserving
the behaviour that fixed the AlertsTest/FrameSwitchingTest regressions.

Thread-safety: biDiAcceptPrompt, biDiNotifyOnPrompt, and biDiHandledPrompt are
written before the volatile write of biDiNavigatingContextId. The listener's
volatile read of biDiNavigatingContextId establishes happens-before with those
prior writes, so no additional synchronisation is needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 17, 2026

Persistent review updated to latest commit b42bd10

Comment thread java/src/org/openqa/selenium/remote/RemoteWebDriver.java Outdated
… retry

ensureBiDiPromptListener() set biDiPromptListenerInstalled before calling
addListener/session.subscribe. If the subscribe threw (transient network error,
browser rejection), the flag stayed true and the listener could never be
installed or retried, permanently disabling prompt handling for the session.

Fix: wrap addListener in try/catch and reset the flag on any RuntimeException
so the next navigation attempt retries the subscription. The exception still
propagates — the navigation fails cleanly rather than silently proceeding
without a handler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 17, 2026

Persistent review updated to latest commit aa400a8

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 Compliance violation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants