Skip to content

Commit fb859ad

Browse files
authored
[dotnet] [bidi] Stateful converters with hydration (#16670)
1 parent cfd57e3 commit fb859ad

40 files changed

+466
-338
lines changed

dotnet/src/webdriver/BiDi/BiDi.cs

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,29 @@ public sealed class BiDi : IAsyncDisposable
3131
{
3232
private readonly ConcurrentDictionary<Type, Module> _modules = new();
3333

34+
private readonly JsonSerializerOptions _jsonOptions;
35+
3436
private BiDi(string url)
3537
{
3638
var uri = new Uri(url);
3739

3840
Broker = new Broker(this, uri);
41+
42+
_jsonOptions = new JsonSerializerOptions
43+
{
44+
PropertyNameCaseInsensitive = true,
45+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
46+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
47+
48+
Converters =
49+
{
50+
new DateTimeOffsetConverter(),
51+
}
52+
};
3953
}
4054

55+
private Broker Broker { get; }
56+
4157
internal Session.SessionModule SessionModule => AsModule<Session.SessionModule>();
4258

4359
public BrowsingContext.BrowsingContextModule BrowsingContext => AsModule<BrowsingContext.BrowsingContextModule>();
@@ -85,35 +101,6 @@ public async ValueTask DisposeAsync()
85101

86102
public T AsModule<T>() where T : Module, new()
87103
{
88-
return (T)_modules.GetOrAdd(typeof(T), _ => Module.Create<T>(this, Broker, GetJsonOptions()));
89-
}
90-
91-
private Broker Broker { get; }
92-
93-
private JsonSerializerOptions GetJsonOptions()
94-
{
95-
return new JsonSerializerOptions
96-
{
97-
PropertyNameCaseInsensitive = true,
98-
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
99-
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
100-
101-
// BiDi returns special numbers such as "NaN" as strings
102-
// Additionally, -0 is returned as a string "-0"
103-
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals | JsonNumberHandling.AllowReadingFromString,
104-
Converters =
105-
{
106-
new BrowsingContextConverter(this),
107-
new BrowserUserContextConverter(this),
108-
new CollectorConverter(this),
109-
new InterceptConverter(this),
110-
new HandleConverter(this),
111-
new InternalIdConverter(this),
112-
new PreloadScriptConverter(this),
113-
new RealmConverter(this),
114-
new DateTimeOffsetConverter(),
115-
new WebExtensionConverter(this),
116-
}
117-
};
104+
return (T)_modules.GetOrAdd(typeof(T), _ => Module.Create<T>(this, Broker, _jsonOptions));
118105
}
119106
}

dotnet/src/webdriver/BiDi/Broker.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ private void ProcessReceivedMessage(byte[]? data)
304304
{
305305
var eventArgs = (EventArgs)JsonSerializer.Deserialize(ref paramsReader, eventInfo)!;
306306

307+
eventArgs.BiDi = _bidi;
308+
307309
var messageEvent = (method, eventArgs);
308310
_pendingEvents.Add(messageEvent);
309311
}

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// under the License.
1818
// </copyright>
1919

20+
using OpenQA.Selenium.BiDi.Json.Converters;
2021
using System.Text.Json;
2122
using System.Text.Json.Serialization;
2223
using System.Threading.Tasks;
@@ -78,9 +79,17 @@ public async Task<SetDownloadBehaviorResult> SetDownloadBehaviorDeniedAsync(SetD
7879
return await Broker.ExecuteCommandAsync(new SetDownloadBehaviorCommand(@params), options, _jsonContext.SetDownloadBehaviorCommand, _jsonContext.SetDownloadBehaviorResult).ConfigureAwait(false);
7980
}
8081

81-
protected override void Initialize(JsonSerializerOptions options)
82+
protected override void Initialize(JsonSerializerOptions jsonSerializerOptions)
8283
{
83-
_jsonContext = new BrowserJsonSerializerContext(options);
84+
var browserOptions = new JsonSerializerOptions(jsonSerializerOptions)
85+
{
86+
Converters =
87+
{
88+
new BrowserUserContextConverter(BiDi),
89+
}
90+
};
91+
92+
_jsonContext = new BrowserJsonSerializerContext(browserOptions);
8493
}
8594
}
8695

@@ -96,4 +105,5 @@ protected override void Initialize(JsonSerializerOptions options)
96105
[JsonSerializable(typeof(GetClientWindowsResult))]
97106
[JsonSerializable(typeof(SetDownloadBehaviorCommand))]
98107
[JsonSerializable(typeof(SetDownloadBehaviorResult))]
108+
99109
internal partial class BrowserJsonSerializerContext : JsonSerializerContext;

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

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,45 +18,32 @@
1818
// </copyright>
1919

2020
using System;
21-
using System.Threading.Tasks;
21+
using System.Text.Json.Serialization;
2222

2323
namespace OpenQA.Selenium.BiDi.Browser;
2424

25-
public sealed class UserContext : IEquatable<UserContext>, IAsyncDisposable
25+
public sealed record UserContext
2626
{
27-
private readonly BiDi _bidi;
28-
29-
internal UserContext(BiDi bidi, string id)
30-
{
31-
_bidi = bidi;
32-
Id = id;
33-
}
34-
35-
internal string Id { get; }
36-
37-
public Task RemoveAsync()
27+
public UserContext(BiDi bidi, string id)
28+
: this(id)
3829
{
39-
return _bidi.Browser.RemoveUserContextAsync(this);
30+
BiDi = bidi ?? throw new ArgumentNullException(nameof(bidi));
4031
}
4132

42-
public async ValueTask DisposeAsync()
33+
[JsonConstructor]
34+
internal UserContext(string id)
4335
{
44-
await RemoveAsync().ConfigureAwait(false);
45-
}
46-
47-
public bool Equals(UserContext? other)
48-
{
49-
return other is not null && string.Equals(Id, other.Id, StringComparison.Ordinal);
36+
Id = id;
5037
}
5138

39+
internal string Id { get; }
5240

53-
public override bool Equals(object? obj)
54-
{
55-
return Equals(obj as UserContext);
56-
}
41+
private BiDi? _bidi;
5742

58-
public override int GetHashCode()
43+
[JsonIgnore]
44+
public BiDi BiDi
5945
{
60-
return StringComparer.Ordinal.GetHashCode(Id);
46+
get => _bidi ?? throw new InvalidOperationException($"{nameof(BiDi)} instance has not been hydrated.");
47+
internal set => _bidi = value;
6148
}
6249
}

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,15 @@ namespace OpenQA.Selenium.BiDi.BrowsingContext;
2626

2727
public sealed class BrowsingContext
2828
{
29-
internal BrowsingContext(BiDi bidi, string id)
29+
public BrowsingContext(BiDi bidi, string id)
30+
: this(id)
31+
{
32+
BiDi = bidi ?? throw new ArgumentNullException(nameof(bidi));
33+
}
34+
35+
[JsonConstructor]
36+
internal BrowsingContext(string id)
3037
{
31-
BiDi = bidi;
3238
Id = id;
3339
}
3440

@@ -40,8 +46,14 @@ internal BrowsingContext(BiDi bidi, string id)
4046

4147
internal string Id { get; }
4248

49+
private BiDi? _bidi;
50+
4351
[JsonIgnore]
44-
public BiDi BiDi { get; }
52+
public BiDi BiDi
53+
{
54+
get => _bidi ?? throw new InvalidOperationException($"{nameof(BiDi)} instance has not been hydrated.");
55+
internal set => _bidi = value;
56+
}
4557

4658
[JsonIgnore]
4759
public BrowsingContextLogModule Log => _logModule ?? Interlocked.CompareExchange(ref _logModule, new BrowsingContextLogModule(this, BiDi.Log), null) ?? _logModule;

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// under the License.
1818
// </copyright>
1919

20+
using OpenQA.Selenium.BiDi.Json.Converters;
2021
using System;
2122
using System.Text.Json;
2223
using System.Text.Json.Serialization;
@@ -252,9 +253,20 @@ public async Task<Subscription> OnUserPromptClosedAsync(Action<UserPromptClosedE
252253
return await Broker.SubscribeAsync("browsingContext.userPromptClosed", handler, options, _jsonContext.UserPromptClosedEventArgs).ConfigureAwait(false);
253254
}
254255

255-
protected override void Initialize(JsonSerializerOptions options)
256+
protected override void Initialize(JsonSerializerOptions jsonSerializerOptions)
256257
{
257-
_jsonContext = new BrowsingContextJsonSerializerContext(options);
258+
var browsingContextOptions = new JsonSerializerOptions(jsonSerializerOptions)
259+
{
260+
Converters =
261+
{
262+
new BrowsingContextConverter(BiDi),
263+
new InternalIdConverter(BiDi),
264+
new HandleConverter(BiDi),
265+
new BrowserUserContextConverter(BiDi),
266+
}
267+
};
268+
269+
_jsonContext = new BrowsingContextJsonSerializerContext(browsingContextOptions);
258270
}
259271
}
260272

@@ -292,4 +304,5 @@ protected override void Initialize(JsonSerializerOptions options)
292304
[JsonSerializable(typeof(NavigationInfo))]
293305
[JsonSerializable(typeof(UserPromptOpenedEventArgs))]
294306
[JsonSerializable(typeof(UserPromptClosedEventArgs))]
307+
295308
internal partial class BrowsingContextJsonSerializerContext : JsonSerializerContext;

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

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,52 +25,58 @@ namespace OpenQA.Selenium.BiDi.BrowsingContext;
2525

2626
public sealed class BrowsingContextNetworkModule(BrowsingContext context, NetworkModule networkModule)
2727
{
28-
public async Task<Intercept> InterceptRequestAsync(Func<InterceptedRequest, Task> handler, InterceptRequestOptions? options = null)
28+
public async Task<Interception> InterceptRequestAsync(Func<InterceptedRequest, Task> handler, InterceptRequestOptions? options = null)
2929
{
3030
AddInterceptOptions addInterceptOptions = new(options)
3131
{
3232
Contexts = [context]
3333
};
3434

35-
var intercept = await networkModule.AddInterceptAsync([InterceptPhase.BeforeRequestSent], addInterceptOptions).ConfigureAwait(false);
35+
var interceptResult = await networkModule.AddInterceptAsync([InterceptPhase.BeforeRequestSent], addInterceptOptions).ConfigureAwait(false);
3636

37-
await intercept.OnBeforeRequestSentAsync(
37+
Interception interception = new(context.BiDi, interceptResult.Intercept);
38+
39+
await interception.OnBeforeRequestSentAsync(
3840
async req => await handler(new(req.BiDi, req.Context, req.IsBlocked, req.Navigation, req.RedirectCount, req.Request, req.Timestamp, req.Initiator, req.Intercepts)),
3941
new() { Contexts = [context] }).ConfigureAwait(false);
4042

41-
return intercept;
43+
return interception;
4244
}
4345

44-
public async Task<Intercept> InterceptResponseAsync(Func<InterceptedResponse, Task> handler, InterceptResponseOptions? options = null)
46+
public async Task<Interception> InterceptResponseAsync(Func<InterceptedResponse, Task> handler, InterceptResponseOptions? options = null)
4547
{
4648
AddInterceptOptions addInterceptOptions = new(options)
4749
{
4850
Contexts = [context]
4951
};
5052

51-
var intercept = await networkModule.AddInterceptAsync([InterceptPhase.ResponseStarted], addInterceptOptions).ConfigureAwait(false);
53+
var interceptResult = await networkModule.AddInterceptAsync([InterceptPhase.ResponseStarted], addInterceptOptions).ConfigureAwait(false);
54+
55+
Interception interception = new(context.BiDi, interceptResult.Intercept);
5256

53-
await intercept.OnResponseStartedAsync(
57+
await interception.OnResponseStartedAsync(
5458
async res => await handler(new(res.BiDi, res.Context, res.IsBlocked, res.Navigation, res.RedirectCount, res.Request, res.Timestamp, res.Response, res.Intercepts)),
5559
new() { Contexts = [context] }).ConfigureAwait(false);
5660

57-
return intercept;
61+
return interception;
5862
}
5963

60-
public async Task<Intercept> InterceptAuthAsync(Func<InterceptedAuth, Task> handler, InterceptAuthOptions? options = null)
64+
public async Task<Interception> InterceptAuthAsync(Func<InterceptedAuth, Task> handler, InterceptAuthOptions? options = null)
6165
{
6266
AddInterceptOptions addInterceptOptions = new(options)
6367
{
6468
Contexts = [context]
6569
};
6670

67-
var intercept = await networkModule.AddInterceptAsync([InterceptPhase.AuthRequired], addInterceptOptions).ConfigureAwait(false);
71+
var interceptResult = await networkModule.AddInterceptAsync([InterceptPhase.AuthRequired], addInterceptOptions).ConfigureAwait(false);
72+
73+
Interception interception = new(context.BiDi, interceptResult.Intercept);
6874

69-
await intercept.OnAuthRequiredAsync(
75+
await interception.OnAuthRequiredAsync(
7076
async auth => await handler(new(auth.BiDi, auth.Context, auth.IsBlocked, auth.Navigation, auth.RedirectCount, auth.Request, auth.Timestamp, auth.Response, auth.Intercepts)),
7177
new() { Contexts = [context] }).ConfigureAwait(false);
7278

73-
return intercept;
79+
return interception;
7480
}
7581

7682
public Task<SetCacheBehaviorResult> SetCacheBehaviorAsync(CacheBehavior behavior, BrowsingContextSetCacheBehaviorOptions? options = null)

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// under the License.
1818
// </copyright>
1919

20+
using OpenQA.Selenium.BiDi.Json.Converters;
2021
using System.Text.Json;
2122
using System.Text.Json.Serialization;
2223
using System.Threading.Tasks;
@@ -92,9 +93,18 @@ public async Task<SetGeolocationOverrideResult> SetGeolocationPositionErrorOverr
9293
return await Broker.ExecuteCommandAsync(new SetGeolocationOverrideCommand(@params), options, _jsonContext.SetGeolocationOverrideCommand, _jsonContext.SetGeolocationOverrideResult).ConfigureAwait(false);
9394
}
9495

95-
protected override void Initialize(JsonSerializerOptions options)
96+
protected override void Initialize(JsonSerializerOptions jsonSerializerOptions)
9697
{
97-
_jsonContext = new EmulationJsonSerializerContext(options);
98+
var emulationOptions = new JsonSerializerOptions(jsonSerializerOptions)
99+
{
100+
Converters =
101+
{
102+
new BrowsingContextConverter(BiDi),
103+
new BrowserUserContextConverter(BiDi),
104+
}
105+
};
106+
107+
_jsonContext = new EmulationJsonSerializerContext(emulationOptions);
98108
}
99109
}
100110

@@ -112,4 +122,5 @@ protected override void Initialize(JsonSerializerOptions options)
112122
[JsonSerializable(typeof(SetScreenOrientationOverrideResult))]
113123
[JsonSerializable(typeof(SetGeolocationOverrideCommand))]
114124
[JsonSerializable(typeof(SetGeolocationOverrideResult))]
125+
115126
internal partial class EmulationJsonSerializerContext : JsonSerializerContext;

dotnet/src/webdriver/BiDi/EventArgs.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,21 @@
1717
// under the License.
1818
// </copyright>
1919

20+
using System;
2021
using System.Text.Json.Serialization;
2122

2223
namespace OpenQA.Selenium.BiDi;
2324

2425
public abstract record EventArgs
2526
{
27+
private BiDi? _bidi;
28+
2629
[JsonIgnore]
27-
public BiDi BiDi { get; internal set; }
30+
public BiDi BiDi
31+
{
32+
get => _bidi ?? throw new InvalidOperationException($"{nameof(BiDi)} instance has not been hydrated.");
33+
internal set => _bidi = value;
34+
}
2835
}
2936

3037
public abstract record BrowsingContextEventArgs(BrowsingContext.BrowsingContext Context)

0 commit comments

Comments
 (0)