[dotnet] [bidi] Additional Event streaming (breaking change)#17349
Conversation
Review Summary by Qodo(Agentic_describe updated until commit 18078e5)[dotnet] [bidi] Refactor event subscription system with property-based event sources and async streaming (breaking change)
WalkthroughsDescription**Major refactoring of BiDi event subscription and streaming system with breaking API changes:** * Introduced generic event subscription API with new SubscribeAsync and StreamAsync methods in BiDi class supporting single and multiple event descriptors with both sync and async handlers * Replaced callback-based OnXxxAsync methods across all modules (BrowsingContext, Network, Script, Log, Input, Speculation) with property-based IEventSource<TEventArgs> pattern for cleaner, more intuitive event consumption * Added IEventStream<TEventArgs> interface enabling LINQ-style async enumerable event consumption via StreamAsync methods * Completely refactored EventDispatcher to support generic subscriptions with centralized event deserialization and delivery through TryDeserializeAndDispatch method * Redesigned Subscription<TEventArgs> system with channel-based event buffering and async dispatch, replacing the previous non-generic implementation * Introduced new EventDescriptor<TEventArgs> abstraction for type-safe event metadata with factory functions and JSON serialization info * Created new EventStream<TEventArgs> class implementing async enumerable event consumption with proper disposal and cancellation token handling * Added EventSource and ContextEventSource implementations for global and context-scoped event handling with optional filtering * Simplified Broker by delegating event handling to EventDispatcher and removing internal event metadata management * Updated all module interfaces and implementations to use new event source pattern with lazy-initialized properties * Updated all existing tests to use new property-based event subscription API * Added comprehensive new tests for SubscribeAsync, StreamAsync, and LINQ-based event stream consumption File Changes1. dotnet/src/webdriver/BiDi/EventDispatcher.cs
|
Code Review by Qodo
1. CompleteAllAsync blocks shutdown
|
There was a problem hiding this comment.
Pull request overview
Refactors the .NET BiDi event subscription pipeline to support strongly-typed subscriptions and optional event streaming, replacing the prior centralized dispatcher approach with per-event subscription registries.
Changes:
- Introduces
Subscription<TEventArgs>backed byChannel<T>and implementsIAsyncEnumerable<TEventArgs>for streaming consumption. - Replaces
EventDispatcherwith a per-eventSubscriptionRegistryinsideBroker, and propagates terminal exceptions to active subscriptions on shutdown. - Updates BiDi module and
BrowsingContextevent APIs to returnSubscription<TEventArgs>and adds handlerless overloads.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 19 comments.
Show a summary per file
| File | Description |
|---|---|
| dotnet/src/webdriver/BiDi/Subscription.cs | Adds generic, channel-backed subscriptions with streaming support and sequential handler draining. |
| dotnet/src/webdriver/BiDi/Broker.cs | Replaces dispatcher with per-event registries and direct delivery/completion to subscriptions. |
| dotnet/src/webdriver/BiDi/Module.cs | Updates subscribe helper to return Subscription<TEventArgs> and accept ValueTask handlers. |
| dotnet/src/webdriver/BiDi/EventDispatcher.cs | Removes the previous centralized queued dispatcher implementation. |
| dotnet/src/webdriver/BiDi/Speculation/SpeculationModule.cs | Updates speculation event subscription APIs to typed subscriptions + handlerless overload. |
| dotnet/src/webdriver/BiDi/Speculation/ISpeculationModule.cs | Updates public speculation interface signatures to typed subscriptions and adds handlerless overload. |
| dotnet/src/webdriver/BiDi/Script/ScriptModule.cs | Updates script event subscription APIs to typed subscriptions + handlerless overload. |
| dotnet/src/webdriver/BiDi/Script/IScriptModule.cs | Updates public script interface signatures to typed subscriptions and adds handlerless overloads. |
| dotnet/src/webdriver/BiDi/Network/NetworkModule.cs | Updates network event subscription APIs to typed subscriptions + handlerless overloads. |
| dotnet/src/webdriver/BiDi/Network/INetworkModule.cs | Updates public network interface signatures to typed subscriptions and adds handlerless overloads. |
| dotnet/src/webdriver/BiDi/Log/LogModule.cs | Updates log event subscription APIs to typed subscriptions + handlerless overload. |
| dotnet/src/webdriver/BiDi/Log/ILogModule.cs | Updates public log interface signatures to typed subscriptions and adds handlerless overload. |
| dotnet/src/webdriver/BiDi/Input/InputModule.cs | Updates input event subscription APIs to typed subscriptions + handlerless overload. |
| dotnet/src/webdriver/BiDi/Input/IInputModule.cs | Updates public input interface signatures to typed subscriptions and adds handlerless overload. |
| dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs | Updates browsing-context module event subscription APIs to typed subscriptions + handlerless overloads. |
| dotnet/src/webdriver/BiDi/BrowsingContext/IBrowsingContextModule.cs | Updates public browsing-context interface signatures to typed subscriptions and adds handlerless overloads. |
| dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContext.cs | Updates BrowsingContext facade methods to use typed subscriptions and adds handlerless overloads. |
| dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextNetworkModule.cs | Updates context-scoped network event subscription wrappers to typed subscriptions + handlerless overloads. |
| dotnet/src/webdriver/BiDi/BrowsingContext/IBrowsingContextNetworkModule.cs | Updates public context-scoped network interface signatures to typed subscriptions and adds handlerless overloads. |
| dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextLogModule.cs | Updates context-scoped log event subscription wrappers to typed subscriptions + handlerless overloads. |
| dotnet/src/webdriver/BiDi/BrowsingContext/IBrowsingContextLogModule.cs | Updates public context-scoped log interface signatures to typed subscriptions and adds handlerless overloads. |
| dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextInputModule.cs | Updates context-scoped input event subscription wrappers to typed subscriptions + handlerless overloads. |
| dotnet/src/webdriver/BiDi/BrowsingContext/IBrowsingContextInputModule.cs | Updates public context-scoped input interface signatures to typed subscriptions and adds handlerless overloads. |
|
Need more design decisions.. |
|
Perfect, I understood how to design events. Before: // Push
await using var sub = await network.OnBeforeRequestSentAsync(e => Console.WriteLine(e));After: // Push
await using var sub = await network.BeforeRequestSent.OnAsync(e => Console.WriteLine(e));
// Pull
await using var sub = await network.BeforeRequestSent.SubscribeAsync();
await foreach (var args in sub) { ... }2 separate clean concepts. Why this way...
Thus example of usage becomes very simple (purely native C#): await using var sub = await network.BeforeRequestSent.SubscribeAsync();
await context.NavigateAsync("example.com");
await sub.Where(req => req.Url.Contains("/api")).FirstAsync();Or if somebody wants to develop extension method: var request = await network.BeforeRequestSent
.During(() => context.NavigateAsync("example.com"))
.Where(req => req.Url.Contains("/api")).FirstAsync() |
|
Persistent review updated to latest commit 18078e5 |
Checkpointawait using var _ = await bidi.SubscribeAsync([NetworkEvent.BeforeRequestSent], args => { });
await using var stream = await bidi.StreamAsync([NetworkEvent.BeforeRequestSent]); // or singleShortcutsawait using var _ = await bidi.Network.BeforeRequestSent.SubscribeAsync(args => { });
// the same for streamingKey pointsDispatching is ordered, always, per subscription/stream. |
|
@nvborisenko, the way I understand this PR is that this touches only the low-level BiDi API, also making it more organized and easier to extend. When you merge it, please add to the commit the link to the blog post: https://www.selenium.dev/blog/2026/dotnet-strong-name-signing/ |

Finally the events model is more cleaner. Each
Subscriptionis backed byChannel, meaning all handlers want to be executed sequentially.New API opens doors for consuming events via async LINQ - big features with minimal maintenance.
🔗 Related Issues
Contributes to #16095
💥 What does this PR do?
This pull request introduces a significant refactor to event subscription and dispatching in the BiDi (Bidirectional) WebDriver implementation. The main changes revolve around centralizing event handling logic into the
EventDispatcherand making it accessible throughout the codebase, simplifying module construction and event subscription APIs, and improving resource management during disposal. The changes also update how modules and browsing context classes interact with event dispatching.These changes collectively improve the maintainability, reliability, and usability of the BiDi event system.
1. Push-based (callback) — handler
2. Pull-based (stream) —
IAsyncEnumerableviaawait foreach3. Multi-event subscription (ordered delivery)
Subscribe to multiple event types with a shared base type:
4. Module shortcuts —
bidi.Network.BeforeRequestSent.SubscribeAsync(...)Every module exposes
IEventSource<T>properties — no need to import event descriptors:Key points:
SubscribeAsync= push (callback),StreamAsync= pull (IAsyncEnumerable)await using/DisposeAsync()sends the wire unsubscribebidi.Network.BeforeRequestSent) are the most concise form🔄 Types of changes