Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dotnet/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ load("//dotnet:selenium-dotnet-version.bzl", "SUPPORTED_DEVTOOLS_VERSIONS")
load("//dotnet/private:dotnet_nunit_test_suite.bzl", _dotnet_nunit_test_suite = "dotnet_nunit_test_suite")
load("//dotnet/private:framework.bzl", _framework = "framework")
load("//dotnet/private:generate_devtools.bzl", _generate_devtools = "generate_devtools")
load("//dotnet/private:generate_resources.bzl", _generated_resource_utilities = "generated_resource_utilities")
load("//dotnet/private:generated_assembly_info.bzl", _generated_assembly_info = "generated_assembly_info")
load("//dotnet/private:nuget_pack.bzl", _nuget_pack = "nuget_pack")
load("//dotnet/private:nunit_test.bzl", _nunit_test = "nunit_test")
Expand All @@ -19,6 +20,7 @@ csharp_test = _csharp_test
dotnet_nunit_test_suite = _dotnet_nunit_test_suite
framework = _framework
generate_devtools = _generate_devtools
generated_resource_utilities = _generated_resource_utilities
generated_assembly_info = _generated_assembly_info
nuget_pack = _nuget_pack
nunit_test = _nunit_test
6 changes: 6 additions & 0 deletions dotnet/private/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
load("//dotnet/private:generate_resources.bzl", "generated_resource_utilities")

py_binary(
name = "generate_resources_tool",
srcs = ["generate_resources_tool.py"],
)
48 changes: 48 additions & 0 deletions dotnet/private/generate_resources.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Generate C# partial class with embedded JS resources via a Python tool."""

def _generate_resource_utilities_impl(ctx):
"""Invoke a Python script to generate ResourceUtilities.cs from input files.

This rule does not inspect file contents itself; it just wires inputs/outputs
for the generator tool.
"""

args = ctx.actions.args()
args.add("--output", ctx.outputs.out)

for src in ctx.files.srcs:
args.add("--input")
# Each --input is "identifier=path"
name = src.basename.rsplit(".", 1)[0]
# Starlark strings are not directly iterable into characters, so use
# a simple replacement-based sanitization: non-identifier characters
# are replaced with '_'. This is conservative but sufficient.
ident = name
ident = ident.replace("-", "_")
ident = ident.replace(".", "_")
ident = ident.replace(" ", "_")
if ident and ident[0].isdigit():
ident = "_" + ident
args.add("%s=%s" % (ident, src.path))

ctx.actions.run(
inputs = ctx.files.srcs,
outputs = [ctx.outputs.out],
executable = ctx.executable._tool,
arguments = [args],
mnemonic = "GenerateResourceUtilities",
progress_message = "Generating C# ResourceUtilities partial class",
)

generated_resource_utilities = rule(
implementation = _generate_resource_utilities_impl,
attrs = {
"srcs": attr.label_list(allow_files = True),
"out": attr.output(mandatory = True),
"_tool": attr.label(
default = Label("//dotnet/private:generate_resources_tool"),
executable = True,
cfg = "exec",
),
},
)
85 changes: 85 additions & 0 deletions dotnet/private/generate_resources_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""Generate C# ResourceUtilities partial class with embedded JS resources.

Usage:
generate_resources_tool.py --output path/to/ResourceUtilities.g.cs \
--input Ident1=path/to/file1.js \
--input Ident2=path/to/file2.js ...

Each identifier becomes a const string in ResourceUtilities class.
The content is emitted as a C# raw string literal using 5-quotes.
"""

import argparse
import os
import sys
from typing import List, Tuple


def parse_args(argv: List[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--output", required=True)
parser.add_argument("--input", action="append", default=[], help="IDENT=path")
return parser.parse_args(argv)


def parse_input_spec(spec: str) -> Tuple[str, str]:
if "=" not in spec:
raise ValueError(f"Invalid --input value, expected IDENT=path, got: {spec}")
ident, path = spec.split("=", 1)
ident = ident.strip()
path = path.strip()
if not ident:
raise ValueError(f"Empty identifier in --input value: {spec}")
if not path:
raise ValueError(f"Empty path in --input value: {spec}")
return ident, path


def generate(output: str, inputs: List[Tuple[str, str]]) -> None:
props: List[str] = []
for ident, path in inputs:
with open(path, "r", encoding="utf-8") as f:
content = f.read()
# Use a C# raw string literal with five quotes. For a valid raw
# literal, the content must start on a new line and the closing
# quotes must be on their own line as well. We assume the content
# does not contain a sequence of five consecutive double quotes.
#
# Resulting C# will look like:
# """""
# <content>
# """""
literal = '"""""\n' + content + '\n"""""'
props.append(
f" internal const string {ident} = {literal};"
)

lines: List[str] = []
lines.append("// <auto-generated />")
lines.append("namespace OpenQA.Selenium.Internal;")
lines.append("")
lines.append("internal static partial class ResourceUtilities")
lines.append("{")
for p in props:
lines.append(p)
lines.append("}")
lines.append("")

os.makedirs(os.path.dirname(output), exist_ok=True)
with open(output, "w", encoding="utf-8", newline="\n") as f:
f.write("\n".join(lines))


def main(argv: List[str]) -> int:
args = parse_args(argv)
inputs: List[Tuple[str, str]] = []
for spec in args.input:
ident, path = parse_input_spec(spec)
inputs.append((ident, path))
generate(args.output, inputs)
return 0


if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))
51 changes: 22 additions & 29 deletions dotnet/src/webdriver/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
load("//common:defs.bzl", "copy_file")
load("//dotnet:defs.bzl", "csharp_library", "devtools_version_targets", "framework", "generated_assembly_info", "nuget_pack")
load("//dotnet:defs.bzl", "csharp_library", "devtools_version_targets", "framework", "generated_assembly_info", "nuget_pack", "generated_resource_utilities")

load(
"//dotnet:selenium-dotnet-version.bzl",
"ASSEMBLY_COMPANY",
Expand All @@ -25,10 +26,23 @@ generated_assembly_info(
version = ASSEMBLY_VERSION,
)

generated_resource_utilities(
name = "resource-utilities",
srcs = [
"//javascript/atoms/fragments:find-elements.js",
"//javascript/atoms/fragments:is-displayed.js",
"//javascript/cdp-support:mutation-listener.js",
"//javascript/webdriver/atoms:get-attribute.js",
"//third_party/js/selenium:webdriver_json",
],
out = "ResourceUtilities.g.cs",
)

csharp_library(
name = "webdriver-netstandard2.0",
srcs = [
":assembly-info",
":resource-utilities",
] + glob([
"**/*.cs",
]) + devtools_version_targets(),
Expand All @@ -38,13 +52,7 @@ csharp_library(
],
langversion = "12.0",
nullable = "enable",
resources = [
"//javascript/atoms/fragments:find-elements.js",
"//javascript/atoms/fragments:is-displayed.js",
"//javascript/cdp-support:mutation-listener.js",
"//javascript/webdriver/atoms:get-attribute.js",
"//third_party/js/selenium:webdriver_json",
],
resources = [],
target_frameworks = [
"netstandard2.0",
],
Expand All @@ -66,6 +74,7 @@ csharp_library(
name = "webdriver-net8.0",
srcs = [
":assembly-info",
":resource-utilities",
] + glob([
"**/*.cs",
]) + devtools_version_targets(),
Expand All @@ -78,13 +87,7 @@ csharp_library(
],
langversion = "12.0",
nullable = "enable",
resources = [
"//javascript/atoms/fragments:find-elements.js",
"//javascript/atoms/fragments:is-displayed.js",
"//javascript/cdp-support:mutation-listener.js",
"//javascript/webdriver/atoms:get-attribute.js",
"//third_party/js/selenium:webdriver_json",
],
resources = [],
target_frameworks = [
"net8.0",
],
Expand All @@ -99,20 +102,15 @@ csharp_library(
name = "webdriver-netstandard2.0-strongnamed",
srcs = [
":assembly-info",
":resource-utilities",
] + glob([
"**/*.cs",
]) + devtools_version_targets(),
out = "WebDriver.StrongNamed",
keyfile = "//dotnet:Selenium.snk",
langversion = "12.0",
nullable = "enable",
resources = [
"//javascript/atoms/fragments:find-elements.js",
"//javascript/atoms/fragments:is-displayed.js",
"//javascript/cdp-support:mutation-listener.js",
"//javascript/webdriver/atoms:get-attribute.js",
"//third_party/js/selenium:webdriver_json",
],
resources = [],
target_frameworks = [
"netstandard2.0",
],
Expand All @@ -134,6 +132,7 @@ csharp_library(
name = "webdriver-net8.0-strongnamed",
srcs = [
":assembly-info",
":resource-utilities",
] + glob([
"**/*.cs",
]) + devtools_version_targets(),
Expand All @@ -144,13 +143,7 @@ csharp_library(
keyfile = "//dotnet:Selenium.snk",
langversion = "12.0",
nullable = "enable",
resources = [
"//javascript/atoms/fragments:find-elements.js",
"//javascript/atoms/fragments:is-displayed.js",
"//javascript/cdp-support:mutation-listener.js",
"//javascript/webdriver/atoms:get-attribute.js",
"//third_party/js/selenium:webdriver_json",
],
resources = [],
target_frameworks = [
"net8.0",
],
Expand Down
24 changes: 2 additions & 22 deletions dotnet/src/webdriver/Firefox/FirefoxExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Text;
using System.Text.Json.Nodes;
using System.Xml;

Expand All @@ -38,7 +38,6 @@ public class FirefoxExtension
private const string JsonManifestFileName = "manifest.json";

private readonly string extensionFileName;
private readonly string extensionResourceId;

/// <summary>
/// Initializes a new instance of the <see cref="FirefoxExtension"/> class.
Expand All @@ -49,27 +48,8 @@ public class FirefoxExtension
/// then using the full path to the file, if a full path is provided.</remarks>
/// <exception cref="ArgumentNullException">If <paramref name="fileName"/> is <see langword="null"/>.</exception>
public FirefoxExtension(string fileName)
: this(fileName, string.Empty)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="FirefoxExtension"/> class.
/// </summary>
/// <param name="fileName">The name of the file containing the Firefox extension.</param>
/// <param name="resourceId">The ID of the resource within the assembly containing the extension
/// if the file is not present in the file system.</param>
/// <remarks>WebDriver attempts to resolve the <paramref name="fileName"/> parameter
/// by looking first for the specified file in the directory of the calling assembly,
/// then using the full path to the file, if a full path is provided. If the file is
/// not found in the file system, WebDriver attempts to locate a resource in the
/// executing assembly with the name specified by the <paramref name="resourceId"/>
/// parameter.</remarks>
/// <exception cref="ArgumentNullException">If <paramref name="fileName"/> or <paramref name="resourceId"/> are <see langword="null"/>.</exception>
internal FirefoxExtension(string fileName, string resourceId)
{
this.extensionFileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
this.extensionResourceId = resourceId ?? throw new ArgumentNullException(nameof(resourceId));
}

/// <summary>
Expand All @@ -89,7 +69,7 @@ public void Install(string profileDirectory)

// First, expand the .xpi archive into a temporary location.
Directory.CreateDirectory(tempFileName);
Stream zipFileStream = ResourceUtilities.GetResourceStream(this.extensionFileName, $"{Assembly.GetExecutingAssembly().GetName().Name}.{this.extensionResourceId}");
using Stream zipFileStream = new MemoryStream(Encoding.UTF8.GetBytes(ResourceUtilities.webdriver_prefs));
using (ZipArchive extensionZipArchive = new ZipArchive(zipFileStream, ZipArchiveMode.Read))
{
extensionZipArchive.ExtractToDirectory(tempFileName);
Expand Down
12 changes: 4 additions & 8 deletions dotnet/src/webdriver/Firefox/FirefoxProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Text.Json;

namespace OpenQA.Selenium.Firefox;
Expand Down Expand Up @@ -298,15 +297,12 @@ private void UpdateUserPreferences(string profileDirectory)

private Preferences ReadDefaultPreferences()
{
using (Stream defaultPrefsStream = ResourceUtilities.GetResourceStream("webdriver_prefs.json", $"{Assembly.GetExecutingAssembly().GetName().Name}.webdriver_prefs.json"))
{
using JsonDocument defaultPreferences = JsonDocument.Parse(defaultPrefsStream);
using JsonDocument defaultPreferences = JsonDocument.Parse(ResourceUtilities.webdriver_prefs);

JsonElement immutableDefaultPreferences = defaultPreferences.RootElement.GetProperty("frozen");
JsonElement editableDefaultPreferences = defaultPreferences.RootElement.GetProperty("mutable");
JsonElement immutableDefaultPreferences = defaultPreferences.RootElement.GetProperty("frozen");
JsonElement editableDefaultPreferences = defaultPreferences.RootElement.GetProperty("mutable");

return new Preferences(immutableDefaultPreferences, editableDefaultPreferences);
}
return new Preferences(immutableDefaultPreferences, editableDefaultPreferences);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion dotnet/src/webdriver/Internal/ResourceUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace OpenQA.Selenium.Internal;
/// <summary>
/// Encapsulates methods for finding and extracting WebDriver resources.
/// </summary>
internal static class ResourceUtilities
internal static partial class ResourceUtilities
{
private static string? productVersion;
private static string? platformFamily;
Expand Down
Loading
Loading