Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions dotnet/src/webdriver/BiDi/Modules/Script/RegExpValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,43 @@
// under the License.
// </copyright>

using System.Text.RegularExpressions;

namespace OpenQA.Selenium.BiDi.Modules.Script;

public record RegExpValue(string Pattern)
{
public string? Flags { get; set; }

internal static RegexOptions JavaScriptFlagsToRegexOptions(string? flags)
{
if (string.IsNullOrEmpty(flags))
{
return RegexOptions.None;
}

RegexOptions value = default;

foreach (char flag in flags!)
{
if (flag == 'i')
{
value |= RegexOptions.IgnoreCase;
}
else if (flag == 'm')
{
value |= RegexOptions.Multiline;
}
else if (flag == 's')
{
value |= RegexOptions.Singleline;
}
else
{
// Unsupported flag
}
}

return value;
}
}
242 changes: 227 additions & 15 deletions dotnet/src/webdriver/BiDi/Modules/Script/RemoteValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Numerics;
using System.Text.Json;
using System.Text.Json.Serialization;
Expand Down Expand Up @@ -55,12 +57,12 @@ namespace OpenQA.Selenium.BiDi.Modules.Script;
//[JsonDerivedType(typeof(WindowProxyRemoteValue), "window")]
public abstract record RemoteValue
{
public static implicit operator double(RemoteValue remoteValue) => (double)((NumberRemoteValue)remoteValue).Value;
public static explicit operator double(RemoteValue remoteValue) => (double)((NumberRemoteValue)remoteValue).Value;

public static implicit operator int(RemoteValue remoteValue) => (int)(double)remoteValue;
public static implicit operator long(RemoteValue remoteValue) => (long)(double)remoteValue;
public static explicit operator int(RemoteValue remoteValue) => (int)(double)remoteValue;
public static explicit operator long(RemoteValue remoteValue) => (long)(double)remoteValue;

public static implicit operator string?(RemoteValue remoteValue)
public static explicit operator string?(RemoteValue remoteValue)
{
return remoteValue switch
{
Expand All @@ -73,27 +75,237 @@ public abstract record RemoteValue
// TODO: extend types
public TResult? ConvertTo<TResult>()
{
var type = typeof(TResult);
if (this is UndefinedRemoteValue)
{
throw new BiDiException($"Unable to convert undefined to {typeof(TResult)}");
}

if (this is NullRemoteValue)
{
if (default(TResult) == null)
{
return default;
}

throw new BiDiException($"Unable to convert null to non-nullable value type {typeof(TResult)}");
}

if (this is BooleanRemoteValue b)
{
if (typeof(TResult) == typeof(bool) ||
typeof(TResult) == typeof(bool?) ||
typeof(TResult) == typeof(object))
{
return (TResult)(object)b.Value;
}
}

if (type == typeof(bool))
if (this is DateRemoteValue date)
{
return (TResult)(Convert.ToBoolean(((BooleanRemoteValue)this).Value) as object);
if (typeof(TResult) == typeof(DateTime) ||
typeof(TResult) == typeof(DateTime?) ||
typeof(TResult) == typeof(object))
{
return (TResult)(object)DateTime.Parse(date.Value);
}

if (typeof(TResult) == typeof(DateTimeOffset) ||
typeof(TResult) == typeof(DateTimeOffset?))
{
return (TResult)(object)DateTimeOffset.Parse(date.Value);
}

if (typeof(TResult) == typeof(string))
{
return (TResult)(object)date.Value;
}
}
if (type == typeof(int))

if (this is NumberRemoteValue number)
{
return (TResult)(Convert.ToInt32(((NumberRemoteValue)this).Value) as object);
if (typeof(TResult) == typeof(double) ||
typeof(TResult) == typeof(double?) ||
typeof(TResult) == typeof(object))
{
return (TResult)(object)number.Value;
}

if (typeof(TResult) == typeof(int) ||
typeof(TResult) == typeof(int?))
{
if (double.IsPositiveInfinity(number.Value) ||
double.IsNegativeInfinity(number.Value) ||
double.IsNaN(number.Value))
{
throw new BiDiException($"Cannot represent {number.Value} as an {nameof(Int32)}");
}

return (TResult)(object)(int)number.Value;
}

if (typeof(TResult) == typeof(float) ||
typeof(TResult) == typeof(float?))
{
return (TResult)(object)(float)number.Value;
}

if (typeof(TResult) == typeof(BigInteger) ||
typeof(TResult) == typeof(BigInteger?))
{
return (TResult)(object)new BigInteger(number.Value);
}
}
else if (type == typeof(string))

if (this is BigIntRemoteValue bigInt)
{
var bigNumber = BigInteger.Parse(bigInt.Value);
if (typeof(TResult) == typeof(BigInteger) ||
typeof(TResult) == typeof(BigInteger?) ||
typeof(TResult) == typeof(object))
{
return (TResult)(object)bigNumber;
}

if (typeof(TResult) == typeof(double) ||
typeof(TResult) == typeof(double?) ||
typeof(TResult) == typeof(object))
{
return (TResult)(object)(double)bigNumber;
}

if (typeof(TResult) == typeof(int) ||
typeof(TResult) == typeof(int?))
{
return (TResult)(object)(int)bigNumber;
}

if (typeof(TResult) == typeof(float) ||
typeof(TResult) == typeof(float?))
{
return (TResult)(object)(float)bigNumber;
}
}

if (this is StringRemoteValue str)
{
return (TResult)(((StringRemoteValue)this).Value as object);
if (typeof(TResult) == typeof(string) ||
typeof(TResult) == typeof(object))
{
return (TResult)(object)str.Value;
}
}
else if (type is object)

if (this is RegExpRemoteValue regex)
{
if (typeof(TResult) == typeof(string) ||
typeof(TResult) == typeof(object))
{
if (string.IsNullOrEmpty(regex.Value.Pattern))
{
return (TResult)(object)$"/{regex.Value.Pattern}";
}

return (TResult)(object)$"/{regex.Value.Pattern}/{regex.Value.Flags}";
}

if (typeof(TResult) == typeof(System.Text.RegularExpressions.Regex))
{
return (TResult)(object)new System.Text.RegularExpressions.Regex(regex.Value.Pattern, RegExpValue.JavaScriptFlagsToRegexOptions(regex.Value.Flags));
}
}

if (this is ArrayRemoteValue array)
{
if (typeof(TResult) == typeof(IReadOnlyList<RemoteValue>) ||
typeof(TResult) == typeof(IEnumerable<RemoteValue>) ||
typeof(TResult) == typeof(object))
{
return (TResult)(object)array.Value;
}

if (typeof(TResult) == typeof(RemoteValue[]))
{
return (TResult)(object)array.Value.ToArray();
}

if (typeof(TResult) == typeof(List<RemoteValue>))
{
return (TResult)(object)array.Value.ToList();
}
}

if (this is ObjectRemoteValue obj)
{
if (typeof(TResult) == typeof(IDictionary<string, RemoteValue>) ||
typeof(TResult) == typeof(Dictionary<string, RemoteValue>))
{
return (TResult)(object)obj.Value.ToDictionary(value => ((StringRemoteValue)value[0]).Value, value => value[1]);
}

if (typeof(TResult) == typeof(IDictionary<RemoteValue, RemoteValue>) ||
typeof(TResult) == typeof(Dictionary<RemoteValue, RemoteValue>))
{
return (TResult)(object)obj.Value.ToDictionary(value => value[0], value => value[1]);
}

if (typeof(TResult) == typeof(object))
{
// Handle dynamic here
IDictionary<string, object?> values = new ExpandoObject();
foreach (IReadOnlyList<RemoteValue> property in obj.Value)
{
string propertyName = ((StringRemoteValue)property[0]).Value;
values[propertyName] = property[1].ConvertTo<dynamic>();
}

return (TResult)(object)values;
}
}

if (this is MapRemoteValue map)
{
if (typeof(TResult) == typeof(IDictionary<RemoteValue, RemoteValue>) ||
typeof(TResult) == typeof(Dictionary<RemoteValue, RemoteValue>))
{
return (TResult)(object)map.Value.ToDictionary(value => value[0], value => value[1]);
}
if (typeof(TResult) == typeof(IDictionary<string, RemoteValue>) ||
typeof(TResult) == typeof(Dictionary<string, RemoteValue>))
{
return (TResult)(object)map.Value.ToDictionary(value => ((StringRemoteValue)value[0]).Value, value => value[1]);
}
}

if (this is SetRemoteValue set)
{
// :)
return (TResult)new object();
if (typeof(TResult) == typeof(IReadOnlyList<RemoteValue>) ||
typeof(TResult) == typeof(IEnumerable<RemoteValue>) ||
typeof(TResult) == typeof(object))
{
return (TResult)(object)set.Value;
}

if (typeof(TResult) == typeof(RemoteValue[]))
{
return (TResult)(object)set.Value.ToArray();
}

if (typeof(TResult) == typeof(List<RemoteValue>))
{
return (TResult)(object)set.Value.ToList();
}

if (typeof(TResult) == typeof(ISet<RemoteValue>) ||
#if NET8_0_OR_GREATER
typeof(TResult) == typeof(IReadOnlySet<RemoteValue>) ||
#endif
typeof(TResult) == typeof(HashSet<RemoteValue>))
{
return (TResult)(object)new HashSet<RemoteValue>(set.Value);
}
}

throw new BiDiException("Cannot convert .....");
throw new BiDiException($"Cannot convert to type {typeof(TResult)} from {this}");
}
}

Expand Down
Loading
Loading