diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BiDiDoubleConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BiDiDoubleConverter.cs new file mode 100644 index 0000000000000..4154d2ac74cca --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BiDiDoubleConverter.cs @@ -0,0 +1,92 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; + +/// +/// Serializes and deserializes into a +/// BiDi spec-compliant number value. +/// +internal sealed class BiDiDoubleConverter : JsonConverter +{ + public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TryGetDouble(out double d)) + { + return d; + } + + var str = reader.GetString() ?? throw new JsonException(); + + if (str.Equals("-0", StringComparison.Ordinal)) + { + return -0.0; + } + else if (str.Equals("NaN", StringComparison.Ordinal)) + { + return double.NaN; + } + else if (str.Equals("Infinity", StringComparison.Ordinal)) + { + return double.PositiveInfinity; + } + else if (str.Equals("-Infinity", StringComparison.Ordinal)) + { + return double.NegativeInfinity; + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) + { + if (double.IsNaN(value)) + { + writer.WriteStringValue("NaN"); + } + else if (double.IsPositiveInfinity(value)) + { + writer.WriteStringValue("Infinity"); + } + else if (double.IsNegativeInfinity(value)) + { + writer.WriteStringValue("-Infinity"); + } + else if (IsNegativeZero(value)) + { + writer.WriteStringValue("-0"); + } + else + { + writer.WriteNumberValue(value); + } + + static bool IsNegativeZero(double x) + { + // Negative zero is less trivial to test, because 0 == -0 is true + // We need to do a bit pattern comparison + + return BitConverter.DoubleToInt64Bits(x) == BitConverter.DoubleToInt64Bits(-0.0); + } + } +} diff --git a/dotnet/src/webdriver/BiDi/Script/LocalValue.cs b/dotnet/src/webdriver/BiDi/Script/LocalValue.cs index 0409f4542b7df..ce37fca5c26d6 100644 --- a/dotnet/src/webdriver/BiDi/Script/LocalValue.cs +++ b/dotnet/src/webdriver/BiDi/Script/LocalValue.cs @@ -24,6 +24,7 @@ using System.Numerics; using System.Text.Json.Serialization; using System.Text.RegularExpressions; +using OpenQA.Selenium.BiDi.Communication.Json.Converters; namespace OpenQA.Selenium.BiDi.Script; @@ -282,7 +283,7 @@ private static LocalValue ReflectionBasedConvertFrom(object? value) public abstract record PrimitiveProtocolLocalValue : LocalValue; -public record NumberLocalValue(double Value) : PrimitiveProtocolLocalValue +public record NumberLocalValue([property: JsonConverter(typeof(BiDiDoubleConverter))] double Value) : PrimitiveProtocolLocalValue { public static explicit operator NumberLocalValue(double n) => new NumberLocalValue(n); } diff --git a/dotnet/src/webdriver/BiDi/Script/RemoteValue.cs b/dotnet/src/webdriver/BiDi/Script/RemoteValue.cs index d1ac8be390be0..1d66710fa563d 100644 --- a/dotnet/src/webdriver/BiDi/Script/RemoteValue.cs +++ b/dotnet/src/webdriver/BiDi/Script/RemoteValue.cs @@ -22,6 +22,7 @@ using System.Numerics; using System.Text.Json; using System.Text.Json.Serialization; +using OpenQA.Selenium.BiDi.Communication.Json.Converters; namespace OpenQA.Selenium.BiDi.Script; @@ -97,7 +98,7 @@ public abstract record RemoteValue } } -public record NumberRemoteValue(double Value) : PrimitiveProtocolRemoteValue; +public record NumberRemoteValue([property: JsonConverter(typeof(BiDiDoubleConverter))] double Value) : PrimitiveProtocolRemoteValue; public record BooleanRemoteValue(bool Value) : PrimitiveProtocolRemoteValue; diff --git a/dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs b/dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs index e265e7f4f5d8f..6a75d595c5034 100644 --- a/dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs +++ b/dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs @@ -221,8 +221,6 @@ public async Task CanCallFunctionWithArgumentNumberZero() } [Test] - [IgnoreBrowser(Selenium.Browser.Edge, "Chromium can't handle -0 argument as a number: https://github.com/w3c/webdriver-bidi/issues/887")] - [IgnoreBrowser(Selenium.Browser.Chrome, "Chromium can't handle -0 argument as a number: https://github.com/w3c/webdriver-bidi/issues/887")] public async Task CanCallFunctionWithArgumentNumberNegativeZero() { var arg = new NumberLocalValue(double.NegativeZero);