[java][BiDi] add clearListners via browsingContextIds for inspectors#17376
[java][BiDi] add clearListners via browsingContextIds for inspectors#17376Delta456 wants to merge 13 commits into
Conversation
Review Summary by QodoAdd clearListener support for browsing context IDs in BiDi inspectors
WalkthroughsDescription• Add clearListener method to BiDi supporting browsing context IDs • Implement clearListener and clearListeners methods in inspector modules • Add comprehensive tests for clearing listeners in BrowsingContext, Log, and Speculation inspectors • Enable selective event listener cleanup for specific browsing contexts File Changes1. java/src/org/openqa/selenium/bidi/BiDi.java
|
Code Review by Qodo
Context used 1. contextListenerIds map not synchronized
|
|
@Delta456 Seems that AI comments are reasonable, I would start from them. |
Done. |
|
Persistent review updated to latest commit 5d07026 |
|
Persistent review updated to latest commit c4c3bcb |
| public <X> void clearListener(Set<String> browsingContextIds, Event<X> event) { | ||
| Require.nonEmpty("List of browsing context ids", browsingContextIds); | ||
| Require.nonNull("Event to listen for", event); | ||
|
|
||
| // The browser throws an error if we try to unsubscribe an event that was not subscribed in the | ||
| // first place | ||
| if (!connection.isEventSubscribed(event)) { | ||
| return; | ||
| } | ||
|
|
||
| List<Long> ids = contextListenerIds.remove(event); | ||
| if (ids != null && !ids.isEmpty()) { | ||
| // Subscription was context-specific: unsubscribe only for those contexts. | ||
| send( | ||
| new Command<>( | ||
| "session.unsubscribe", | ||
| Map.of("contexts", browsingContextIds, "events", List.of(event.getMethod())))); | ||
| ids.forEach(connection::removeListener); |
There was a problem hiding this comment.
1. contextlistenerids map not synchronized 📘 Rule violation ☼ Reliability
BiDi introduces shared mutable state (contextListenerIds) that is mutated from public APIs without any synchronization, even though listener callbacks/subscriptions are handled concurrently in Connection. This can lead to lost updates, internal map/list corruption, or inconsistent listener cleanup when multiple threads add or clear listeners at the same time.
Agent Prompt
## Issue description
`BiDi.contextListenerIds` is a shared `HashMap<Event<?>, List<Long>>` updated and removed from public APIs (e.g., `addListener(Set, ...)` and `clearListener(Set, ...)`) without synchronization. Because BiDi listener callbacks can run concurrently and `Connection` already uses locks to protect its callback registry, these unsynchronized `HashMap`/`ArrayList` operations can race, causing lost updates, internal map/list corruption, inconsistent listener cleanup/removal, and potential runtime failures.
## Issue Context
`Connection` explicitly guards its callback/subscription state with locking (e.g., a `ReadWriteLock`/`callbacksLock` around callback maps), indicating multi-threaded access is expected in this codepath. The newly introduced bookkeeping in `BiDi` should adopt a consistent thread-safety strategy (either thread-safe data structures or a dedicated lock) so that listener ID tracking remains coherent under concurrent client usage.
## Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDi.java[34-40]
- java/src/org/openqa/selenium/bidi/BiDi.java[39-39]
- java/src/org/openqa/selenium/bidi/BiDi.java[109-121]
- java/src/org/openqa/selenium/bidi/BiDi.java[119-121]
- java/src/org/openqa/selenium/bidi/BiDi.java[135-153]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| List<Long> ids = contextListenerIds.remove(event); | ||
| if (ids != null && !ids.isEmpty()) { | ||
| // Subscription was context-specific: unsubscribe only for those contexts. | ||
| send( | ||
| new Command<>( | ||
| "session.unsubscribe", | ||
| Map.of("contexts", browsingContextIds, "events", List.of(event.getMethod())))); | ||
| ids.forEach(connection::removeListener); | ||
| } else { | ||
| // Subscription was global: context-specific unsubscription is invalid per the BiDi protocol, | ||
| // so fall back to a global unsubscription. | ||
| send(new Command<>("session.unsubscribe", Map.of("events", List.of(event.getMethod())))); | ||
| connection.clearListener(event); | ||
| } |
There was a problem hiding this comment.
2. String context clears globally 🐞 Bug ≡ Correctness
BiDi.addListener(String, ...) performs a context-scoped session.subscribe but does not record the listener ID in contextListenerIds, so clearListener(Set, ...) will treat it as a global subscription and send a global session.unsubscribe, potentially removing subscriptions beyond the intended contexts. This can also clear all local callbacks for the event even though the caller asked for context-scoped cleanup.
Agent Prompt
### Issue description
`BiDi.addListener(String browsingContextId, Event<X> event, Consumer<X> handler)` subscribes with `contexts: [browsingContextId]` but does not add the returned listener id to the context-specific bookkeeping used by `clearListener(Set<String>, Event<X>)`. As a result, `clearListener(Set, event)` falls into the "global" branch and performs a global `session.unsubscribe` + `connection.clearListener(event)`, which is broader than intended.
### Issue Context
- Context-specific subscription (String overload) exists and is public API.
- New context-specific clear relies on `contextListenerIds` presence to decide whether it can do context-scoped unsubscribe.
### Fix Focus Areas
- java/src/org/openqa/selenium/bidi/BiDi.java[96-121]
- java/src/org/openqa/selenium/bidi/BiDi.java[135-159]
### Suggested fix
- Record listener ids created via `addListener(String, ...)` into the same context-specific tracking used by the Set overload (or, preferably, refactor tracking to include context IDs, e.g., `Map<Event<?>, Map<String, List<Long>>>`).
- Update `clearListener(Set, event)` to use that tracking to decide whether to perform context-scoped unsubscribe vs global unsubscribe.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
🔗 Related Issues
💥 What does this PR do?
Add
clearListnersviabrowsingContextIdsfor Inspectors for Java BiDiFollowup of #17130 specifically #17130 (comment)
🔧 Implementation Notes
🤖 AI assistance
💡 Additional Considerations
🔄 Types of changes