diff --git a/dotnet/BUILD.bazel b/dotnet/BUILD.bazel index 44ded9c69201b..da92a9358747b 100644 --- a/dotnet/BUILD.bazel +++ b/dotnet/BUILD.bazel @@ -26,3 +26,12 @@ pkg_zip( "release-artifact", ], ) + +filegroup( + name = "source_files_support_needs_from_core", + srcs = [ + "//dotnet/src/webdriver:Internal/StringSyntaxAttribute.cs", + "//dotnet/src/webdriver:Internal/StringSyntaxConstants.cs", + ], + visibility = ["__subpackages__"], +) diff --git a/dotnet/src/support/BUILD.bazel b/dotnet/src/support/BUILD.bazel index 930e842870ee8..9cc66665c7618 100644 --- a/dotnet/src/support/BUILD.bazel +++ b/dotnet/src/support/BUILD.bazel @@ -32,7 +32,10 @@ csharp_library( "Events/*.cs", "Extensions/*.cs", "UI/*.cs", - ]) + [":assembly-info"], + ]) + [ + ":assembly-info", + "//dotnet:source_files_support_needs_from_core", + ], out = "WebDriver.Support", langversion = "12.0", nullable = "enable", @@ -71,7 +74,10 @@ csharp_library( "Events/*.cs", "Extensions/*.cs", "UI/*.cs", - ]) + [":assembly-info"], + ]) + [ + ":assembly-info", + "//dotnet:source_files_support_needs_from_core", + ], out = "WebDriver.Support.StrongNamed", keyfile = "//dotnet:Selenium.snk", langversion = "12.0", diff --git a/dotnet/src/support/Events/EventFiringWebDriver.cs b/dotnet/src/support/Events/EventFiringWebDriver.cs index a23c4a04bf9ed..974843dbce1e4 100644 --- a/dotnet/src/support/Events/EventFiringWebDriver.cs +++ b/dotnet/src/support/Events/EventFiringWebDriver.cs @@ -20,8 +20,10 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Threading.Tasks; +using OpenQA.Selenium.Internal; namespace OpenQA.Selenium.Support.Events; @@ -442,7 +444,7 @@ public void Dispose() /// variable, as if the function were called via "Function.apply" /// /// - public object? ExecuteScript(string script, params object?[] args) + public object? ExecuteScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[] args) { if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) { @@ -542,7 +544,7 @@ public void Dispose() /// The JavaScript code to execute. /// The arguments to the script. /// The value returned by the script. - public object? ExecuteAsyncScript(string script, params object?[] args) + public object? ExecuteAsyncScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[] args) { if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) { diff --git a/dotnet/src/support/Events/WebDriverScriptEventArgs.cs b/dotnet/src/support/Events/WebDriverScriptEventArgs.cs index be198ecf62945..ab9bfd254eccb 100644 --- a/dotnet/src/support/Events/WebDriverScriptEventArgs.cs +++ b/dotnet/src/support/Events/WebDriverScriptEventArgs.cs @@ -18,6 +18,8 @@ // using System; +using System.Diagnostics.CodeAnalysis; +using OpenQA.Selenium.Internal; namespace OpenQA.Selenium.Support.Events; @@ -32,7 +34,7 @@ public class WebDriverScriptEventArgs : EventArgs /// The WebDriver instance used to execute the script. /// The script executed by the driver. /// If or are . - public WebDriverScriptEventArgs(IWebDriver driver, string script) + public WebDriverScriptEventArgs(IWebDriver driver, [StringSyntax(StringSyntaxConstants.JavaScript)] string script) { this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); this.Script = script ?? throw new ArgumentNullException(nameof(script)); diff --git a/dotnet/src/support/Extensions/WebDriverExtensions.cs b/dotnet/src/support/Extensions/WebDriverExtensions.cs index 8ef995719dfda..64b81bdc62caa 100644 --- a/dotnet/src/support/Extensions/WebDriverExtensions.cs +++ b/dotnet/src/support/Extensions/WebDriverExtensions.cs @@ -18,7 +18,9 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; +using OpenQA.Selenium.Internal; namespace OpenQA.Selenium.Support.Extensions; @@ -71,7 +73,7 @@ public static Screenshot TakeScreenshot(this IWebDriver driver) /// The arguments to the script. /// Thrown if this instance /// does not implement - public static void ExecuteJavaScript(this IWebDriver driver, string script, params object?[] args) + public static void ExecuteJavaScript(this IWebDriver driver, [StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[] args) { ExecuteJavaScriptInternal(driver, script, args); } @@ -87,7 +89,7 @@ public static void ExecuteJavaScript(this IWebDriver driver, string script, para /// Thrown if this instance /// does not implement , or if the actual return type /// of the JavaScript execution does not match the expected type. - public static T? ExecuteJavaScript(this IWebDriver driver, string script, params object?[] args) + public static T? ExecuteJavaScript(this IWebDriver driver, [StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[] args) { var value = ExecuteJavaScriptInternal(driver, script, args); if (value == null) diff --git a/dotnet/src/support/Selenium.WebDriver.Support.csproj b/dotnet/src/support/Selenium.WebDriver.Support.csproj index 1ddf670d553c8..9c0e10cd7a79e 100644 --- a/dotnet/src/support/Selenium.WebDriver.Support.csproj +++ b/dotnet/src/support/Selenium.WebDriver.Support.csproj @@ -40,4 +40,10 @@ + + + + + + diff --git a/dotnet/src/webdriver/BUILD.bazel b/dotnet/src/webdriver/BUILD.bazel index 0d57751f521e1..2d6a8735d4e77 100644 --- a/dotnet/src/webdriver/BUILD.bazel +++ b/dotnet/src/webdriver/BUILD.bazel @@ -12,6 +12,8 @@ load( exports_files([ "WebDriver.csproj", + "Internal/StringSyntaxAttribute.cs", + "Internal/StringSyntaxConstants.cs", ]) generated_assembly_info( diff --git a/dotnet/src/webdriver/DevTools/JavaScript.cs b/dotnet/src/webdriver/DevTools/JavaScript.cs index 5ef23219b5563..69f488397754d 100644 --- a/dotnet/src/webdriver/DevTools/JavaScript.cs +++ b/dotnet/src/webdriver/DevTools/JavaScript.cs @@ -18,7 +18,9 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; +using OpenQA.Selenium.Internal; namespace OpenQA.Selenium.DevTools; @@ -85,7 +87,7 @@ public abstract class JavaScript /// /// The script to add to be evaluated when a new document is opened. /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. - public abstract Task AddScriptToEvaluateOnNewDocument(string script); + public abstract Task AddScriptToEvaluateOnNewDocument([StringSyntax(StringSyntaxConstants.JavaScript)] string script); /// /// Removes a JavaScript snippet from evaluate when a new document is opened. @@ -103,7 +105,7 @@ public abstract class JavaScript /// This method is internal to the operation of pinned scripts in Selenium, and /// is therefore internal by design. /// - internal abstract Task Evaluate(string script); + internal abstract Task Evaluate([StringSyntax(StringSyntaxConstants.JavaScript)] string script); /// /// Raises the BindingCalled event. diff --git a/dotnet/src/webdriver/IJavaScriptEngine.cs b/dotnet/src/webdriver/IJavaScriptEngine.cs index 0ae8bf8cafb7f..2dd71d6bc1fd8 100644 --- a/dotnet/src/webdriver/IJavaScriptEngine.cs +++ b/dotnet/src/webdriver/IJavaScriptEngine.cs @@ -19,7 +19,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; +using OpenQA.Selenium.Internal; namespace OpenQA.Selenium; @@ -88,7 +90,7 @@ public interface IJavaScriptEngine : IDisposable /// The JavaScript to be loaded on every page. /// A task containing an object representing the script to be loaded on each page. /// If or are . - Task AddInitializationScript(string scriptName, string script); + Task AddInitializationScript(string scriptName, [StringSyntax(StringSyntaxConstants.JavaScript)] string script); /// /// Asynchronously removes JavaScript from being loaded on every document load. @@ -112,7 +114,7 @@ public interface IJavaScriptEngine : IDisposable /// The JavaScript to pin /// A task containing a object to use to execute the script. /// If is . - Task PinScript(string script); + Task PinScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script); /// /// Unpins a previously pinned script from the browser. diff --git a/dotnet/src/webdriver/IJavascriptExecutor.cs b/dotnet/src/webdriver/IJavascriptExecutor.cs index 5b1fe570e02b3..9f4de14c29c32 100644 --- a/dotnet/src/webdriver/IJavascriptExecutor.cs +++ b/dotnet/src/webdriver/IJavascriptExecutor.cs @@ -19,6 +19,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using OpenQA.Selenium.Internal; namespace OpenQA.Selenium; @@ -62,7 +64,7 @@ public interface IJavaScriptExecutor /// variable, as if the function were called via "Function.apply" /// /// - object? ExecuteScript(string script, params object?[] args); + object? ExecuteScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[] args); /// /// Executes JavaScript in the context of the currently selected frame or window. @@ -108,5 +110,5 @@ public interface IJavaScriptExecutor /// The JavaScript code to execute. /// The arguments to the script. /// The value returned by the script. - object? ExecuteAsyncScript(string script, params object?[] args); + object? ExecuteAsyncScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[] args); } diff --git a/dotnet/src/webdriver/InitializationScript.cs b/dotnet/src/webdriver/InitializationScript.cs index 189cf02b88888..6cf16079c68ac 100644 --- a/dotnet/src/webdriver/InitializationScript.cs +++ b/dotnet/src/webdriver/InitializationScript.cs @@ -18,7 +18,9 @@ // using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; +using OpenQA.Selenium.Internal; namespace OpenQA.Selenium; @@ -47,6 +49,7 @@ internal InitializationScript(string scriptId, string scriptName, string scriptS /// /// Gets the JavaScript source of the initialization script. /// + [StringSyntax(StringSyntaxConstants.JavaScript)] public string ScriptSource { get; } /// diff --git a/dotnet/src/webdriver/Internal/FileUtilities.cs b/dotnet/src/webdriver/Internal/FileUtilities.cs index 1603cdbde8ea5..e74dcabc0a93b 100644 --- a/dotnet/src/webdriver/Internal/FileUtilities.cs +++ b/dotnet/src/webdriver/Internal/FileUtilities.cs @@ -19,6 +19,7 @@ using OpenQA.Selenium.Internal.Logging; using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Reflection; @@ -209,11 +210,7 @@ public static string GetCurrentDirectory() /// The pattern to use in creating the directory name, following standard /// .NET string replacement tokens. /// The full path to the random directory name in the temporary directory. - public static string GenerateRandomTempDirectoryName( -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.StringSyntax(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.CompositeFormat)] -#endif - string directoryPattern) + public static string GenerateRandomTempDirectoryName([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string directoryPattern) { string directoryName = string.Format(CultureInfo.InvariantCulture, directoryPattern, Guid.NewGuid().ToString("N")); return Path.Combine(Path.GetTempPath(), directoryName); diff --git a/dotnet/src/webdriver/Internal/StringSyntaxAttribute.cs b/dotnet/src/webdriver/Internal/StringSyntaxAttribute.cs new file mode 100644 index 0000000000000..bbb12f6a804b1 --- /dev/null +++ b/dotnet/src/webdriver/Internal/StringSyntaxAttribute.cs @@ -0,0 +1,89 @@ +// +// 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 + +namespace System.Diagnostics.CodeAnalysis; + +/// Specifies the syntax used in a string. +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +internal sealed class StringSyntaxAttribute : Attribute +{ + /// Initializes the with the identifier of the syntax used. + /// The syntax identifier. + public StringSyntaxAttribute(string syntax) + { + Syntax = syntax; + Arguments = []; + } + + /// Initializes the with the identifier of the syntax used. + /// The syntax identifier. + /// Optional arguments associated with the specific syntax employed. + public StringSyntaxAttribute(string syntax, params object?[] arguments) + { + Syntax = syntax; + Arguments = arguments; + } + + /// Gets the identifier of the syntax used. + public string Syntax { get; } + + /// Optional arguments associated with the specific syntax employed. + public object?[] Arguments { get; } + + /// The syntax identifier for strings containing composite formats for string formatting. + public const string CompositeFormat = nameof(CompositeFormat); + + /// The syntax identifier for strings containing date format specifiers. + public const string DateOnlyFormat = nameof(DateOnlyFormat); + + /// The syntax identifier for strings containing date and time format specifiers. + public const string DateTimeFormat = nameof(DateTimeFormat); + + /// The syntax identifier for strings containing format specifiers. + public const string EnumFormat = nameof(EnumFormat); + + /// The syntax identifier for strings containing format specifiers. + public const string GuidFormat = nameof(GuidFormat); + + /// The syntax identifier for strings containing JavaScript Object Notation (JSON). + public const string Json = nameof(Json); + + /// The syntax identifier for strings containing numeric format specifiers. + public const string NumericFormat = nameof(NumericFormat); + + /// The syntax identifier for strings containing regular expressions. + public const string Regex = nameof(Regex); + + /// The syntax identifier for strings containing time format specifiers. + public const string TimeOnlyFormat = nameof(TimeOnlyFormat); + + /// The syntax identifier for strings containing format specifiers. + public const string TimeSpanFormat = nameof(TimeSpanFormat); + + /// The syntax identifier for strings containing URIs. + public const string Uri = nameof(Uri); + + /// The syntax identifier for strings containing XML. + public const string Xml = nameof(Xml); +} + +#endif + diff --git a/dotnet/src/webdriver/Internal/StringSyntaxConstants.cs b/dotnet/src/webdriver/Internal/StringSyntaxConstants.cs new file mode 100644 index 0000000000000..55547e66244bc --- /dev/null +++ b/dotnet/src/webdriver/Internal/StringSyntaxConstants.cs @@ -0,0 +1,25 @@ +// +// 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. +// + +namespace OpenQA.Selenium.Internal; + +internal static class StringSyntaxConstants +{ + public const string JavaScript = "javascript"; +} diff --git a/dotnet/src/webdriver/JavaScriptEngine.cs b/dotnet/src/webdriver/JavaScriptEngine.cs index d44f786a06943..86f20c64c6f2c 100644 --- a/dotnet/src/webdriver/JavaScriptEngine.cs +++ b/dotnet/src/webdriver/JavaScriptEngine.cs @@ -164,7 +164,7 @@ public async Task DisableDomMutationMonitoring() /// The JavaScript to be loaded on every page. /// A task containing an object representing the script to be loaded on each page. /// If or are . - public async Task AddInitializationScript(string scriptName, string script) + public async Task AddInitializationScript(string scriptName, [StringSyntax(StringSyntaxConstants.JavaScript)] string script) { if (scriptName is null) { @@ -233,7 +233,7 @@ public async Task ClearInitializationScripts() /// The JavaScript to pin /// A task containing a object to use to execute the script. /// If is . - public async Task PinScript(string script) + public async Task PinScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script) { if (script == null) { diff --git a/dotnet/src/webdriver/PinnedScript.cs b/dotnet/src/webdriver/PinnedScript.cs index 158f7683c9fdd..a51fea3d0cc60 100644 --- a/dotnet/src/webdriver/PinnedScript.cs +++ b/dotnet/src/webdriver/PinnedScript.cs @@ -17,7 +17,9 @@ // under the License. // +using System.Diagnostics.CodeAnalysis; using System.Globalization; +using OpenQA.Selenium.Internal; namespace OpenQA.Selenium; @@ -53,6 +55,7 @@ internal PinnedScript(string script, string stringHandle, string scriptId) /// /// Gets the source representing the body of the function in the pinned script. /// + [StringSyntax(StringSyntaxConstants.JavaScript)] public string Source { get; } internal static string MakeCreationScript(string scriptHandle, string scriptSource) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 77f82461053d7..45181ed88a2c0 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -234,7 +234,7 @@ public void Dispose() /// The JavaScript code to execute. /// The arguments to the script. /// The value returned by the script. - public object? ExecuteAsyncScript(string script, params object?[]? args) + public object? ExecuteAsyncScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[]? args) { return this.ExecuteScriptCommand(script, DriverCommand.ExecuteAsyncScript, args); } @@ -245,7 +245,7 @@ public void Dispose() /// The JavaScript code to execute. /// The arguments to the script. /// The value returned by the script. - public object? ExecuteScript(string script, params object?[]? args) + public object? ExecuteScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[]? args) { return this.ExecuteScriptCommand(script, DriverCommand.ExecuteScript, args); } @@ -834,7 +834,7 @@ private static void UnpackAndThrowOnError(Response errorResponse, string command /// The name of the command to execute. /// The arguments to the script. /// The value returned by the script. - protected object? ExecuteScriptCommand(string script, string commandName, params object?[]? args) + protected object? ExecuteScriptCommand([StringSyntax(StringSyntaxConstants.JavaScript)] string script, string commandName, params object?[]? args) { object?[] convertedArgs = ConvertArgumentsToJavaScriptObjects(args);