From 5e84d9dfd641725c915c0158f987ec2233abc019 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 8 Oct 2025 19:07:13 +0300 Subject: [PATCH 1/6] [bidi] [dotnet] Use events JsonTypeInfo for deserialization --- .../BrowsingContext/BrowsingContextModule.cs | 56 +++++++++---------- .../webdriver/BiDi/Communication/Broker.cs | 15 ++--- dotnet/src/webdriver/BiDi/Log/LogModule.cs | 4 +- .../webdriver/BiDi/Network/NetworkModule.cs | 20 +++---- .../src/webdriver/BiDi/Script/ScriptModule.cs | 12 ++-- 5 files changed, 52 insertions(+), 55 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs b/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs index e1c0419a1d71c..7420e4dcb4ab7 100644 --- a/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs +++ b/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs @@ -113,141 +113,141 @@ public async Task HandleUserPromptAsync(BrowsingContext context, Ha public async Task OnNavigationStartedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.navigationStarted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.navigationStarted", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnNavigationStartedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.navigationStarted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.navigationStarted", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnFragmentNavigatedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.fragmentNavigated", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.fragmentNavigated", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnFragmentNavigatedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.fragmentNavigated", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.fragmentNavigated", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnHistoryUpdatedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.historyUpdated", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.historyUpdated", handler, options, JsonContext.HistoryUpdatedEventArgs).ConfigureAwait(false); } public async Task OnHistoryUpdatedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.historyUpdated", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.historyUpdated", handler, options, JsonContext.HistoryUpdatedEventArgs).ConfigureAwait(false); } public async Task OnDomContentLoadedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.domContentLoaded", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.domContentLoaded", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnDomContentLoadedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.domContentLoaded", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.domContentLoaded", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnLoadAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.load", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.load", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnLoadAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.load", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.load", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnDownloadWillBeginAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.downloadWillBegin", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.downloadWillBegin", handler, options, JsonContext.DownloadWillBeginEventArgs).ConfigureAwait(false); } public async Task OnDownloadWillBeginAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.downloadWillBegin", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.downloadWillBegin", handler, options, JsonContext.DownloadWillBeginEventArgs).ConfigureAwait(false); } public async Task OnDownloadEndAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.downloadEnd", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.downloadEnd", handler, options, JsonContext.DownloadEndEventArgs).ConfigureAwait(false); } public async Task OnDownloadEndAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.downloadEnd", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.downloadEnd", handler, options, JsonContext.DownloadEndEventArgs).ConfigureAwait(false); } public async Task OnNavigationAbortedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.navigationAborted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.navigationAborted", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnNavigationAbortedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.navigationAborted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.navigationAborted", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnNavigationFailedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.navigationFailed", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.navigationFailed", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnNavigationFailedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.navigationFailed", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.navigationFailed", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnNavigationCommittedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.navigationCommitted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.navigationCommitted", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnNavigationCommittedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.navigationCommitted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.navigationCommitted", handler, options, JsonContext.NavigationInfo).ConfigureAwait(false); } public async Task OnContextCreatedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.contextCreated", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.contextCreated", handler, options, JsonContext.BrowsingContextInfo).ConfigureAwait(false); } public async Task OnContextCreatedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.contextCreated", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.contextCreated", handler, options, JsonContext.BrowsingContextInfo).ConfigureAwait(false); } public async Task OnContextDestroyedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.contextDestroyed", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.contextDestroyed", handler, options, JsonContext.BrowsingContextInfo).ConfigureAwait(false); } public async Task OnContextDestroyedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.contextDestroyed", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.contextDestroyed", handler, options, JsonContext.BrowsingContextInfo).ConfigureAwait(false); } public async Task OnUserPromptOpenedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.userPromptOpened", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.userPromptOpened", handler, options, JsonContext.UserPromptOpenedEventArgs).ConfigureAwait(false); } public async Task OnUserPromptOpenedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.userPromptOpened", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.userPromptOpened", handler, options, JsonContext.UserPromptOpenedEventArgs).ConfigureAwait(false); } public async Task OnUserPromptClosedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.userPromptClosed", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.userPromptClosed", handler, options, JsonContext.UserPromptClosedEventArgs).ConfigureAwait(false); } public async Task OnUserPromptClosedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("browsingContext.userPromptClosed", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("browsingContext.userPromptClosed", handler, options, JsonContext.UserPromptClosedEventArgs).ConfigureAwait(false); } } diff --git a/dotnet/src/webdriver/BiDi/Communication/Broker.cs b/dotnet/src/webdriver/BiDi/Communication/Broker.cs index 449715c3f47ef..e5946ba349394 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Broker.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Broker.cs @@ -24,7 +24,6 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; -using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; @@ -40,7 +39,7 @@ public sealed class Broker : IAsyncDisposable private readonly ConcurrentDictionary _pendingCommands = new(); private readonly BlockingCollection _pendingEvents = []; - private readonly Dictionary _eventTypesMap = []; + private readonly Dictionary _eventTypesMap = []; private readonly ConcurrentDictionary> _eventHandlers = new(); @@ -157,10 +156,10 @@ public async Task ExecuteCommandAsync(TCommand comma return JsonSerializer.Deserialize(resultJson, jsonResultTypeInfo)!; } - public async Task SubscribeAsync(string eventName, Action action, SubscriptionOptions? options, JsonSerializerContext jsonContext) + public async Task SubscribeAsync(string eventName, Action action, SubscriptionOptions? options, JsonTypeInfo jsonTypeInfo) where TEventArgs : EventArgs { - _eventTypesMap[eventName] = (typeof(TEventArgs), jsonContext); + _eventTypesMap[eventName] = jsonTypeInfo; var handlers = _eventHandlers.GetOrAdd(eventName, (a) => []); @@ -186,10 +185,10 @@ public async Task SubscribeAsync(string eventName, Act } } - public async Task SubscribeAsync(string eventName, Func func, SubscriptionOptions? options, JsonSerializerContext jsonContext) + public async Task SubscribeAsync(string eventName, Func func, SubscriptionOptions? options, JsonTypeInfo jsonTypeInfo) where TEventArgs : EventArgs { - _eventTypesMap[eventName] = (typeof(TEventArgs), jsonContext); + _eventTypesMap[eventName] = jsonTypeInfo; var handlers = _eventHandlers.GetOrAdd(eventName, (a) => []); @@ -317,7 +316,7 @@ private void ProcessReceivedMessage(byte[]? data) if (_eventTypesMap.TryGetValue(method, out var eventInfo)) { - var eventArgs = (EventArgs)JsonSerializer.Deserialize(ref paramsReader, eventInfo.EventType, eventInfo.JsonContext)!; + var eventArgs = (EventArgs)JsonSerializer.Deserialize(ref paramsReader, eventInfo)!; var messageEvent = new MessageEvent(method, eventArgs); _pendingEvents.Add(messageEvent); @@ -350,8 +349,6 @@ class CommandInfo(long id, Type resultType, TaskCompletionSource ta { public long Id { get; } = id; - public Type ResultType { get; } = resultType; - public TaskCompletionSource TaskCompletionSource { get; } = taskCompletionSource; }; } diff --git a/dotnet/src/webdriver/BiDi/Log/LogModule.cs b/dotnet/src/webdriver/BiDi/Log/LogModule.cs index d6e36263c1117..a3887d7f052cf 100644 --- a/dotnet/src/webdriver/BiDi/Log/LogModule.cs +++ b/dotnet/src/webdriver/BiDi/Log/LogModule.cs @@ -27,11 +27,11 @@ public sealed class LogModule : Module { public async Task OnEntryAddedAsync(Func handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("log.entryAdded", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("log.entryAdded", handler, options, JsonContext.LogEntry).ConfigureAwait(false); } public async Task OnEntryAddedAsync(Action handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("log.entryAdded", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("log.entryAdded", handler, options, JsonContext.LogEntry).ConfigureAwait(false); } } diff --git a/dotnet/src/webdriver/BiDi/Network/NetworkModule.cs b/dotnet/src/webdriver/BiDi/Network/NetworkModule.cs index acbb737c307de..ceb132784b13c 100644 --- a/dotnet/src/webdriver/BiDi/Network/NetworkModule.cs +++ b/dotnet/src/webdriver/BiDi/Network/NetworkModule.cs @@ -126,51 +126,51 @@ public async Task ContinueWithAuthAsync(Request request, ContinueWi public async Task OnBeforeRequestSentAsync(Func handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.beforeRequestSent", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.beforeRequestSent", handler, options, JsonContext.BeforeRequestSentEventArgs).ConfigureAwait(false); } public async Task OnBeforeRequestSentAsync(Action handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.beforeRequestSent", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.beforeRequestSent", handler, options, JsonContext.BeforeRequestSentEventArgs).ConfigureAwait(false); } public async Task OnResponseStartedAsync(Func handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.responseStarted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.responseStarted", handler, options, JsonContext.ResponseStartedEventArgs).ConfigureAwait(false); } public async Task OnResponseStartedAsync(Action handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.responseStarted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.responseStarted", handler, options, JsonContext.ResponseStartedEventArgs).ConfigureAwait(false); } public async Task OnResponseCompletedAsync(Func handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.responseCompleted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.responseCompleted", handler, options, JsonContext.ResponseCompletedEventArgs).ConfigureAwait(false); } public async Task OnResponseCompletedAsync(Action handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.responseCompleted", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.responseCompleted", handler, options, JsonContext.ResponseCompletedEventArgs).ConfigureAwait(false); } public async Task OnFetchErrorAsync(Func handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.fetchError", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.fetchError", handler, options, JsonContext.FetchErrorEventArgs).ConfigureAwait(false); } public async Task OnFetchErrorAsync(Action handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.fetchError", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.fetchError", handler, options, JsonContext.FetchErrorEventArgs).ConfigureAwait(false); } public async Task OnAuthRequiredAsync(Func handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.authRequired", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.authRequired", handler, options, JsonContext.AuthRequiredEventArgs).ConfigureAwait(false); } public async Task OnAuthRequiredAsync(Action handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("network.authRequired", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("network.authRequired", handler, options, JsonContext.AuthRequiredEventArgs).ConfigureAwait(false); } } diff --git a/dotnet/src/webdriver/BiDi/Script/ScriptModule.cs b/dotnet/src/webdriver/BiDi/Script/ScriptModule.cs index e5968f76bdef0..3ebd7912638d9 100644 --- a/dotnet/src/webdriver/BiDi/Script/ScriptModule.cs +++ b/dotnet/src/webdriver/BiDi/Script/ScriptModule.cs @@ -78,31 +78,31 @@ public async Task RemovePreloadScriptAsync(PreloadScript script, Re public async Task OnMessageAsync(Func handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("script.message", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("script.message", handler, options, JsonContext.MessageEventArgs).ConfigureAwait(false); } public async Task OnMessageAsync(Action handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("script.message", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("script.message", handler, options, JsonContext.MessageEventArgs).ConfigureAwait(false); } public async Task OnRealmCreatedAsync(Func handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("script.realmCreated", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("script.realmCreated", handler, options, JsonContext.RealmInfo).ConfigureAwait(false); } public async Task OnRealmCreatedAsync(Action handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("script.realmCreated", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("script.realmCreated", handler, options, JsonContext.RealmInfo).ConfigureAwait(false); } public async Task OnRealmDestroyedAsync(Func handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("script.realmDestroyed", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("script.realmDestroyed", handler, options, JsonContext.RealmDestroyedEventArgs).ConfigureAwait(false); } public async Task OnRealmDestroyedAsync(Action handler, SubscriptionOptions? options = null) { - return await Broker.SubscribeAsync("script.realmDestroyed", handler, options, JsonContext).ConfigureAwait(false); + return await Broker.SubscribeAsync("script.realmDestroyed", handler, options, JsonContext.RealmDestroyedEventArgs).ConfigureAwait(false); } } From d7f0493815f2059293598af5d5690795fff1b2e1 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 8 Oct 2025 20:24:56 +0300 Subject: [PATCH 2/6] Immediate --- .../webdriver/BiDi/Communication/Broker.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/Communication/Broker.cs b/dotnet/src/webdriver/BiDi/Communication/Broker.cs index e5946ba349394..65fb508d6af3b 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Broker.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Broker.cs @@ -37,7 +37,7 @@ public sealed class Broker : IAsyncDisposable private readonly BiDi _bidi; private readonly ITransport _transport; - private readonly ConcurrentDictionary _pendingCommands = new(); + private readonly ConcurrentDictionary> _pendingCommands = new(); private readonly BlockingCollection _pendingEvents = []; private readonly Dictionary _eventTypesMap = []; @@ -143,17 +143,17 @@ public async Task ExecuteCommandAsync(TCommand comma where TResult : EmptyResult { command.Id = Interlocked.Increment(ref _currentCommandId); - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var timeout = options?.Timeout ?? TimeSpan.FromSeconds(30); using var cts = new CancellationTokenSource(timeout); cts.Token.Register(() => tcs.TrySetCanceled(cts.Token)); - var commandInfo = new CommandInfo(command.Id, command.ResultType, tcs); + var commandInfo = new CommandInfo(command.Id, tcs, jsonResultTypeInfo); _pendingCommands[command.Id] = commandInfo; var data = JsonSerializer.SerializeToUtf8Bytes(command, jsonCommandTypeInfo); await _transport.SendAsync(data, cts.Token).ConfigureAwait(false); - var resultJson = await tcs.Task.ConfigureAwait(false); - return JsonSerializer.Deserialize(resultJson, jsonResultTypeInfo)!; + + return (TResult)await tcs.Task.ConfigureAwait(false); } public async Task SubscribeAsync(string eventName, Action action, SubscriptionOptions? options, JsonTypeInfo jsonTypeInfo) @@ -301,7 +301,7 @@ private void ProcessReceivedMessage(byte[]? data) if (_pendingCommands.TryGetValue(id.Value, out var successCommand)) { - successCommand.TaskCompletionSource.SetResult(JsonElement.ParseValue(ref resultReader)); + successCommand.TaskCompletionSource.SetResult((EmptyResult)JsonSerializer.Deserialize(ref resultReader, successCommand.JsonResultTypeInfo)!); _pendingCommands.TryRemove(id.Value, out _); } else @@ -345,10 +345,13 @@ private void ProcessReceivedMessage(byte[]? data) } } - class CommandInfo(long id, Type resultType, TaskCompletionSource taskCompletionSource) + class CommandInfo(long id, TaskCompletionSource taskCompletionSource, JsonTypeInfo jsonResultTypeInfo) + where TResult : EmptyResult { public long Id { get; } = id; - public TaskCompletionSource TaskCompletionSource { get; } = taskCompletionSource; + public TaskCompletionSource TaskCompletionSource { get; } = taskCompletionSource; + + public JsonTypeInfo JsonResultTypeInfo { get; } = jsonResultTypeInfo; }; } From ba6b7edc9ab0e4a1da65ed6fc4f1de53cbd52175 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 8 Oct 2025 20:54:48 +0300 Subject: [PATCH 3/6] Event args type safety --- dotnet/src/webdriver/BiDi/Communication/Broker.cs | 4 ++-- dotnet/src/webdriver/BiDi/Communication/EventHandler.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/Communication/Broker.cs b/dotnet/src/webdriver/BiDi/Communication/Broker.cs index 65fb508d6af3b..b5265ac161063 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Broker.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Broker.cs @@ -156,7 +156,7 @@ public async Task ExecuteCommandAsync(TCommand comma return (TResult)await tcs.Task.ConfigureAwait(false); } - public async Task SubscribeAsync(string eventName, Action action, SubscriptionOptions? options, JsonTypeInfo jsonTypeInfo) + public async Task SubscribeAsync(string eventName, Action action, SubscriptionOptions? options, JsonTypeInfo jsonTypeInfo) where TEventArgs : EventArgs { _eventTypesMap[eventName] = jsonTypeInfo; @@ -185,7 +185,7 @@ public async Task SubscribeAsync(string eventName, Act } } - public async Task SubscribeAsync(string eventName, Func func, SubscriptionOptions? options, JsonTypeInfo jsonTypeInfo) + public async Task SubscribeAsync(string eventName, Func func, SubscriptionOptions? options, JsonTypeInfo jsonTypeInfo) where TEventArgs : EventArgs { _eventTypesMap[eventName] = jsonTypeInfo; diff --git a/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs b/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs index 4f291fcb8da08..21fccc4a73542 100644 --- a/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs +++ b/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs @@ -23,17 +23,17 @@ namespace OpenQA.Selenium.BiDi.Communication; -public abstract class EventHandler(string eventName, Type eventArgsType, IEnumerable? contexts = null) +public abstract class EventHandler(string eventName, IEnumerable? contexts = null) { public string EventName { get; } = eventName; - public Type EventArgsType { get; set; } = eventArgsType; + public IEnumerable? Contexts { get; } = contexts; public abstract ValueTask InvokeAsync(object args); } internal class AsyncEventHandler(string eventName, Func func, IEnumerable? contexts = null) - : EventHandler(eventName, typeof(TEventArgs), contexts) where TEventArgs : EventArgs + : EventHandler(eventName, contexts) where TEventArgs : EventArgs { private readonly Func _func = func; @@ -44,7 +44,7 @@ public override async ValueTask InvokeAsync(object args) } internal class SyncEventHandler(string eventName, Action action, IEnumerable? contexts = null) - : EventHandler(eventName, typeof(TEventArgs), contexts) where TEventArgs : EventArgs + : EventHandler(eventName, contexts) where TEventArgs : EventArgs { private readonly Action _action = action; From 02796ea6322e27b279b98e62e94bdaa648c513b2 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 8 Oct 2025 22:22:27 +0300 Subject: [PATCH 4/6] Remove unused Mesage type --- .../webdriver/BiDi/Communication/Broker.cs | 4 +-- .../webdriver/BiDi/Communication/Message.cs | 34 ------------------- 2 files changed, 2 insertions(+), 36 deletions(-) delete mode 100644 dotnet/src/webdriver/BiDi/Communication/Message.cs diff --git a/dotnet/src/webdriver/BiDi/Communication/Broker.cs b/dotnet/src/webdriver/BiDi/Communication/Broker.cs index b5265ac161063..35328d8dd18f7 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Broker.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Broker.cs @@ -38,7 +38,7 @@ public sealed class Broker : IAsyncDisposable private readonly ITransport _transport; private readonly ConcurrentDictionary> _pendingCommands = new(); - private readonly BlockingCollection _pendingEvents = []; + private readonly BlockingCollection<(string Method, EventArgs Params)> _pendingEvents = []; private readonly Dictionary _eventTypesMap = []; private readonly ConcurrentDictionary> _eventHandlers = new(); @@ -318,7 +318,7 @@ private void ProcessReceivedMessage(byte[]? data) { var eventArgs = (EventArgs)JsonSerializer.Deserialize(ref paramsReader, eventInfo)!; - var messageEvent = new MessageEvent(method, eventArgs); + var messageEvent = (method, eventArgs); _pendingEvents.Add(messageEvent); } else diff --git a/dotnet/src/webdriver/BiDi/Communication/Message.cs b/dotnet/src/webdriver/BiDi/Communication/Message.cs deleted file mode 100644 index 03f948cc18873..0000000000000 --- a/dotnet/src/webdriver/BiDi/Communication/Message.cs +++ /dev/null @@ -1,34 +0,0 @@ -// -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -// - -namespace OpenQA.Selenium.BiDi.Communication; - -internal abstract record Message; - -internal record MessageSuccess(long Id, EmptyResult Result) : Message; - -internal record MessageError(long Id) : Message -{ - public string? Error { get; set; } - - public string? Message { get; set; } -} - -internal record MessageEvent(string Method, EventArgs Params) : Message; - From b805e9379e5cf7fe1368d46cf60f8a0025895ac8 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:56:03 +0300 Subject: [PATCH 5/6] Non generic CommandInfo --- dotnet/src/webdriver/BiDi/Communication/Broker.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/Communication/Broker.cs b/dotnet/src/webdriver/BiDi/Communication/Broker.cs index 35328d8dd18f7..64f82aa59499f 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Broker.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Broker.cs @@ -37,7 +37,7 @@ public sealed class Broker : IAsyncDisposable private readonly BiDi _bidi; private readonly ITransport _transport; - private readonly ConcurrentDictionary> _pendingCommands = new(); + private readonly ConcurrentDictionary _pendingCommands = new(); private readonly BlockingCollection<(string Method, EventArgs Params)> _pendingEvents = []; private readonly Dictionary _eventTypesMap = []; @@ -147,7 +147,7 @@ public async Task ExecuteCommandAsync(TCommand comma var timeout = options?.Timeout ?? TimeSpan.FromSeconds(30); using var cts = new CancellationTokenSource(timeout); cts.Token.Register(() => tcs.TrySetCanceled(cts.Token)); - var commandInfo = new CommandInfo(command.Id, tcs, jsonResultTypeInfo); + var commandInfo = new CommandInfo(command.Id, tcs, jsonResultTypeInfo); _pendingCommands[command.Id] = commandInfo; var data = JsonSerializer.SerializeToUtf8Bytes(command, jsonCommandTypeInfo); @@ -345,12 +345,11 @@ private void ProcessReceivedMessage(byte[]? data) } } - class CommandInfo(long id, TaskCompletionSource taskCompletionSource, JsonTypeInfo jsonResultTypeInfo) - where TResult : EmptyResult + class CommandInfo(long id, TaskCompletionSource taskCompletionSource, JsonTypeInfo jsonResultTypeInfo) { public long Id { get; } = id; - public TaskCompletionSource TaskCompletionSource { get; } = taskCompletionSource; + public TaskCompletionSource TaskCompletionSource { get; } = taskCompletionSource; public JsonTypeInfo JsonResultTypeInfo { get; } = jsonResultTypeInfo; }; From 82b1a6bec3530e9271adbe5e8a62c5ece8008b66 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 10 Oct 2025 14:00:32 +0300 Subject: [PATCH 6/6] Concrete EventArgs --- dotnet/src/webdriver/BiDi/Communication/EventHandler.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs b/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs index 21fccc4a73542..16e95beec1265 100644 --- a/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs +++ b/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs @@ -29,7 +29,7 @@ public abstract class EventHandler(string eventName, IEnumerable? Contexts { get; } = contexts; - public abstract ValueTask InvokeAsync(object args); + public abstract ValueTask InvokeAsync(EventArgs args); } internal class AsyncEventHandler(string eventName, Func func, IEnumerable? contexts = null) @@ -37,7 +37,7 @@ internal class AsyncEventHandler(string eventName, Func _func = func; - public override async ValueTask InvokeAsync(object args) + public override async ValueTask InvokeAsync(EventArgs args) { await _func((TEventArgs)args).ConfigureAwait(false); } @@ -48,7 +48,7 @@ internal class SyncEventHandler(string eventName, Action { private readonly Action _action = action; - public override ValueTask InvokeAsync(object args) + public override ValueTask InvokeAsync(EventArgs args) { _action((TEventArgs)args);