Skip to content

Commit 7de9f07

Browse files
committed
[dotnet] [bidi] Expand usability of CallFunctionAsync<TResult> and EvaluateAsync<TResult>
1 parent dbf3dae commit 7de9f07

File tree

3 files changed

+594
-15
lines changed

3 files changed

+594
-15
lines changed

dotnet/src/webdriver/BiDi/Modules/Script/RegExpValue.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,43 @@
1717
// under the License.
1818
// </copyright>
1919

20+
using System.Text.RegularExpressions;
21+
2022
namespace OpenQA.Selenium.BiDi.Modules.Script;
2123

2224
public record RegExpValue(string Pattern)
2325
{
2426
public string? Flags { get; set; }
27+
28+
internal static RegexOptions JavaScriptFlagsToRegexOptions(string? flags)
29+
{
30+
if (string.IsNullOrEmpty(flags))
31+
{
32+
return RegexOptions.None;
33+
}
34+
35+
RegexOptions value = default;
36+
37+
foreach (char flag in flags!)
38+
{
39+
if (flag == 'i')
40+
{
41+
value |= RegexOptions.IgnoreCase;
42+
}
43+
else if (flag == 'm')
44+
{
45+
value |= RegexOptions.Multiline;
46+
}
47+
else if (flag == 's')
48+
{
49+
value |= RegexOptions.Singleline;
50+
}
51+
else
52+
{
53+
// Unsupported flag
54+
}
55+
}
56+
57+
return value;
58+
}
2559
}

dotnet/src/webdriver/BiDi/Modules/Script/RemoteValue.cs

Lines changed: 227 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
using System;
2121
using System.Collections.Generic;
22+
using System.Dynamic;
23+
using System.Linq;
2224
using System.Numerics;
2325
using System.Text.Json;
2426
using System.Text.Json.Serialization;
@@ -55,12 +57,12 @@ namespace OpenQA.Selenium.BiDi.Modules.Script;
5557
//[JsonDerivedType(typeof(WindowProxyRemoteValue), "window")]
5658
public abstract record RemoteValue
5759
{
58-
public static implicit operator double(RemoteValue remoteValue) => (double)((NumberRemoteValue)remoteValue).Value;
60+
public static explicit operator double(RemoteValue remoteValue) => (double)((NumberRemoteValue)remoteValue).Value;
5961

60-
public static implicit operator int(RemoteValue remoteValue) => (int)(double)remoteValue;
61-
public static implicit operator long(RemoteValue remoteValue) => (long)(double)remoteValue;
62+
public static explicit operator int(RemoteValue remoteValue) => (int)(double)remoteValue;
63+
public static explicit operator long(RemoteValue remoteValue) => (long)(double)remoteValue;
6264

63-
public static implicit operator string?(RemoteValue remoteValue)
65+
public static explicit operator string?(RemoteValue remoteValue)
6466
{
6567
return remoteValue switch
6668
{
@@ -73,27 +75,237 @@ public abstract record RemoteValue
7375
// TODO: extend types
7476
public TResult? ConvertTo<TResult>()
7577
{
76-
var type = typeof(TResult);
78+
if (this is UndefinedRemoteValue)
79+
{
80+
throw new BiDiException($"Unable to convert undefined to {typeof(TResult)}");
81+
}
82+
83+
if (this is NullRemoteValue)
84+
{
85+
if (default(TResult) == null)
86+
{
87+
return default;
88+
}
89+
90+
throw new BiDiException($"Unable to convert null to non-nullable value type {typeof(TResult)}");
91+
}
92+
93+
if (this is BooleanRemoteValue b)
94+
{
95+
if (typeof(TResult) == typeof(bool) ||
96+
typeof(TResult) == typeof(bool?) ||
97+
typeof(TResult) == typeof(object))
98+
{
99+
return (TResult)(object)b.Value;
100+
}
101+
}
77102

78-
if (type == typeof(bool))
103+
if (this is DateRemoteValue date)
79104
{
80-
return (TResult)(Convert.ToBoolean(((BooleanRemoteValue)this).Value) as object);
105+
if (typeof(TResult) == typeof(DateTime) ||
106+
typeof(TResult) == typeof(DateTime?) ||
107+
typeof(TResult) == typeof(object))
108+
{
109+
return (TResult)(object)DateTime.Parse(date.Value);
110+
}
111+
112+
if (typeof(TResult) == typeof(DateTimeOffset) ||
113+
typeof(TResult) == typeof(DateTimeOffset?))
114+
{
115+
return (TResult)(object)DateTimeOffset.Parse(date.Value);
116+
}
117+
118+
if (typeof(TResult) == typeof(string))
119+
{
120+
return (TResult)(object)date.Value;
121+
}
81122
}
82-
if (type == typeof(int))
123+
124+
if (this is NumberRemoteValue number)
83125
{
84-
return (TResult)(Convert.ToInt32(((NumberRemoteValue)this).Value) as object);
126+
if (typeof(TResult) == typeof(double) ||
127+
typeof(TResult) == typeof(double?) ||
128+
typeof(TResult) == typeof(object))
129+
{
130+
return (TResult)(object)number.Value;
131+
}
132+
133+
if (typeof(TResult) == typeof(int) ||
134+
typeof(TResult) == typeof(int?))
135+
{
136+
if (double.IsPositiveInfinity(number.Value) ||
137+
double.IsNegativeInfinity(number.Value) ||
138+
double.IsNaN(number.Value))
139+
{
140+
throw new BiDiException($"Cannot represent {number.Value} as an {nameof(Int32)}");
141+
}
142+
143+
return (TResult)(object)(int)number.Value;
144+
}
145+
146+
if (typeof(TResult) == typeof(float) ||
147+
typeof(TResult) == typeof(float?))
148+
{
149+
return (TResult)(object)(float)number.Value;
150+
}
151+
152+
if (typeof(TResult) == typeof(BigInteger) ||
153+
typeof(TResult) == typeof(BigInteger?))
154+
{
155+
return (TResult)(object)new BigInteger(number.Value);
156+
}
85157
}
86-
else if (type == typeof(string))
158+
159+
if (this is BigIntRemoteValue bigInt)
160+
{
161+
var bigNumber = BigInteger.Parse(bigInt.Value);
162+
if (typeof(TResult) == typeof(BigInteger) ||
163+
typeof(TResult) == typeof(BigInteger?) ||
164+
typeof(TResult) == typeof(object))
165+
{
166+
return (TResult)(object)bigNumber;
167+
}
168+
169+
if (typeof(TResult) == typeof(double) ||
170+
typeof(TResult) == typeof(double?) ||
171+
typeof(TResult) == typeof(object))
172+
{
173+
return (TResult)(object)(double)bigNumber;
174+
}
175+
176+
if (typeof(TResult) == typeof(int) ||
177+
typeof(TResult) == typeof(int?))
178+
{
179+
return (TResult)(object)(int)bigNumber;
180+
}
181+
182+
if (typeof(TResult) == typeof(float) ||
183+
typeof(TResult) == typeof(float?))
184+
{
185+
return (TResult)(object)(float)bigNumber;
186+
}
187+
}
188+
189+
if (this is StringRemoteValue str)
87190
{
88-
return (TResult)(((StringRemoteValue)this).Value as object);
191+
if (typeof(TResult) == typeof(string) ||
192+
typeof(TResult) == typeof(object))
193+
{
194+
return (TResult)(object)str.Value;
195+
}
89196
}
90-
else if (type is object)
197+
198+
if (this is RegExpRemoteValue regex)
199+
{
200+
if (typeof(TResult) == typeof(string) ||
201+
typeof(TResult) == typeof(object))
202+
{
203+
if (string.IsNullOrEmpty(regex.Value.Pattern))
204+
{
205+
return (TResult)(object)$"/{regex.Value.Pattern}";
206+
}
207+
208+
return (TResult)(object)$"/{regex.Value.Pattern}/{regex.Value.Flags}";
209+
}
210+
211+
if (typeof(TResult) == typeof(System.Text.RegularExpressions.Regex))
212+
{
213+
return (TResult)(object)new System.Text.RegularExpressions.Regex(regex.Value.Pattern, RegExpValue.JavaScriptFlagsToRegexOptions(regex.Value.Flags));
214+
}
215+
}
216+
217+
if (this is ArrayRemoteValue array)
218+
{
219+
if (typeof(TResult) == typeof(IReadOnlyList<RemoteValue>) ||
220+
typeof(TResult) == typeof(IEnumerable<RemoteValue>) ||
221+
typeof(TResult) == typeof(object))
222+
{
223+
return (TResult)(object)array.Value;
224+
}
225+
226+
if (typeof(TResult) == typeof(RemoteValue[]))
227+
{
228+
return (TResult)(object)array.Value.ToArray();
229+
}
230+
231+
if (typeof(TResult) == typeof(List<RemoteValue>))
232+
{
233+
return (TResult)(object)array.Value.ToList();
234+
}
235+
}
236+
237+
if (this is ObjectRemoteValue obj)
238+
{
239+
if (typeof(TResult) == typeof(IDictionary<string, RemoteValue>) ||
240+
typeof(TResult) == typeof(Dictionary<string, RemoteValue>))
241+
{
242+
return (TResult)(object)obj.Value.ToDictionary(value => ((StringRemoteValue)value[0]).Value, value => value[1]);
243+
}
244+
245+
if (typeof(TResult) == typeof(IDictionary<RemoteValue, RemoteValue>) ||
246+
typeof(TResult) == typeof(Dictionary<RemoteValue, RemoteValue>))
247+
{
248+
return (TResult)(object)obj.Value.ToDictionary(value => value[0], value => value[1]);
249+
}
250+
251+
if (typeof(TResult) == typeof(object))
252+
{
253+
// Handle dynamic here
254+
IDictionary<string, object?> values = new ExpandoObject();
255+
foreach (IReadOnlyList<RemoteValue> property in obj.Value)
256+
{
257+
string propertyName = ((StringRemoteValue)property[0]).Value;
258+
values[propertyName] = property[1].ConvertTo<dynamic>();
259+
}
260+
261+
return (TResult)(object)values;
262+
}
263+
}
264+
265+
if (this is MapRemoteValue map)
266+
{
267+
if (typeof(TResult) == typeof(IDictionary<RemoteValue, RemoteValue>) ||
268+
typeof(TResult) == typeof(Dictionary<RemoteValue, RemoteValue>))
269+
{
270+
return (TResult)(object)map.Value.ToDictionary(value => value[0], value => value[1]);
271+
}
272+
if (typeof(TResult) == typeof(IDictionary<string, RemoteValue>) ||
273+
typeof(TResult) == typeof(Dictionary<string, RemoteValue>))
274+
{
275+
return (TResult)(object)map.Value.ToDictionary(value => ((StringRemoteValue)value[0]).Value, value => value[1]);
276+
}
277+
}
278+
279+
if (this is SetRemoteValue set)
91280
{
92-
// :)
93-
return (TResult)new object();
281+
if (typeof(TResult) == typeof(IReadOnlyList<RemoteValue>) ||
282+
typeof(TResult) == typeof(IEnumerable<RemoteValue>) ||
283+
typeof(TResult) == typeof(object))
284+
{
285+
return (TResult)(object)set.Value;
286+
}
287+
288+
if (typeof(TResult) == typeof(RemoteValue[]))
289+
{
290+
return (TResult)(object)set.Value.ToArray();
291+
}
292+
293+
if (typeof(TResult) == typeof(List<RemoteValue>))
294+
{
295+
return (TResult)(object)set.Value.ToList();
296+
}
297+
298+
if (typeof(TResult) == typeof(ISet<RemoteValue>) ||
299+
#if NET8_0_OR_GREATER
300+
typeof(TResult) == typeof(IReadOnlySet<RemoteValue>) ||
301+
#endif
302+
typeof(TResult) == typeof(HashSet<RemoteValue>))
303+
{
304+
return (TResult)(object)new HashSet<RemoteValue>(set.Value);
305+
}
94306
}
95307

96-
throw new BiDiException("Cannot convert .....");
308+
throw new BiDiException($"Cannot convert to type {typeof(TResult)} from {this}");
97309
}
98310
}
99311

0 commit comments

Comments
 (0)