Description
BiDi.clearListener(Set<String> browsingContextIds, Event<X> event) used to unsubscribe only the browser-side subscription(s) scoped to the given browsing context(s), via a contextListenerIds map. Since #17759 ("[java][bidi] Track events subscriptions using subscription ids"), the method ignores the browsingContextIds argument entirely and just delegates to a blanket clear:
// java/src/org/openqa/selenium/bidi/BiDi.java
public <X> void clearListener(Set<String> browsingContextIds, Event<X> event) {
Require.nonEmpty("List of browsing context ids", browsingContextIds);
// This is not the correct implementation, everything needs to be tired to subscription id.
// This is currently used by hand-written BiDi code, soon to be replaced by generated BiDi code.
// After that this method will be removed.
// This is just to avoid changes to hand-written classes meanwhile.
this.clearListener(event);
}
clearListener(event) does a full session.unsubscribe for the event type and wipes every registered handler for it, then callers immediately resubscribe. Because Connection.handleEventResponse dispatches an incoming event to every consumer registered for that event method (it never filters by which specific browsing-context/subscription the event actually belongs to), a late/in-flight event tied to the old subscription can land on the newly (re)subscribed listener during the brief unsubscribe→resubscribe window.
This surfaced as a consistent (non-flaky, 2/2 reproductions) CI failure on SpeculationInspectorTest:
org.opentest4j.AssertionFailedError:
expected: ".../common/square.png"
but was: ".../common/dummy.xml"
at org.openqa.selenium.bidi.speculation.SpeculationInspectorTest.canClearListenersForBrowsingContext(SpeculationInspectorTest.java:280)
at org.openqa.selenium.bidi.speculation.SpeculationInspectorTest.canClearListenersForMultipleBrowsingContexts(SpeculationInspectorTest.java:337)
Both tests: subscribe → prefetch dummy.xml → clear listener for the browsing context → resubscribe → prefetch square.png → assert the first new event's URL is square.png. The first event received by the new subscription is instead the stale dummy.xml status update from before the clear, because the clear no longer isolates by context and the dispatch has no context/subscription filtering to fall back on.
Seen on the release-preparation-selenium-4.46.0 branch, run: https://github.com/SeleniumHQ/selenium/actions/runs/29127293306/job/86475511308 (PR #17763 — unrelated to the failure; the release-prep PR doesn't touch this code at all, the regression was already on trunk via #17759, merged the day before).
How to reproduce
bazel test //java/test/org/openqa/selenium/bidi/speculation:SpeculationInspectorTest-chrome-beta --runs_per_test=3
(or any Chrome variant) — canClearListenersForBrowsingContext / canClearListenersForMultipleBrowsingContexts fail deterministically enough to show up in CI.
Suggested direction
Restore context-scoped unsubscribe (like the pre-#17759 contextListenerIds approach), or — since the comment says this is meant to be temporary until "generated BiDi code" replaces the hand-written classes — make sure Connection's event dispatch filters by the subscription/context that produced the event rather than broadcasting to every handler registered for that event method.
Environment
Trunk, as of commit 9f6ccde (#17759).
Description
BiDi.clearListener(Set<String> browsingContextIds, Event<X> event)used to unsubscribe only the browser-side subscription(s) scoped to the given browsing context(s), via acontextListenerIdsmap. Since #17759 ("[java][bidi] Track events subscriptions using subscription ids"), the method ignores thebrowsingContextIdsargument entirely and just delegates to a blanket clear:clearListener(event)does a fullsession.unsubscribefor the event type and wipes every registered handler for it, then callers immediately resubscribe. BecauseConnection.handleEventResponsedispatches an incoming event to every consumer registered for that event method (it never filters by which specific browsing-context/subscription the event actually belongs to), a late/in-flight event tied to the old subscription can land on the newly (re)subscribed listener during the brief unsubscribe→resubscribe window.This surfaced as a consistent (non-flaky, 2/2 reproductions) CI failure on
SpeculationInspectorTest:Both tests: subscribe → prefetch
dummy.xml→ clear listener for the browsing context → resubscribe → prefetchsquare.png→ assert the first new event's URL issquare.png. The first event received by the new subscription is instead the staledummy.xmlstatus update from before the clear, because the clear no longer isolates by context and the dispatch has no context/subscription filtering to fall back on.Seen on the
release-preparation-selenium-4.46.0branch, run: https://github.com/SeleniumHQ/selenium/actions/runs/29127293306/job/86475511308 (PR #17763 — unrelated to the failure; the release-prep PR doesn't touch this code at all, the regression was already ontrunkvia #17759, merged the day before).How to reproduce
(or any Chrome variant) —
canClearListenersForBrowsingContext/canClearListenersForMultipleBrowsingContextsfail deterministically enough to show up in CI.Suggested direction
Restore context-scoped unsubscribe (like the pre-#17759
contextListenerIdsapproach), or — since the comment says this is meant to be temporary until "generated BiDi code" replaces the hand-written classes — make sureConnection's event dispatch filters by the subscription/context that produced the event rather than broadcasting to every handler registered for that event method.Environment
Trunk, as of commit 9f6ccde (#17759).