Skip to content

Commit

Permalink
[dotnet] [bidi] Avoid polymorphic commands to be more statically easi…
Browse files Browse the repository at this point in the history
…er (#15202)
  • Loading branch information
nvborisenko authored Jan 31, 2025
1 parent 74feb5d commit 3de1d94
Show file tree
Hide file tree
Showing 51 changed files with 173 additions and 130 deletions.
17 changes: 10 additions & 7 deletions dotnet/src/webdriver/BiDi/Communication/Broker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class Broker : IAsyncDisposable
private readonly BiDi _bidi;
private readonly ITransport _transport;

private readonly ConcurrentDictionary<int, TaskCompletionSource<object>> _pendingCommands = new();
private readonly ConcurrentDictionary<int, TaskCompletionSource<JsonElement>> _pendingCommands = new();
private readonly BlockingCollection<MessageEvent> _pendingEvents = [];

private readonly ConcurrentDictionary<string, List<EventHandler>> _eventHandlers = new();
Expand Down Expand Up @@ -176,23 +176,26 @@ private async Task ProcessEventsAwaiterAsync()
}
}

public async Task<TResult> ExecuteCommandAsync<TResult>(Command command, CommandOptions? options)
public async Task<TResult> ExecuteCommandAsync<TCommand, TResult>(TCommand command, CommandOptions? options)
where TCommand: Command
{
var result = await ExecuteCommandCoreAsync(command, options).ConfigureAwait(false);
var jsonElement = await ExecuteCommandCoreAsync(command, options).ConfigureAwait(false);

return (TResult)((JsonElement)result).Deserialize(typeof(TResult), _jsonSerializerContext)!;
return (TResult)jsonElement.Deserialize(typeof(TResult), _jsonSerializerContext)!;
}

public async Task ExecuteCommandAsync(Command command, CommandOptions? options)
public async Task ExecuteCommandAsync<TCommand>(TCommand command, CommandOptions? options)
where TCommand: Command
{
await ExecuteCommandCoreAsync(command, options).ConfigureAwait(false);
}

private async Task<object> ExecuteCommandCoreAsync(Command command, CommandOptions? options)
private async Task<JsonElement> ExecuteCommandCoreAsync<TCommand>(TCommand command, CommandOptions? options)
where TCommand: Command
{
command.Id = Interlocked.Increment(ref _currentCommandId);

var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var tcs = new TaskCompletionSource<JsonElement>(TaskCreationOptions.RunContinuationsAsynchronously);

var timeout = options?.Timeout ?? TimeSpan.FromSeconds(30);

Expand Down
60 changes: 8 additions & 52 deletions dotnet/src/webdriver/BiDi/Communication/Command.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,67 +17,23 @@
// under the License.
// </copyright>

using System.Text.Json.Serialization;

#nullable enable

namespace OpenQA.Selenium.BiDi.Communication;

[JsonPolymorphic(TypeDiscriminatorPropertyName = "method")]

[JsonDerivedType(typeof(Modules.Session.StatusCommand), "session.status")]
[JsonDerivedType(typeof(Modules.Session.SubscribeCommand), "session.subscribe")]
[JsonDerivedType(typeof(Modules.Session.UnsubscribeCommand), "session.unsubscribe")]
[JsonDerivedType(typeof(Modules.Session.NewCommand), "session.new")]
[JsonDerivedType(typeof(Modules.Session.EndCommand), "session.end")]

[JsonDerivedType(typeof(Modules.Browser.CreateUserContextCommand), "browser.createUserContext")]
[JsonDerivedType(typeof(Modules.Browser.GetUserContextsCommand), "browser.getUserContexts")]
[JsonDerivedType(typeof(Modules.Browser.RemoveUserContextCommand), "browser.removeUserContext")]
[JsonDerivedType(typeof(Modules.Browser.CloseCommand), "browser.close")]

[JsonDerivedType(typeof(Modules.BrowsingContext.CreateCommand), "browsingContext.create")]
[JsonDerivedType(typeof(Modules.BrowsingContext.NavigateCommand), "browsingContext.navigate")]
[JsonDerivedType(typeof(Modules.BrowsingContext.ReloadCommand), "browsingContext.reload")]
[JsonDerivedType(typeof(Modules.BrowsingContext.TraverseHistoryCommand), "browsingContext.traverseHistory")]
[JsonDerivedType(typeof(Modules.BrowsingContext.LocateNodesCommand), "browsingContext.locateNodes")]
[JsonDerivedType(typeof(Modules.BrowsingContext.ActivateCommand), "browsingContext.activate")]
[JsonDerivedType(typeof(Modules.BrowsingContext.CaptureScreenshotCommand), "browsingContext.captureScreenshot")]
[JsonDerivedType(typeof(Modules.BrowsingContext.SetViewportCommand), "browsingContext.setViewport")]
[JsonDerivedType(typeof(Modules.BrowsingContext.GetTreeCommand), "browsingContext.getTree")]
[JsonDerivedType(typeof(Modules.BrowsingContext.PrintCommand), "browsingContext.print")]
[JsonDerivedType(typeof(Modules.BrowsingContext.HandleUserPromptCommand), "browsingContext.handleUserPrompt")]
[JsonDerivedType(typeof(Modules.BrowsingContext.CloseCommand), "browsingContext.close")]

[JsonDerivedType(typeof(Modules.Network.AddInterceptCommand), "network.addIntercept")]
[JsonDerivedType(typeof(Modules.Network.ContinueRequestCommand), "network.continueRequest")]
[JsonDerivedType(typeof(Modules.Network.ContinueResponseCommand), "network.continueResponse")]
[JsonDerivedType(typeof(Modules.Network.FailRequestCommand), "network.failRequest")]
[JsonDerivedType(typeof(Modules.Network.ProvideResponseCommand), "network.provideResponse")]
[JsonDerivedType(typeof(Modules.Network.ContinueWithAuthCommand), "network.continueWithAuth")]
[JsonDerivedType(typeof(Modules.Network.RemoveInterceptCommand), "network.removeIntercept")]
[JsonDerivedType(typeof(Modules.Network.SetCacheBehaviorCommand), "network.setCacheBehavior")]

[JsonDerivedType(typeof(Modules.Script.AddPreloadScriptCommand), "script.addPreloadScript")]
[JsonDerivedType(typeof(Modules.Script.RemovePreloadScriptCommand), "script.removePreloadScript")]
[JsonDerivedType(typeof(Modules.Script.EvaluateCommand), "script.evaluate")]
[JsonDerivedType(typeof(Modules.Script.CallFunctionCommand), "script.callFunction")]
[JsonDerivedType(typeof(Modules.Script.DisownCommand), "script.disown")]
[JsonDerivedType(typeof(Modules.Script.GetRealmsCommand), "script.getRealms")]

[JsonDerivedType(typeof(Modules.Input.PerformActionsCommand), "input.performActions")]
[JsonDerivedType(typeof(Modules.Input.ReleaseActionsCommand), "input.releaseActions")]

[JsonDerivedType(typeof(Modules.Storage.GetCookiesCommand), "storage.getCookies")]
[JsonDerivedType(typeof(Modules.Storage.DeleteCookiesCommand), "storage.deleteCookies")]
[JsonDerivedType(typeof(Modules.Storage.SetCookieCommand), "storage.setCookie")]

public abstract class Command
{
protected Command(string method)
{
Method = method;
}

public string Method { get; }

public int Id { get; internal set; }
}

internal abstract class Command<TCommandParameters>(TCommandParameters @params) : Command
internal abstract class Command<TCommandParameters>(TCommandParameters @params, string method) : Command(method)
where TCommandParameters : CommandParameters
{
public TCommandParameters Params { get; } = @params;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,31 +73,62 @@ namespace OpenQA.Selenium.BiDi.Communication.Json;
[JsonSerializable(typeof(Command))]
[JsonSerializable(typeof(Message))]

[JsonSerializable(typeof(Modules.Session.StatusCommand))]
[JsonSerializable(typeof(Modules.Session.StatusResult))]
[JsonSerializable(typeof(Modules.Session.NewCommand))]
[JsonSerializable(typeof(Modules.Session.NewResult))]
[JsonSerializable(typeof(Modules.Session.EndCommand))]
[JsonSerializable(typeof(Modules.Session.SubscribeCommand))]
[JsonSerializable(typeof(Modules.Session.UnsubscribeCommand))]

[JsonSerializable(typeof(Modules.Browser.CloseCommand), TypeInfoPropertyName = "Browser_CloseCommand")]
[JsonSerializable(typeof(Modules.Browser.CreateUserContextCommand))]
[JsonSerializable(typeof(Modules.Browser.GetUserContextsCommand))]
[JsonSerializable(typeof(Modules.Browser.GetUserContextsResult))]
[JsonSerializable(typeof(Modules.Browser.RemoveUserContextCommand))]
[JsonSerializable(typeof(Modules.Browser.UserContextInfo))]
[JsonSerializable(typeof(IReadOnlyList<Modules.Browser.UserContextInfo>))]


[JsonSerializable(typeof(Modules.BrowsingContext.ActivateCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.CaptureScreenshotCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.CaptureScreenshotResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.CloseCommand), TypeInfoPropertyName = "BrowsingContext_CloseCommand")]
[JsonSerializable(typeof(Modules.BrowsingContext.CreateCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.CreateResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.BrowsingContextInfo))]
[JsonSerializable(typeof(Modules.BrowsingContext.NavigateResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.NavigationInfo))]
[JsonSerializable(typeof(Modules.BrowsingContext.TraverseHistoryResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.LocateNodesResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.CaptureScreenshotResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.GetTreeCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.GetTreeResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.HandleUserPromptCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.LocateNodesCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.LocateNodesResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.NavigateCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.NavigateResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.PrintCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.PrintResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.ReloadCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.SetViewportCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.TraverseHistoryCommand))]
[JsonSerializable(typeof(Modules.BrowsingContext.TraverseHistoryResult))]
[JsonSerializable(typeof(Modules.BrowsingContext.BrowsingContextInfo))]
[JsonSerializable(typeof(Modules.BrowsingContext.NavigationInfo))]

[JsonSerializable(typeof(Modules.BrowsingContext.UserPromptOpenedEventArgs))]
[JsonSerializable(typeof(Modules.BrowsingContext.UserPromptClosedEventArgs))]
[JsonSerializable(typeof(Modules.BrowsingContext.Origin), TypeInfoPropertyName = "BrowsingContext_Origin")]

[JsonSerializable(typeof(Modules.Network.BytesValue.String), TypeInfoPropertyName = "Network_BytesValue_String")]
[JsonSerializable(typeof(Modules.Network.UrlPattern.String), TypeInfoPropertyName = "Network_UrlPattern_String")]
[JsonSerializable(typeof(Modules.Network.ContinueWithAuthParameters.Default), TypeInfoPropertyName = "Network_ContinueWithAuthParameters_Default")]
[JsonSerializable(typeof(Modules.Network.AddInterceptCommand))]
[JsonSerializable(typeof(Modules.Network.AddInterceptResult))]
[JsonSerializable(typeof(Modules.Network.ContinueRequestCommand))]
[JsonSerializable(typeof(Modules.Network.ContinueResponseCommand))]
[JsonSerializable(typeof(Modules.Network.ContinueWithAuthCommand))]
[JsonSerializable(typeof(Modules.Network.FailRequestCommand))]
[JsonSerializable(typeof(Modules.Network.ProvideResponseCommand))]
[JsonSerializable(typeof(Modules.Network.RemoveInterceptCommand))]
[JsonSerializable(typeof(Modules.Network.SetCacheBehaviorCommand))]

[JsonSerializable(typeof(Modules.Network.BeforeRequestSentEventArgs))]
[JsonSerializable(typeof(Modules.Network.ResponseStartedEventArgs))]
[JsonSerializable(typeof(Modules.Network.ResponseCompletedEventArgs))]
Expand All @@ -108,20 +139,32 @@ namespace OpenQA.Selenium.BiDi.Communication.Json;
[JsonSerializable(typeof(Modules.Script.LocalValue.String), TypeInfoPropertyName = "Script_LocalValue_String")]
[JsonSerializable(typeof(Modules.Script.Target.Realm), TypeInfoPropertyName = "Script_Target_Realm")]
[JsonSerializable(typeof(Modules.Script.Target.Context), TypeInfoPropertyName = "Script_Target_Context")]

[JsonSerializable(typeof(Modules.Script.AddPreloadScriptCommand))]
[JsonSerializable(typeof(Modules.Script.AddPreloadScriptResult))]
[JsonSerializable(typeof(Modules.Script.DisownCommand))]
[JsonSerializable(typeof(Modules.Script.CallFunctionCommand))]
[JsonSerializable(typeof(Modules.Script.EvaluateCommand))]
[JsonSerializable(typeof(Modules.Script.EvaluateResult))]
[JsonSerializable(typeof(Modules.Script.GetRealmsCommand))]
[JsonSerializable(typeof(Modules.Script.GetRealmsResult))]
[JsonSerializable(typeof(Modules.Script.RemovePreloadScriptCommand))]

[JsonSerializable(typeof(Modules.Script.MessageEventArgs))]
[JsonSerializable(typeof(Modules.Script.RealmDestroyedEventArgs))]
[JsonSerializable(typeof(IReadOnlyList<Modules.Script.RealmInfo>))]

[JsonSerializable(typeof(Modules.Log.Entry))]

[JsonSerializable(typeof(Modules.Storage.GetCookiesCommand))]
[JsonSerializable(typeof(Modules.Storage.GetCookiesResult))]
[JsonSerializable(typeof(Modules.Storage.DeleteCookiesResult))]
[JsonSerializable(typeof(Modules.Storage.SetCookieCommand))]
[JsonSerializable(typeof(Modules.Storage.SetCookieResult))]
[JsonSerializable(typeof(Modules.Storage.DeleteCookiesCommand))]
[JsonSerializable(typeof(Modules.Storage.DeleteCookiesResult))]

[JsonSerializable(typeof(Modules.Input.PerformActionsCommand))]
[JsonSerializable(typeof(Modules.Input.ReleaseActionsCommand))]
[JsonSerializable(typeof(Modules.Input.Pointer.Down), TypeInfoPropertyName = "Input_Pointer_Down")]
[JsonSerializable(typeof(Modules.Input.Pointer.Up), TypeInfoPropertyName = "Input_Pointer_Up")]
[JsonSerializable(typeof(Modules.Input.Pointer.Move), TypeInfoPropertyName = "Input_Pointer_Move")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ interface ITransport : IDisposable

Task<T> ReceiveAsJsonAsync<T>(JsonSerializerContext jsonSerializerContext, CancellationToken cancellationToken);

Task SendAsJsonAsync(Command command, JsonSerializerContext jsonSerializerContext, CancellationToken cancellationToken);
Task SendAsJsonAsync<TCommand>(TCommand command, JsonSerializerContext jsonSerializerContext, CancellationToken cancellationToken)
where TCommand : Command;
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ public async Task<T> ReceiveAsJsonAsync<T>(JsonSerializerContext jsonSerializerC
return (T)res!;
}

public async Task SendAsJsonAsync(Command command, JsonSerializerContext jsonSerializerContext, CancellationToken cancellationToken)
public async Task SendAsJsonAsync<TCommand>(TCommand command, JsonSerializerContext jsonSerializerContext, CancellationToken cancellationToken)
where TCommand : Command
{
var buffer = JsonSerializer.SerializeToUtf8Bytes(command, typeof(Command), jsonSerializerContext);
var buffer = JsonSerializer.SerializeToUtf8Bytes(command, typeof(TCommand), jsonSerializerContext);

await _socketSendSemaphoreSlim.WaitAsync(cancellationToken);

Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/webdriver/BiDi/Modules/Browser/BrowserModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ public async Task CloseAsync(CloseOptions? options = null)

public async Task<UserContextInfo> CreateUserContextAsync(CreateUserContextOptions? options = null)
{
return await Broker.ExecuteCommandAsync<UserContextInfo>(new CreateUserContextCommand(), options).ConfigureAwait(false);
return await Broker.ExecuteCommandAsync<CreateUserContextCommand, UserContextInfo>(new CreateUserContextCommand(), options).ConfigureAwait(false);
}

public async Task<GetUserContextsResult> GetUserContextsAsync(GetUserContextsOptions? options = null)
{
return await Broker.ExecuteCommandAsync<GetUserContextsResult>(new GetUserContextsCommand(), options).ConfigureAwait(false);
return await Broker.ExecuteCommandAsync<GetUserContextsCommand, GetUserContextsResult>(new GetUserContextsCommand(), options).ConfigureAwait(false);
}

public async Task RemoveUserContextAsync(UserContext userContext, RemoveUserContextOptions? options = null)
Expand Down
3 changes: 2 additions & 1 deletion dotnet/src/webdriver/BiDi/Modules/Browser/CloseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

namespace OpenQA.Selenium.BiDi.Modules.Browser;

internal class CloseCommand() : Command<CommandParameters>(CommandParameters.Empty);
internal class CloseCommand()
: Command<CommandParameters>(CommandParameters.Empty, "browser.close");

public record CloseOptions : CommandOptions;
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

namespace OpenQA.Selenium.BiDi.Modules.Browser;

internal class CreateUserContextCommand() : Command<CommandParameters>(CommandParameters.Empty);
internal class CreateUserContextCommand()
: Command<CommandParameters>(CommandParameters.Empty, "browser.createUserContext");

public record CreateUserContextOptions : CommandOptions;
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

namespace OpenQA.Selenium.BiDi.Modules.Browser;

internal class GetUserContextsCommand() : Command<CommandParameters>(CommandParameters.Empty);
internal class GetUserContextsCommand()
: Command<CommandParameters>(CommandParameters.Empty, "browser.getUserContexts");

public record GetUserContextsOptions : CommandOptions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

namespace OpenQA.Selenium.BiDi.Modules.Browser;

internal class RemoveUserContextCommand(RemoveUserContextCommandParameters @params) : Command<RemoveUserContextCommandParameters>(@params);
internal class RemoveUserContextCommand(RemoveUserContextCommandParameters @params)
: Command<RemoveUserContextCommandParameters>(@params, "browser.removeUserContext");

internal record RemoveUserContextCommandParameters(UserContext UserContext) : CommandParameters;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext;

internal class ActivateCommand(ActivateCommandParameters @params) : Command<ActivateCommandParameters>(@params);
internal class ActivateCommand(ActivateCommandParameters @params)
: Command<ActivateCommandParameters>(@params, "browsingContext.activate");

internal record ActivateCommandParameters(BrowsingContext Context) : CommandParameters;

Expand Down
Loading

0 comments on commit 3de1d94

Please sign in to comment.