Skip to content

Commit

Permalink
[dotnet] Make FirefoxProfile AOT-safe (SeleniumHQ#14742)
Browse files Browse the repository at this point in the history
  • Loading branch information
RenderMichael authored and jkim2492 committed Nov 17, 2024
1 parent 70a74a7 commit 0365379
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 66 deletions.
22 changes: 6 additions & 16 deletions dotnet/src/webdriver/Firefox/FirefoxProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,24 +296,14 @@ private void UpdateUserPreferences()

private void ReadDefaultPreferences()
{
var jsonSerializerOptions = new JsonSerializerOptions
{
Converters =
{
new ResponseValueJsonConverter()
}
};

using (Stream defaultPrefsStream = ResourceUtilities.GetResourceStream("webdriver_prefs.json", "webdriver_prefs.json"))
{
using (StreamReader reader = new StreamReader(defaultPrefsStream))
{
string defaultPreferences = reader.ReadToEnd();
Dictionary<string, object> deserializedPreferences = JsonSerializer.Deserialize<Dictionary<string, object>>(defaultPreferences, jsonSerializerOptions);
Dictionary<string, object> immutableDefaultPreferences = deserializedPreferences["frozen"] as Dictionary<string, object>;
Dictionary<string, object> editableDefaultPreferences = deserializedPreferences["mutable"] as Dictionary<string, object>;
this.profilePreferences = new Preferences(immutableDefaultPreferences, editableDefaultPreferences);
}
using JsonDocument defaultPreferences = JsonDocument.Parse(defaultPrefsStream);

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

this.profilePreferences = new Preferences(immutableDefaultPreferences, editableDefaultPreferences);
}
}

Expand Down
112 changes: 62 additions & 50 deletions dotnet/src/webdriver/Firefox/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text.Json;

#nullable enable

namespace OpenQA.Selenium.Firefox
{
Expand All @@ -29,31 +32,27 @@ namespace OpenQA.Selenium.Firefox
/// </summary>
internal class Preferences
{
private Dictionary<string, string> preferences = new Dictionary<string, string>();
private Dictionary<string, string> immutablePreferences = new Dictionary<string, string>();
private readonly Dictionary<string, string> preferences = new Dictionary<string, string>();
private readonly HashSet<string> immutablePreferences = new HashSet<string>();

/// <summary>
/// Initializes a new instance of the <see cref="Preferences"/> class.
/// </summary>
/// <param name="defaultImmutablePreferences">A set of preferences that cannot be modified once set.</param>
/// <param name="defaultPreferences">A set of default preferences.</param>
public Preferences(Dictionary<string, object> defaultImmutablePreferences, Dictionary<string, object> defaultPreferences)
public Preferences(JsonElement defaultImmutablePreferences, JsonElement defaultPreferences)
{
if (defaultImmutablePreferences != null)
foreach (JsonProperty pref in defaultImmutablePreferences.EnumerateObject())
{
foreach (KeyValuePair<string, object> pref in defaultImmutablePreferences)
{
this.SetPreferenceValue(pref.Key, pref.Value);
this.immutablePreferences.Add(pref.Key, pref.Value.ToString());
}
this.ThrowIfPreferenceIsImmutable(pref.Name, pref.Value);
this.preferences[pref.Name] = pref.Value.GetRawText();
this.immutablePreferences.Add(pref.Name);
}

if (defaultPreferences != null)
foreach (JsonProperty pref in defaultPreferences.EnumerateObject())
{
foreach (KeyValuePair<string, object> pref in defaultPreferences)
{
this.SetPreferenceValue(pref.Key, pref.Value);
}
this.ThrowIfPreferenceIsImmutable(pref.Name, pref.Value);
this.preferences[pref.Name] = pref.Value.GetRawText();
}
}

Expand All @@ -64,9 +63,31 @@ public Preferences(Dictionary<string, object> defaultImmutablePreferences, Dicti
/// <param name="value">A <see cref="string"/> value give the preference.</param>
/// <remarks>If the preference already exists in the currently-set list of preferences,
/// the value will be updated.</remarks>
/// <exception cref="ArgumentNullException">If <paramref name="key"/> or <paramref name="value"/> are <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">
/// <para>If <paramref name="value"/> is wrapped with double-quotes.</para>
/// <para>-or-</para>
/// <para>If the specified preference is immutable.</para>
/// </exception>
internal void SetPreference(string key, string value)
{
this.SetPreferenceValue(key, value);
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}

if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

if (IsWrappedAsString(value))
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Preference values must be plain strings: {0}: {1}", key, value));
}

this.ThrowIfPreferenceIsImmutable(key, value);
this.preferences[key] = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", value);
}

/// <summary>
Expand All @@ -76,9 +97,17 @@ internal void SetPreference(string key, string value)
/// <param name="value">A <see cref="int"/> value give the preference.</param>
/// <remarks>If the preference already exists in the currently-set list of preferences,
/// the value will be updated.</remarks>
/// <exception cref="ArgumentNullException">If <paramref name="key"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">If the specified preference is immutable.</exception>
internal void SetPreference(string key, int value)
{
this.SetPreferenceValue(key, value);
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}

this.ThrowIfPreferenceIsImmutable(key, value);
this.preferences[key] = value.ToString(CultureInfo.InvariantCulture);
}

/// <summary>
Expand All @@ -88,16 +117,25 @@ internal void SetPreference(string key, int value)
/// <param name="value">A <see cref="bool"/> value give the preference.</param>
/// <remarks>If the preference already exists in the currently-set list of preferences,
/// the value will be updated.</remarks>
/// <exception cref="ArgumentNullException">If <paramref name="key"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">If the specified preference is immutable.</exception>
internal void SetPreference(string key, bool value)
{
this.SetPreferenceValue(key, value);
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}

this.ThrowIfPreferenceIsImmutable(key, value);
this.preferences[key] = value ? "true" : "false";
}

/// <summary>
/// Gets a preference from the list of preferences.
/// </summary>
/// <param name="preferenceName">The name of the preference to retrieve.</param>
/// <returns>The value of the preference, or an empty string if the preference is not set.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="preferenceName"/> is <see langword="null"/>.</exception>
internal string GetPreference(string preferenceName)
{
if (this.preferences.ContainsKey(preferenceName))
Expand Down Expand Up @@ -151,44 +189,18 @@ private static bool IsWrappedAsString(string value)
return value.StartsWith("\"", StringComparison.OrdinalIgnoreCase) && value.EndsWith("\"", StringComparison.OrdinalIgnoreCase);
}

private bool IsSettablePreference(string preferenceName)
private void ThrowIfPreferenceIsImmutable<TValue>(string preferenceName, TValue value)
{
return !this.immutablePreferences.ContainsKey(preferenceName);
}

private void SetPreferenceValue(string key, object value)
{
if (!this.IsSettablePreference(key))
if (this.immutablePreferences.Contains(preferenceName))
{
string message = string.Format(CultureInfo.InvariantCulture, "Preference {0} may not be overridden: frozen value={1}, requested value={2}", key, this.immutablePreferences[key], value.ToString());
string message = string.Format(CultureInfo.InvariantCulture, "Preference {0} may not be overridden: frozen value={1}, requested value={2}", preferenceName, this.preferences[preferenceName], value?.ToString());
throw new ArgumentException(message);
}
}

string stringValue = value as string;
if (stringValue != null)
{
if (IsWrappedAsString(stringValue))
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Preference values must be plain strings: {0}: {1}", key, value));
}

this.preferences[key] = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", value);
return;
}

if (value is bool)
{
this.preferences[key] = Convert.ToBoolean(value, CultureInfo.InvariantCulture).ToString().ToLowerInvariant();
return;
}

if (value is int || value is long)
{
this.preferences[key] = Convert.ToInt32(value, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture);
return;
}

throw new WebDriverException("Value must be string, int or boolean");
private bool IsSettablePreference(string preferenceName)
{
return !this.immutablePreferences.Contains(preferenceName);
}
}
}

0 comments on commit 0365379

Please sign in to comment.