diff --git a/dotnet/src/webdriver/BiDi/BiDi.cs b/dotnet/src/webdriver/BiDi/BiDi.cs index a6593ab29db86..c3a838f10d14e 100644 --- a/dotnet/src/webdriver/BiDi/BiDi.cs +++ b/dotnet/src/webdriver/BiDi/BiDi.cs @@ -85,7 +85,7 @@ public async ValueTask DisposeAsync() public T AsModule() where T : Module, new() { - return (T)_modules.GetOrAdd(typeof(T), _ => Module.Create(this, Broker, GetJsonOptions())); + return (T)_modules.GetOrAdd(typeof(T), _ => Module.Create(this, Broker)); } private Broker Broker { get; } @@ -94,25 +94,25 @@ private JsonSerializerOptions GetJsonOptions() { return new JsonSerializerOptions { - PropertyNameCaseInsensitive = true, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + //PropertyNameCaseInsensitive = true, + //PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + //DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // BiDi returns special numbers such as "NaN" as strings // Additionally, -0 is returned as a string "-0" NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals | JsonNumberHandling.AllowReadingFromString, Converters = { - new BrowsingContextConverter(this), - new BrowserUserContextConverter(this), - new CollectorConverter(this), - new InterceptConverter(this), - new HandleConverter(this), - new InternalIdConverter(this), - new PreloadScriptConverter(this), - new RealmConverter(this), + //new BrowsingContextConverter(), + //new BrowserUserContextConverter(), + //new CollectorConverter(), + //new InterceptConverter(), + //new HandleConverter(), + //new InternalIdConverter(), + //new PreloadScriptConverter(), + //new RealmConverter(), new DateTimeOffsetConverter(), - new WebExtensionConverter(this), + //new WebExtensionConverter(), } }; } diff --git a/dotnet/src/webdriver/BiDi/Broker.cs b/dotnet/src/webdriver/BiDi/Broker.cs index 6e74edcfb5522..fb7d960c6b350 100644 --- a/dotnet/src/webdriver/BiDi/Broker.cs +++ b/dotnet/src/webdriver/BiDi/Broker.cs @@ -279,6 +279,11 @@ private void ProcessReceivedMessage(byte[]? data) var commandResult = JsonSerializer.Deserialize(ref resultReader, command.JsonResultTypeInfo) ?? throw new JsonException("Remote end returned null command result in the 'result' property."); + if (commandResult is IBiDiHydratable bidiHydratable) + { + bidiHydratable.Hydrate(_bidi); + } + command.TaskCompletionSource.SetResult((EmptyResult)commandResult); } catch (Exception ex) @@ -304,6 +309,11 @@ private void ProcessReceivedMessage(byte[]? data) { var eventArgs = (EventArgs)JsonSerializer.Deserialize(ref paramsReader, eventInfo)!; + if (eventArgs is IBiDiHydratable bidiHydratable) + { + bidiHydratable.Hydrate(_bidi); + } + var messageEvent = (method, eventArgs); _pendingEvents.Add(messageEvent); } diff --git a/dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs b/dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs index a1427163d6e24..c043bbaabd5e2 100644 --- a/dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs +++ b/dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs @@ -17,7 +17,7 @@ // under the License. // -using System.Text.Json; +using OpenQA.Selenium.BiDi.Json.Converters; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -25,7 +25,7 @@ namespace OpenQA.Selenium.BiDi.Browser; public sealed class BrowserModule : Module { - private BrowserJsonSerializerContext _jsonContext = null!; + private static readonly BrowserJsonSerializerContext _jsonContext = BrowserJsonSerializerContext.Default; public async Task CloseAsync(CloseOptions? options = null) { @@ -77,11 +77,6 @@ public async Task SetDownloadBehaviorDeniedAsync(SetD return await Broker.ExecuteCommandAsync(new SetDownloadBehaviorCommand(@params), options, _jsonContext.SetDownloadBehaviorCommand, _jsonContext.SetDownloadBehaviorResult).ConfigureAwait(false); } - - protected override void Initialize(JsonSerializerOptions options) - { - _jsonContext = new BrowserJsonSerializerContext(options); - } } [JsonSerializable(typeof(CloseCommand))] @@ -96,4 +91,12 @@ protected override void Initialize(JsonSerializerOptions options) [JsonSerializable(typeof(GetClientWindowsResult))] [JsonSerializable(typeof(SetDownloadBehaviorCommand))] [JsonSerializable(typeof(SetDownloadBehaviorResult))] + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant +[JsonSourceGenerationOptions( + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + Converters = [typeof(DateTimeOffsetConverter)])] +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant internal partial class BrowserJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/src/webdriver/BiDi/Browser/UserContext.cs b/dotnet/src/webdriver/BiDi/Browser/UserContext.cs index 46066271be9e1..1c4dc029ed2f7 100644 --- a/dotnet/src/webdriver/BiDi/Browser/UserContext.cs +++ b/dotnet/src/webdriver/BiDi/Browser/UserContext.cs @@ -17,46 +17,10 @@ // under the License. // -using System; -using System.Threading.Tasks; +using OpenQA.Selenium.BiDi.Json.Converters; +using System.Text.Json.Serialization; namespace OpenQA.Selenium.BiDi.Browser; -public sealed class UserContext : IEquatable, IAsyncDisposable -{ - private readonly BiDi _bidi; - - internal UserContext(BiDi bidi, string id) - { - _bidi = bidi; - Id = id; - } - - internal string Id { get; } - - public Task RemoveAsync() - { - return _bidi.Browser.RemoveUserContextAsync(this); - } - - public async ValueTask DisposeAsync() - { - await RemoveAsync().ConfigureAwait(false); - } - - public bool Equals(UserContext? other) - { - return other is not null && string.Equals(Id, other.Id, StringComparison.Ordinal); - } - - - public override bool Equals(object? obj) - { - return Equals(obj as UserContext); - } - - public override int GetHashCode() - { - return StringComparer.Ordinal.GetHashCode(Id); - } -} +[JsonConverter(typeof(BrowserUserContextConverter))] +public sealed record UserContext(string Id); diff --git a/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContext.cs b/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContext.cs index 24fdc57b0b1bd..59e5c50597c2a 100644 --- a/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContext.cs +++ b/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContext.cs @@ -17,6 +17,7 @@ // under the License. // +using OpenQA.Selenium.BiDi.Json.Converters; using System; using System.Text.Json.Serialization; using System.Threading; @@ -24,11 +25,11 @@ namespace OpenQA.Selenium.BiDi.BrowsingContext; +[JsonConverter(typeof(BrowsingContextConverter))] public sealed class BrowsingContext { - internal BrowsingContext(BiDi bidi, string id) + internal BrowsingContext(string id) { - BiDi = bidi; Id = id; } @@ -40,8 +41,14 @@ internal BrowsingContext(BiDi bidi, string id) internal string Id { get; } + private BiDi? _bidi; + [JsonIgnore] - public BiDi BiDi { get; } + public BiDi BiDi + { + get => _bidi ?? throw new InvalidOperationException($"{nameof(BiDi)} instance has not been hydrated."); + internal set => _bidi = value; + } [JsonIgnore] public BrowsingContextLogModule Log => _logModule ?? Interlocked.CompareExchange(ref _logModule, new BrowsingContextLogModule(this, BiDi.Log), null) ?? _logModule; diff --git a/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs b/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs index 1e6b1ca4e3d70..b922e1cfa6faa 100644 --- a/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs +++ b/dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs @@ -17,8 +17,8 @@ // under the License. // +using OpenQA.Selenium.BiDi.Json.Converters; using System; -using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -26,7 +26,7 @@ namespace OpenQA.Selenium.BiDi.BrowsingContext; public sealed class BrowsingContextModule : Module { - private BrowsingContextJsonSerializerContext _jsonContext = null!; + private static readonly BrowsingContextJsonSerializerContext _jsonContext = BrowsingContextJsonSerializerContext.Default; public async Task CreateAsync(ContextType type, CreateOptions? options = null) { @@ -251,11 +251,6 @@ public async Task OnUserPromptClosedAsync(Action InterceptRequestAsync(Func handler, InterceptRequestOptions? options = null) + public async Task InterceptRequestAsync(Func handler, InterceptRequestOptions? options = null) { AddInterceptOptions addInterceptOptions = new(options) { Contexts = [context] }; - var intercept = await networkModule.AddInterceptAsync([InterceptPhase.BeforeRequestSent], addInterceptOptions).ConfigureAwait(false); + var interceptResult = await networkModule.AddInterceptAsync([InterceptPhase.BeforeRequestSent], addInterceptOptions).ConfigureAwait(false); - await intercept.OnBeforeRequestSentAsync( + Interception interception = new(context.BiDi, interceptResult.Intercept); + + await interception.OnBeforeRequestSentAsync( async req => await handler(new(req.BiDi, req.Context, req.IsBlocked, req.Navigation, req.RedirectCount, req.Request, req.Timestamp, req.Initiator, req.Intercepts)), new() { Contexts = [context] }).ConfigureAwait(false); - return intercept; + return interception; } - public async Task InterceptResponseAsync(Func handler, InterceptResponseOptions? options = null) + public async Task InterceptResponseAsync(Func handler, InterceptResponseOptions? options = null) { AddInterceptOptions addInterceptOptions = new(options) { Contexts = [context] }; - var intercept = await networkModule.AddInterceptAsync([InterceptPhase.ResponseStarted], addInterceptOptions).ConfigureAwait(false); + var interceptResult = await networkModule.AddInterceptAsync([InterceptPhase.ResponseStarted], addInterceptOptions).ConfigureAwait(false); + + Interception interception = new(context.BiDi, interceptResult.Intercept); - await intercept.OnResponseStartedAsync( + await interception.OnResponseStartedAsync( async res => await handler(new(res.BiDi, res.Context, res.IsBlocked, res.Navigation, res.RedirectCount, res.Request, res.Timestamp, res.Response, res.Intercepts)), new() { Contexts = [context] }).ConfigureAwait(false); - return intercept; + return interception; } - public async Task InterceptAuthAsync(Func handler, InterceptAuthOptions? options = null) + public async Task InterceptAuthAsync(Func handler, InterceptAuthOptions? options = null) { AddInterceptOptions addInterceptOptions = new(options) { Contexts = [context] }; - var intercept = await networkModule.AddInterceptAsync([InterceptPhase.AuthRequired], addInterceptOptions).ConfigureAwait(false); + var interceptResult = await networkModule.AddInterceptAsync([InterceptPhase.AuthRequired], addInterceptOptions).ConfigureAwait(false); + + Interception interception = new(context.BiDi, interceptResult.Intercept); - await intercept.OnAuthRequiredAsync( + await interception.OnAuthRequiredAsync( async auth => await handler(new(auth.BiDi, auth.Context, auth.IsBlocked, auth.Navigation, auth.RedirectCount, auth.Request, auth.Timestamp, auth.Response, auth.Intercepts)), new() { Contexts = [context] }).ConfigureAwait(false); - return intercept; + return interception; } public Task SetCacheBehaviorAsync(CacheBehavior behavior, BrowsingContextSetCacheBehaviorOptions? options = null) diff --git a/dotnet/src/webdriver/BiDi/BrowsingContext/CreateCommand.cs b/dotnet/src/webdriver/BiDi/BrowsingContext/CreateCommand.cs index 892e3cc3a3674..44bfd0385a830 100644 --- a/dotnet/src/webdriver/BiDi/BrowsingContext/CreateCommand.cs +++ b/dotnet/src/webdriver/BiDi/BrowsingContext/CreateCommand.cs @@ -43,4 +43,10 @@ public enum ContextType Window } -public sealed record CreateResult(BrowsingContext Context) : EmptyResult; +public sealed record CreateResult(BrowsingContext Context) : EmptyResult, IBiDiHydratable +{ + void IBiDiHydratable.Hydrate(BiDi bidi) + { + Context.BiDi = bidi; + } +} diff --git a/dotnet/src/webdriver/BiDi/BrowsingContext/GetTreeCommand.cs b/dotnet/src/webdriver/BiDi/BrowsingContext/GetTreeCommand.cs index 425cea68e0d5e..8798dd7d7ef8a 100644 --- a/dotnet/src/webdriver/BiDi/BrowsingContext/GetTreeCommand.cs +++ b/dotnet/src/webdriver/BiDi/BrowsingContext/GetTreeCommand.cs @@ -45,4 +45,13 @@ public sealed record BrowsingContextGetTreeOptions public long? MaxDepth { get; set; } } -public sealed record GetTreeResult(IReadOnlyList Contexts) : EmptyResult; +public sealed record GetTreeResult(IReadOnlyList Contexts) : EmptyResult, IBiDiHydratable +{ + void IBiDiHydratable.Hydrate(BiDi bidi) + { + foreach (var contextInfo in Contexts) + { + contextInfo.Context.BiDi = bidi; + } + } +} diff --git a/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs b/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs index e4993e9b6209e..1a5aee459b810 100644 --- a/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs +++ b/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs @@ -17,7 +17,7 @@ // under the License. // -using System.Text.Json; +using OpenQA.Selenium.BiDi.Json.Converters; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -25,7 +25,7 @@ namespace OpenQA.Selenium.BiDi.Emulation; public sealed class EmulationModule : Module { - private EmulationJsonSerializerContext _jsonContext = null!; + private static readonly EmulationJsonSerializerContext _jsonContext = EmulationJsonSerializerContext.Default; public async Task SetTimezoneOverrideAsync(string? timezone, SetTimezoneOverrideOptions? options = null) { @@ -91,11 +91,6 @@ public async Task SetGeolocationPositionErrorOverr return await Broker.ExecuteCommandAsync(new SetGeolocationOverrideCommand(@params), options, _jsonContext.SetGeolocationOverrideCommand, _jsonContext.SetGeolocationOverrideResult).ConfigureAwait(false); } - - protected override void Initialize(JsonSerializerOptions options) - { - _jsonContext = new EmulationJsonSerializerContext(options); - } } [JsonSerializable(typeof(SetTimezoneOverrideCommand))] @@ -112,4 +107,12 @@ protected override void Initialize(JsonSerializerOptions options) [JsonSerializable(typeof(SetScreenOrientationOverrideResult))] [JsonSerializable(typeof(SetGeolocationOverrideCommand))] [JsonSerializable(typeof(SetGeolocationOverrideResult))] + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant +[JsonSourceGenerationOptions( + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + Converters = [typeof(DateTimeOffsetConverter)])] +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant internal partial class EmulationJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/src/webdriver/BiDi/EventArgs.cs b/dotnet/src/webdriver/BiDi/EventArgs.cs index 2e1b59ec59e01..2d81c4914367c 100644 --- a/dotnet/src/webdriver/BiDi/EventArgs.cs +++ b/dotnet/src/webdriver/BiDi/EventArgs.cs @@ -17,14 +17,26 @@ // under the License. // +using System; using System.Text.Json.Serialization; namespace OpenQA.Selenium.BiDi; -public abstract record EventArgs +public abstract record EventArgs : IBiDiHydratable { + private BiDi? _bidi; + [JsonIgnore] - public BiDi BiDi { get; internal set; } + public BiDi BiDi + { + get => _bidi ?? throw new InvalidOperationException($"{nameof(BiDi)} instance has not been hydrated."); + internal set => _bidi = value; + } + + void IBiDiHydratable.Hydrate(BiDi bidi) + { + BiDi = bidi; + } } public abstract record BrowsingContextEventArgs(BrowsingContext.BrowsingContext Context) diff --git a/dotnet/src/webdriver/BiDi/IBiDiHydratable.cs b/dotnet/src/webdriver/BiDi/IBiDiHydratable.cs new file mode 100644 index 0000000000000..2e9775a380777 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/IBiDiHydratable.cs @@ -0,0 +1,25 @@ +// +// 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; + +public interface IBiDiHydratable +{ + internal void Hydrate(BiDi bidi); +} diff --git a/dotnet/src/webdriver/BiDi/Input/InputModule.cs b/dotnet/src/webdriver/BiDi/Input/InputModule.cs index fb6ded16381d4..293e33416cdd5 100644 --- a/dotnet/src/webdriver/BiDi/Input/InputModule.cs +++ b/dotnet/src/webdriver/BiDi/Input/InputModule.cs @@ -17,8 +17,8 @@ // under the License. // +using OpenQA.Selenium.BiDi.Json.Converters; using System.Collections.Generic; -using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -26,7 +26,7 @@ namespace OpenQA.Selenium.BiDi.Input; public sealed class InputModule : Module { - private InputJsonSerializerContext _jsonContext = null!; + private static readonly InputJsonSerializerContext _jsonContext = InputJsonSerializerContext.Default; public async Task PerformActionsAsync(BrowsingContext.BrowsingContext context, IEnumerable actions, PerformActionsOptions? options = null) { @@ -48,11 +48,6 @@ public async Task SetFilesAsync(BrowsingContext.BrowsingContext return await Broker.ExecuteCommandAsync(new SetFilesCommand(@params), options, _jsonContext.SetFilesCommand, _jsonContext.SetFilesResult).ConfigureAwait(false); } - - protected override void Initialize(JsonSerializerOptions options) - { - _jsonContext = new InputJsonSerializerContext(options); - } } [JsonSerializable(typeof(PerformActionsCommand))] @@ -65,4 +60,12 @@ protected override void Initialize(JsonSerializerOptions options) [JsonSerializable(typeof(IEnumerable))] [JsonSerializable(typeof(IEnumerable))] [JsonSerializable(typeof(IEnumerable))] + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant +[JsonSourceGenerationOptions( + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + Converters = [typeof(DateTimeOffsetConverter)])] +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant internal partial class InputJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/src/webdriver/BiDi/Json/Converters/BrowserUserContextConverter.cs b/dotnet/src/webdriver/BiDi/Json/Converters/BrowserUserContextConverter.cs index e660c9843f086..7c0576a25303e 100644 --- a/dotnet/src/webdriver/BiDi/Json/Converters/BrowserUserContextConverter.cs +++ b/dotnet/src/webdriver/BiDi/Json/Converters/BrowserUserContextConverter.cs @@ -26,18 +26,11 @@ namespace OpenQA.Selenium.BiDi.Json.Converters; internal class BrowserUserContextConverter : JsonConverter { - private readonly BiDi _bidi; - - public BrowserUserContextConverter(BiDi bidi) - { - _bidi = bidi; - } - public override UserContext? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new UserContext(_bidi, id!); + return new UserContext(id!); } public override void Write(Utf8JsonWriter writer, UserContext value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Json/Converters/BrowsingContextConverter.cs b/dotnet/src/webdriver/BiDi/Json/Converters/BrowsingContextConverter.cs index 208b2698ad01f..38a852aa70172 100644 --- a/dotnet/src/webdriver/BiDi/Json/Converters/BrowsingContextConverter.cs +++ b/dotnet/src/webdriver/BiDi/Json/Converters/BrowsingContextConverter.cs @@ -25,18 +25,11 @@ namespace OpenQA.Selenium.BiDi.Json.Converters; internal class BrowsingContextConverter : JsonConverter { - private readonly BiDi _bidi; - - public BrowsingContextConverter(BiDi bidi) - { - _bidi = bidi; - } - public override BrowsingContext.BrowsingContext? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new BrowsingContext.BrowsingContext(_bidi, id!); + return new BrowsingContext.BrowsingContext(id!); } public override void Write(Utf8JsonWriter writer, BrowsingContext.BrowsingContext value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Json/Converters/CollectorConverter.cs b/dotnet/src/webdriver/BiDi/Json/Converters/CollectorConverter.cs index 9f77d0f03fcbc..87209845e93f1 100644 --- a/dotnet/src/webdriver/BiDi/Json/Converters/CollectorConverter.cs +++ b/dotnet/src/webdriver/BiDi/Json/Converters/CollectorConverter.cs @@ -26,18 +26,11 @@ namespace OpenQA.Selenium.BiDi.Json.Converters; internal class CollectorConverter : JsonConverter { - private readonly BiDi _bidi; - - public CollectorConverter(BiDi bidi) - { - _bidi = bidi; - } - public override Collector? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new Collector(_bidi, id!); + return new Collector(id!); } public override void Write(Utf8JsonWriter writer, Collector value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Json/Converters/HandleConverter.cs b/dotnet/src/webdriver/BiDi/Json/Converters/HandleConverter.cs index 852a3d925cc00..38e15e516a274 100644 --- a/dotnet/src/webdriver/BiDi/Json/Converters/HandleConverter.cs +++ b/dotnet/src/webdriver/BiDi/Json/Converters/HandleConverter.cs @@ -26,18 +26,11 @@ namespace OpenQA.Selenium.BiDi.Json.Converters; internal class HandleConverter : JsonConverter { - private readonly BiDi _bidi; - - public HandleConverter(BiDi bidi) - { - _bidi = bidi; - } - public override Handle? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new Handle(_bidi, id!); + return new Handle(id!); } public override void Write(Utf8JsonWriter writer, Handle value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Json/Converters/InterceptConverter.cs b/dotnet/src/webdriver/BiDi/Json/Converters/InterceptConverter.cs index a72e6cfa96921..f4bd6f4a34650 100644 --- a/dotnet/src/webdriver/BiDi/Json/Converters/InterceptConverter.cs +++ b/dotnet/src/webdriver/BiDi/Json/Converters/InterceptConverter.cs @@ -26,18 +26,11 @@ namespace OpenQA.Selenium.BiDi.Json.Converters; internal class InterceptConverter : JsonConverter { - private readonly BiDi _bidi; - - public InterceptConverter(BiDi bidi) - { - _bidi = bidi; - } - public override Intercept? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new Intercept(_bidi, id!); + return new Intercept(id!); } public override void Write(Utf8JsonWriter writer, Intercept value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Json/Converters/InternalIdConverter.cs b/dotnet/src/webdriver/BiDi/Json/Converters/InternalIdConverter.cs index 6f14fa6571d64..98a7ca5d5c575 100644 --- a/dotnet/src/webdriver/BiDi/Json/Converters/InternalIdConverter.cs +++ b/dotnet/src/webdriver/BiDi/Json/Converters/InternalIdConverter.cs @@ -26,18 +26,11 @@ namespace OpenQA.Selenium.BiDi.Json.Converters; internal class InternalIdConverter : JsonConverter { - private readonly BiDi _bidi; - - public InternalIdConverter(BiDi bidi) - { - _bidi = bidi; - } - public override InternalId? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new InternalId(_bidi, id!); + return new InternalId(id!); } public override void Write(Utf8JsonWriter writer, InternalId value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Json/Converters/PreloadScriptConverter.cs b/dotnet/src/webdriver/BiDi/Json/Converters/PreloadScriptConverter.cs index 9a531f60f79ed..feae08b69321c 100644 --- a/dotnet/src/webdriver/BiDi/Json/Converters/PreloadScriptConverter.cs +++ b/dotnet/src/webdriver/BiDi/Json/Converters/PreloadScriptConverter.cs @@ -26,18 +26,11 @@ namespace OpenQA.Selenium.BiDi.Json.Converters; internal class PreloadScriptConverter : JsonConverter { - private readonly BiDi _bidi; - - public PreloadScriptConverter(BiDi bidi) - { - _bidi = bidi; - } - public override PreloadScript? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new PreloadScript(_bidi, id!); + return new PreloadScript(id!); } public override void Write(Utf8JsonWriter writer, PreloadScript value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Json/Converters/RealmConverter.cs b/dotnet/src/webdriver/BiDi/Json/Converters/RealmConverter.cs index 235c40b0363f4..7b8c28eb2eba7 100644 --- a/dotnet/src/webdriver/BiDi/Json/Converters/RealmConverter.cs +++ b/dotnet/src/webdriver/BiDi/Json/Converters/RealmConverter.cs @@ -26,18 +26,11 @@ namespace OpenQA.Selenium.BiDi.Json.Converters; internal class RealmConverter : JsonConverter { - private readonly BiDi _bidi; - - public RealmConverter(BiDi bidi) - { - _bidi = bidi; - } - public override Realm? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new Realm(_bidi, id!); + return new Realm(id!); } public override void Write(Utf8JsonWriter writer, Realm value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Json/Converters/WebExtensionConverter.cs b/dotnet/src/webdriver/BiDi/Json/Converters/WebExtensionConverter.cs index ec3bc5e9cb7dd..0c6a7f4cfde73 100644 --- a/dotnet/src/webdriver/BiDi/Json/Converters/WebExtensionConverter.cs +++ b/dotnet/src/webdriver/BiDi/Json/Converters/WebExtensionConverter.cs @@ -26,18 +26,11 @@ namespace OpenQA.Selenium.BiDi.Json.Converters; internal class WebExtensionConverter : JsonConverter { - private readonly BiDi _bidi; - - public WebExtensionConverter(BiDi bidi) - { - _bidi = bidi; - } - public override Extension? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new Extension(_bidi, id!); + return new Extension(id!); } public override void Write(Utf8JsonWriter writer, Extension value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Log/LogModule.cs b/dotnet/src/webdriver/BiDi/Log/LogModule.cs index 34ea0e1d66c4c..286fabd699e54 100644 --- a/dotnet/src/webdriver/BiDi/Log/LogModule.cs +++ b/dotnet/src/webdriver/BiDi/Log/LogModule.cs @@ -17,8 +17,8 @@ // under the License. // +using OpenQA.Selenium.BiDi.Json.Converters; using System; -using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -26,7 +26,7 @@ namespace OpenQA.Selenium.BiDi.Log; public sealed class LogModule : Module { - private LogJsonSerializerContext _jsonContext = null!; + private static readonly LogJsonSerializerContext _jsonContext = LogJsonSerializerContext.Default; public async Task OnEntryAddedAsync(Func handler, SubscriptionOptions? options = null) { @@ -37,11 +37,6 @@ public async Task OnEntryAddedAsync(Action handler, Subs { return await Broker.SubscribeAsync("log.entryAdded", handler, options, _jsonContext.LogEntry).ConfigureAwait(false); } - - protected override void Initialize(JsonSerializerOptions options) - { - _jsonContext = new LogJsonSerializerContext(options); - } } #region https://github.com/dotnet/runtime/issues/72604 Script.RemoteValue type dependency @@ -80,4 +75,12 @@ protected override void Initialize(JsonSerializerOptions options) #endregion [JsonSerializable(typeof(LogEntry))] + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant +[JsonSourceGenerationOptions( + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + Converters = [typeof(DateTimeOffsetConverter)])] +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant internal partial class LogJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/src/webdriver/BiDi/Module.cs b/dotnet/src/webdriver/BiDi/Module.cs index 4e90c2db7df67..e8c1a27efe6a7 100644 --- a/dotnet/src/webdriver/BiDi/Module.cs +++ b/dotnet/src/webdriver/BiDi/Module.cs @@ -17,8 +17,6 @@ // under the License. // -using System.Text.Json; - namespace OpenQA.Selenium.BiDi; public abstract class Module @@ -27,9 +25,7 @@ public abstract class Module protected Broker Broker { get; private set; } = null!; - protected abstract void Initialize(JsonSerializerOptions options); - - internal static TModule Create(BiDi bidi, Broker broker, JsonSerializerOptions jsonSerializerOptions) + internal static TModule Create(BiDi bidi, Broker broker) where TModule : Module, new() { TModule module = new() @@ -38,8 +34,6 @@ internal static TModule Create(BiDi bidi, Broker broker, JsonSerializer Broker = broker }; - module.Initialize(jsonSerializerOptions); - return module; } } diff --git a/dotnet/src/webdriver/BiDi/Network/Collector.cs b/dotnet/src/webdriver/BiDi/Network/Collector.cs index 0a1f56a5064a5..7caf28b83edc8 100644 --- a/dotnet/src/webdriver/BiDi/Network/Collector.cs +++ b/dotnet/src/webdriver/BiDi/Network/Collector.cs @@ -17,42 +17,10 @@ // under the License. // -using System; -using System.Threading.Tasks; +using OpenQA.Selenium.BiDi.Json.Converters; +using System.Text.Json.Serialization; namespace OpenQA.Selenium.BiDi.Network; -public sealed class Collector : IAsyncDisposable -{ - private readonly BiDi _bidi; - - internal Collector(BiDi bidi, string id) - { - _bidi = bidi; - Id = id; - } - - internal string Id { get; } - - public async Task RemoveAsync() - { - await _bidi.Network.RemoveDataCollectorAsync(this).ConfigureAwait(false); - } - - public async ValueTask DisposeAsync() - { - await RemoveAsync(); - } - - public override bool Equals(object? obj) - { - if (obj is Collector collectortObj) return collectortObj.Id == Id; - - return false; - } - - public override int GetHashCode() - { - return Id.GetHashCode(); - } -} +[JsonConverter(typeof(CollectorConverter))] +public sealed record Collector(string Id); diff --git a/dotnet/src/webdriver/BiDi/Network/Intercept.cs b/dotnet/src/webdriver/BiDi/Network/Intercept.cs index c266f791efaa3..dbb43aad377b9 100644 --- a/dotnet/src/webdriver/BiDi/Network/Intercept.cs +++ b/dotnet/src/webdriver/BiDi/Network/Intercept.cs @@ -17,108 +17,10 @@ // under the License. // -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using OpenQA.Selenium.BiDi.Json.Converters; +using System.Text.Json.Serialization; namespace OpenQA.Selenium.BiDi.Network; -public sealed class Intercept : IAsyncDisposable -{ - private readonly BiDi _bidi; - - internal Intercept(BiDi bidi, string id) - { - _bidi = bidi; - Id = id; - } - - internal string Id { get; } - - IList OnBeforeRequestSentSubscriptions { get; } = []; - IList OnResponseStartedSubscriptions { get; } = []; - IList OnAuthRequiredSubscriptions { get; } = []; - - public async Task RemoveAsync() - { - await _bidi.Network.RemoveInterceptAsync(this).ConfigureAwait(false); - - foreach (var subscription in OnBeforeRequestSentSubscriptions) - { - await subscription.UnsubscribeAsync().ConfigureAwait(false); - } - - foreach (var subscription in OnResponseStartedSubscriptions) - { - await subscription.UnsubscribeAsync().ConfigureAwait(false); - } - - foreach (var subscription in OnAuthRequiredSubscriptions) - { - await subscription.UnsubscribeAsync().ConfigureAwait(false); - } - } - - public async Task OnBeforeRequestSentAsync(Func handler, SubscriptionOptions? options = null) - { - var subscription = await _bidi.Network.OnBeforeRequestSentAsync(async args => await Filter(args, handler), options).ConfigureAwait(false); - - OnBeforeRequestSentSubscriptions.Add(subscription); - } - - public async Task OnResponseStartedAsync(Func handler, SubscriptionOptions? options = null) - { - var subscription = await _bidi.Network.OnResponseStartedAsync(async args => await Filter(args, handler), options).ConfigureAwait(false); - - OnResponseStartedSubscriptions.Add(subscription); - } - - public async Task OnAuthRequiredAsync(Func handler, SubscriptionOptions? options = null) - { - var subscription = await _bidi.Network.OnAuthRequiredAsync(async args => await Filter(args, handler), options).ConfigureAwait(false); - - OnAuthRequiredSubscriptions.Add(subscription); - } - - private async Task Filter(BeforeRequestSentEventArgs args, Func handler) - { - if (args.Intercepts?.Contains(this) is true && args.IsBlocked) - { - await handler(args).ConfigureAwait(false); - } - } - - private async Task Filter(ResponseStartedEventArgs args, Func handler) - { - if (args.Intercepts?.Contains(this) is true && args.IsBlocked) - { - await handler(args).ConfigureAwait(false); - } - } - - private async Task Filter(AuthRequiredEventArgs args, Func handler) - { - if (args.Intercepts?.Contains(this) is true && args.IsBlocked) - { - await handler(args).ConfigureAwait(false); - } - } - - public async ValueTask DisposeAsync() - { - await RemoveAsync(); - } - - public override bool Equals(object? obj) - { - if (obj is Intercept interceptObj) return interceptObj.Id == Id; - - return false; - } - - public override int GetHashCode() - { - return Id.GetHashCode(); - } -} +[JsonConverter(typeof(InterceptConverter))] +public sealed record Intercept(string Id); diff --git a/dotnet/src/webdriver/BiDi/Network/NetworkModule.HighLevel.cs b/dotnet/src/webdriver/BiDi/Network/NetworkModule.HighLevel.cs index 58b9dfc4cd091..54324e11cf8f2 100644 --- a/dotnet/src/webdriver/BiDi/Network/NetworkModule.HighLevel.cs +++ b/dotnet/src/webdriver/BiDi/Network/NetworkModule.HighLevel.cs @@ -19,37 +19,44 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace OpenQA.Selenium.BiDi.Network; public partial class NetworkModule { - public async Task InterceptRequestAsync(Func handler, InterceptRequestOptions? options = null) + public async Task InterceptRequestAsync(Func handler, InterceptRequestOptions? options = null) { - var intercept = await AddInterceptAsync([InterceptPhase.BeforeRequestSent], options).ConfigureAwait(false); + var interceptResult = await AddInterceptAsync([InterceptPhase.BeforeRequestSent], options).ConfigureAwait(false); - await intercept.OnBeforeRequestSentAsync(async req => await handler(new(req.BiDi, req.Context, req.IsBlocked, req.Navigation, req.RedirectCount, req.Request, req.Timestamp, req.Initiator, req.Intercepts))).ConfigureAwait(false); + Interception interception = new(BiDi, interceptResult.Intercept); - return intercept; + await interception.OnBeforeRequestSentAsync(async req => await handler(new(req.BiDi, req.Context, req.IsBlocked, req.Navigation, req.RedirectCount, req.Request, req.Timestamp, req.Initiator, req.Intercepts))).ConfigureAwait(false); + + return interception; } - public async Task InterceptResponseAsync(Func handler, InterceptResponseOptions? options = null) + public async Task InterceptResponseAsync(Func handler, InterceptResponseOptions? options = null) { - var intercept = await AddInterceptAsync([InterceptPhase.ResponseStarted], options).ConfigureAwait(false); + var interceptResult = await AddInterceptAsync([InterceptPhase.ResponseStarted], options).ConfigureAwait(false); + + Interception interception = new(BiDi, interceptResult.Intercept); - await intercept.OnResponseStartedAsync(async res => await handler(new(res.BiDi, res.Context, res.IsBlocked, res.Navigation, res.RedirectCount, res.Request, res.Timestamp, res.Response, res.Intercepts))).ConfigureAwait(false); + await interception.OnResponseStartedAsync(async res => await handler(new(res.BiDi, res.Context, res.IsBlocked, res.Navigation, res.RedirectCount, res.Request, res.Timestamp, res.Response, res.Intercepts))).ConfigureAwait(false); - return intercept; + return interception; } - public async Task InterceptAuthAsync(Func handler, InterceptAuthOptions? options = null) + public async Task InterceptAuthAsync(Func handler, InterceptAuthOptions? options = null) { - var intercept = await AddInterceptAsync([InterceptPhase.AuthRequired], options).ConfigureAwait(false); + var interceptResult = await AddInterceptAsync([InterceptPhase.AuthRequired], options).ConfigureAwait(false); + + Interception interception = new(BiDi, interceptResult.Intercept); - await intercept.OnAuthRequiredAsync(async auth => await handler(new(auth.BiDi, auth.Context, auth.IsBlocked, auth.Navigation, auth.RedirectCount, auth.Request, auth.Timestamp, auth.Response, auth.Intercepts))).ConfigureAwait(false); + await interception.OnAuthRequiredAsync(async auth => await handler(new(auth.BiDi, auth.Context, auth.IsBlocked, auth.Navigation, auth.RedirectCount, auth.Request, auth.Timestamp, auth.Response, auth.Intercepts))).ConfigureAwait(false); - return intercept; + return interception; } } @@ -120,3 +127,80 @@ public Task ContinueAsync(ContinueWithAuthCancelCredentialsOptions? options = nu return BiDi.Network.ContinueWithAuthAsync(Request.Request, options); } } + +public sealed record Interception(BiDi BiDi, Intercept Intercept) : IAsyncDisposable +{ + IList OnBeforeRequestSentSubscriptions { get; } = []; + IList OnResponseStartedSubscriptions { get; } = []; + IList OnAuthRequiredSubscriptions { get; } = []; + + public async Task RemoveAsync() + { + await BiDi.Network.RemoveInterceptAsync(Intercept).ConfigureAwait(false); + + foreach (var subscription in OnBeforeRequestSentSubscriptions) + { + await subscription.UnsubscribeAsync().ConfigureAwait(false); + } + + foreach (var subscription in OnResponseStartedSubscriptions) + { + await subscription.UnsubscribeAsync().ConfigureAwait(false); + } + + foreach (var subscription in OnAuthRequiredSubscriptions) + { + await subscription.UnsubscribeAsync().ConfigureAwait(false); + } + } + + public async Task OnBeforeRequestSentAsync(Func handler, SubscriptionOptions? options = null) + { + var subscription = await BiDi.Network.OnBeforeRequestSentAsync(async args => await Filter(args, handler), options).ConfigureAwait(false); + + OnBeforeRequestSentSubscriptions.Add(subscription); + } + + public async Task OnResponseStartedAsync(Func handler, SubscriptionOptions? options = null) + { + var subscription = await BiDi.Network.OnResponseStartedAsync(async args => await Filter(args, handler), options).ConfigureAwait(false); + + OnResponseStartedSubscriptions.Add(subscription); + } + + public async Task OnAuthRequiredAsync(Func handler, SubscriptionOptions? options = null) + { + var subscription = await BiDi.Network.OnAuthRequiredAsync(async args => await Filter(args, handler), options).ConfigureAwait(false); + + OnAuthRequiredSubscriptions.Add(subscription); + } + + private async Task Filter(BeforeRequestSentEventArgs args, Func handler) + { + if (args.Intercepts?.Contains(Intercept) is true && args.IsBlocked) + { + await handler(args).ConfigureAwait(false); + } + } + + private async Task Filter(ResponseStartedEventArgs args, Func handler) + { + if (args.Intercepts?.Contains(Intercept) is true && args.IsBlocked) + { + await handler(args).ConfigureAwait(false); + } + } + + private async Task Filter(AuthRequiredEventArgs args, Func handler) + { + if (args.Intercepts?.Contains(Intercept) is true && args.IsBlocked) + { + await handler(args).ConfigureAwait(false); + } + } + + public async ValueTask DisposeAsync() + { + await RemoveAsync().ConfigureAwait(false); + } +} diff --git a/dotnet/src/webdriver/BiDi/Network/NetworkModule.cs b/dotnet/src/webdriver/BiDi/Network/NetworkModule.cs index 74b874078e237..172e0943ad2bc 100644 --- a/dotnet/src/webdriver/BiDi/Network/NetworkModule.cs +++ b/dotnet/src/webdriver/BiDi/Network/NetworkModule.cs @@ -17,9 +17,9 @@ // under the License. // +using OpenQA.Selenium.BiDi.Json.Converters; using System; using System.Collections.Generic; -using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -27,7 +27,7 @@ namespace OpenQA.Selenium.BiDi.Network; public sealed partial class NetworkModule : Module { - private NetworkJsonSerializerContext _jsonContext = null!; + private static readonly NetworkJsonSerializerContext _jsonContext = NetworkJsonSerializerContext.Default; public async Task AddDataCollectorAsync(IEnumerable DataTypes, int MaxEncodedDataSize, AddDataCollectorOptions? options = null) { @@ -38,13 +38,11 @@ public async Task AddDataCollectorAsync(IEnumerable DataTyp return result.Collector; } - public async Task AddInterceptAsync(IEnumerable phases, AddInterceptOptions? options = null) + public async Task AddInterceptAsync(IEnumerable phases, AddInterceptOptions? options = null) { var @params = new AddInterceptParameters(phases, options?.Contexts, options?.UrlPatterns); - var result = await Broker.ExecuteCommandAsync(new AddInterceptCommand(@params), options, _jsonContext.AddInterceptCommand, _jsonContext.AddInterceptResult).ConfigureAwait(false); - - return result.Intercept; + return await Broker.ExecuteCommandAsync(new AddInterceptCommand(@params), options, _jsonContext.AddInterceptCommand, _jsonContext.AddInterceptResult).ConfigureAwait(false); } public async Task RemoveDataCollectorAsync(Collector collector, RemoveDataCollectorOptions? options = null) @@ -176,11 +174,6 @@ public async Task OnAuthRequiredAsync(Action +using OpenQA.Selenium.BiDi.Json.Converters; using OpenQA.Selenium.BiDi.Permissions; -using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -26,7 +26,7 @@ namespace OpenQA.Selenium.BiDi.Extensions.Permissions; public class PermissionsModule : Module { - private PermissionsJsonSerializerContext _jsonContext = null!; + private static readonly PermissionsJsonSerializerContext _jsonContext = PermissionsJsonSerializerContext.Default; public async Task SetPermissionAsync(PermissionDescriptor desriptor, PermissionState state, string origin, SetPermissionOptions? options = null) { @@ -34,13 +34,16 @@ public async Task SetPermissionAsync(PermissionDescriptor d return await Broker.ExecuteCommandAsync(new SetPermissionCommand(@params), options, _jsonContext.SetPermissionCommand, _jsonContext.SetPermissionResult).ConfigureAwait(false); } - - protected override void Initialize(JsonSerializerOptions options) - { - _jsonContext = new PermissionsJsonSerializerContext(options); - } } [JsonSerializable(typeof(SetPermissionCommand))] [JsonSerializable(typeof(SetPermissionResult))] + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant +[JsonSourceGenerationOptions( + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + Converters = [typeof(DateTimeOffsetConverter)])] +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant internal partial class PermissionsJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/src/webdriver/BiDi/Script/AddPreloadScriptCommand.cs b/dotnet/src/webdriver/BiDi/Script/AddPreloadScriptCommand.cs index 4cb6e75b0e7f5..f01acd608504a 100644 --- a/dotnet/src/webdriver/BiDi/Script/AddPreloadScriptCommand.cs +++ b/dotnet/src/webdriver/BiDi/Script/AddPreloadScriptCommand.cs @@ -17,7 +17,6 @@ // under the License. // -using OpenQA.Selenium.Internal; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/dotnet/src/webdriver/BiDi/Script/Handle.cs b/dotnet/src/webdriver/BiDi/Script/Handle.cs index 5b5e4f63ede08..5be7d5a8ab611 100644 --- a/dotnet/src/webdriver/BiDi/Script/Handle.cs +++ b/dotnet/src/webdriver/BiDi/Script/Handle.cs @@ -17,17 +17,10 @@ // under the License. // -namespace OpenQA.Selenium.BiDi.Script; - -public sealed class Handle -{ - private readonly BiDi _bidi; +using OpenQA.Selenium.BiDi.Json.Converters; +using System.Text.Json.Serialization; - public Handle(BiDi bidi, string id) - { - _bidi = bidi; - Id = id; - } +namespace OpenQA.Selenium.BiDi.Script; - public string Id { get; } -} +[JsonConverter(typeof(HandleConverter))] +public sealed record Handle(string Id); diff --git a/dotnet/src/webdriver/BiDi/Script/InternalId.cs b/dotnet/src/webdriver/BiDi/Script/InternalId.cs index 2914c77c99a0c..260288d08bb3f 100644 --- a/dotnet/src/webdriver/BiDi/Script/InternalId.cs +++ b/dotnet/src/webdriver/BiDi/Script/InternalId.cs @@ -17,17 +17,10 @@ // under the License. // -namespace OpenQA.Selenium.BiDi.Script; - -public sealed class InternalId -{ - readonly BiDi _bidi; +using OpenQA.Selenium.BiDi.Json.Converters; +using System.Text.Json.Serialization; - public InternalId(BiDi bidi, string id) - { - _bidi = bidi; - Id = id; - } +namespace OpenQA.Selenium.BiDi.Script; - public string Id { get; } -} +[JsonConverter(typeof(InternalIdConverter))] +public sealed record InternalId(string Id); diff --git a/dotnet/src/webdriver/BiDi/Script/PreloadScript.cs b/dotnet/src/webdriver/BiDi/Script/PreloadScript.cs index 428825305709c..0b088b7023a2a 100644 --- a/dotnet/src/webdriver/BiDi/Script/PreloadScript.cs +++ b/dotnet/src/webdriver/BiDi/Script/PreloadScript.cs @@ -17,30 +17,10 @@ // under the License. // -using System; -using System.Threading.Tasks; +using OpenQA.Selenium.BiDi.Json.Converters; +using System.Text.Json.Serialization; namespace OpenQA.Selenium.BiDi.Script; -public sealed class PreloadScript : IAsyncDisposable -{ - private readonly BiDi _bidi; - - public PreloadScript(BiDi bidi, string id) - { - _bidi = bidi; - Id = id; - } - - public string Id { get; } - - public Task RemoveAsync() - { - return _bidi.Script.RemovePreloadScriptAsync(this); - } - - public async ValueTask DisposeAsync() - { - await RemoveAsync().ConfigureAwait(false); - } -} +[JsonConverter(typeof(PreloadScriptConverter))] +public sealed record PreloadScript(string Id); diff --git a/dotnet/src/webdriver/BiDi/Script/Realm.cs b/dotnet/src/webdriver/BiDi/Script/Realm.cs index b3fd6f776e0c7..0385a1747d4f5 100644 --- a/dotnet/src/webdriver/BiDi/Script/Realm.cs +++ b/dotnet/src/webdriver/BiDi/Script/Realm.cs @@ -17,17 +17,10 @@ // under the License. // -namespace OpenQA.Selenium.BiDi.Script; - -public sealed class Realm -{ - private readonly BiDi _bidi; +using OpenQA.Selenium.BiDi.Json.Converters; +using System.Text.Json.Serialization; - public Realm(BiDi bidi, string id) - { - _bidi = bidi; - Id = id; - } +namespace OpenQA.Selenium.BiDi.Script; - public string Id { get; } -} +[JsonConverter(typeof(RealmConverter))] +public sealed record Realm(string Id); diff --git a/dotnet/src/webdriver/BiDi/Script/ScriptModule.cs b/dotnet/src/webdriver/BiDi/Script/ScriptModule.cs index bd1354decc6e8..5abae98c24dc6 100644 --- a/dotnet/src/webdriver/BiDi/Script/ScriptModule.cs +++ b/dotnet/src/webdriver/BiDi/Script/ScriptModule.cs @@ -17,10 +17,10 @@ // under the License. // +using OpenQA.Selenium.BiDi.Json.Converters; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -28,7 +28,7 @@ namespace OpenQA.Selenium.BiDi.Script; public sealed class ScriptModule : Module { - private ScriptJsonSerializerContext _jsonContext = null!; + private static readonly ScriptJsonSerializerContext _jsonContext = ScriptJsonSerializerContext.Default; public async Task EvaluateAsync([StringSyntax(StringSyntaxConstants.JavaScript)] string expression, bool awaitPromise, Target target, EvaluateOptions? options = null) { @@ -115,11 +115,6 @@ public async Task OnRealmDestroyedAsync(Action +using OpenQA.Selenium.BiDi.Json.Converters; using System.Collections.Generic; -using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -26,7 +26,7 @@ namespace OpenQA.Selenium.BiDi.Session; internal sealed class SessionModule : Module { - private SessionJsonSerializerContext _jsonContext = null!; + private static readonly SessionJsonSerializerContext _jsonContext = SessionJsonSerializerContext.Default; public async Task StatusAsync(StatusOptions? options = null) { @@ -58,11 +58,6 @@ public async Task EndAsync(EndOptions? options = null) { return await Broker.ExecuteCommandAsync(new EndCommand(), options, _jsonContext.EndCommand, _jsonContext.EndResult).ConfigureAwait(false); } - - protected override void Initialize(JsonSerializerOptions options) - { - _jsonContext = new SessionJsonSerializerContext(options); - } } [JsonSerializable(typeof(StatusCommand))] @@ -75,4 +70,12 @@ protected override void Initialize(JsonSerializerOptions options) [JsonSerializable(typeof(SubscribeResult))] [JsonSerializable(typeof(UnsubscribeByIdCommand))] [JsonSerializable(typeof(UnsubscribeResult))] + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant +[JsonSourceGenerationOptions( + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + Converters = [typeof(DateTimeOffsetConverter)])] +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant internal partial class SessionJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/src/webdriver/BiDi/Storage/StorageModule.cs b/dotnet/src/webdriver/BiDi/Storage/StorageModule.cs index d2ef4c263f515..1997c6b19deca 100644 --- a/dotnet/src/webdriver/BiDi/Storage/StorageModule.cs +++ b/dotnet/src/webdriver/BiDi/Storage/StorageModule.cs @@ -17,7 +17,7 @@ // under the License. // -using System.Text.Json; +using OpenQA.Selenium.BiDi.Json.Converters; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -25,7 +25,7 @@ namespace OpenQA.Selenium.BiDi.Storage; public sealed class StorageModule : Module { - private StorageJsonSerializerContext _jsonContext = null!; + private static readonly StorageJsonSerializerContext _jsonContext = StorageJsonSerializerContext.Default; public async Task GetCookiesAsync(GetCookiesOptions? options = null) { @@ -47,11 +47,6 @@ public async Task SetCookieAsync(PartialCookie cookie, SetCooki return await Broker.ExecuteCommandAsync(new SetCookieCommand(@params), options, _jsonContext.SetCookieCommand, _jsonContext.SetCookieResult).ConfigureAwait(false); } - - protected override void Initialize(JsonSerializerOptions options) - { - _jsonContext = new StorageJsonSerializerContext(options); - } } [JsonSerializable(typeof(GetCookiesCommand))] @@ -60,4 +55,12 @@ protected override void Initialize(JsonSerializerOptions options) [JsonSerializable(typeof(SetCookieResult))] [JsonSerializable(typeof(DeleteCookiesCommand))] [JsonSerializable(typeof(DeleteCookiesResult))] + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant +[JsonSourceGenerationOptions( + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + Converters = [typeof(DateTimeOffsetConverter)])] +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant internal partial class StorageJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/src/webdriver/BiDi/WebExtension/Extension.cs b/dotnet/src/webdriver/BiDi/WebExtension/Extension.cs index c19beb1579e70..7e1b78f38fa8e 100644 --- a/dotnet/src/webdriver/BiDi/WebExtension/Extension.cs +++ b/dotnet/src/webdriver/BiDi/WebExtension/Extension.cs @@ -17,24 +17,10 @@ // under the License. // -using System.Threading.Tasks; +using OpenQA.Selenium.BiDi.Json.Converters; +using System.Text.Json.Serialization; namespace OpenQA.Selenium.BiDi.WebExtension; -public sealed class Extension -{ - private readonly BiDi _bidi; - - public Extension(BiDi bidi, string id) - { - _bidi = bidi; - Id = id; - } - - internal string Id { get; } - - public Task UninstallAsync(UninstallOptions? options = null) - { - return _bidi.WebExtension.UninstallAsync(this, options); - } -} +[JsonConverter(typeof(WebExtensionConverter))] +public sealed record Extension(string Id); diff --git a/dotnet/src/webdriver/BiDi/WebExtension/WebExtensionModule.cs b/dotnet/src/webdriver/BiDi/WebExtension/WebExtensionModule.cs index 86cfea9d1f9a5..e0a972d64b88a 100644 --- a/dotnet/src/webdriver/BiDi/WebExtension/WebExtensionModule.cs +++ b/dotnet/src/webdriver/BiDi/WebExtension/WebExtensionModule.cs @@ -17,7 +17,7 @@ // under the License. // -using System.Text.Json; +using OpenQA.Selenium.BiDi.Json.Converters; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -25,7 +25,7 @@ namespace OpenQA.Selenium.BiDi.WebExtension; public sealed class WebExtensionModule : Module { - private WebExtensionJsonSerializerContext _jsonContext = null!; + private static readonly WebExtensionJsonSerializerContext _jsonContext = WebExtensionJsonSerializerContext.Default; public async Task InstallAsync(ExtensionData extensionData, InstallOptions? options = null) { @@ -40,15 +40,18 @@ public async Task UninstallAsync(Extension extension, Uninstall return await Broker.ExecuteCommandAsync(new UninstallCommand(@params), options, _jsonContext.UninstallCommand, _jsonContext.UninstallResult).ConfigureAwait(false); } - - protected override void Initialize(JsonSerializerOptions options) - { - _jsonContext = new WebExtensionJsonSerializerContext(options); - } } [JsonSerializable(typeof(InstallCommand))] [JsonSerializable(typeof(InstallResult))] [JsonSerializable(typeof(UninstallCommand))] [JsonSerializable(typeof(UninstallResult))] + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant +[JsonSourceGenerationOptions( + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + Converters = [typeof(DateTimeOffsetConverter)])] +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant internal partial class WebExtensionJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/test/common/BiDi/Browser/BrowserTest.cs b/dotnet/test/common/BiDi/Browser/BrowserTest.cs index 39c4686b60124..44f4309850fce 100644 --- a/dotnet/test/common/BiDi/Browser/BrowserTest.cs +++ b/dotnet/test/common/BiDi/Browser/BrowserTest.cs @@ -53,7 +53,7 @@ public async Task CanRemoveUserContext() var userContext1 = await bidi.Browser.CreateUserContextAsync(); var userContext2 = await bidi.Browser.CreateUserContextAsync(); - await userContext2.UserContext.RemoveAsync(); + await bidi.Browser.RemoveUserContextAsync(userContext2.UserContext); var userContextsResult = await bidi.Browser.GetUserContextsAsync(); diff --git a/dotnet/test/common/BiDi/Network/NetworkTest.cs b/dotnet/test/common/BiDi/Network/NetworkTest.cs index 577e79a419515..25c46a33b8eca 100644 --- a/dotnet/test/common/BiDi/Network/NetworkTest.cs +++ b/dotnet/test/common/BiDi/Network/NetworkTest.cs @@ -31,7 +31,7 @@ public async Task CanAddDataCollector() { // Firefox doesn't like int.MaxValue as max encoded data size // invalid argument: Expected "maxEncodedDataSize" to be less than the max total data size available (200000000), got 2147483647 - await using var collector = await bidi.Network.AddDataCollectorAsync([DataType.Response], 200000000); + var collector = await bidi.Network.AddDataCollectorAsync([DataType.Response], 200000000); Assert.That(collector, Is.Not.Null); } @@ -226,7 +226,7 @@ public async Task CanGetData() { // Firefox doesn't like int.MaxValue as max encoded data size // invalid argument: Expected "maxEncodedDataSize" to be less than the max total data size available (200000000), got 2147483647 - await using var collector = await bidi.Network.AddDataCollectorAsync([DataType.Response], 200000000); + var collector = await bidi.Network.AddDataCollectorAsync([DataType.Response], 200000000); TaskCompletionSource responseBodyCompletionSource = new(); diff --git a/dotnet/test/common/BiDi/Script/ScriptCommandsTest.cs b/dotnet/test/common/BiDi/Script/ScriptCommandsTest.cs index e21b1748681c5..f12882bf7128b 100644 --- a/dotnet/test/common/BiDi/Script/ScriptCommandsTest.cs +++ b/dotnet/test/common/BiDi/Script/ScriptCommandsTest.cs @@ -159,7 +159,7 @@ public async Task CanRemovePreloadedScript() Assert.That(bar, Is.EqualTo(2)); - await preloadScript.Script.RemoveAsync(); + await bidi.Script.RemovePreloadScriptAsync(preloadScript.Script); var resultAfterRemoval = await context.Script.EvaluateAsync("window.bar", true, targetOptions: new() { Sandbox = "sandbox" }); diff --git a/dotnet/test/common/BiDi/WebExtension/WebExtensionTest.cs b/dotnet/test/common/BiDi/WebExtension/WebExtensionTest.cs index 5065abe2554be..19f1c8464ecbe 100644 --- a/dotnet/test/common/BiDi/WebExtension/WebExtensionTest.cs +++ b/dotnet/test/common/BiDi/WebExtension/WebExtensionTest.cs @@ -81,7 +81,7 @@ public async Task CanUninstallExtension() var result = await bidi.WebExtension.InstallAsync(new ExtensionPath(path)); - await result.Extension.UninstallAsync(); + await bidi.WebExtension.UninstallAsync(result.Extension); } private static string LocateRelativePath(string path)