1818// </copyright>
1919
2020using System ;
21+ using System . Collections ;
2122using System . Collections . Generic ;
2223using System . Linq ;
2324using System . Numerics ;
2425using System . Text . Json . Serialization ;
26+ using System . Text . RegularExpressions ;
2527
2628namespace OpenQA . Selenium . BiDi . Modules . Script ;
2729
@@ -41,10 +43,11 @@ namespace OpenQA.Selenium.BiDi.Modules.Script;
4143[ JsonDerivedType ( typeof ( SetLocalValue ) , "set" ) ]
4244public abstract record LocalValue
4345{
44- public static implicit operator LocalValue ( bool ? value ) { return value is bool b ? new BooleanLocalValue ( b ) : new NullLocalValue ( ) ; }
45- public static implicit operator LocalValue ( int ? value ) { return value is int i ? new NumberLocalValue ( i ) : new NullLocalValue ( ) ; }
46- public static implicit operator LocalValue ( double ? value ) { return value is double d ? new NumberLocalValue ( d ) : new NullLocalValue ( ) ; }
47- public static implicit operator LocalValue ( string ? value ) { return value is null ? new NullLocalValue ( ) : new StringLocalValue ( value ) ; }
46+ public static implicit operator LocalValue ( bool ? value ) { return ConvertFrom ( value ) ; }
47+ public static implicit operator LocalValue ( int ? value ) { return ConvertFrom ( value ) ; }
48+ public static implicit operator LocalValue ( double ? value ) { return ConvertFrom ( value ) ; }
49+ public static implicit operator LocalValue ( string ? value ) { return ConvertFrom ( value ) ; }
50+ public static implicit operator LocalValue ( DateTimeOffset ? value ) { return ConvertFrom ( value ) ; }
4851
4952 // TODO: Extend converting from types
5053 public static LocalValue ConvertFrom ( object ? value )
@@ -58,86 +61,222 @@ public static LocalValue ConvertFrom(object? value)
5861 return new NullLocalValue ( ) ;
5962
6063 case bool b :
61- return new BooleanLocalValue ( b ) ;
64+ return ConvertFrom ( b ) ;
6265
6366 case int i :
64- return new NumberLocalValue ( i ) ;
67+ return ConvertFrom ( i ) ;
6568
6669 case double d :
67- return new NumberLocalValue ( d ) ;
70+ return ConvertFrom ( d ) ;
6871
6972 case long l :
70- return new NumberLocalValue ( l ) ;
73+ return ConvertFrom ( l ) ;
7174
72- case DateTime dt :
73- return new DateLocalValue ( dt . ToString ( "o" ) ) ;
75+ case DateTimeOffset dt :
76+ return ConvertFrom ( dt ) ;
7477
7578 case BigInteger bigInt :
76- return new BigIntLocalValue ( bigInt . ToString ( ) ) ;
79+ return ConvertFrom ( bigInt ) ;
7780
7881 case string str :
79- return new StringLocalValue ( str ) ;
82+ return ConvertFrom ( str ) ;
8083
81- case IDictionary < string , string ? > dictionary :
84+ case Regex regex :
85+ return ConvertFrom ( regex ) ;
86+
87+ case { } when value . GetType ( ) . GetInterfaces ( )
88+ . Any ( i => i . IsGenericType && i . GetGenericTypeDefinition ( ) == typeof ( ISet < > ) ) :
8289 {
83- var bidiObject = new List < List < LocalValue > > ( dictionary . Count ) ;
84- foreach ( var item in dictionary )
85- {
86- bidiObject . Add ( [ new StringLocalValue ( item . Key ) , ConvertFrom ( item . Value ) ] ) ;
87- }
90+ IEnumerable set = ( IEnumerable ) value ;
8891
89- return new ObjectLocalValue ( bidiObject ) ;
90- }
92+ List < LocalValue > setValues = [ ] ;
9193
92- case IDictionary < string , object ? > dictionary :
93- {
94- var bidiObject = new List < List < LocalValue > > ( dictionary . Count ) ;
95- foreach ( var item in dictionary )
94+ foreach ( var obj in set )
9695 {
97- bidiObject . Add ( [ new StringLocalValue ( item . Key ) , ConvertFrom ( item . Value ) ] ) ;
96+ setValues . Add ( ConvertFrom ( obj ) ) ;
9897 }
9998
100- return new ObjectLocalValue ( bidiObject ) ;
99+ return new SetLocalValue ( setValues ) ;
101100 }
102101
103- case IDictionary < int , object ? > dictionary :
104- {
105- var bidiObject = new List < List < LocalValue > > ( dictionary . Count ) ;
106- foreach ( var item in dictionary )
107- {
108- bidiObject . Add ( [ ConvertFrom ( item . Key ) , ConvertFrom ( item . Value ) ] ) ;
109- }
102+ case IDictionary dictionary :
103+ return ConvertFrom ( dictionary ) ;
110104
111- return new MapLocalValue ( bidiObject ) ;
112- }
105+ case IEnumerable enumerable :
106+ return ConvertFrom ( enumerable ) ;
113107
114- case IEnumerable < object ? > list :
115- return new ArrayLocalValue ( list . Select ( ConvertFrom ) . ToList ( ) ) ;
108+ default :
109+ return ReflectionBasedConvertFrom ( value ) ;
110+ }
111+ }
116112
117- case object :
118- {
119- const System . Reflection . BindingFlags Flags = System . Reflection . BindingFlags . Public | System . Reflection . BindingFlags . Instance ;
113+ public static LocalValue ConvertFrom ( bool ? value )
114+ {
115+ if ( value is bool b )
116+ {
117+ return new BooleanLocalValue ( b ) ;
118+ }
120119
121- var properties = value . GetType ( ) . GetProperties ( Flags ) ;
120+ return new NullLocalValue ( ) ;
121+ }
122122
123- var values = new List < List < LocalValue > > ( properties . Length ) ;
124- foreach ( var property in properties )
125- {
126- object ? propertyValue ;
127- try
128- {
129- propertyValue = property . GetValue ( value ) ;
130- }
131- catch ( Exception ex )
132- {
133- throw new BiDiException ( $ "Could not retrieve property { property . Name } from { property . DeclaringType } ", ex ) ;
134- }
135- values . Add ( [ property. Name , ConvertFrom ( propertyValue ) ] ) ;
136- }
123+ public static LocalValue ConvertFrom ( int ? value )
124+ {
125+ if ( value is int b )
126+ {
127+ return new NumberLocalValue ( b ) ;
128+ }
137129
138- return new ObjectLocalValue ( values ) ;
139- }
130+ return new NullLocalValue ( ) ;
131+ }
132+
133+ public static LocalValue ConvertFrom ( double ? value )
134+ {
135+ if ( value is double b )
136+ {
137+ return new NumberLocalValue ( b ) ;
138+ }
139+
140+ return new NullLocalValue ( ) ;
141+ }
142+
143+ public static LocalValue ConvertFrom ( long ? value )
144+ {
145+ if ( value is long b )
146+ {
147+ return new NumberLocalValue ( b ) ;
148+ }
149+
150+ return new NullLocalValue ( ) ;
151+ }
152+
153+ public static LocalValue ConvertFrom ( string ? value )
154+ {
155+ if ( value is not null )
156+ {
157+ return new StringLocalValue ( value ) ;
158+ }
159+
160+ return new NullLocalValue ( ) ;
161+ }
162+
163+ /// <summary>
164+ /// Converts a .NET Regex into a BiDi Regex
165+ /// </summary>
166+ /// <param name="regex">A .NET Regex.</param>
167+ /// <returns>A BiDi Regex.</returns>
168+ /// <remarks>
169+ /// Note that the .NET regular expression engine does not work the same as the JavaScript engine.
170+ /// To minimize the differences between the two engines, it is recommended to enabled the <see cref="RegexOptions.ECMAScript"/> option.
171+ /// </remarks>
172+ public static LocalValue ConvertFrom ( Regex ? regex )
173+ {
174+ if ( regex is null )
175+ {
176+ return new NullLocalValue ( ) ;
140177 }
178+
179+ string ? flags = RegExpValue . GetRegExpFlags ( regex . Options ) ;
180+
181+ return new RegExpLocalValue ( new RegExpValue ( regex . ToString ( ) ) { Flags = flags } ) ;
182+ }
183+
184+ public static LocalValue ConvertFrom ( DateTimeOffset ? value )
185+ {
186+ if ( value is null )
187+ {
188+ return new NullLocalValue ( ) ;
189+ }
190+
191+ return new DateLocalValue ( value . Value . ToString ( "o" ) ) ;
192+ }
193+
194+ public static LocalValue ConvertFrom ( BigInteger ? value )
195+ {
196+ if ( value is not null )
197+ {
198+ return new BigIntLocalValue ( value . Value . ToString ( ) ) ;
199+ }
200+
201+ return new NullLocalValue ( ) ;
202+ }
203+
204+ public static LocalValue ConvertFrom ( IEnumerable ? value )
205+ {
206+ if ( value is null )
207+ {
208+ return new NullLocalValue ( ) ;
209+ }
210+
211+ List < LocalValue > list = [ ] ;
212+
213+ foreach ( var element in value )
214+ {
215+ list . Add ( ConvertFrom ( element ) ) ;
216+ }
217+
218+ return new ArrayLocalValue ( list ) ;
219+ }
220+
221+ public static LocalValue ConvertFrom ( IDictionary ? value )
222+ {
223+ if ( value is null )
224+ {
225+ return new NullLocalValue ( ) ;
226+ }
227+
228+ var bidiObject = new List < List < LocalValue > > ( value . Count ) ;
229+
230+ foreach ( var key in value . Keys )
231+ {
232+ bidiObject . Add ( [ ConvertFrom ( key ) , ConvertFrom ( value [ key ] ) ] ) ;
233+ }
234+
235+ return new MapLocalValue ( bidiObject ) ;
236+ }
237+
238+ public static LocalValue ConvertFrom < T > ( ISet < T ? > ? value )
239+ {
240+ if ( value is null )
241+ {
242+ return new NullLocalValue ( ) ;
243+ }
244+
245+ LocalValue [ ] convertedValues = [ .. value . Select ( x => ConvertFrom ( x ) ) ] ;
246+
247+ return new SetLocalValue ( convertedValues ) ;
248+ }
249+
250+ private static LocalValue ReflectionBasedConvertFrom ( object ? value )
251+ {
252+ if ( value is null )
253+ {
254+ return new NullLocalValue ( ) ;
255+ }
256+
257+ const System . Reflection . BindingFlags Flags = System . Reflection . BindingFlags . Public | System . Reflection . BindingFlags . Instance ;
258+
259+ System . Reflection . PropertyInfo [ ] properties = value . GetType ( ) . GetProperties ( Flags ) ;
260+
261+ var values = new List < List < LocalValue > > ( properties . Length ) ;
262+
263+ foreach ( System . Reflection . PropertyInfo ? property in properties )
264+ {
265+ object ? propertyValue ;
266+
267+ try
268+ {
269+ propertyValue = property . GetValue ( value ) ;
270+ }
271+ catch ( Exception ex )
272+ {
273+ throw new BiDiException ( $ "Could not retrieve property { property . Name } from { property . DeclaringType } ", ex ) ;
274+ }
275+
276+ values . Add ( [ property. Name , ConvertFrom ( propertyValue ) ] ) ;
277+ }
278+
279+ return new ObjectLocalValue ( values ) ;
141280 }
142281}
143282
@@ -160,9 +299,9 @@ public record BigIntLocalValue(string Value) : PrimitiveProtocolLocalValue;
160299
161300public record ChannelLocalValue ( ChannelProperties Value ) : LocalValue
162301{
163- // TODO: Revise why we need it
302+ // AddPreloadScript takes arguments typed as ChannelLocalValue but still requires "type":"channel"
164303 [ JsonInclude ]
165- internal string type = "channel" ;
304+ internal string Type => "channel" ;
166305}
167306
168307public record ArrayLocalValue ( IEnumerable < LocalValue > Value ) : LocalValue ;
0 commit comments