[java][bidi]: add `DownloadInfo` class and test for `onDownloadWillBegin` by navin772 · Pull Request #16267 · SeleniumHQ/selenium
🎫 Ticket compliance analysis 🔶
1234 - Partially compliant
Compliant requirements:
- None
Non-compliant requirements:
- Fix regression for JS-in-href click behavior in Firefox.
- Add tests validating alert/JS execution on click.
Requires further human verification:
- None
5678 - Partially compliant
Compliant requirements:
- None
Non-compliant requirements:
- Resolve ConnectFailure across multiple ChromeDriver instantiations.
- Add tests or documentation to verify multiple instance stability.
Requires further human verification:
- None
Null Handling
Validate that all fields parsed from JSON, especially 'timestamp' and IDs, are present and non-null. Currently defaults (e.g., 0 for long) are used without validation, which could mask malformed events.
public static DownloadInfo fromJson(JsonInput input) { String browsingContextId = null; String navigationId = null; long timestamp = 0; String url = null; String suggestedFilename = null; input.beginObject(); while (input.hasNext()) { switch (input.nextName()) { case "context": browsingContextId = input.read(String.class); break; case "navigation": navigationId = input.read(String.class); break; case "timestamp": timestamp = input.read(Long.class); break; case "url": url = input.read(String.class); break; case "suggestedFilename": suggestedFilename = input.read(String.class); break; default: input.skipValue(); break; } } input.endObject(); return new DownloadInfo(browsingContextId, navigationId, timestamp, url, suggestedFilename); }
Listener Cleanup
Ensure no listeners remain when specific context-scoped listeners are added; verify that 'downloadWillBeginEvent' respects context filters like other navigation events and is always cleared in close().
public void onBrowsingContextLoaded(Consumer<NavigationInfo> consumer) { addNavigationEventListener("browsingContext.load", consumer); } public void onDownloadWillBegin(Consumer<DownloadInfo> consumer) { if (browsingContextIds.isEmpty()) { this.bidi.addListener(downloadWillBeginEvent, consumer); } else { this.bidi.addListener(browsingContextIds, downloadWillBeginEvent, consumer); } } public void onNavigationAborted(Consumer<NavigationInfo> consumer) { addNavigationEventListener("browsingContext.navigationAborted", consumer); } public void onNavigationFailed(Consumer<NavigationInfo> consumer) { addNavigationEventListener("browsingContext.navigationFailed", consumer); } public void onNavigationCommitted(Consumer<NavigationInfo> consumer) { addNavigationEventListener("browsingContext.navigationCommitted", consumer); } public void onUserPromptClosed(Consumer<UserPromptClosed> consumer) { if (browsingContextIds.isEmpty()) { this.bidi.addListener(userPromptClosed, consumer); } else { this.bidi.addListener(browsingContextIds, userPromptClosed, consumer); } } public void onUserPromptOpened(Consumer<UserPromptOpened> consumer) { if (browsingContextIds.isEmpty()) { this.bidi.addListener(userPromptOpened, consumer); } else { this.bidi.addListener(browsingContextIds, userPromptOpened, consumer); } } public void onHistoryUpdated(Consumer<HistoryUpdated> consumer) { if (browsingContextIds.isEmpty()) { this.bidi.addListener(historyUpdated, consumer); } else { this.bidi.addListener(browsingContextIds, historyUpdated, consumer); } } private void addNavigationEventListener(String name, Consumer<NavigationInfo> consumer) { Event<NavigationInfo> navigationEvent = new Event<>(name, navigationInfoMapper); navigationEventSet.add(navigationEvent); if (browsingContextIds.isEmpty()) { this.bidi.addListener(navigationEvent, consumer); } else { this.bidi.addListener(browsingContextIds, navigationEvent, consumer); } } @Override public void close() { this.bidi.clearListener(browsingContextCreated); this.bidi.clearListener(browsingContextDestroyed); this.bidi.clearListener(userPromptOpened); this.bidi.clearListener(userPromptClosed); this.bidi.clearListener(historyUpdated); this.bidi.clearListener(downloadWillBeginEvent); navigationEventSet.forEach(this.bidi::clearListener);