From e885999cdb65617feb8a0bc73d52499da9101e66 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Tue, 29 Oct 2024 01:10:13 -0400 Subject: [PATCH 01/15] [dotnet] Enable NRT on exceptional types --- dotnet/src/webdriver/DefaultFileDetector.cs | 6 +- .../webdriver/DetachedShadowRootException.cs | 3 +- .../DevTools/CommandResponseException.cs | 6 +- .../DriverServiceNotFoundException.cs | 9 +- .../ElementClickInterceptedException.cs | 7 +- .../ElementNotInteractableException.cs | 9 +- .../ElementNotSelectableException.cs | 9 +- .../webdriver/ElementNotVisibleException.cs | 7 +- dotnet/src/webdriver/ErrorResponse.cs | 18 ++- dotnet/src/webdriver/Firefox/FirefoxDriver.cs | 25 ++- dotnet/src/webdriver/IFileDetector.cs | 6 +- .../webdriver/InsecureCertificateException.cs | 9 +- .../webdriver/Internal/NullableAttributes.cs | 149 ++++++++++++++++++ .../webdriver/InvalidCookieDomainException.cs | 7 +- .../webdriver/InvalidElementStateException.cs | 7 +- .../src/webdriver/InvalidSelectorException.cs | 11 +- dotnet/src/webdriver/JavaScriptException.cs | 7 +- .../MoveTargetOutOfBoundsException.cs | 7 +- .../src/webdriver/NoAlertPresentException.cs | 7 +- dotnet/src/webdriver/NoSuchDriverException.cs | 9 +- .../src/webdriver/NoSuchElementException.cs | 9 +- dotnet/src/webdriver/NoSuchFrameException.cs | 7 +- .../webdriver/NoSuchShadowRootException.cs | 9 +- dotnet/src/webdriver/NoSuchWindowException.cs | 7 +- dotnet/src/webdriver/NotFoundException.cs | 7 +- .../src/webdriver/Remote/LocalFileDetector.cs | 5 +- dotnet/src/webdriver/StackTraceElement.cs | 10 +- .../StaleElementReferenceException.cs | 9 +- .../webdriver/UnableToSetCookieException.cs | 7 +- .../src/webdriver/UnhandledAlertException.cs | 15 +- .../webdriver/WebDriverArgumentException.cs | 7 +- dotnet/src/webdriver/WebDriverException.cs | 7 +- .../webdriver/WebDriverTimeoutException.cs | 7 +- dotnet/src/webdriver/XPathLookupException.cs | 7 +- 34 files changed, 320 insertions(+), 111 deletions(-) create mode 100644 dotnet/src/webdriver/Internal/NullableAttributes.cs diff --git a/dotnet/src/webdriver/DefaultFileDetector.cs b/dotnet/src/webdriver/DefaultFileDetector.cs index b391d385af6be..51d68e711a993 100644 --- a/dotnet/src/webdriver/DefaultFileDetector.cs +++ b/dotnet/src/webdriver/DefaultFileDetector.cs @@ -16,6 +16,10 @@ // limitations under the License. // +#nullable enable + +using System.Diagnostics.CodeAnalysis; + namespace OpenQA.Selenium { /// @@ -30,7 +34,7 @@ public class DefaultFileDetector : IFileDetector /// /// The sequence to test for file existence. /// This method always returns in this implementation. - public bool IsFile(string keySequence) + public bool IsFile([NotNullWhen(true)] string? keySequence) { return false; } diff --git a/dotnet/src/webdriver/DetachedShadowRootException.cs b/dotnet/src/webdriver/DetachedShadowRootException.cs index d8af8c148db6f..dd794c0b9df69 100644 --- a/dotnet/src/webdriver/DetachedShadowRootException.cs +++ b/dotnet/src/webdriver/DetachedShadowRootException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { diff --git a/dotnet/src/webdriver/DevTools/CommandResponseException.cs b/dotnet/src/webdriver/DevTools/CommandResponseException.cs index a7ac5a92de295..5f7a0430b480e 100644 --- a/dotnet/src/webdriver/DevTools/CommandResponseException.cs +++ b/dotnet/src/webdriver/DevTools/CommandResponseException.cs @@ -18,6 +18,8 @@ using System; +#nullable enable + namespace OpenQA.Selenium.DevTools { /// @@ -38,7 +40,7 @@ public CommandResponseException() /// Initializes a new instance of the class with the specified message. /// /// The message of the exception. - public CommandResponseException(string message) + public CommandResponseException(string? message) : base(message) { } @@ -48,7 +50,7 @@ public CommandResponseException(string message) /// /// The message of the exception. /// The inner exception for this exception. - public CommandResponseException(string message, Exception innerException) + public CommandResponseException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/DriverServiceNotFoundException.cs b/dotnet/src/webdriver/DriverServiceNotFoundException.cs index 2094d61cc2761..a1342a7525c11 100644 --- a/dotnet/src/webdriver/DriverServiceNotFoundException.cs +++ b/dotnet/src/webdriver/DriverServiceNotFoundException.cs @@ -17,12 +17,13 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { /// - /// The exception that is thrown when an element is not visible. + /// The exception that is thrown when the driver service is not available. /// [Serializable] public class DriverServiceNotFoundException : WebDriverException @@ -40,7 +41,7 @@ public DriverServiceNotFoundException() /// a specified error message. /// /// The message that describes the error. - public DriverServiceNotFoundException(string message) + public DriverServiceNotFoundException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public DriverServiceNotFoundException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public DriverServiceNotFoundException(string message, Exception innerException) + public DriverServiceNotFoundException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/ElementClickInterceptedException.cs b/dotnet/src/webdriver/ElementClickInterceptedException.cs index f937dc5e58af9..cdeae275349ed 100644 --- a/dotnet/src/webdriver/ElementClickInterceptedException.cs +++ b/dotnet/src/webdriver/ElementClickInterceptedException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public ElementClickInterceptedException() /// a specified error message. /// /// The message that describes the error. - public ElementClickInterceptedException(string message) + public ElementClickInterceptedException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public ElementClickInterceptedException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public ElementClickInterceptedException(string message, Exception innerException) + public ElementClickInterceptedException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/ElementNotInteractableException.cs b/dotnet/src/webdriver/ElementNotInteractableException.cs index 65ae5395e2207..5f0b5eccd270f 100644 --- a/dotnet/src/webdriver/ElementNotInteractableException.cs +++ b/dotnet/src/webdriver/ElementNotInteractableException.cs @@ -17,12 +17,13 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { /// - /// The exception that is thrown when an element is not visible. + /// The exception that is thrown when an element is not interactable. /// [Serializable] public class ElementNotInteractableException : InvalidElementStateException @@ -40,7 +41,7 @@ public ElementNotInteractableException() /// a specified error message. /// /// The message that describes the error. - public ElementNotInteractableException(string message) + public ElementNotInteractableException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public ElementNotInteractableException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public ElementNotInteractableException(string message, Exception innerException) + public ElementNotInteractableException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/ElementNotSelectableException.cs b/dotnet/src/webdriver/ElementNotSelectableException.cs index 68fa2b7f4ad6e..35170970d0293 100644 --- a/dotnet/src/webdriver/ElementNotSelectableException.cs +++ b/dotnet/src/webdriver/ElementNotSelectableException.cs @@ -17,12 +17,13 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { /// - /// The exception that is thrown when an element is not visible. + /// The exception that is thrown when an element is not selectable. /// [Serializable] public class ElementNotSelectableException : InvalidElementStateException @@ -40,7 +41,7 @@ public ElementNotSelectableException() /// a specified error message. /// /// The message that describes the error. - public ElementNotSelectableException(string message) + public ElementNotSelectableException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public ElementNotSelectableException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public ElementNotSelectableException(string message, Exception innerException) + public ElementNotSelectableException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/ElementNotVisibleException.cs b/dotnet/src/webdriver/ElementNotVisibleException.cs index 43f815ae105ad..bbe44b79a0f7c 100644 --- a/dotnet/src/webdriver/ElementNotVisibleException.cs +++ b/dotnet/src/webdriver/ElementNotVisibleException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public ElementNotVisibleException() /// a specified error message. /// /// The message that describes the error. - public ElementNotVisibleException(string message) + public ElementNotVisibleException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public ElementNotVisibleException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public ElementNotVisibleException(string message, Exception innerException) + public ElementNotVisibleException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/ErrorResponse.cs b/dotnet/src/webdriver/ErrorResponse.cs index c0c2bd269f5d9..4e08e55e08637 100644 --- a/dotnet/src/webdriver/ErrorResponse.cs +++ b/dotnet/src/webdriver/ErrorResponse.cs @@ -18,6 +18,8 @@ using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium { /// @@ -25,7 +27,7 @@ namespace OpenQA.Selenium /// public class ErrorResponse { - private StackTraceElement[] stackTrace; + private StackTraceElement[]? stackTrace; private string message = string.Empty; private string className = string.Empty; private string screenshot = string.Empty; @@ -42,7 +44,7 @@ public ErrorResponse() /// /// A containing names and values of /// the properties of this . - public ErrorResponse(Dictionary responseValue) + public ErrorResponse(Dictionary? responseValue) { if (responseValue != null) { @@ -50,7 +52,7 @@ public ErrorResponse(Dictionary responseValue) { if (responseValue["message"] != null) { - this.message = responseValue["message"].ToString(); + this.message = responseValue["message"].ToString() ?? ""; } else { @@ -60,17 +62,17 @@ public ErrorResponse(Dictionary responseValue) if (responseValue.ContainsKey("screen") && responseValue["screen"] != null) { - this.screenshot = responseValue["screen"].ToString(); + this.screenshot = responseValue["screen"].ToString() ?? ""; } if (responseValue.ContainsKey("class") && responseValue["class"] != null) { - this.className = responseValue["class"].ToString(); + this.className = responseValue["class"].ToString() ?? ""; } if (responseValue.ContainsKey("stackTrace") || responseValue.ContainsKey("stacktrace")) { - object[] stackTraceArray = null; + object[]? stackTraceArray = null; if (responseValue.ContainsKey("stackTrace")) { @@ -86,7 +88,7 @@ public ErrorResponse(Dictionary responseValue) List stackTraceList = new List(); foreach (object rawStackTraceElement in stackTraceArray) { - Dictionary elementAsDictionary = rawStackTraceElement as Dictionary; + Dictionary? elementAsDictionary = rawStackTraceElement as Dictionary; if (elementAsDictionary != null) { stackTraceList.Add(new StackTraceElement(elementAsDictionary)); @@ -130,7 +132,7 @@ public string Screenshot /// /// Gets or sets the stack trace of the error /// - public StackTraceElement[] StackTrace + public StackTraceElement[]? StackTrace { get { return this.stackTrace; } set { this.stackTrace = value; } diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs index 53452e02a917a..48fcdaad3a13c 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs @@ -21,11 +21,14 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.Firefox { /// @@ -109,7 +112,7 @@ public class FirefoxDriver : WebDriver, IDevTools { GetFullPageScreenshotCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/moz/screenshot/full") } }; - private DevToolsSession devToolsSession; + private DevToolsSession? devToolsSession; /// /// Initializes a new instance of the class. @@ -246,6 +249,7 @@ public override IFileDetector FileDetector /// /// Gets a value indicating whether a DevTools session is active. /// + [MemberNotNullWhen(true, nameof(devToolsSession))] public bool HasActiveDevToolsSession { get { return this.devToolsSession != null; } @@ -259,7 +263,7 @@ public bool HasActiveDevToolsSession public FirefoxCommandContext GetContext() { FirefoxCommandContext output; - string response = this.Execute(GetContextCommand, null).Value.ToString(); + string? response = this.Execute(GetContextCommand, null).Value.ToString(); bool success = Enum.TryParse(response, true, out output); if (!success) @@ -287,6 +291,12 @@ public void SetContext(FirefoxCommandContext context) /// /// Full path of the directory of the add-on to install. /// Whether the add-on is temporary; required for unsigned add-ons. + /// The add-on ID. + /// + /// If is null or empty. + /// or + /// If the directory at does not exist. + /// public string InstallAddOnFromDirectory(string addOnDirectoryToInstall, bool temporary = false) { if (string.IsNullOrEmpty(addOnDirectoryToInstall)) @@ -310,6 +320,12 @@ public string InstallAddOnFromDirectory(string addOnDirectoryToInstall, bool tem /// /// Full path and file name of the add-on to install. /// Whether the add-on is temporary; required for unsigned add-ons. + /// The add-on ID. + /// + /// If is null or empty. + /// or + /// If the file at does not exist. + /// public string InstallAddOnFromFile(string addOnFileToInstall, bool temporary = false) { if (string.IsNullOrEmpty(addOnFileToInstall)) @@ -333,6 +349,8 @@ public string InstallAddOnFromFile(string addOnFileToInstall, bool temporary = f /// /// The base64-encoded string representation of the add-on binary. /// Whether the add-on is temporary; required for unsigned add-ons. + /// The add-on ID. + /// If is null or empty. public string InstallAddOn(string base64EncodedAddOn, bool temporary = false) { if (string.IsNullOrEmpty(base64EncodedAddOn)) @@ -353,6 +371,7 @@ public string InstallAddOn(string base64EncodedAddOn, bool temporary = false) /// Uninstalls a Firefox add-on. /// /// The ID of the add-on to uninstall. + /// If is null or empty. public void UninstallAddOn(string addOnId) { if (string.IsNullOrEmpty(addOnId)) @@ -372,7 +391,7 @@ public void UninstallAddOn(string addOnId) public Screenshot GetFullPageScreenshot() { Response screenshotResponse = this.Execute(GetFullPageScreenshotCommand, null); - string base64 = screenshotResponse.Value.ToString(); + string base64 = screenshotResponse.Value.ToString()!; return new Screenshot(base64); } diff --git a/dotnet/src/webdriver/IFileDetector.cs b/dotnet/src/webdriver/IFileDetector.cs index 3a8ea7cf13880..370317c07343b 100644 --- a/dotnet/src/webdriver/IFileDetector.cs +++ b/dotnet/src/webdriver/IFileDetector.cs @@ -16,6 +16,10 @@ // limitations under the License. // +using System.Diagnostics.CodeAnalysis; + +#nullable enable + namespace OpenQA.Selenium { /// @@ -30,6 +34,6 @@ public interface IFileDetector /// /// The sequence to test for file existence. /// if the key sequence represents a file; otherwise, . - bool IsFile(string keySequence); + bool IsFile([NotNullWhen(true)] string? keySequence); } } diff --git a/dotnet/src/webdriver/InsecureCertificateException.cs b/dotnet/src/webdriver/InsecureCertificateException.cs index 37da31170bf21..6652022c6a30f 100644 --- a/dotnet/src/webdriver/InsecureCertificateException.cs +++ b/dotnet/src/webdriver/InsecureCertificateException.cs @@ -17,12 +17,13 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { /// - /// The exception that is thrown when a frame is not found. + /// The exception that is thrown when an insecure certificate is used. /// [Serializable] public class InsecureCertificateException : WebDriverException @@ -40,7 +41,7 @@ public InsecureCertificateException() /// a specified error message. /// /// The message that describes the error. - public InsecureCertificateException(string message) + public InsecureCertificateException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public InsecureCertificateException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public InsecureCertificateException(string message, Exception innerException) + public InsecureCertificateException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/Internal/NullableAttributes.cs b/dotnet/src/webdriver/Internal/NullableAttributes.cs new file mode 100644 index 0000000000000..101978b719999 --- /dev/null +++ b/dotnet/src/webdriver/Internal/NullableAttributes.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if !NET8_0_OR_GREATER + +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute + { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute + { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute + { } + + /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute + { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute + { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal + sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated field or property member will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated field and property members will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } +} + +#endif diff --git a/dotnet/src/webdriver/InvalidCookieDomainException.cs b/dotnet/src/webdriver/InvalidCookieDomainException.cs index 0b9294bb6d5bd..2be0a7b378947 100644 --- a/dotnet/src/webdriver/InvalidCookieDomainException.cs +++ b/dotnet/src/webdriver/InvalidCookieDomainException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public InvalidCookieDomainException() /// a specified error message. /// /// The message that describes the error. - public InvalidCookieDomainException(string message) + public InvalidCookieDomainException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public InvalidCookieDomainException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public InvalidCookieDomainException(string message, Exception innerException) + public InvalidCookieDomainException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/InvalidElementStateException.cs b/dotnet/src/webdriver/InvalidElementStateException.cs index 1010a9f084d8f..8ca57c80213f4 100644 --- a/dotnet/src/webdriver/InvalidElementStateException.cs +++ b/dotnet/src/webdriver/InvalidElementStateException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public InvalidElementStateException() /// a specified error message. /// /// The message that describes the error. - public InvalidElementStateException(string message) + public InvalidElementStateException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public InvalidElementStateException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public InvalidElementStateException(string message, Exception innerException) + public InvalidElementStateException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/InvalidSelectorException.cs b/dotnet/src/webdriver/InvalidSelectorException.cs index 509dc2d74a785..19dd098e731cf 100644 --- a/dotnet/src/webdriver/InvalidSelectorException.cs +++ b/dotnet/src/webdriver/InvalidSelectorException.cs @@ -17,12 +17,13 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { /// - /// The exception that is thrown when an element is not visible. + /// The exception that is thrown when an invalid selector is used. /// [Serializable] public class InvalidSelectorException : WebDriverException @@ -45,7 +46,7 @@ public InvalidSelectorException() /// a specified error message. /// /// The message that describes the error. - public InvalidSelectorException(string message) + public InvalidSelectorException(string? message) : base(GetMessage(message)) { } @@ -58,7 +59,7 @@ public InvalidSelectorException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public InvalidSelectorException(string message, Exception innerException) + public InvalidSelectorException(string? message, Exception? innerException) : base(GetMessage(message), innerException) { } @@ -68,7 +69,7 @@ public InvalidSelectorException(string message, Exception innerException) /// /// The original message for exception /// The final message for exception - protected static string GetMessage(string message) + protected static string GetMessage(string? message) { return message + "; " + supportMsg + supportUrl; } diff --git a/dotnet/src/webdriver/JavaScriptException.cs b/dotnet/src/webdriver/JavaScriptException.cs index 79045e4093c08..a73886f275c8c 100644 --- a/dotnet/src/webdriver/JavaScriptException.cs +++ b/dotnet/src/webdriver/JavaScriptException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public JavaScriptException() /// a specified error message. /// /// The message that describes the error. - public JavaScriptException(string message) + public JavaScriptException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public JavaScriptException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public JavaScriptException(string message, Exception innerException) + public JavaScriptException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs b/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs index d2cbd4ea1f1c0..f678ad1a7bdfb 100644 --- a/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs +++ b/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -41,7 +42,7 @@ public MoveTargetOutOfBoundsException() /// a specified error message. /// /// The message that describes the error. - public MoveTargetOutOfBoundsException(string message) + public MoveTargetOutOfBoundsException(string? message) : base(message) { } @@ -54,7 +55,7 @@ public MoveTargetOutOfBoundsException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public MoveTargetOutOfBoundsException(string message, Exception innerException) + public MoveTargetOutOfBoundsException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/NoAlertPresentException.cs b/dotnet/src/webdriver/NoAlertPresentException.cs index 72c9600280e55..b85f92f25ab68 100644 --- a/dotnet/src/webdriver/NoAlertPresentException.cs +++ b/dotnet/src/webdriver/NoAlertPresentException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public NoAlertPresentException() /// a specified error message. /// /// The message that describes the error. - public NoAlertPresentException(string message) + public NoAlertPresentException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public NoAlertPresentException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public NoAlertPresentException(string message, Exception innerException) + public NoAlertPresentException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/NoSuchDriverException.cs b/dotnet/src/webdriver/NoSuchDriverException.cs index 9bd0bf09be271..996746f096ae0 100644 --- a/dotnet/src/webdriver/NoSuchDriverException.cs +++ b/dotnet/src/webdriver/NoSuchDriverException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -46,7 +47,7 @@ public NoSuchDriverException() /// a specified error message. /// /// The message that describes the error. - public NoSuchDriverException(string message) + public NoSuchDriverException(string? message) : base(GetMessage(message)) { } @@ -59,7 +60,7 @@ public NoSuchDriverException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public NoSuchDriverException(string message, Exception innerException) + public NoSuchDriverException(string? message, Exception? innerException) : base(GetMessage(message), innerException) { } @@ -69,7 +70,7 @@ public NoSuchDriverException(string message, Exception innerException) /// /// The original message for exception /// The final message for exception - protected static string GetMessage(string message) + protected static string GetMessage(string? message) { return message + "; " + supportMsg + supportUrl; } diff --git a/dotnet/src/webdriver/NoSuchElementException.cs b/dotnet/src/webdriver/NoSuchElementException.cs index cfce90ed7e621..c74dfae0c80e7 100644 --- a/dotnet/src/webdriver/NoSuchElementException.cs +++ b/dotnet/src/webdriver/NoSuchElementException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -46,7 +47,7 @@ public NoSuchElementException() /// a specified error message. /// /// The message that describes the error. - public NoSuchElementException(string message) + public NoSuchElementException(string? message) : base(GetMessage(message)) { } @@ -59,7 +60,7 @@ public NoSuchElementException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public NoSuchElementException(string message, Exception innerException) + public NoSuchElementException(string? message, Exception? innerException) : base(GetMessage(message), innerException) { } @@ -69,7 +70,7 @@ public NoSuchElementException(string message, Exception innerException) /// /// The original message for exception /// The final message for exception - protected static string GetMessage(string message) + protected static string GetMessage(string? message) { return message + "; " + supportMsg + supportUrl; } diff --git a/dotnet/src/webdriver/NoSuchFrameException.cs b/dotnet/src/webdriver/NoSuchFrameException.cs index a3c98cef0509e..0f1489009aafe 100644 --- a/dotnet/src/webdriver/NoSuchFrameException.cs +++ b/dotnet/src/webdriver/NoSuchFrameException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public NoSuchFrameException() /// a specified error message. /// /// The message that describes the error. - public NoSuchFrameException(string message) + public NoSuchFrameException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public NoSuchFrameException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public NoSuchFrameException(string message, Exception innerException) + public NoSuchFrameException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/NoSuchShadowRootException.cs b/dotnet/src/webdriver/NoSuchShadowRootException.cs index 32398d6aecfd2..de05a0a05bdb1 100644 --- a/dotnet/src/webdriver/NoSuchShadowRootException.cs +++ b/dotnet/src/webdriver/NoSuchShadowRootException.cs @@ -17,12 +17,13 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { /// - /// The exception that is thrown when a frame is not found. + /// The exception that is thrown when a shadow root is not found. /// [Serializable] public class NoSuchShadowRootException : NotFoundException @@ -40,7 +41,7 @@ public NoSuchShadowRootException() /// a specified error message. /// /// The message that describes the error. - public NoSuchShadowRootException(string message) + public NoSuchShadowRootException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public NoSuchShadowRootException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public NoSuchShadowRootException(string message, Exception innerException) + public NoSuchShadowRootException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/NoSuchWindowException.cs b/dotnet/src/webdriver/NoSuchWindowException.cs index 410d1878e3076..19e77ec0fc2fd 100644 --- a/dotnet/src/webdriver/NoSuchWindowException.cs +++ b/dotnet/src/webdriver/NoSuchWindowException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public NoSuchWindowException() /// a specified error message. /// /// The message that describes the error. - public NoSuchWindowException(string message) + public NoSuchWindowException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public NoSuchWindowException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public NoSuchWindowException(string message, Exception innerException) + public NoSuchWindowException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/NotFoundException.cs b/dotnet/src/webdriver/NotFoundException.cs index 1621ca93d999e..62b2c7938da89 100644 --- a/dotnet/src/webdriver/NotFoundException.cs +++ b/dotnet/src/webdriver/NotFoundException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public NotFoundException() /// a specified error message. /// /// The message that describes the error. - public NotFoundException(string message) + public NotFoundException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public NotFoundException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public NotFoundException(string message, Exception innerException) + public NotFoundException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/Remote/LocalFileDetector.cs b/dotnet/src/webdriver/Remote/LocalFileDetector.cs index de96d629207e5..fe8e95ac742fe 100644 --- a/dotnet/src/webdriver/Remote/LocalFileDetector.cs +++ b/dotnet/src/webdriver/Remote/LocalFileDetector.cs @@ -16,8 +16,11 @@ // limitations under the License. // +using System.Diagnostics.CodeAnalysis; using System.IO; +#nullable enable + namespace OpenQA.Selenium.Remote { /// @@ -32,7 +35,7 @@ public class LocalFileDetector : IFileDetector /// /// The sequence to test for file existence. /// if the key sequence represents a file; otherwise, . - public bool IsFile(string keySequence) + public bool IsFile([NotNullWhen(true)] string? keySequence) { return File.Exists(keySequence); } diff --git a/dotnet/src/webdriver/StackTraceElement.cs b/dotnet/src/webdriver/StackTraceElement.cs index ea78043b24334..dbde23aba669b 100644 --- a/dotnet/src/webdriver/StackTraceElement.cs +++ b/dotnet/src/webdriver/StackTraceElement.cs @@ -20,6 +20,8 @@ using System.Globalization; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium { /// @@ -43,18 +45,18 @@ public StackTraceElement() /// Initializes a new instance of the class using the given property values. /// /// A containing the names and values for the properties of this . - public StackTraceElement(Dictionary elementAttributes) + public StackTraceElement(Dictionary? elementAttributes) { if (elementAttributes != null) { if (elementAttributes.ContainsKey("className") && elementAttributes["className"] != null) { - this.className = elementAttributes["className"].ToString(); + this.className = elementAttributes["className"].ToString() ?? ""; } if (elementAttributes.ContainsKey("methodName") && elementAttributes["methodName"] != null) { - this.methodName = elementAttributes["methodName"].ToString(); + this.methodName = elementAttributes["methodName"].ToString() ?? ""; } if (elementAttributes.ContainsKey("lineNumber")) @@ -68,7 +70,7 @@ public StackTraceElement(Dictionary elementAttributes) if (elementAttributes.ContainsKey("fileName") && elementAttributes["fileName"] != null) { - this.fileName = elementAttributes["fileName"].ToString(); + this.fileName = elementAttributes["fileName"].ToString() ?? ""; } } } diff --git a/dotnet/src/webdriver/StaleElementReferenceException.cs b/dotnet/src/webdriver/StaleElementReferenceException.cs index 81f1fdcd97948..8cda034d7558c 100644 --- a/dotnet/src/webdriver/StaleElementReferenceException.cs +++ b/dotnet/src/webdriver/StaleElementReferenceException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -45,7 +46,7 @@ public StaleElementReferenceException() /// a specified error message. /// /// The message that describes the error. - public StaleElementReferenceException(string message) + public StaleElementReferenceException(string? message) : base(GetMessage(message)) { } @@ -58,7 +59,7 @@ public StaleElementReferenceException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public StaleElementReferenceException(string message, Exception innerException) + public StaleElementReferenceException(string? message, Exception? innerException) : base(GetMessage(message), innerException) { } @@ -68,7 +69,7 @@ public StaleElementReferenceException(string message, Exception innerException) /// /// The original message for exception /// The final message for exception - protected static string GetMessage(string message) + protected static string GetMessage(string? message) { return message + "; " + supportMsg + supportUrl; } diff --git a/dotnet/src/webdriver/UnableToSetCookieException.cs b/dotnet/src/webdriver/UnableToSetCookieException.cs index 6ff0d1ddcf5e8..8c871c16adf3f 100644 --- a/dotnet/src/webdriver/UnableToSetCookieException.cs +++ b/dotnet/src/webdriver/UnableToSetCookieException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public UnableToSetCookieException() /// a specified error message. /// /// The message that describes the error. - public UnableToSetCookieException(string message) + public UnableToSetCookieException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public UnableToSetCookieException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public UnableToSetCookieException(string message, Exception innerException) + public UnableToSetCookieException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/UnhandledAlertException.cs b/dotnet/src/webdriver/UnhandledAlertException.cs index 7126a7326728e..7c918b29abd78 100644 --- a/dotnet/src/webdriver/UnhandledAlertException.cs +++ b/dotnet/src/webdriver/UnhandledAlertException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -27,11 +28,10 @@ namespace OpenQA.Selenium [Serializable] public class UnhandledAlertException : WebDriverException { - private string alertText; - /// /// Initializes a new instance of the class. /// + [Obsolete("Use a constructor overload that sets alertText")] public UnhandledAlertException() : base() { @@ -42,6 +42,7 @@ public UnhandledAlertException() /// a specified error message. /// /// The message that describes the error. + [Obsolete("Use a constructor overload that sets alertText")] public UnhandledAlertException(string message) : base(message) { @@ -56,7 +57,7 @@ public UnhandledAlertException(string message) public UnhandledAlertException(string message, string alertText) : base(message) { - this.alertText = alertText; + this.AlertText = alertText; } /// @@ -67,6 +68,7 @@ public UnhandledAlertException(string message, string alertText) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. + [Obsolete("Use a constructor overload that sets alertText")] public UnhandledAlertException(string message, Exception innerException) : base(message, innerException) { @@ -75,9 +77,6 @@ public UnhandledAlertException(string message, Exception innerException) /// /// Gets the text of the unhandled alert. /// - public string AlertText - { - get { return this.alertText; } - } + public string AlertText { get; } = string.Empty; } } diff --git a/dotnet/src/webdriver/WebDriverArgumentException.cs b/dotnet/src/webdriver/WebDriverArgumentException.cs index dcb16ca2cf38c..57b41fa944d42 100644 --- a/dotnet/src/webdriver/WebDriverArgumentException.cs +++ b/dotnet/src/webdriver/WebDriverArgumentException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public WebDriverArgumentException() /// a specified error message. /// /// The message that describes the error. - public WebDriverArgumentException(string message) + public WebDriverArgumentException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public WebDriverArgumentException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public WebDriverArgumentException(string message, Exception innerException) + public WebDriverArgumentException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/WebDriverException.cs b/dotnet/src/webdriver/WebDriverException.cs index 787f5feed25c4..3eb0cca96c028 100644 --- a/dotnet/src/webdriver/WebDriverException.cs +++ b/dotnet/src/webdriver/WebDriverException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -50,7 +51,7 @@ public WebDriverException() /// a specified error message. /// /// The message that describes the error. - public WebDriverException(string message) + public WebDriverException(string? message) : base(message) { } @@ -63,7 +64,7 @@ public WebDriverException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public WebDriverException(string message, Exception innerException) + public WebDriverException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/WebDriverTimeoutException.cs b/dotnet/src/webdriver/WebDriverTimeoutException.cs index eecadbb915523..df3ac462c9336 100644 --- a/dotnet/src/webdriver/WebDriverTimeoutException.cs +++ b/dotnet/src/webdriver/WebDriverTimeoutException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public WebDriverTimeoutException() /// a specified error message. /// /// The message that describes the error. - public WebDriverTimeoutException(string message) + public WebDriverTimeoutException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public WebDriverTimeoutException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public WebDriverTimeoutException(string message, Exception innerException) + public WebDriverTimeoutException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/XPathLookupException.cs b/dotnet/src/webdriver/XPathLookupException.cs index eca80a9e29aba..064894c94a7a1 100644 --- a/dotnet/src/webdriver/XPathLookupException.cs +++ b/dotnet/src/webdriver/XPathLookupException.cs @@ -17,7 +17,8 @@ // using System; -using System.Runtime.Serialization; + +#nullable enable namespace OpenQA.Selenium { @@ -40,7 +41,7 @@ public XPathLookupException() /// a specified error message. /// /// The message that describes the error. - public XPathLookupException(string message) + public XPathLookupException(string? message) : base(message) { } @@ -53,7 +54,7 @@ public XPathLookupException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public XPathLookupException(string message, Exception innerException) + public XPathLookupException(string? message, Exception? innerException) : base(message, innerException) { } From 7da1cb7665af88946da4242c9b05423262031678 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Tue, 29 Oct 2024 01:16:26 -0400 Subject: [PATCH 02/15] Finish nullability for FirefoxDriver --- dotnet/src/webdriver/Firefox/FirefoxDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs index 48fcdaad3a13c..2d301d82ba7f2 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs @@ -428,7 +428,7 @@ public DevToolsSession GetDevToolsSession(DevToolsOptions options) throw new WebDriverException("Cannot find " + FirefoxDevToolsCapabilityName + " capability for driver"); } - string debuggerAddress = this.Capabilities.GetCapability(FirefoxDevToolsCapabilityName).ToString(); + string? debuggerAddress = this.Capabilities.GetCapability(FirefoxDevToolsCapabilityName).ToString(); try { DevToolsSession session = new DevToolsSession(debuggerAddress, options); From 107d4842814f5b6b4fce45500e6ab1e1683afe0c Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 30 Oct 2024 00:00:24 -0400 Subject: [PATCH 03/15] PR feedbacl --- dotnet/src/webdriver/DefaultFileDetector.cs | 6 +- .../webdriver/DetachedShadowRootException.cs | 4 +- dotnet/src/webdriver/ErrorResponse.cs | 49 +++--- dotnet/src/webdriver/Firefox/FirefoxDriver.cs | 2 + dotnet/src/webdriver/IFileDetector.cs | 6 +- .../webdriver/Internal/NullableAttributes.cs | 149 ------------------ .../src/webdriver/InvalidSelectorException.cs | 2 +- dotnet/src/webdriver/NoSuchDriverException.cs | 2 +- .../src/webdriver/NoSuchElementException.cs | 2 +- .../src/webdriver/Remote/LocalFileDetector.cs | 6 +- dotnet/src/webdriver/StackTraceElement.cs | 31 ++-- .../StaleElementReferenceException.cs | 2 +- 12 files changed, 63 insertions(+), 198 deletions(-) delete mode 100644 dotnet/src/webdriver/Internal/NullableAttributes.cs diff --git a/dotnet/src/webdriver/DefaultFileDetector.cs b/dotnet/src/webdriver/DefaultFileDetector.cs index 51d68e711a993..d8e366f210101 100644 --- a/dotnet/src/webdriver/DefaultFileDetector.cs +++ b/dotnet/src/webdriver/DefaultFileDetector.cs @@ -34,7 +34,11 @@ public class DefaultFileDetector : IFileDetector /// /// The sequence to test for file existence. /// This method always returns in this implementation. - public bool IsFile([NotNullWhen(true)] string? keySequence) + public bool IsFile( +#if NET + [NotNullWhen(true)] +#endif + string? keySequence) { return false; } diff --git a/dotnet/src/webdriver/DetachedShadowRootException.cs b/dotnet/src/webdriver/DetachedShadowRootException.cs index dd794c0b9df69..1eb2fdfeda896 100644 --- a/dotnet/src/webdriver/DetachedShadowRootException.cs +++ b/dotnet/src/webdriver/DetachedShadowRootException.cs @@ -41,7 +41,7 @@ public DetachedShadowRootException() /// a specified error message. /// /// The message that describes the error. - public DetachedShadowRootException(string message) + public DetachedShadowRootException(string? message) : base(message) { } @@ -54,7 +54,7 @@ public DetachedShadowRootException(string message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - public DetachedShadowRootException(string message, Exception innerException) + public DetachedShadowRootException(string? message, Exception? innerException) : base(message, innerException) { } diff --git a/dotnet/src/webdriver/ErrorResponse.cs b/dotnet/src/webdriver/ErrorResponse.cs index 4e08e55e08637..648a38332ab04 100644 --- a/dotnet/src/webdriver/ErrorResponse.cs +++ b/dotnet/src/webdriver/ErrorResponse.cs @@ -44,52 +44,41 @@ public ErrorResponse() /// /// A containing names and values of /// the properties of this . - public ErrorResponse(Dictionary? responseValue) + public ErrorResponse(Dictionary? responseValue) { if (responseValue != null) { - if (responseValue.ContainsKey("message")) + if (responseValue.TryGetValue("message", out object? messageObj) + && messageObj?.ToString() is string message) { - if (responseValue["message"] != null) - { - this.message = responseValue["message"].ToString() ?? ""; - } - else - { - this.message = "The error did not contain a message."; - } + this.message = message; } - - if (responseValue.ContainsKey("screen") && responseValue["screen"] != null) + else { - this.screenshot = responseValue["screen"].ToString() ?? ""; + this.message = "The error did not contain a message."; } - if (responseValue.ContainsKey("class") && responseValue["class"] != null) + if (responseValue.TryGetValue("screen", out object? screenObj) + && screenObj?.ToString() is string screen) { - this.className = responseValue["class"].ToString() ?? ""; + this.screenshot = screen; } - if (responseValue.ContainsKey("stackTrace") || responseValue.ContainsKey("stacktrace")) + if (responseValue.TryGetValue("class", out object? classObj) + && classObj?.ToString() is string @class) { - object[]? stackTraceArray = null; - - if (responseValue.ContainsKey("stackTrace")) - { - stackTraceArray = responseValue["stackTrace"] as object[]; - } - else if (responseValue.ContainsKey("stacktrace")) - { - stackTraceArray = responseValue["stacktrace"] as object[]; - } + this.className = @class; + } - if (stackTraceArray != null) + if (responseValue.TryGetValue("stackTrace", out object? stackTraceObj) + || responseValue.TryGetValue("stacktrace", out stackTraceObj)) + { + if (stackTraceObj is object?[] stackTraceArray) { List stackTraceList = new List(); - foreach (object rawStackTraceElement in stackTraceArray) + foreach (object? rawStackTraceElement in stackTraceArray) { - Dictionary? elementAsDictionary = rawStackTraceElement as Dictionary; - if (elementAsDictionary != null) + if (rawStackTraceElement is Dictionary elementAsDictionary) { stackTraceList.Add(new StackTraceElement(elementAsDictionary)); } diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs index 2d301d82ba7f2..62980a7d87134 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs @@ -249,7 +249,9 @@ public override IFileDetector FileDetector /// /// Gets a value indicating whether a DevTools session is active. /// +#if NET [MemberNotNullWhen(true, nameof(devToolsSession))] +#endif public bool HasActiveDevToolsSession { get { return this.devToolsSession != null; } diff --git a/dotnet/src/webdriver/IFileDetector.cs b/dotnet/src/webdriver/IFileDetector.cs index 370317c07343b..c070934c5037d 100644 --- a/dotnet/src/webdriver/IFileDetector.cs +++ b/dotnet/src/webdriver/IFileDetector.cs @@ -34,6 +34,10 @@ public interface IFileDetector /// /// The sequence to test for file existence. /// if the key sequence represents a file; otherwise, . - bool IsFile([NotNullWhen(true)] string? keySequence); + bool IsFile( +#if NET + [NotNullWhen(true)] +#endif + string? keySequence); } } diff --git a/dotnet/src/webdriver/Internal/NullableAttributes.cs b/dotnet/src/webdriver/Internal/NullableAttributes.cs deleted file mode 100644 index 101978b719999..0000000000000 --- a/dotnet/src/webdriver/Internal/NullableAttributes.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#if !NET8_0_OR_GREATER - -namespace System.Diagnostics.CodeAnalysis -{ - /// Specifies that null is allowed as an input even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class AllowNullAttribute : Attribute - { } - - /// Specifies that null is disallowed as an input even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class DisallowNullAttribute : Attribute - { } - - /// Specifies that an output may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class MaybeNullAttribute : Attribute - { } - - /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class NotNullAttribute : Attribute - { } - - /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class MaybeNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - /// - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } - - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } - - /// Specifies that the output will be non-null if the named parameter is non-null. - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] - internal sealed class NotNullIfNotNullAttribute : Attribute - { - /// Initializes the attribute with the associated parameter name. - /// - /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. - /// - public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; - - /// Gets the associated parameter name. - public string ParameterName { get; } - } - - /// Applied to a method that will never return under any circumstance. - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class DoesNotReturnAttribute : Attribute - { } - - /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal - sealed class DoesNotReturnIfAttribute : Attribute - { - /// Initializes the attribute with the specified parameter value. - /// - /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to - /// the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; - - /// Gets the condition parameter value. - public bool ParameterValue { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullAttribute : Attribute - { - /// Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) => Members = new[] { member }; - - /// Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) => Members = members; - - /// Gets field or property member names. - public string[] Members { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated field or property member will not be null. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = new[] { member }; - } - - /// Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated field and property members will not be null. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// Gets the return value condition. - public bool ReturnValue { get; } - - /// Gets field or property member names. - public string[] Members { get; } - } -} - -#endif diff --git a/dotnet/src/webdriver/InvalidSelectorException.cs b/dotnet/src/webdriver/InvalidSelectorException.cs index 19dd098e731cf..648405e372f27 100644 --- a/dotnet/src/webdriver/InvalidSelectorException.cs +++ b/dotnet/src/webdriver/InvalidSelectorException.cs @@ -71,7 +71,7 @@ public InvalidSelectorException(string? message, Exception? innerException) /// The final message for exception protected static string GetMessage(string? message) { - return message + "; " + supportMsg + supportUrl; + return $"{message}; {supportMsg}{supportUrl}"; } } } diff --git a/dotnet/src/webdriver/NoSuchDriverException.cs b/dotnet/src/webdriver/NoSuchDriverException.cs index 996746f096ae0..7dc475f58237b 100644 --- a/dotnet/src/webdriver/NoSuchDriverException.cs +++ b/dotnet/src/webdriver/NoSuchDriverException.cs @@ -72,7 +72,7 @@ public NoSuchDriverException(string? message, Exception? innerException) /// The final message for exception protected static string GetMessage(string? message) { - return message + "; " + supportMsg + supportUrl; + return $"{message}; {supportMsg}{supportUrl}"; } } } diff --git a/dotnet/src/webdriver/NoSuchElementException.cs b/dotnet/src/webdriver/NoSuchElementException.cs index c74dfae0c80e7..a6e9a81961bf8 100644 --- a/dotnet/src/webdriver/NoSuchElementException.cs +++ b/dotnet/src/webdriver/NoSuchElementException.cs @@ -72,7 +72,7 @@ public NoSuchElementException(string? message, Exception? innerException) /// The final message for exception protected static string GetMessage(string? message) { - return message + "; " + supportMsg + supportUrl; + return $"{message}; {supportMsg}{supportUrl}"; } } } diff --git a/dotnet/src/webdriver/Remote/LocalFileDetector.cs b/dotnet/src/webdriver/Remote/LocalFileDetector.cs index fe8e95ac742fe..24d0b3becb4d9 100644 --- a/dotnet/src/webdriver/Remote/LocalFileDetector.cs +++ b/dotnet/src/webdriver/Remote/LocalFileDetector.cs @@ -35,7 +35,11 @@ public class LocalFileDetector : IFileDetector /// /// The sequence to test for file existence. /// if the key sequence represents a file; otherwise, . - public bool IsFile([NotNullWhen(true)] string? keySequence) + public bool IsFile( +#if NET + [NotNullWhen(true)] +#endif + string? keySequence) { return File.Exists(keySequence); } diff --git a/dotnet/src/webdriver/StackTraceElement.cs b/dotnet/src/webdriver/StackTraceElement.cs index dbde23aba669b..6373d61a8d180 100644 --- a/dotnet/src/webdriver/StackTraceElement.cs +++ b/dotnet/src/webdriver/StackTraceElement.cs @@ -45,32 +45,43 @@ public StackTraceElement() /// Initializes a new instance of the class using the given property values. /// /// A containing the names and values for the properties of this . - public StackTraceElement(Dictionary? elementAttributes) + public StackTraceElement(Dictionary? elementAttributes) { if (elementAttributes != null) { - if (elementAttributes.ContainsKey("className") && elementAttributes["className"] != null) + if (elementAttributes.TryGetValue("className", out object? classNameObj)) { - this.className = elementAttributes["className"].ToString() ?? ""; + string? className = classNameObj?.ToString(); + if (className is not null) + { + this.className = className; + } } - if (elementAttributes.ContainsKey("methodName") && elementAttributes["methodName"] != null) + if (elementAttributes.TryGetValue("methodName", out object? methodNameObj)) { - this.methodName = elementAttributes["methodName"].ToString() ?? ""; + string? methodName = methodNameObj?.ToString(); + if (methodName is not null) + { + this.methodName = methodName; + } } - if (elementAttributes.ContainsKey("lineNumber")) + if (elementAttributes.TryGetValue("lineNumber", out object? lineNumberObj)) { - int line = 0; - if (int.TryParse(elementAttributes["lineNumber"].ToString(), out line)) + if (int.TryParse(lineNumberObj?.ToString(), out int line)) { this.lineNumber = line; } } - if (elementAttributes.ContainsKey("fileName") && elementAttributes["fileName"] != null) + if (elementAttributes.TryGetValue("fileName", out object? fileNameObj)) { - this.fileName = elementAttributes["fileName"].ToString() ?? ""; + string? fileName = fileNameObj?.ToString(); + if (fileName is not null) + { + this.fileName = fileName; + } } } } diff --git a/dotnet/src/webdriver/StaleElementReferenceException.cs b/dotnet/src/webdriver/StaleElementReferenceException.cs index 8cda034d7558c..3762d621a11a8 100644 --- a/dotnet/src/webdriver/StaleElementReferenceException.cs +++ b/dotnet/src/webdriver/StaleElementReferenceException.cs @@ -71,7 +71,7 @@ public StaleElementReferenceException(string? message, Exception? innerException /// The final message for exception protected static string GetMessage(string? message) { - return message + "; " + supportMsg + supportUrl; + return $"{message}; {supportMsg}{supportUrl}"; } } } From 19ea2956813979a289b692ec3eee32aeefd43865 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 30 Oct 2024 00:13:08 -0400 Subject: [PATCH 04/15] Remove exception obsoletion --- dotnet/src/webdriver/UnhandledAlertException.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dotnet/src/webdriver/UnhandledAlertException.cs b/dotnet/src/webdriver/UnhandledAlertException.cs index 7c918b29abd78..9d28e92fe3347 100644 --- a/dotnet/src/webdriver/UnhandledAlertException.cs +++ b/dotnet/src/webdriver/UnhandledAlertException.cs @@ -28,10 +28,14 @@ namespace OpenQA.Selenium [Serializable] public class UnhandledAlertException : WebDriverException { + /// + /// Gets the text of the unhandled alert. + /// + public string AlertText { get; } = string.Empty; + /// /// Initializes a new instance of the class. /// - [Obsolete("Use a constructor overload that sets alertText")] public UnhandledAlertException() : base() { @@ -42,7 +46,6 @@ public UnhandledAlertException() /// a specified error message. /// /// The message that describes the error. - [Obsolete("Use a constructor overload that sets alertText")] public UnhandledAlertException(string message) : base(message) { @@ -68,15 +71,9 @@ public UnhandledAlertException(string message, string alertText) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or if no inner exception is specified. - [Obsolete("Use a constructor overload that sets alertText")] public UnhandledAlertException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Gets the text of the unhandled alert. - /// - public string AlertText { get; } = string.Empty; } } From b52cf3556ba126ff6b9eca1851d6a1bd465d8a26 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 30 Oct 2024 12:45:23 -0400 Subject: [PATCH 05/15] Make ErrorResponse fields nullable --- dotnet/src/webdriver/ErrorResponse.cs | 47 +++++++-------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/dotnet/src/webdriver/ErrorResponse.cs b/dotnet/src/webdriver/ErrorResponse.cs index 648a38332ab04..1f65f77161e32 100644 --- a/dotnet/src/webdriver/ErrorResponse.cs +++ b/dotnet/src/webdriver/ErrorResponse.cs @@ -27,11 +27,6 @@ namespace OpenQA.Selenium /// public class ErrorResponse { - private StackTraceElement[]? stackTrace; - private string message = string.Empty; - private string className = string.Empty; - private string screenshot = string.Empty; - /// /// Initializes a new instance of the class. /// @@ -51,23 +46,21 @@ public ErrorResponse(Dictionary? responseValue) if (responseValue.TryGetValue("message", out object? messageObj) && messageObj?.ToString() is string message) { - this.message = message; + this.Message = message; } else { - this.message = "The error did not contain a message."; + this.Message = "The error did not contain a message."; } - if (responseValue.TryGetValue("screen", out object? screenObj) - && screenObj?.ToString() is string screen) + if (responseValue.TryGetValue("screen", out object? screenObj)) { - this.screenshot = screen; + this.Screenshot = screenObj?.ToString(); } - if (responseValue.TryGetValue("class", out object? classObj) - && classObj?.ToString() is string @class) + if (responseValue.TryGetValue("class", out object? classObj)) { - this.className = @class; + this.ClassName = classObj?.ToString(); } if (responseValue.TryGetValue("stackTrace", out object? stackTraceObj) @@ -84,7 +77,7 @@ public ErrorResponse(Dictionary? responseValue) } } - this.stackTrace = stackTraceList.ToArray(); + this.StackTrace = stackTraceList.ToArray(); } } } @@ -93,38 +86,22 @@ public ErrorResponse(Dictionary? responseValue) /// /// Gets or sets the message from the response /// - public string Message - { - get { return this.message; } - set { this.message = value; } - } + public string? Message { get; } = string.Empty; /// /// Gets or sets the class name that threw the error /// - public string ClassName - { - get { return this.className; } - set { this.className = value; } - } + public string? ClassName { get; } + // TODO: (JimEvans) Change this to return an Image. /// /// Gets or sets the screenshot of the error /// - public string Screenshot - { - // TODO: (JimEvans) Change this to return an Image. - get { return this.screenshot; } - set { this.screenshot = value; } - } + public string? Screenshot { get; } /// /// Gets or sets the stack trace of the error /// - public StackTraceElement[]? StackTrace - { - get { return this.stackTrace; } - set { this.stackTrace = value; } - } + public StackTraceElement[]? StackTrace { get; } } } From 8c8367a5fc7fd6297bc4161d79593a5fa92e2233 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 30 Oct 2024 12:50:32 -0400 Subject: [PATCH 06/15] fix exception XML --- dotnet/src/webdriver/Firefox/FirefoxDriver.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs index 62980a7d87134..4d177ece0c5da 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs @@ -294,11 +294,8 @@ public void SetContext(FirefoxCommandContext context) /// Full path of the directory of the add-on to install. /// Whether the add-on is temporary; required for unsigned add-ons. /// The add-on ID. - /// - /// If is null or empty. - /// or - /// If the directory at does not exist. - /// + /// If is null or empty. + /// If the directory at does not exist. public string InstallAddOnFromDirectory(string addOnDirectoryToInstall, bool temporary = false) { if (string.IsNullOrEmpty(addOnDirectoryToInstall)) From 9e9bc8433e8fa70c11754ee9d8903e2706121a1b Mon Sep 17 00:00:00 2001 From: Michael Render Date: Fri, 1 Nov 2024 23:15:11 -0400 Subject: [PATCH 07/15] synchronize preprocessor directives on `NET8_0_OR_GREATER` --- dotnet/src/webdriver/DefaultFileDetector.cs | 6 ++---- dotnet/src/webdriver/Firefox/FirefoxDriver.cs | 5 ++--- dotnet/src/webdriver/IFileDetector.cs | 6 ++---- dotnet/src/webdriver/Remote/LocalFileDetector.cs | 5 ++--- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/dotnet/src/webdriver/DefaultFileDetector.cs b/dotnet/src/webdriver/DefaultFileDetector.cs index d8e366f210101..219e2197947a8 100644 --- a/dotnet/src/webdriver/DefaultFileDetector.cs +++ b/dotnet/src/webdriver/DefaultFileDetector.cs @@ -18,8 +18,6 @@ #nullable enable -using System.Diagnostics.CodeAnalysis; - namespace OpenQA.Selenium { /// @@ -35,8 +33,8 @@ public class DefaultFileDetector : IFileDetector /// The sequence to test for file existence. /// This method always returns in this implementation. public bool IsFile( -#if NET - [NotNullWhen(true)] +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? keySequence) { diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs index 4d177ece0c5da..5171a13a40ebf 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs @@ -21,7 +21,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; @@ -249,8 +248,8 @@ public override IFileDetector FileDetector /// /// Gets a value indicating whether a DevTools session is active. /// -#if NET - [MemberNotNullWhen(true, nameof(devToolsSession))] +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.MemberNotNullWhen(true, nameof(devToolsSession))] #endif public bool HasActiveDevToolsSession { diff --git a/dotnet/src/webdriver/IFileDetector.cs b/dotnet/src/webdriver/IFileDetector.cs index c070934c5037d..bfe236bdcd75c 100644 --- a/dotnet/src/webdriver/IFileDetector.cs +++ b/dotnet/src/webdriver/IFileDetector.cs @@ -16,8 +16,6 @@ // limitations under the License. // -using System.Diagnostics.CodeAnalysis; - #nullable enable namespace OpenQA.Selenium @@ -35,8 +33,8 @@ public interface IFileDetector /// The sequence to test for file existence. /// if the key sequence represents a file; otherwise, . bool IsFile( -#if NET - [NotNullWhen(true)] +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? keySequence); } diff --git a/dotnet/src/webdriver/Remote/LocalFileDetector.cs b/dotnet/src/webdriver/Remote/LocalFileDetector.cs index 24d0b3becb4d9..a092fda4e8da9 100644 --- a/dotnet/src/webdriver/Remote/LocalFileDetector.cs +++ b/dotnet/src/webdriver/Remote/LocalFileDetector.cs @@ -16,7 +16,6 @@ // limitations under the License. // -using System.Diagnostics.CodeAnalysis; using System.IO; #nullable enable @@ -36,8 +35,8 @@ public class LocalFileDetector : IFileDetector /// The sequence to test for file existence. /// if the key sequence represents a file; otherwise, . public bool IsFile( -#if NET - [NotNullWhen(true)] +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] #endif string? keySequence) { From 652001007f0bb7a092fa97f9b1f5e77da16a7bc7 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Mon, 4 Nov 2024 14:36:14 -0500 Subject: [PATCH 08/15] Add nullable reference type attribute polyfill --- dotnet/src/webdriver/DefaultFileDetector.cs | 8 +- dotnet/src/webdriver/Firefox/FirefoxDriver.cs | 5 +- dotnet/src/webdriver/IFileDetector.cs | 8 +- .../webdriver/Internal/NullableAttributes.cs | 165 ++++++++++++++++++ .../src/webdriver/Remote/LocalFileDetector.cs | 7 +- 5 files changed, 175 insertions(+), 18 deletions(-) create mode 100644 dotnet/src/webdriver/Internal/NullableAttributes.cs diff --git a/dotnet/src/webdriver/DefaultFileDetector.cs b/dotnet/src/webdriver/DefaultFileDetector.cs index 219e2197947a8..51d68e711a993 100644 --- a/dotnet/src/webdriver/DefaultFileDetector.cs +++ b/dotnet/src/webdriver/DefaultFileDetector.cs @@ -18,6 +18,8 @@ #nullable enable +using System.Diagnostics.CodeAnalysis; + namespace OpenQA.Selenium { /// @@ -32,11 +34,7 @@ public class DefaultFileDetector : IFileDetector /// /// The sequence to test for file existence. /// This method always returns in this implementation. - public bool IsFile( -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] -#endif - string? keySequence) + public bool IsFile([NotNullWhen(true)] string? keySequence) { return false; } diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs index 5171a13a40ebf..399a50e9829b1 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; @@ -248,9 +249,7 @@ public override IFileDetector FileDetector /// /// Gets a value indicating whether a DevTools session is active. /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.MemberNotNullWhen(true, nameof(devToolsSession))] -#endif + [MemberNotNullWhen(true, nameof(devToolsSession))] public bool HasActiveDevToolsSession { get { return this.devToolsSession != null; } diff --git a/dotnet/src/webdriver/IFileDetector.cs b/dotnet/src/webdriver/IFileDetector.cs index bfe236bdcd75c..5273e1ab005dc 100644 --- a/dotnet/src/webdriver/IFileDetector.cs +++ b/dotnet/src/webdriver/IFileDetector.cs @@ -18,6 +18,8 @@ #nullable enable +using System.Diagnostics.CodeAnalysis; + namespace OpenQA.Selenium { /// @@ -32,10 +34,6 @@ public interface IFileDetector /// /// The sequence to test for file existence. /// if the key sequence represents a file; otherwise, . - bool IsFile( -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] -#endif - string? keySequence); + bool IsFile([NotNullWhen(true)] string? keySequence); } } diff --git a/dotnet/src/webdriver/Internal/NullableAttributes.cs b/dotnet/src/webdriver/Internal/NullableAttributes.cs new file mode 100644 index 0000000000000..409d5d3a1cb1a --- /dev/null +++ b/dotnet/src/webdriver/Internal/NullableAttributes.cs @@ -0,0 +1,165 @@ +// +// 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. +// + +#if !NET8_0_OR_GREATER + +// Original code in https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs + +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute + { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute + { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute + { } + + /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute + { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute + { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated field or property member will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated field and property members will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } +} + +#endif diff --git a/dotnet/src/webdriver/Remote/LocalFileDetector.cs b/dotnet/src/webdriver/Remote/LocalFileDetector.cs index a092fda4e8da9..fe8e95ac742fe 100644 --- a/dotnet/src/webdriver/Remote/LocalFileDetector.cs +++ b/dotnet/src/webdriver/Remote/LocalFileDetector.cs @@ -16,6 +16,7 @@ // limitations under the License. // +using System.Diagnostics.CodeAnalysis; using System.IO; #nullable enable @@ -34,11 +35,7 @@ public class LocalFileDetector : IFileDetector /// /// The sequence to test for file existence. /// if the key sequence represents a file; otherwise, . - public bool IsFile( -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] -#endif - string? keySequence) + public bool IsFile([NotNullWhen(true)] string? keySequence) { return File.Exists(keySequence); } From 07375e7270f6462c4d2a80de35fbdaf4f1af401d Mon Sep 17 00:00:00 2001 From: Michael Render Date: Mon, 4 Nov 2024 16:27:27 -0500 Subject: [PATCH 09/15] make `ErrorResponse.Message` non-nullable --- dotnet/src/webdriver/ErrorResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/ErrorResponse.cs b/dotnet/src/webdriver/ErrorResponse.cs index 1f65f77161e32..3e4f64dd03f8f 100644 --- a/dotnet/src/webdriver/ErrorResponse.cs +++ b/dotnet/src/webdriver/ErrorResponse.cs @@ -86,7 +86,7 @@ public ErrorResponse(Dictionary? responseValue) /// /// Gets or sets the message from the response /// - public string? Message { get; } = string.Empty; + public string Message { get; } = string.Empty; /// /// Gets or sets the class name that threw the error From 7a102a43438873a6e87a7fd8b2988f692a0df335 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Tue, 5 Nov 2024 14:43:35 -0500 Subject: [PATCH 10/15] Add more meaningful explanation of nullable attribute polyfill --- dotnet/src/webdriver/Internal/NullableAttributes.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/dotnet/src/webdriver/Internal/NullableAttributes.cs b/dotnet/src/webdriver/Internal/NullableAttributes.cs index 409d5d3a1cb1a..834dcc0b5d261 100644 --- a/dotnet/src/webdriver/Internal/NullableAttributes.cs +++ b/dotnet/src/webdriver/Internal/NullableAttributes.cs @@ -18,6 +18,7 @@ #if !NET8_0_OR_GREATER +// Following polyfill guidance explained here https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#targetframeworks // Original code in https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs namespace System.Diagnostics.CodeAnalysis From 67a40dd7dd902915a3ae571f5777cf9a2774624a Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 17 Nov 2024 13:08:19 -0500 Subject: [PATCH 11/15] Update dotnet/src/webdriver/Internal/NullableAttributes.cs --- dotnet/src/webdriver/Internal/NullableAttributes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/Internal/NullableAttributes.cs b/dotnet/src/webdriver/Internal/NullableAttributes.cs index 834dcc0b5d261..a853de0aff4ec 100644 --- a/dotnet/src/webdriver/Internal/NullableAttributes.cs +++ b/dotnet/src/webdriver/Internal/NullableAttributes.cs @@ -1,4 +1,4 @@ -// +// // 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 From ef7a735176a56274b711088b4b19e1ed0a467ed3 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 17 Nov 2024 13:21:19 -0500 Subject: [PATCH 12/15] Set nullability after using statements --- dotnet/src/webdriver/DefaultFileDetector.cs | 4 ++-- dotnet/src/webdriver/IFileDetector.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dotnet/src/webdriver/DefaultFileDetector.cs b/dotnet/src/webdriver/DefaultFileDetector.cs index 51d68e711a993..691380f79a751 100644 --- a/dotnet/src/webdriver/DefaultFileDetector.cs +++ b/dotnet/src/webdriver/DefaultFileDetector.cs @@ -16,10 +16,10 @@ // limitations under the License. // -#nullable enable - using System.Diagnostics.CodeAnalysis; +#nullable enable + namespace OpenQA.Selenium { /// diff --git a/dotnet/src/webdriver/IFileDetector.cs b/dotnet/src/webdriver/IFileDetector.cs index 5273e1ab005dc..370317c07343b 100644 --- a/dotnet/src/webdriver/IFileDetector.cs +++ b/dotnet/src/webdriver/IFileDetector.cs @@ -16,10 +16,10 @@ // limitations under the License. // -#nullable enable - using System.Diagnostics.CodeAnalysis; +#nullable enable + namespace OpenQA.Selenium { /// From b69bd019e3e3f3e0ff132897e673a1da6a0ac2ce Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 17 Nov 2024 15:50:29 -0500 Subject: [PATCH 13/15] remove nullability from `FirefoxDriver` --- dotnet/src/webdriver/Firefox/FirefoxDriver.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs index 869339039aa11..c65c4f66563f6 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs @@ -22,14 +22,11 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Threading.Tasks; -#nullable enable - namespace OpenQA.Selenium.Firefox { /// @@ -113,7 +110,7 @@ public class FirefoxDriver : WebDriver, IDevTools { GetFullPageScreenshotCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/moz/screenshot/full") } }; - private DevToolsSession? devToolsSession; + private DevToolsSession devToolsSession; /// /// Initializes a new instance of the class. @@ -250,7 +247,6 @@ public override IFileDetector FileDetector /// /// Gets a value indicating whether a DevTools session is active. /// - [MemberNotNullWhen(true, nameof(devToolsSession))] public bool HasActiveDevToolsSession { get { return this.devToolsSession != null; } @@ -264,7 +260,7 @@ public bool HasActiveDevToolsSession public FirefoxCommandContext GetContext() { FirefoxCommandContext output; - string? response = this.Execute(GetContextCommand, null).Value.ToString(); + string response = this.Execute(GetContextCommand, null).Value.ToString(); bool success = Enum.TryParse(response, true, out output); if (!success) @@ -389,7 +385,7 @@ public void UninstallAddOn(string addOnId) public Screenshot GetFullPageScreenshot() { Response screenshotResponse = this.Execute(GetFullPageScreenshotCommand, null); - string base64 = screenshotResponse.Value.ToString()!; + string base64 = screenshotResponse.Value.ToString(); return new Screenshot(base64); } @@ -426,7 +422,7 @@ public DevToolsSession GetDevToolsSession(DevToolsOptions options) throw new WebDriverException("Cannot find " + FirefoxDevToolsCapabilityName + " capability for driver"); } - string? debuggerAddress = this.Capabilities.GetCapability(FirefoxDevToolsCapabilityName).ToString(); + string debuggerAddress = this.Capabilities.GetCapability(FirefoxDevToolsCapabilityName).ToString(); try { DevToolsSession session = new DevToolsSession(debuggerAddress, options); From f88124cd9d7d1ee0a7e134bef53ef71c84266946 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 17 Nov 2024 15:52:23 -0500 Subject: [PATCH 14/15] Refer to firefox add-on ID as unique identifier --- dotnet/src/webdriver/Firefox/FirefoxDriver.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs index c65c4f66563f6..da3061c897d8b 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs @@ -288,7 +288,7 @@ public void SetContext(FirefoxCommandContext context) /// /// Full path of the directory of the add-on to install. /// Whether the add-on is temporary; required for unsigned add-ons. - /// The add-on ID. + /// The unique identifier of the installed add-on. /// If is null or empty. /// If the directory at does not exist. public string InstallAddOnFromDirectory(string addOnDirectoryToInstall, bool temporary = false) @@ -314,7 +314,7 @@ public string InstallAddOnFromDirectory(string addOnDirectoryToInstall, bool tem /// /// Full path and file name of the add-on to install. /// Whether the add-on is temporary; required for unsigned add-ons. - /// The add-on ID. + /// The unique identifier of the installed add-on. /// /// If is null or empty. /// or @@ -343,7 +343,7 @@ public string InstallAddOnFromFile(string addOnFileToInstall, bool temporary = f /// /// The base64-encoded string representation of the add-on binary. /// Whether the add-on is temporary; required for unsigned add-ons. - /// The add-on ID. + /// The unique identifier of the installed add-on. /// If is null or empty. public string InstallAddOn(string base64EncodedAddOn, bool temporary = false) { From e2d0d7c546d5ca9f1eb6d0ee9a81fc48f462fcf8 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 17 Nov 2024 16:25:13 -0500 Subject: [PATCH 15/15] fix CLS compliance for `NullableAttributes.cs` --- .../webdriver/Internal/NullableAttributes.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/dotnet/src/webdriver/Internal/NullableAttributes.cs b/dotnet/src/webdriver/Internal/NullableAttributes.cs index a853de0aff4ec..bbb123a0a86e2 100644 --- a/dotnet/src/webdriver/Internal/NullableAttributes.cs +++ b/dotnet/src/webdriver/Internal/NullableAttributes.cs @@ -1,21 +1,23 @@ -// +// // Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file +// 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 +// 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 +// 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. +// 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. // + #if !NET8_0_OR_GREATER // Following polyfill guidance explained here https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#targetframeworks