1- // <copyright file="Broker .cs" company="Selenium Committers">
1+ // <copyright file="BiDiConnection .cs" company="Selenium Committers">
22// Licensed to the Software Freedom Conservancy (SFC) under one
33// or more contributor license agreements. See the NOTICE file
44// distributed with this work for additional information
1818// </copyright>
1919
2020using OpenQA . Selenium . BiDi . Communication . Json ;
21- using OpenQA . Selenium . BiDi . Communication . Json . Converters ;
2221using OpenQA . Selenium . BiDi . Communication . Transport ;
2322using OpenQA . Selenium . Internal . Logging ;
2423using System ;
2524using System . Collections . Concurrent ;
2625using System . Collections . Generic ;
26+ using System . Diagnostics . CodeAnalysis ;
2727using System . Linq ;
2828using System . Text . Json ;
2929using System . Text . Json . Serialization ;
30+ using System . Text . Json . Serialization . Metadata ;
3031using System . Threading ;
3132using System . Threading . Tasks ;
3233
3334namespace OpenQA . Selenium . BiDi . Communication ;
3435
35- public class Broker : IAsyncDisposable
36+ public class BiDiConnection : IAsyncDisposable
3637{
37- private readonly ILogger _logger = Log . GetLogger < Broker > ( ) ;
38-
39- private readonly BiDi _bidi ;
38+ private readonly ILogger _logger = Log . GetLogger < BiDiConnection > ( ) ;
4039 private readonly ITransport _transport ;
4140
4241 private readonly ConcurrentDictionary < int , TaskCompletionSource < JsonElement > > _pendingCommands = new ( ) ;
@@ -52,39 +51,42 @@ public class Broker : IAsyncDisposable
5251 private Task ? _eventEmitterTask ;
5352 private CancellationTokenSource ? _receiveMessagesCancellationTokenSource ;
5453
55- private readonly BiDiJsonSerializerContext _jsonSerializerContext ;
54+ private readonly JsonSerializerOptions _jsonSerializerContext ;
55+ private readonly Lazy < Modules . Session . SessionModule > _sessionModule ;
56+
57+ internal Modules . Session . SessionModule SessionModule => _sessionModule . Value ;
5658
57- internal Broker ( BiDi bidi , Uri url )
59+ public BiDiConnection ( Uri url )
5860 {
59- _bidi = bidi ;
6061 _transport = new WebSocketTransport ( url ) ;
62+ _jsonSerializerContext = BiDiConnectionJsonSerializerContext . CreateOptions ( ) ;
63+ _sessionModule = new Lazy < Modules . Session . SessionModule > ( ( ) => new Modules . Session . SessionModule ( this ) ) ;
64+ }
6165
62- var jsonSerializerOptions = new JsonSerializerOptions
66+ [ RequiresUnreferencedCode ( "Enables reflection-based JSON serialization. Use a source-generated JsonSerializerContext for AOT safety." ) ]
67+ [ RequiresDynamicCode ( "Enables reflection-based JSON serialization. Use a source-generated JsonSerializerContext for AOT safety." ) ]
68+ public void EnableReflectionBasedJson ( )
69+ {
70+ if ( _jsonSerializerContext . IsReadOnly )
6371 {
64- PropertyNameCaseInsensitive = true ,
65- PropertyNamingPolicy = JsonNamingPolicy . CamelCase ,
66- DefaultIgnoreCondition = JsonIgnoreCondition . WhenWritingNull ,
67-
68- // BiDi returns special numbers such as "NaN" as strings
69- // Additionally, -0 is returned as a string "-0"
70- NumberHandling = JsonNumberHandling . AllowNamedFloatingPointLiterals | JsonNumberHandling . AllowReadingFromString ,
71- Converters =
72- {
73- new BrowsingContextConverter ( _bidi ) ,
74- new BrowserUserContextConverter ( _bidi ) ,
75- new InterceptConverter ( _bidi ) ,
76- new RequestConverter ( _bidi ) ,
77- new HandleConverter ( _bidi ) ,
78- new InternalIdConverter ( _bidi ) ,
79- new PreloadScriptConverter ( _bidi ) ,
80- new RealmConverter ( _bidi ) ,
81-
82- new BiDiDateTimeOffsetConverter ( ) ,
83- new JsonStringEnumConverter ( JsonNamingPolicy . CamelCase ) ,
84- }
85- } ;
72+ throw new InvalidOperationException ( "Cannot add JSON serializer context after ConnectAsync has been called" ) ;
73+ }
74+
75+ _jsonSerializerContext . TypeInfoResolverChain . Add ( new DefaultJsonTypeInfoResolver ( ) ) ;
76+ }
77+
78+ public void AddSerializerContextAndConverters ( JsonSerializerContext context , IList < JsonConverter > ? converters = null )
79+ {
80+ if ( _jsonSerializerContext . IsReadOnly )
81+ {
82+ throw new InvalidOperationException ( "Cannot add JSON serializer context after ConnectAsync has been called" ) ;
83+ }
8684
87- _jsonSerializerContext = new BiDiJsonSerializerContext ( jsonSerializerOptions ) ;
85+ _jsonSerializerContext . TypeInfoResolverChain . Add ( context ) ;
86+ foreach ( JsonConverter converter in converters ?? [ ] )
87+ {
88+ _jsonSerializerContext . Converters . Add ( converter ) ;
89+ }
8890 }
8991
9092 public async Task ConnectAsync ( CancellationToken cancellationToken )
@@ -102,20 +104,31 @@ private async Task ReceiveMessagesAsync(CancellationToken cancellationToken)
102104 {
103105 var data = await _transport . ReceiveAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
104106
105- var message = JsonSerializer . Deserialize ( new ReadOnlySpan < byte > ( data ) , _jsonSerializerContext . Message ) ;
107+ var messageTypeInfo = ( JsonTypeInfo < Message > ) _jsonSerializerContext . GetTypeInfo ( typeof ( Message ) ) ;
108+ var message = JsonSerializer . Deserialize ( new ReadOnlySpan < byte > ( data ) , messageTypeInfo ) ;
106109
107110 switch ( message )
108111 {
109112 case MessageSuccess messageSuccess :
110113 _pendingCommands [ messageSuccess . Id ] . SetResult ( messageSuccess . Result ) ;
111114 _pendingCommands . TryRemove ( messageSuccess . Id , out _ ) ;
112115 break ;
116+
113117 case MessageEvent messageEvent :
114118 _pendingEvents . Add ( messageEvent ) ;
115119 break ;
116- case MessageError mesageError :
117- _pendingCommands [ mesageError . Id ] . SetException ( new BiDiException ( $ "{ mesageError . Error } : { mesageError . Message } ") ) ;
118- _pendingCommands . TryRemove ( mesageError . Id , out _ ) ;
120+
121+ case MessageError messageError :
122+ _pendingCommands [ messageError . Id ] . SetException ( new BiDiException ( $ "{ messageError . Error } : { messageError . Message } ") ) ;
123+ _pendingCommands . TryRemove ( messageError . Id , out _ ) ;
124+ break ;
125+
126+ default :
127+ if ( _logger . IsEnabled ( LogEventLevel . Warn ) )
128+ {
129+ _logger . Warn ( $ "Received invalid message type: { message } ") ;
130+ }
131+
119132 break ;
120133 }
121134 }
@@ -135,7 +148,7 @@ private async Task ProcessEventsAwaiterAsync()
135148 {
136149 var args = ( EventArgs ) result . Params . Deserialize ( handler . EventArgsType , _jsonSerializerContext ) ! ;
137150
138- args . BiDi = _bidi ;
151+ args . BiDi = this ;
139152
140153 // handle browsing context subscriber
141154 if ( handler . Contexts is not null && args is BrowsingContextEventArgs browsingContextEventArgs && handler . Contexts . Contains ( browsingContextEventArgs . Context ) )
@@ -204,7 +217,7 @@ public async Task<Subscription> SubscribeAsync<TEventArgs>(string eventName, Act
204217
205218 if ( options is BrowsingContextsSubscriptionOptions browsingContextsOptions )
206219 {
207- var subscribeResult = await _bidi . SessionModule . SubscribeAsync ( [ eventName ] , new ( ) { Contexts = browsingContextsOptions . Contexts } ) . ConfigureAwait ( false ) ;
220+ var subscribeResult = await SessionModule . SubscribeAsync ( [ eventName ] , new ( ) { Contexts = browsingContextsOptions . Contexts } ) . ConfigureAwait ( false ) ;
208221
209222 var eventHandler = new SyncEventHandler < TEventArgs > ( eventName , action , browsingContextsOptions ? . Contexts ) ;
210223
@@ -214,7 +227,7 @@ public async Task<Subscription> SubscribeAsync<TEventArgs>(string eventName, Act
214227 }
215228 else
216229 {
217- var subscribeResult = await _bidi . SessionModule . SubscribeAsync ( [ eventName ] ) . ConfigureAwait ( false ) ;
230+ var subscribeResult = await SessionModule . SubscribeAsync ( [ eventName ] ) . ConfigureAwait ( false ) ;
218231
219232 var eventHandler = new SyncEventHandler < TEventArgs > ( eventName , action ) ;
220233
@@ -231,7 +244,7 @@ public async Task<Subscription> SubscribeAsync<TEventArgs>(string eventName, Fun
231244
232245 if ( options is BrowsingContextsSubscriptionOptions browsingContextsOptions )
233246 {
234- var subscribeResult = await _bidi . SessionModule . SubscribeAsync ( [ eventName ] , new ( ) { Contexts = browsingContextsOptions . Contexts } ) . ConfigureAwait ( false ) ;
247+ var subscribeResult = await SessionModule . SubscribeAsync ( [ eventName ] , new ( ) { Contexts = browsingContextsOptions . Contexts } ) . ConfigureAwait ( false ) ;
235248
236249 var eventHandler = new AsyncEventHandler < TEventArgs > ( eventName , func , browsingContextsOptions . Contexts ) ;
237250
@@ -241,7 +254,7 @@ public async Task<Subscription> SubscribeAsync<TEventArgs>(string eventName, Fun
241254 }
242255 else
243256 {
244- var subscribeResult = await _bidi . SessionModule . SubscribeAsync ( [ eventName ] ) . ConfigureAwait ( false ) ;
257+ var subscribeResult = await SessionModule . SubscribeAsync ( [ eventName ] ) . ConfigureAwait ( false ) ;
245258
246259 var eventHandler = new AsyncEventHandler < TEventArgs > ( eventName , func ) ;
247260
@@ -259,22 +272,22 @@ public async Task UnsubscribeAsync(Modules.Session.Subscription subscription, Ev
259272
260273 if ( subscription is not null )
261274 {
262- await _bidi . SessionModule . UnsubscribeAsync ( [ subscription ] ) . ConfigureAwait ( false ) ;
275+ await SessionModule . UnsubscribeAsync ( [ subscription ] ) . ConfigureAwait ( false ) ;
263276 }
264277 else
265278 {
266279 if ( eventHandler . Contexts is not null )
267280 {
268281 if ( ! eventHandlers . Any ( h => eventHandler . Contexts . Equals ( h . Contexts ) ) && ! eventHandlers . Any ( h => h . Contexts is null ) )
269282 {
270- await _bidi . SessionModule . UnsubscribeAsync ( [ eventHandler . EventName ] , new ( ) { Contexts = eventHandler . Contexts } ) . ConfigureAwait ( false ) ;
283+ await SessionModule . UnsubscribeAsync ( [ eventHandler . EventName ] , new ( ) { Contexts = eventHandler . Contexts } ) . ConfigureAwait ( false ) ;
271284 }
272285 }
273286 else
274287 {
275288 if ( ! eventHandlers . Any ( h => h . Contexts is not null ) && ! eventHandlers . Any ( h => h . Contexts is null ) )
276289 {
277- await _bidi . SessionModule . UnsubscribeAsync ( [ eventHandler . EventName ] ) . ConfigureAwait ( false ) ;
290+ await SessionModule . UnsubscribeAsync ( [ eventHandler . EventName ] ) . ConfigureAwait ( false ) ;
278291 }
279292 }
280293 }
0 commit comments