Skip to content

[dotnet] [bidi] Statically declared events#17331

Merged
nvborisenko merged 12 commits intoSeleniumHQ:trunkfrom
nvborisenko:bidi-event-descriptor
Apr 10, 2026
Merged

[dotnet] [bidi] Statically declared events#17331
nvborisenko merged 12 commits intoSeleniumHQ:trunkfrom
nvborisenko:bidi-event-descriptor

Conversation

@nvborisenko
Copy link
Copy Markdown
Member

Continuation of #17330

🔗 Related Issues

Contributes to #16095

💥 What does this PR do?

....

🔄 Types of changes

  • Cleanup (formatting, renaming)
  • New feature (non-breaking change which adds functionality and tests!)

Copilot AI review requested due to automatic review settings April 9, 2026 23:42
@selenium-ci selenium-ci added the C-dotnet .NET Bindings label Apr 9, 2026
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

[dotnet] [bidi] Statically declare commands and events with descriptors

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Refactor BiDi commands and events to use static descriptors
• Replace dynamic command/event creation with pre-defined static fields
• Simplify module implementations by removing factory methods
• Update broker to work with command/event descriptors instead of instances

Grey Divider

File Changes

1. dotnet/src/webdriver/BiDi/Command.cs ✨ Enhancement +6/-21

Convert Command to readonly record struct with metadata

dotnet/src/webdriver/BiDi/Command.cs


2. dotnet/src/webdriver/BiDi/Event.cs ✨ Enhancement +28/-0

Create new Event descriptor record struct

dotnet/src/webdriver/BiDi/Event.cs


3. dotnet/src/webdriver/BiDi/Module.cs ✨ Enhancement +7/-9

Update Module base class to use descriptors

dotnet/src/webdriver/BiDi/Module.cs


View more (80)
4. dotnet/src/webdriver/BiDi/Broker.cs ✨ Enhancement +19/-19

Refactor Broker to accept command/event descriptors

dotnet/src/webdriver/BiDi/Broker.cs


5. dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs ✨ Enhancement +164/-87

Declare static command and event descriptors

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs


6. dotnet/src/webdriver/BiDi/Network/NetworkModule.cs ✨ Enhancement +99/-53

Declare static command and event descriptors

dotnet/src/webdriver/BiDi/Network/NetworkModule.cs


7. dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs ✨ Enhancement +58/-40

Declare static command descriptors for emulation

dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs


8. dotnet/src/webdriver/BiDi/Script/ScriptModule.cs ✨ Enhancement +63/-39

Declare static command and event descriptors

dotnet/src/webdriver/BiDi/Script/ScriptModule.cs


9. dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs ✨ Enhancement +31/-16

Declare static command descriptors for browser

dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs


10. dotnet/src/webdriver/BiDi/Input/InputModule.cs ✨ Enhancement +23/-15

Declare static command and event descriptors

dotnet/src/webdriver/BiDi/Input/InputModule.cs


11. dotnet/src/webdriver/BiDi/Session/SessionModule.cs ✨ Enhancement +24/-11

Declare static command descriptors for session

dotnet/src/webdriver/BiDi/Session/SessionModule.cs


12. dotnet/src/webdriver/BiDi/Log/LogModule.cs ✨ Enhancement +14/-12

Declare static event descriptor for logging

dotnet/src/webdriver/BiDi/Log/LogModule.cs


13. dotnet/src/webdriver/BiDi/Storage/StorageModule.cs ✨ Enhancement +16/-8

Declare static command descriptors for storage

dotnet/src/webdriver/BiDi/Storage/StorageModule.cs


14. dotnet/src/webdriver/BiDi/Speculation/SpeculationModule.cs ✨ Enhancement +8/-7

Declare static event descriptor for speculation

dotnet/src/webdriver/BiDi/Speculation/SpeculationModule.cs


15. dotnet/src/webdriver/BiDi/WebExtension/WebExtensionModule.cs ✨ Enhancement +11/-6

Declare static command descriptors for extensions

dotnet/src/webdriver/BiDi/WebExtension/WebExtensionModule.cs


16. dotnet/src/webdriver/BiDi/Permissions/PermissionsModule.cs ✨ Enhancement +6/-4

Declare static command descriptor for permissions

dotnet/src/webdriver/BiDi/Permissions/PermissionsModule.cs


17. dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextScriptModule.cs I-cleanup +1/-1

Change class visibility to internal

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextScriptModule.cs


18. dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextNetworkModule.cs I-cleanup +1/-1

Change class visibility to internal

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextNetworkModule.cs


19. dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextLogModule.cs I-cleanup +1/-1

Change class visibility to internal

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextLogModule.cs


20. dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextInputModule.cs I-cleanup +1/-1

Change class visibility to internal

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextInputModule.cs


21. dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextStorageModule.cs I-cleanup +1/-1

Change class visibility to internal

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextStorageModule.cs


22. dotnet/test/webdriver/BiDi/Session/SessionTests.cs 🧪 Tests +5/-5

Update test to use command descriptors

dotnet/test/webdriver/BiDi/Session/SessionTests.cs


23. dotnet/src/webdriver/BiDi/Browser/Close.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Browser/Close.cs


24. dotnet/src/webdriver/BiDi/Browser/CreateUserContext.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Browser/CreateUserContext.cs


25. dotnet/src/webdriver/BiDi/Browser/GetClientWindows.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Browser/GetClientWindows.cs


26. dotnet/src/webdriver/BiDi/Browser/GetUserContexts.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Browser/GetUserContexts.cs


27. dotnet/src/webdriver/BiDi/Browser/RemoveUserContext.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Browser/RemoveUserContext.cs


28. dotnet/src/webdriver/BiDi/Browser/SetDownloadBehavior.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Browser/SetDownloadBehavior.cs


29. dotnet/src/webdriver/BiDi/BrowsingContext/Activate.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/Activate.cs


30. dotnet/src/webdriver/BiDi/BrowsingContext/CaptureScreenshot.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/CaptureScreenshot.cs


31. dotnet/src/webdriver/BiDi/BrowsingContext/Close.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/Close.cs


32. dotnet/src/webdriver/BiDi/BrowsingContext/Create.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/Create.cs


33. dotnet/src/webdriver/BiDi/BrowsingContext/GetTree.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/GetTree.cs


34. dotnet/src/webdriver/BiDi/BrowsingContext/HandleUserPrompt.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/HandleUserPrompt.cs


35. dotnet/src/webdriver/BiDi/BrowsingContext/LocateNodes.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/LocateNodes.cs


36. dotnet/src/webdriver/BiDi/BrowsingContext/Navigate.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/Navigate.cs


37. dotnet/src/webdriver/BiDi/BrowsingContext/Print.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/Print.cs


38. dotnet/src/webdriver/BiDi/BrowsingContext/Reload.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/Reload.cs


39. dotnet/src/webdriver/BiDi/BrowsingContext/SetViewport.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/SetViewport.cs


40. dotnet/src/webdriver/BiDi/BrowsingContext/TraverseHistory.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/BrowsingContext/TraverseHistory.cs


41. dotnet/src/webdriver/BiDi/Emulation/SetForcedColorsModeThemeOverride.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetForcedColorsModeThemeOverride.cs


42. dotnet/src/webdriver/BiDi/Emulation/SetGeolocationOverride.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetGeolocationOverride.cs


43. dotnet/src/webdriver/BiDi/Emulation/SetLocaleOverride.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetLocaleOverride.cs


44. dotnet/src/webdriver/BiDi/Emulation/SetNetworkConditions.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetNetworkConditions.cs


45. dotnet/src/webdriver/BiDi/Emulation/SetScreenOrientationOverride.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetScreenOrientationOverride.cs


46. dotnet/src/webdriver/BiDi/Emulation/SetScreenSettingsOverride.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetScreenSettingsOverride.cs


47. dotnet/src/webdriver/BiDi/Emulation/SetScriptingEnabled.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetScriptingEnabled.cs


48. dotnet/src/webdriver/BiDi/Emulation/SetScrollbarTypeOverride.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetScrollbarTypeOverride.cs


49. dotnet/src/webdriver/BiDi/Emulation/SetTimezoneOverride.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetTimezoneOverride.cs


50. dotnet/src/webdriver/BiDi/Emulation/SetTouchOverride.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetTouchOverride.cs


51. dotnet/src/webdriver/BiDi/Emulation/SetUserAgentOverride.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Emulation/SetUserAgentOverride.cs


52. dotnet/src/webdriver/BiDi/Input/PerformActions.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Input/PerformActions.cs


53. dotnet/src/webdriver/BiDi/Input/ReleaseActions.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Input/ReleaseActions.cs


54. dotnet/src/webdriver/BiDi/Input/SetFiles.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Input/SetFiles.cs


55. dotnet/src/webdriver/BiDi/Network/AddDataCollector.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/AddDataCollector.cs


56. dotnet/src/webdriver/BiDi/Network/AddIntercept.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/AddIntercept.cs


57. dotnet/src/webdriver/BiDi/Network/ContinueRequest.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/ContinueRequest.cs


58. dotnet/src/webdriver/BiDi/Network/ContinueResponse.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/ContinueResponse.cs


59. dotnet/src/webdriver/BiDi/Network/ContinueWithAuth.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/ContinueWithAuth.cs


60. dotnet/src/webdriver/BiDi/Network/FailRequest.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/FailRequest.cs


61. dotnet/src/webdriver/BiDi/Network/GetData.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/GetData.cs


62. dotnet/src/webdriver/BiDi/Network/ProvideResponse.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/ProvideResponse.cs


63. dotnet/src/webdriver/BiDi/Network/RemoveDataCollector.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/RemoveDataCollector.cs


64. dotnet/src/webdriver/BiDi/Network/RemoveIntercept.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/RemoveIntercept.cs


65. dotnet/src/webdriver/BiDi/Network/SetCacheBehavior.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/SetCacheBehavior.cs


66. dotnet/src/webdriver/BiDi/Network/SetExtraHeaders.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Network/SetExtraHeaders.cs


67. dotnet/src/webdriver/BiDi/Permissions/SetPermission.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Permissions/SetPermission.cs


68. dotnet/src/webdriver/BiDi/Script/AddPreloadScript.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Script/AddPreloadScript.cs


69. dotnet/src/webdriver/BiDi/Script/CallFunction.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Script/CallFunction.cs


70. dotnet/src/webdriver/BiDi/Script/Disown.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Script/Disown.cs


71. dotnet/src/webdriver/BiDi/Script/Evaluate.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Script/Evaluate.cs


72. dotnet/src/webdriver/BiDi/Script/GetRealms.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Script/GetRealms.cs


73. dotnet/src/webdriver/BiDi/Script/RemovePreloadScript.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Script/RemovePreloadScript.cs


74. dotnet/src/webdriver/BiDi/Session/End.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Session/End.cs


75. dotnet/src/webdriver/BiDi/Session/New.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Session/New.cs


76. dotnet/src/webdriver/BiDi/Session/Status.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Session/Status.cs


77. dotnet/src/webdriver/BiDi/Session/Subscribe.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Session/Subscribe.cs


78. dotnet/src/webdriver/BiDi/Session/Unsubscribe.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Session/Unsubscribe.cs


79. dotnet/src/webdriver/BiDi/Storage/DeleteCookies.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Storage/DeleteCookies.cs


80. dotnet/src/webdriver/BiDi/Storage/GetCookies.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Storage/GetCookies.cs


81. dotnet/src/webdriver/BiDi/Storage/SetCookie.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/Storage/SetCookie.cs


82. dotnet/src/webdriver/BiDi/WebExtension/Install.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/WebExtension/Install.cs


83. dotnet/src/webdriver/BiDi/WebExtension/Uninstall.cs Additional files +0/-3

...

dotnet/src/webdriver/BiDi/WebExtension/Uninstall.cs


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review bot commented Apr 9, 2026

Code Review by Qodo

🐞 Bugs (1)   📘 Rule violations (3)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ☼ Reliability (1)
📘\ ⚙ Maintainability (3)

Grey Divider


Action required

1. Command base type removed 📘
Description
The public Command inheritance model was removed and replaced with a readonly record struct
descriptor, which breaks consumers that previously derived from Command/`Command<TParameters,
TResult>`. This violates the requirement to preserve user-facing API/ABI compatibility.
Code

dotnet/src/webdriver/BiDi/Command.cs[R24-29]

+public readonly record struct Command<TParameters, TResult>(
+    string Method,
+    JsonTypeInfo<TParameters> ParamsTypeInfo,
+    JsonTypeInfo<TResult> ResultTypeInfo)
    where TParameters : Parameters
-    where TResult : EmptyResult
-{
-    [JsonPropertyOrder(2)]
-    public TParameters Params { get; } = @params;
-}
+    where TResult : EmptyResult;
Evidence
PR Compliance ID 15 forbids breaking public API/ABI without an explicit compatibility plan.
Command.cs now defines only public readonly record struct Command<TParameters, TResult>, meaning
the previous public base class hierarchy is no longer available to consumers (e.g., custom command
types).

AGENTS.md
dotnet/src/webdriver/BiDi/Command.cs[24-29]
dotnet/test/webdriver/BiDi/Session/SessionTests.cs[78-88]

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

## Issue description
Public `Command` inheritance types were removed, which is a breaking API change for consumers that built custom commands/modules.

## Issue Context
The code now uses a descriptor-style `Command<TParameters, TResult>` record struct, but existing consumers may rely on deriving from `Command`/`Command<TParameters, TResult>`.

## Fix Focus Areas
- dotnet/src/webdriver/BiDi/Command.cs[24-36]
- dotnet/src/webdriver/BiDi/Module.cs[22-44]

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


2. ExecuteCommandAsync removed 📘
Description
The protected Module.ExecuteCommandAsync(...) API was removed/renamed to ExecuteAsync(...),
breaking consumers implementing custom modules derived from Module. This violates the
compatibility requirement for user-facing APIs.
Code

dotnet/src/webdriver/BiDi/Module.cs[R26-31]

+    protected Task<TResult> ExecuteAsync<TParameters, TResult>(Command<TParameters, TResult> descriptor, TParameters @params, CommandOptions? options, CancellationToken cancellationToken)
+        where TParameters : Parameters
        where TResult : EmptyResult
    {
-        return Broker.ExecuteCommandAsync(command, options, jsonCommandTypeInfo, jsonResultTypeInfo, cancellationToken);
+        return Broker.ExecuteAsync(descriptor, @params, options, cancellationToken);
    }
Evidence
PR Compliance ID 15 requires backward-compatible public/protected API evolution. Module now only
exposes ExecuteAsync(...), so external derived modules that previously called
ExecuteCommandAsync(...) will fail to compile without a compatibility shim.

AGENTS.md
dotnet/src/webdriver/BiDi/Module.cs[26-31]
dotnet/test/webdriver/BiDi/Session/SessionTests.cs[85-88]

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

## Issue description
`Module.ExecuteCommandAsync(...)` was removed, breaking external modules that derived from `Module` and called the protected helper.

## Issue Context
The new descriptor-based execution can still be supported while preserving the old method as an adapter (potentially `[Obsolete]`) for existing consumers.

## Fix Focus Areas
- dotnet/src/webdriver/BiDi/Module.cs[22-44]
- dotnet/src/webdriver/BiDi/Broker.cs[117-165]

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


3. Public modules made internal 📘
Description
Multiple previously public *Module concrete types were changed to internal, breaking consumers
who referenced these types directly (even if interfaces still exist). This is a user-facing API/ABI
break without an indicated compatibility plan.
Code

dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs[25]

+internal sealed class BrowserModule : Module, IBrowserModule
Evidence
PR Compliance ID 15 disallows breaking changes to public APIs. The concrete module classes are now
internal (e.g., BrowserModule, NetworkModule, BrowsingContextModule), removing previously
public symbols from the public surface.

AGENTS.md
dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs[25-25]
dotnet/src/webdriver/BiDi/Network/NetworkModule.cs[25-25]
dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs[25-25]

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

## Issue description
Concrete `*Module` classes were changed from `public` to `internal`, which is a breaking change for any consumers referencing those types directly.

## Issue Context
Even if `BiDi` exposes public interfaces, removing public concrete types breaks source and binary compatibility for advanced usage patterns.

## Fix Focus Areas
- dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Network/NetworkModule.cs[25-26]
- dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Input/InputModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Script/ScriptModule.cs[26-27]
- dotnet/src/webdriver/BiDi/Storage/StorageModule.cs[25-26]
- dotnet/src/webdriver/BiDi/WebExtension/WebExtensionModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Log/LogModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Permissions/PermissionsModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Speculation/SpeculationModule.cs[25-26]

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



Remediation recommended

4. Unvalidated descriptor structs 🐞
Description
Command<TParameters,TResult> and Event<TEventArgs,TEventParams> are public structs, so
default(descriptor) can contain null Method/Name/Factory/JsonTypeInfo;
Broker.ExecuteAsync/SubscribeAsync dereference these without validation, leading to
NullReferenceException/ArgumentNullException instead of a clear argument error.
Code

dotnet/src/webdriver/BiDi/Broker.cs[R72-84]

+    public async Task<Subscription> SubscribeAsync<TEventArgs, TEventParams>(Event<TEventArgs, TEventParams> descriptor, Action<TEventArgs> action, SubscriptionOptions? options, CancellationToken cancellationToken)
        where TEventArgs : EventArgs
    {
        ValueTask InvokeAction(EventArgs args) { action((TEventArgs)args); return default; }
-        return await SubscribeAsync(eventName, InvokeAction, (bidi, ep) => factory(bidi, (TEventParams)ep), jsonTypeInfo, options, cancellationToken).ConfigureAwait(false);
+        return await SubscribeAsync(descriptor.Name, InvokeAction, (bidi, ep) => descriptor.Factory(bidi, (TEventParams)ep), descriptor.JsonTypeInfo, options, cancellationToken).ConfigureAwait(false);
    }

-    public async Task<Subscription> SubscribeAsync<TEventArgs, TEventParams>(string eventName, Func<TEventArgs, Task> func, Func<IBiDi, TEventParams, TEventArgs> factory, SubscriptionOptions? options, JsonTypeInfo<TEventParams> jsonTypeInfo, CancellationToken cancellationToken)
+    public async Task<Subscription> SubscribeAsync<TEventArgs, TEventParams>(Event<TEventArgs, TEventParams> descriptor, Func<TEventArgs, Task> func, SubscriptionOptions? options, CancellationToken cancellationToken)
        where TEventArgs : EventArgs
    {
        ValueTask InvokeFunc(EventArgs args) => new(func((TEventArgs)args));
-        return await SubscribeAsync(eventName, InvokeFunc, (bidi, ep) => factory(bidi, (TEventParams)ep), jsonTypeInfo, options, cancellationToken).ConfigureAwait(false);
+        return await SubscribeAsync(descriptor.Name, InvokeFunc, (bidi, ep) => descriptor.Factory(bidi, (TEventParams)ep), descriptor.JsonTypeInfo, options, cancellationToken).ConfigureAwait(false);
    }
Evidence
Because Command/Event are structs, default initialization is always possible and leaves
reference-typed fields null. Broker then directly uses descriptor.Name/Factory/JsonTypeInfo and
descriptor.Method/ParamsTypeInfo without guarding, so passing a default descriptor (or any
descriptor with null internals) will fail at runtime with non-actionable exceptions.

dotnet/src/webdriver/BiDi/Command.cs[24-29]
dotnet/src/webdriver/BiDi/Event.cs[24-28]
dotnet/src/webdriver/BiDi/Broker.cs[72-84]
dotnet/src/webdriver/BiDi/Broker.cs[141-149]

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

### Issue description
`Command<,>` and `Event<,>` are public struct descriptors. Structs can be default-initialized, producing null reference fields (`Method`, `ParamsTypeInfo`, `ResultTypeInfo`, `Name`, `Factory`, `JsonTypeInfo`). `Broker.ExecuteAsync` and `Broker.SubscribeAsync` dereference these fields without validation, causing runtime null-dereference failures.

### Issue Context
This is a new footgun introduced by switching from command/event classes to descriptor structs.

### Fix Focus Areas
- dotnet/src/webdriver/BiDi/Broker.cs[72-84]
- dotnet/src/webdriver/BiDi/Broker.cs[117-150]
- dotnet/src/webdriver/BiDi/Command.cs[24-29]
- dotnet/src/webdriver/BiDi/Event.cs[24-28]

### What to change
- In `Broker.SubscribeAsync(...)`, validate:
 - `descriptor.Name` is not null/empty
 - `descriptor.Factory` is not null
 - `descriptor.JsonTypeInfo` is not null
 - throw `ArgumentException`/`ArgumentNullException` with a clear message.
- In `Broker.ExecuteAsync(...)`, validate:
 - `descriptor.Method` is not null/empty
 - `descriptor.ParamsTypeInfo` is not null
 - `descriptor.ResultTypeInfo` is not null
 - throw `ArgumentException`/`ArgumentNullException`.
- (Optional, stronger) Consider changing `Command`/`Event` from `record struct` to a `sealed class` or a struct with an explicit validating constructor/factory to reduce the chance of invalid default values escaping.

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR continues the .NET BiDi refactor to avoid per-call allocations by switching from per-command/per-event classes to statically declared command/event descriptors, and updates the broker/module plumbing accordingly.

Changes:

  • Replace command object allocation/serialization with static Command<TParameters, TResult> descriptors and a new Module.ExecuteAsync/Broker.ExecuteAsync pipeline.
  • Replace string-based event subscription metadata with static Event<TEventArgs, TEventParams> descriptors.
  • Update module serializer contexts to generate JsonTypeInfo for parameter/result types (instead of command types), and remove many command wrapper classes.

Reviewed changes

Copilot reviewed 83 out of 83 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
dotnet/test/webdriver/BiDi/Session/SessionTests.cs Updates custom-module test usage to the new descriptor-based ExecuteAsync path.
dotnet/src/webdriver/BiDi/WebExtension/WebExtensionModule.cs Converts WebExtension commands to static Command<,> descriptors and updates JSON context registrations.
dotnet/src/webdriver/BiDi/WebExtension/Uninstall.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/WebExtension/Install.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Storage/StorageModule.cs Converts storage commands to static descriptors and updates JSON context registrations.
dotnet/src/webdriver/BiDi/Storage/SetCookie.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Storage/GetCookies.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Storage/DeleteCookies.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Speculation/SpeculationModule.cs Converts event subscription to static Event<,> descriptor usage.
dotnet/src/webdriver/BiDi/Session/Unsubscribe.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Session/Subscribe.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Session/Status.cs Removes per-call command wrapper class; keeps result/options types.
dotnet/src/webdriver/BiDi/Session/SessionModule.cs Converts session commands to static descriptors and updates JSON context registrations.
dotnet/src/webdriver/BiDi/Session/New.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Session/End.cs Removes per-call command wrapper class; keeps result/options types.
dotnet/src/webdriver/BiDi/Script/ScriptModule.cs Converts script commands/events to static descriptors and updates JSON context registrations.
dotnet/src/webdriver/BiDi/Script/RemovePreloadScript.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Script/GetRealms.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Script/Evaluate.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Script/Disown.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Script/CallFunction.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Script/AddPreloadScript.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Permissions/SetPermission.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Permissions/PermissionsModule.cs Converts permissions command to static descriptor and updates JSON context registrations.
dotnet/src/webdriver/BiDi/Network/SetExtraHeaders.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/SetCacheBehavior.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/RemoveIntercept.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/RemoveDataCollector.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/ProvideResponse.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/NetworkModule.cs Converts network commands/events to static descriptors and updates JSON context registrations.
dotnet/src/webdriver/BiDi/Network/GetData.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/FailRequest.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/ContinueWithAuth.cs Removes per-call command wrapper class; keeps polymorphic parameter type hierarchy.
dotnet/src/webdriver/BiDi/Network/ContinueResponse.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/ContinueRequest.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/AddIntercept.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Network/AddDataCollector.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Module.cs Replaces protected execution/subscription API with descriptor-based overloads.
dotnet/src/webdriver/BiDi/Log/LogModule.cs Converts log event subscription to static Event<,> descriptor usage.
dotnet/src/webdriver/BiDi/Input/SetFiles.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Input/ReleaseActions.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Input/PerformActions.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Input/InputModule.cs Converts input commands/event to static descriptors and updates JSON context registrations.
dotnet/src/webdriver/BiDi/Event.cs Introduces Event<TEventArgs, TEventParams> descriptor type for static event metadata.
dotnet/src/webdriver/BiDi/Emulation/SetUserAgentOverride.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/SetTouchOverride.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/SetTimezoneOverride.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/SetScrollbarTypeOverride.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/SetScriptingEnabled.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/SetScreenSettingsOverride.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/SetScreenOrientationOverride.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/SetNetworkConditions.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/SetLocaleOverride.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/SetGeolocationOverride.cs Removes per-call command wrapper class; keeps polymorphic parameter type hierarchy.
dotnet/src/webdriver/BiDi/Emulation/SetForcedColorsModeThemeOverride.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs Converts emulation commands to static descriptors and updates JSON context registrations.
dotnet/src/webdriver/BiDi/Command.cs Replaces class-based command model with Command<TParameters, TResult> descriptor and keeps Parameters/EmptyResult.
dotnet/src/webdriver/BiDi/BrowsingContext/TraverseHistory.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/SetViewport.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/Reload.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/Print.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/Navigate.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/LocateNodes.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/HandleUserPrompt.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/GetTree.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/Create.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/Close.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/CaptureScreenshot.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextStorageModule.cs Makes browsing-context storage module implementation internal.
dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextScriptModule.cs Makes browsing-context script module implementation internal.
dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextNetworkModule.cs Makes browsing-context network module implementation internal.
dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs Converts browsingContext commands/events to static descriptors and updates JSON context registrations.
dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextLogModule.cs Makes browsing-context log module implementation internal.
dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextInputModule.cs Makes browsing-context input module implementation internal.
dotnet/src/webdriver/BiDi/BrowsingContext/Activate.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Browser/SetDownloadBehavior.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Browser/RemoveUserContext.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Browser/GetUserContexts.cs Removes per-call command wrapper class; keeps result/options types.
dotnet/src/webdriver/BiDi/Browser/GetClientWindows.cs Removes per-call command wrapper class; keeps result/options types.
dotnet/src/webdriver/BiDi/Browser/CreateUserContext.cs Removes per-call command wrapper class; keeps parameter/options/result types.
dotnet/src/webdriver/BiDi/Browser/Close.cs Removes per-call command wrapper class; keeps result/options types.
dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs Converts browser commands to static descriptors, updates JSON context registrations, and changes visibility.
dotnet/src/webdriver/BiDi/Broker.cs Updates execution/subscription plumbing to use descriptors; writes command JSON envelopes manually; adjusts pending-command removal semantics.

@nvborisenko nvborisenko marked this pull request as draft April 9, 2026 23:52
Copilot AI review requested due to automatic review settings April 10, 2026 18:47
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 35 changed files in this pull request and generated no new comments.

@nvborisenko nvborisenko marked this pull request as ready for review April 10, 2026 18:56
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

[dotnet] [bidi] Statically declare events with descriptors

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Refactor BiDi event subscription to use static event descriptors
• Replace individual event factory methods with Event<TEventArgs, TEventParams> records
• Simplify SubscribeAsync method signatures by consolidating event metadata
• Update all module event handlers to use descriptor-based approach

Grey Divider

File Changes

1. dotnet/src/webdriver/BiDi/Event.cs ✨ Enhancement +9/-1

Add Event record struct for event descriptors

dotnet/src/webdriver/BiDi/Event.cs


2. dotnet/src/webdriver/BiDi/Broker.cs ✨ Enhancement +4/-4

Simplify SubscribeAsync to use Event descriptors

dotnet/src/webdriver/BiDi/Broker.cs


3. dotnet/src/webdriver/BiDi/Module.cs ✨ Enhancement +4/-6

Update protected SubscribeAsync methods signatures

dotnet/src/webdriver/BiDi/Module.cs


View more (32)
4. dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs ✨ Enhancement +103/-61

Declare static events and remove factory methods

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs


5. dotnet/src/webdriver/BiDi/Input/InputModule.cs ✨ Enhancement +7/-7

Declare static FileDialogOpenedEvent descriptor

dotnet/src/webdriver/BiDi/Input/InputModule.cs


6. dotnet/src/webdriver/BiDi/Log/LogModule.cs ✨ Enhancement +13/-10

Declare static EntryAddedEvent with factory logic

dotnet/src/webdriver/BiDi/Log/LogModule.cs


7. dotnet/src/webdriver/BiDi/Network/NetworkModule.cs ✨ Enhancement +35/-25

Declare static network event descriptors

dotnet/src/webdriver/BiDi/Network/NetworkModule.cs


8. dotnet/src/webdriver/BiDi/Script/ScriptModule.cs ✨ Enhancement +32/-25

Declare static script event descriptors

dotnet/src/webdriver/BiDi/Script/ScriptModule.cs


9. dotnet/src/webdriver/BiDi/Speculation/SpeculationModule.cs ✨ Enhancement +7/-5

Declare static PrefetchStatusUpdatedEvent descriptor

dotnet/src/webdriver/BiDi/Speculation/SpeculationModule.cs


10. dotnet/src/webdriver/BiDi/BrowsingContext/NavigationStartedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/NavigationStartedEvent.cs


11. dotnet/src/webdriver/BiDi/BrowsingContext/NavigationEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/NavigationEvent.cs


12. dotnet/src/webdriver/BiDi/BrowsingContext/FragmentNavigatedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/FragmentNavigatedEvent.cs


13. dotnet/src/webdriver/BiDi/BrowsingContext/HistoryUpdatedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/HistoryUpdatedEvent.cs


14. dotnet/src/webdriver/BiDi/BrowsingContext/DomContentLoadedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/DomContentLoadedEvent.cs


15. dotnet/src/webdriver/BiDi/BrowsingContext/LoadEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/LoadEvent.cs


16. dotnet/src/webdriver/BiDi/BrowsingContext/DownloadWillBeginEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/DownloadWillBeginEvent.cs


17. dotnet/src/webdriver/BiDi/BrowsingContext/DownloadEndEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/DownloadEndEvent.cs


18. dotnet/src/webdriver/BiDi/BrowsingContext/NavigationAbortedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/NavigationAbortedEvent.cs


19. dotnet/src/webdriver/BiDi/BrowsingContext/NavigationFailedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/NavigationFailedEvent.cs


20. dotnet/src/webdriver/BiDi/BrowsingContext/NavigationCommittedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/NavigationCommittedEvent.cs


21. dotnet/src/webdriver/BiDi/BrowsingContext/ContextCreatedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/ContextCreatedEvent.cs


22. dotnet/src/webdriver/BiDi/BrowsingContext/ContextDestroyedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/ContextDestroyedEvent.cs


23. dotnet/src/webdriver/BiDi/BrowsingContext/UserPromptOpenedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/UserPromptOpenedEvent.cs


24. dotnet/src/webdriver/BiDi/BrowsingContext/UserPromptClosedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/BrowsingContext/UserPromptClosedEvent.cs


25. dotnet/src/webdriver/BiDi/Input/FileDialogOpenedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Input/FileDialogOpenedEvent.cs


26. dotnet/src/webdriver/BiDi/Log/EntryAddedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Log/EntryAddedEvent.cs


27. dotnet/src/webdriver/BiDi/Network/BeforeRequestSentEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Network/BeforeRequestSentEvent.cs


28. dotnet/src/webdriver/BiDi/Network/ResponseStartedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Network/ResponseStartedEvent.cs


29. dotnet/src/webdriver/BiDi/Network/ResponseCompletedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Network/ResponseCompletedEvent.cs


30. dotnet/src/webdriver/BiDi/Network/FetchErrorEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Network/FetchErrorEvent.cs


31. dotnet/src/webdriver/BiDi/Network/AuthRequiredEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Network/AuthRequiredEvent.cs


32. dotnet/src/webdriver/BiDi/Script/MessageEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Script/MessageEvent.cs


33. dotnet/src/webdriver/BiDi/Script/RealmInfoEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Script/RealmInfoEvent.cs


34. dotnet/src/webdriver/BiDi/Script/RealmDestroyedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Script/RealmDestroyedEvent.cs


35. dotnet/src/webdriver/BiDi/Speculation/PrefetchStatusUpdatedEvent.cs Formatting +1/-1

Update copyright header filename

dotnet/src/webdriver/BiDi/Speculation/PrefetchStatusUpdatedEvent.cs


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review bot commented Apr 10, 2026

Code Review by Qodo

🐞 Bugs (1)   📘 Rule violations (3)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ☼ Reliability (1)
📘\ ⚙ Maintainability (3)

Grey Divider


Action required

1. Command base type removed 📘
Description
The public Command inheritance model was removed and replaced with a readonly record struct
descriptor, which breaks consumers that previously derived from Command/`Command<TParameters,
TResult>`. This violates the requirement to preserve user-facing API/ABI compatibility.
Code

dotnet/src/webdriver/BiDi/Command.cs[R24-29]

+public readonly record struct Command<TParameters, TResult>(
+    string Method,
+    JsonTypeInfo<TParameters> ParamsTypeInfo,
+    JsonTypeInfo<TResult> ResultTypeInfo)
    where TParameters : Parameters
-    where TResult : EmptyResult
-{
-    [JsonPropertyOrder(2)]
-    public TParameters Params { get; } = @params;
-}
+    where TResult : EmptyResult;
Evidence
PR Compliance ID 15 forbids breaking public API/ABI without an explicit compatibility plan.
Command.cs now defines only public readonly record struct Command<TParameters, TResult>, meaning
the previous public base class hierarchy is no longer available to consumers (e.g., custom command
types).

AGENTS.md
dotnet/src/webdriver/BiDi/Command.cs[24-29]
dotnet/test/webdriver/BiDi/Session/SessionTests.cs[78-88]

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

## Issue description
Public `Command` inheritance types were removed, which is a breaking API change for consumers that built custom commands/modules.

## Issue Context
The code now uses a descriptor-style `Command<TParameters, TResult>` record struct, but existing consumers may rely on deriving from `Command`/`Command<TParameters, TResult>`.

## Fix Focus Areas
- dotnet/src/webdriver/BiDi/Command.cs[24-36]
- dotnet/src/webdriver/BiDi/Module.cs[22-44]

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


2. ExecuteCommandAsync removed 📘
Description
The protected Module.ExecuteCommandAsync(...) API was removed/renamed to ExecuteAsync(...),
breaking consumers implementing custom modules derived from Module. This violates the
compatibility requirement for user-facing APIs.
Code

dotnet/src/webdriver/BiDi/Module.cs[R26-31]

+    protected Task<TResult> ExecuteAsync<TParameters, TResult>(Command<TParameters, TResult> descriptor, TParameters @params, CommandOptions? options, CancellationToken cancellationToken)
+        where TParameters : Parameters
        where TResult : EmptyResult
    {
-        return Broker.ExecuteCommandAsync(command, options, jsonCommandTypeInfo, jsonResultTypeInfo, cancellationToken);
+        return Broker.ExecuteAsync(descriptor, @params, options, cancellationToken);
    }
Evidence
PR Compliance ID 15 requires backward-compatible public/protected API evolution. Module now only
exposes ExecuteAsync(...), so external derived modules that previously called
ExecuteCommandAsync(...) will fail to compile without a compatibility shim.

AGENTS.md
dotnet/src/webdriver/BiDi/Module.cs[26-31]
dotnet/test/webdriver/BiDi/Session/SessionTests.cs[85-88]

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

## Issue description
`Module.ExecuteCommandAsync(...)` was removed, breaking external modules that derived from `Module` and called the protected helper.

## Issue Context
The new descriptor-based execution can still be supported while preserving the old method as an adapter (potentially `[Obsolete]`) for existing consumers.

## Fix Focus Areas
- dotnet/src/webdriver/BiDi/Module.cs[22-44]
- dotnet/src/webdriver/BiDi/Broker.cs[117-165]

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


3. Public modules made internal 📘
Description
Multiple previously public *Module concrete types were changed to internal, breaking consumers
who referenced these types directly (even if interfaces still exist). This is a user-facing API/ABI
break without an indicated compatibility plan.
Code

dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs[25]

+internal sealed class BrowserModule : Module, IBrowserModule
Evidence
PR Compliance ID 15 disallows breaking changes to public APIs. The concrete module classes are now
internal (e.g., BrowserModule, NetworkModule, BrowsingContextModule), removing previously
public symbols from the public surface.

AGENTS.md
dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs[25-25]
dotnet/src/webdriver/BiDi/Network/NetworkModule.cs[25-25]
dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs[25-25]

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

## Issue description
Concrete `*Module` classes were changed from `public` to `internal`, which is a breaking change for any consumers referencing those types directly.

## Issue Context
Even if `BiDi` exposes public interfaces, removing public concrete types breaks source and binary compatibility for advanced usage patterns.

## Fix Focus Areas
- dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Network/NetworkModule.cs[25-26]
- dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Input/InputModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Script/ScriptModule.cs[26-27]
- dotnet/src/webdriver/BiDi/Storage/StorageModule.cs[25-26]
- dotnet/src/webdriver/BiDi/WebExtension/WebExtensionModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Log/LogModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Permissions/PermissionsModule.cs[25-26]
- dotnet/src/webdriver/BiDi/Speculation/SpeculationModule.cs[25-26]

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



Remediation recommended

4. Unvalidated descriptor structs 🐞
Description
Command<TParameters,TResult> and Event<TEventArgs,TEventParams> are public structs, so
default(descriptor) can contain null Method/Name/Factory/JsonTypeInfo;
Broker.ExecuteAsync/SubscribeAsync dereference these without validation, leading to
NullReferenceException/ArgumentNullException instead of a clear argument error.
Code

dotnet/src/webdriver/BiDi/Broker.cs[R72-84]

+    public async Task<Subscription> SubscribeAsync<TEventArgs, TEventParams>(Event<TEventArgs, TEventParams> descriptor, Action<TEventArgs> action, SubscriptionOptions? options, CancellationToken cancellationToken)
        where TEventArgs : EventArgs
    {
        ValueTask InvokeAction(EventArgs args) { action((TEventArgs)args); return default; }
-        return await SubscribeAsync(eventName, InvokeAction, (bidi, ep) => factory(bidi, (TEventParams)ep), jsonTypeInfo, options, cancellationToken).ConfigureAwait(false);
+        return await SubscribeAsync(descriptor.Name, InvokeAction, (bidi, ep) => descriptor.Factory(bidi, (TEventParams)ep), descriptor.JsonTypeInfo, options, cancellationToken).ConfigureAwait(false);
    }

-    public async Task<Subscription> SubscribeAsync<TEventArgs, TEventParams>(string eventName, Func<TEventArgs, Task> func, Func<IBiDi, TEventParams, TEventArgs> factory, SubscriptionOptions? options, JsonTypeInfo<TEventParams> jsonTypeInfo, CancellationToken cancellationToken)
+    public async Task<Subscription> SubscribeAsync<TEventArgs, TEventParams>(Event<TEventArgs, TEventParams> descriptor, Func<TEventArgs, Task> func, SubscriptionOptions? options, CancellationToken cancellationToken)
        where TEventArgs : EventArgs
    {
        ValueTask InvokeFunc(EventArgs args) => new(func((TEventArgs)args));
-        return await SubscribeAsync(eventName, InvokeFunc, (bidi, ep) => factory(bidi, (TEventParams)ep), jsonTypeInfo, options, cancellationToken).ConfigureAwait(false);
+        return await SubscribeAsync(descriptor.Name, InvokeFunc, (bidi, ep) => descriptor.Factory(bidi, (TEventParams)ep), descriptor.JsonTypeInfo, options, cancellationToken).ConfigureAwait(false);
    }
Evidence
Because Command/Event are structs, default initialization is always possible and leaves
reference-typed fields null. Broker then directly uses descriptor.Name/Factory/JsonTypeInfo and
descriptor.Method/ParamsTypeInfo without guarding, so passing a default descriptor (or any
descriptor with null internals) will fail at runtime with non-actionable exceptions.

dotnet/src/webdriver/BiDi/Command.cs[24-29]
dotnet/src/webdriver/BiDi/Event.cs[24-28]
dotnet/src/webdriver/BiDi/Broker.cs[72-84]
dotnet/src/webdriver/BiDi/Broker.cs[141-149]

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

### Issue description
`Command<,>` and `Event<,>` are public struct descriptors. Structs can be default-initialized, producing null reference fields (`Method`, `ParamsTypeInfo`, `ResultTypeInfo`, `Name`, `Factory`, `JsonTypeInfo`). `Broker.ExecuteAsync` and `Broker.SubscribeAsync` dereference these fields without validation, causing runtime null-dereference failures.

### Issue Context
This is a new footgun introduced by switching from command/event classes to descriptor structs.

### Fix Focus Areas
- dotnet/src/webdriver/BiDi/Broker.cs[72-84]
- dotnet/src/webdriver/BiDi/Broker.cs[117-150]
- dotnet/src/webdriver/BiDi/Command.cs[24-29]
- dotnet/src/webdriver/BiDi/Event.cs[24-28]

### What to change
- In `Broker.SubscribeAsync(...)`, validate:
 - `descriptor.Name` is not null/empty
 - `descriptor.Factory` is not null
 - `descriptor.JsonTypeInfo` is not null
 - throw `ArgumentException`/`ArgumentNullException` with a clear message.
- In `Broker.ExecuteAsync(...)`, validate:
 - `descriptor.Method` is not null/empty
 - `descriptor.ParamsTypeInfo` is not null
 - `descriptor.ResultTypeInfo` is not null
 - throw `ArgumentException`/`ArgumentNullException`.
- (Optional, stronger) Consider changing `Command`/`Event` from `record struct` to a `sealed class` or a struct with an explicit validating constructor/factory to reduce the chance of invalid default values escaping.

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@nvborisenko nvborisenko merged commit 83b8e82 into SeleniumHQ:trunk Apr 10, 2026
22 checks passed
@nvborisenko nvborisenko deleted the bidi-event-descriptor branch April 10, 2026 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-dotnet .NET Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants