diff --git a/dotnet/src/support/Events/EventFiringWebDriver.cs b/dotnet/src/support/Events/EventFiringWebDriver.cs
index 46661f9225838..e68b261e387c3 100644
--- a/dotnet/src/support/Events/EventFiringWebDriver.cs
+++ b/dotnet/src/support/Events/EventFiringWebDriver.cs
@@ -478,6 +478,7 @@ public object ExecuteScript(string script, params object[] args)
/// A object containing the code to execute.
/// The arguments to the script.
/// The value returned by the script.
+ /// If is .
///
///
/// The ExecuteScript method executes JavaScript in the context of
@@ -509,6 +510,11 @@ public object ExecuteScript(string script, params object[] args)
///
public object ExecuteScript(PinnedScript script, params object[] args)
{
+ if (script == null)
+ {
+ throw new ArgumentNullException(nameof(script));
+ }
+
IJavaScriptExecutor javascriptDriver = this.driver as IJavaScriptExecutor;
if (javascriptDriver == null)
{
diff --git a/dotnet/src/webdriver/IJavaScriptEngine.cs b/dotnet/src/webdriver/IJavaScriptEngine.cs
index 0c4481d970b85..3445c59753d60 100644
--- a/dotnet/src/webdriver/IJavaScriptEngine.cs
+++ b/dotnet/src/webdriver/IJavaScriptEngine.cs
@@ -87,6 +87,7 @@ public interface IJavaScriptEngine : IDisposable
/// The friendly name by which to refer to this initialization script.
/// The JavaScript to be loaded on every page.
/// A task containing an object representing the script to be loaded on each page.
+ /// If is .
Task AddInitializationScript(string scriptName, string script);
///
@@ -94,6 +95,7 @@ public interface IJavaScriptEngine : IDisposable
///
/// The friendly name of the initialization script to be removed.
/// A task that represents the asynchronous operation.
+ /// If is .
Task RemoveInitializationScript(string scriptName);
///
@@ -109,6 +111,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);
///
@@ -116,6 +119,7 @@ public interface IJavaScriptEngine : IDisposable
///
/// The object to unpin.
/// A task that represents the asynchronous operation.
+ /// If is .
Task UnpinScript(PinnedScript script);
///
diff --git a/dotnet/src/webdriver/IJavascriptExecutor.cs b/dotnet/src/webdriver/IJavascriptExecutor.cs
index c8680a0c14c6c..fcbf9080efd2c 100644
--- a/dotnet/src/webdriver/IJavascriptExecutor.cs
+++ b/dotnet/src/webdriver/IJavascriptExecutor.cs
@@ -17,6 +17,7 @@
// under the License.
//
+using System;
using System.Collections.Generic;
namespace OpenQA.Selenium
@@ -98,6 +99,7 @@ public interface IJavaScriptExecutor
/// variable, as if the function were called via "Function.apply"
///
///
+ /// If is .
object ExecuteScript(PinnedScript script, params object[] args);
///
diff --git a/dotnet/src/webdriver/ISearchContext.cs b/dotnet/src/webdriver/ISearchContext.cs
index 6f88562e296c8..53c5f33ae36da 100644
--- a/dotnet/src/webdriver/ISearchContext.cs
+++ b/dotnet/src/webdriver/ISearchContext.cs
@@ -17,6 +17,7 @@
// under the License.
//
+using System;
using System.Collections.ObjectModel;
namespace OpenQA.Selenium
@@ -31,6 +32,7 @@ public interface ISearchContext
///
/// The locating mechanism to use.
/// The first matching on the current context.
+ /// If is .
/// If no element matches the criteria.
IWebElement FindElement(By by);
diff --git a/dotnet/src/webdriver/JavaScriptEngine.cs b/dotnet/src/webdriver/JavaScriptEngine.cs
index 35dc6764aac54..4bd4fe4676eb4 100644
--- a/dotnet/src/webdriver/JavaScriptEngine.cs
+++ b/dotnet/src/webdriver/JavaScriptEngine.cs
@@ -219,14 +219,25 @@ 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)
{
+ if (script == null)
+ {
+ throw new ArgumentNullException(nameof(script));
+ }
+
+ string newScriptHandle = Guid.NewGuid().ToString("N");
+
// We do an "Evaluate" first so as to immediately create the script on the loaded
// page, then will add it to the initialization of future pages.
- PinnedScript pinnedScript = new PinnedScript(script);
await this.EnableDomains().ConfigureAwait(false);
- await this.session.Value.Domains.JavaScript.Evaluate(pinnedScript.CreationScript).ConfigureAwait(false);
- pinnedScript.ScriptId = await this.session.Value.Domains.JavaScript.AddScriptToEvaluateOnNewDocument(pinnedScript.CreationScript).ConfigureAwait(false);
+
+ string creationScript = PinnedScript.MakeCreationScript(newScriptHandle, script);
+ await this.session.Value.Domains.JavaScript.Evaluate(creationScript).ConfigureAwait(false);
+ string scriptId = await this.session.Value.Domains.JavaScript.AddScriptToEvaluateOnNewDocument(creationScript).ConfigureAwait(false);
+
+ PinnedScript pinnedScript = new PinnedScript(script, newScriptHandle, scriptId);
this.pinnedScripts[pinnedScript.Handle] = pinnedScript;
return pinnedScript;
}
@@ -236,11 +247,17 @@ public async Task PinScript(string script)
///
/// The object to unpin.
/// A task that represents the asynchronous operation.
+ /// If is .
public async Task UnpinScript(PinnedScript script)
{
+ if (script == null)
+ {
+ throw new ArgumentNullException(nameof(script));
+ }
+
if (this.pinnedScripts.ContainsKey(script.Handle))
{
- await this.session.Value.Domains.JavaScript.Evaluate(script.RemovalScript).ConfigureAwait(false);
+ await this.session.Value.Domains.JavaScript.Evaluate(script.MakeRemovalScript()).ConfigureAwait(false);
await this.session.Value.Domains.JavaScript.RemoveScriptToEvaluateOnNewDocument(script.ScriptId).ConfigureAwait(false);
this.pinnedScripts.Remove(script.Handle);
}
diff --git a/dotnet/src/webdriver/PinnedScript.cs b/dotnet/src/webdriver/PinnedScript.cs
index bf01e982385b6..629c1bd7212f3 100644
--- a/dotnet/src/webdriver/PinnedScript.cs
+++ b/dotnet/src/webdriver/PinnedScript.cs
@@ -17,83 +17,70 @@
// under the License.
//
-using System;
using System.Globalization;
+#nullable enable
+
namespace OpenQA.Selenium
{
///
/// A class representing a pinned JavaScript function that can be repeatedly called
/// without sending the entire script across the wire for every execution.
///
- public class PinnedScript
+ public sealed class PinnedScript
{
- private string scriptSource;
- private string scriptHandle;
- private string scriptId;
-
///
/// Initializes a new instance of the class.
///
/// The body of the JavaScript function to pin.
+ /// The unique handle for this pinned script.
+ /// The internal ID of this script.
///
/// This constructor is explicitly internal. Creation of pinned script objects
/// is strictly the perview of Selenium, and should not be required by external
/// libraries.
///
- internal PinnedScript(string script)
+ internal PinnedScript(string script, string stringHandle, string scriptId)
{
- this.scriptSource = script;
- this.scriptHandle = Guid.NewGuid().ToString("N");
+ this.Source = script;
+ this.Handle = stringHandle;
+ this.ScriptId = scriptId;
}
///
/// Gets the unique handle for this pinned script.
///
- public string Handle
- {
- get { return this.scriptHandle; }
- }
+ public string Handle { get; }
///
/// Gets the source representing the body of the function in the pinned script.
///
- public string Source
- {
- get { return this.scriptSource; }
- }
+ public string Source { get; }
- ///
- /// Gets the script to create the pinned script in the browser.
- ///
- internal string CreationScript
+ internal static string MakeCreationScript(string scriptHandle, string scriptSource)
{
- get { return string.Format(CultureInfo.InvariantCulture, "function __webdriver_{0}(arguments) {{ {1} }}", this.scriptHandle, this.scriptSource); }
+ return string.Format(CultureInfo.InvariantCulture, "function __webdriver_{0}(arguments) {{ {1} }}", scriptHandle, scriptSource);
}
///
/// Gets the script used to execute the pinned script in the browser.
///
- internal string ExecutionScript
+ internal string MakeExecutionScript()
{
- get { return string.Format(CultureInfo.InvariantCulture, "return __webdriver_{0}(arguments)", this.scriptHandle); }
+ return string.Format(CultureInfo.InvariantCulture, "return __webdriver_{0}(arguments)", this.Handle);
}
///
/// Gets the script used to remove the pinned script from the browser.
///
- internal string RemovalScript
+ internal string MakeRemovalScript()
{
- get { return string.Format(CultureInfo.InvariantCulture, "__webdriver_{0} = undefined", this.scriptHandle); }
+ return string.Format(CultureInfo.InvariantCulture, "__webdriver_{0} = undefined", this.Handle);
}
///
/// Gets or sets the ID of this script.
///
- internal string ScriptId
- {
- get { return this.scriptId; }
- set { this.scriptId = value; }
- }
+ internal string ScriptId { get; }
}
}
diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs
index 1106fb0da8bbd..e29ce29a12c79 100644
--- a/dotnet/src/webdriver/WebDriver.cs
+++ b/dotnet/src/webdriver/WebDriver.cs
@@ -280,9 +280,15 @@ public object ExecuteScript(string script, params object[] args)
/// A object containing the JavaScript code to execute.
/// The arguments to the script.
/// The value returned by the script.
+ /// If is .
public object ExecuteScript(PinnedScript script, params object[] args)
{
- return this.ExecuteScript(script.ExecutionScript, args);
+ if (script == null)
+ {
+ throw new ArgumentNullException(nameof(script));
+ }
+
+ return this.ExecuteScript(script.MakeExecutionScript(), args);
}
///
@@ -290,6 +296,7 @@ public object ExecuteScript(PinnedScript script, params object[] args)
///
/// By mechanism to find the object
/// IWebElement object so that you can interact with that object
+ /// If is .
///
///
/// IWebDriver driver = new InternetExplorerDriver();
diff --git a/dotnet/test/common/ExecutingJavascriptTest.cs b/dotnet/test/common/ExecutingJavascriptTest.cs
index 463f82a4c434f..048b712d3178d 100644
--- a/dotnet/test/common/ExecutingJavascriptTest.cs
+++ b/dotnet/test/common/ExecutingJavascriptTest.cs
@@ -21,6 +21,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Threading.Tasks;
namespace OpenQA.Selenium
{
@@ -468,6 +469,33 @@ public void ShouldBeAbleToExecuteABigChunkOfJavascriptCode()
}
}
+ [Test]
+ [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")]
+ [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")]
+ [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")]
+ public async Task ShouldBeAbleToPinJavascriptCodeAndExecuteRepeatedly()
+ {
+ IJavaScriptEngine jsEngine = new JavaScriptEngine(driver);
+
+ driver.Url = xhtmlTestPage;
+
+ PinnedScript script = await jsEngine.PinScript("return document.title;");
+ for (int i = 0; i < 5; i++)
+ {
+ object result = ((IJavaScriptExecutor)driver).ExecuteScript(script);
+
+ Assert.That(result, Is.InstanceOf());
+ Assert.That(result, Is.EqualTo("XHTML Test Page"));
+ }
+
+ await jsEngine.UnpinScript(script);
+
+ Assert.That(() =>
+ {
+ _ = ((IJavaScriptExecutor)driver).ExecuteScript(script);
+ }, Throws.TypeOf());
+ }
+
[Test]
public void ShouldBeAbleToExecuteScriptAndReturnElementsList()
{
diff --git a/javascript/node/selenium-webdriver/package.json b/javascript/node/selenium-webdriver/package.json
index e06d9132c886a..aaa10a9f2a682 100644
--- a/javascript/node/selenium-webdriver/package.json
+++ b/javascript/node/selenium-webdriver/package.json
@@ -61,5 +61,15 @@
},
"publishConfig": {
"registry": "https://registry.npmjs.org/"
- }
+ },
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/SeleniumHQ"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/selenium"
+ }
+ ]
}
diff --git a/py/pyproject.toml b/py/pyproject.toml
index eecffc1f3c3c8..6488cda070f56 100644
--- a/py/pyproject.toml
+++ b/py/pyproject.toml
@@ -2,23 +2,65 @@
requires = ["setuptools", "setuptools-rust"]
build-backend = "setuptools.build_meta"
+[project]
+name = "selenium"
+version = "4.27.0.202410311942"
+license = "Apache 2.0"
+description = "Official Python bindings for Selenium WebDriver."
+readme = "README.rst"
+requires-python = "~=3.8"
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: Apache Software License",
+ "Operating System :: POSIX",
+ "Operating System :: Microsoft :: Windows",
+ "Operating System :: MacOS :: MacOS X",
+ "Topic :: Software Development :: Testing",
+ "Topic :: Software Development :: Libraries",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ ]
+dependencies = [
+ "urllib3[socks]>=1.26,<3",
+ "trio~=0.17",
+ "trio-websocket~=0.9",
+ "certifi>=2021.10.8",
+ "typing_extensions~=4.9",
+ "websocket-client~=1.8",
+ ]
+
+[tool.setuptools]
+zip-safe = false
+
[tool.setuptools.packages.find]
include = ["selenium*"]
exclude = ["test*"]
-namespaces = false
+namespace = false
# include-package-data is `true` by default in pyproject.toml
+[project.urls]
+Repository = "https://github.com/SeleniumHQ/selenium/"
+BugTracker = "https://github.com/SeleniumHQ/selenium/issues"
+Changelog = "https://github.com/SeleniumHQ/selenium/blob/trunk/py/CHANGES"
+Documentation = "https://www.selenium.dev/documentation/overview/"
+SourceCode = "https://github.com/SeleniumHQ/selenium/tree/trunk/py"
+
[tool.setuptools.package-data]
selenium_package = [
- "*.py",
+ "*.py",
"*.rst",
- "*.json",
- "*.xpi",
+ "*.json",
+ "*.xpi",
"*.js",
"py.typed",
"prune*",
"selenium.egg-info*",
- "selenium-manager",
+ "selenium-manager",
"selenium-manager.exe",
"CHANGES",
"LICENSE"
diff --git a/py/setup.py b/py/setup.py
index 6fe0b7a774d81..0f93e33f0ecae 100755
--- a/py/setup.py
+++ b/py/setup.py
@@ -27,70 +27,12 @@
setup_args = {
'cmdclass': {'install': install},
- 'name': 'selenium',
- 'version': "4.27.0.202410311942",
- 'license': 'Apache 2.0',
- 'description': 'Official Python bindings for Selenium WebDriver.',
- 'long_description': open(join(abspath(dirname(__file__)), "README.rst")).read(),
- 'url': 'https://github.com/SeleniumHQ/selenium/',
- 'project_urls': {
- 'Bug Tracker': 'https://github.com/SeleniumHQ/selenium/issues',
- 'Changes': 'https://github.com/SeleniumHQ/selenium/blob/trunk/py/CHANGES',
- 'Documentation': 'https://www.selenium.dev/documentation/overview/',
- 'Source Code': 'https://github.com/SeleniumHQ/selenium/tree/trunk/py',
- },
- 'python_requires': '~=3.8',
- 'classifiers': ['Development Status :: 5 - Production/Stable',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: Apache Software License',
- 'Operating System :: POSIX',
- 'Operating System :: Microsoft :: Windows',
- 'Operating System :: MacOS :: MacOS X',
- 'Topic :: Software Development :: Testing',
- 'Topic :: Software Development :: Libraries',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 3.8',
- 'Programming Language :: Python :: 3.9',
- 'Programming Language :: Python :: 3.10',
- 'Programming Language :: Python :: 3.11',
- 'Programming Language :: Python :: 3.12',
- ],
- 'package_dir': {
- 'selenium': 'selenium',
- 'selenium.common': 'selenium/common',
- 'selenium.webdriver': 'selenium/webdriver',
- },
- 'packages': ['selenium',
- 'selenium.common',
- 'selenium.webdriver',
- 'selenium.webdriver.chrome',
- 'selenium.webdriver.chromium',
- 'selenium.webdriver.common',
- 'selenium.webdriver.edge',
- 'selenium.webdriver.firefox',
- 'selenium.webdriver.ie',
- 'selenium.webdriver.remote',
- 'selenium.webdriver.safari',
- 'selenium.webdriver.support',
- 'selenium.webdriver.webkitgtk',
- 'selenium.webdriver.wpewebkit',
- ],
- 'include_package_data': True,
- 'install_requires': [
- "urllib3[socks]>=1.26,<3",
- "trio~=0.17",
- "trio-websocket~=0.9",
- "certifi>=2021.10.8",
- "typing_extensions~=4.9",
- "websocket-client~=1.8",
- ],
'rust_extensions': [
RustExtension(
{"selenium-manager": "selenium.webdriver.common.selenium-manager"},
binding=Binding.Exec
)
],
- 'zip_safe': False
}
setup(**setup_args)
diff --git a/rb/lib/selenium/webdriver/remote/bridge.rb b/rb/lib/selenium/webdriver/remote/bridge.rb
index 1678065010108..2c26285a780e0 100644
--- a/rb/lib/selenium/webdriver/remote/bridge.rb
+++ b/rb/lib/selenium/webdriver/remote/bridge.rb
@@ -686,7 +686,7 @@ def execute(command, opts = {}, command_hash = nil)
end
def escaper
- @escaper ||= defined?(URI::Parser) ? URI::DEFAULT_PARSER : URI
+ @escaper ||= defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
end
def commands(command)
diff --git a/rb/selenium-devtools.gemspec b/rb/selenium-devtools.gemspec
index 709efc6bcdb2c..ce776dc765631 100644
--- a/rb/selenium-devtools.gemspec
+++ b/rb/selenium-devtools.gemspec
@@ -23,7 +23,8 @@ Gem::Specification.new do |s|
'changelog_uri' => 'https://github.com/SeleniumHQ/selenium/blob/trunk/rb/CHANGES',
'github_repo' => 'ssh://github.com/SeleniumHQ/selenium',
'source_code_uri' => 'https://github.com/SeleniumHQ/selenium/tree/trunk/rb',
- 'rubygems_mfa_required' => 'true'
+ 'rubygems_mfa_required' => 'true',
+ 'funding_uri' => 'https://github.com/sponsors/SeleniumHQ'
}
s.required_rubygems_version = Gem::Requirement.new('> 1.3.1') if s.respond_to? :required_rubygems_version=
diff --git a/rb/selenium-webdriver.gemspec b/rb/selenium-webdriver.gemspec
index b6c09f273c326..183981a1ba634 100644
--- a/rb/selenium-webdriver.gemspec
+++ b/rb/selenium-webdriver.gemspec
@@ -24,7 +24,8 @@ Gem::Specification.new do |s|
'changelog_uri' => 'https://github.com/SeleniumHQ/selenium/blob/trunk/rb/CHANGES',
'github_repo' => 'ssh://github.com/SeleniumHQ/selenium',
'source_code_uri' => 'https://github.com/SeleniumHQ/selenium/tree/trunk/rb',
- 'rubygems_mfa_required' => 'true'
+ 'rubygems_mfa_required' => 'true',
+ 'funding_uri' => 'https://github.com/sponsors/SeleniumHQ'
}
s.required_rubygems_version = Gem::Requirement.new('> 1.3.1') if s.respond_to? :required_rubygems_version=