Path: blob/trunk/dotnet/src/webdriver/Firefox/FirefoxOptions.cs
3991 views
// <copyright file="FirefoxOptions.cs" company="Selenium Committers">
// 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.
// </copyright>
using System;
using System.Collections.Generic;
namespace OpenQA.Selenium.Firefox;
/// <summary>
/// Class to manage options specific to <see cref="FirefoxDriver"/>
/// </summary>
/// <remarks>
/// Used with the marionette executable wires.exe.
/// </remarks>
/// <example>
/// <code>
/// FirefoxOptions options = new FirefoxOptions();
/// </code>
/// <para></para>
/// <para>For use with FirefoxDriver:</para>
/// <para></para>
/// <code>
/// FirefoxDriver driver = new FirefoxDriver(options);
/// </code>
/// <para></para>
/// <para>For use with RemoteWebDriver:</para>
/// <para></para>
/// <code>
/// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities());
/// </code>
/// </example>
public class FirefoxOptions : DriverOptions
{
private const string BrowserNameValue = "firefox";
private const string IsMarionetteCapability = "marionette";
private const string FirefoxLegacyProfileCapability = "firefox_profile";
private const string FirefoxLegacyBinaryCapability = "firefox_binary";
private const string FirefoxProfileCapability = "profile";
private const string FirefoxBinaryCapability = "binary";
private const string FirefoxArgumentsCapability = "args";
private const string FirefoxLogCapability = "log";
private const string FirefoxPrefsCapability = "prefs";
private const string FirefoxEnvCapability = "env";
private const string FirefoxOptionsCapability = "moz:firefoxOptions";
private const string FirefoxEnableDevToolsProtocolCapability = "moz:debuggerAddress";
private readonly List<string> firefoxArguments = new List<string>();
private readonly Dictionary<string, object> profilePreferences = new Dictionary<string, object>();
private readonly Dictionary<string, object> additionalFirefoxOptions = new Dictionary<string, object>();
private readonly Dictionary<string, object> environmentVariables = new Dictionary<string, object>();
/// <summary>
/// Initializes a new instance of the <see cref="FirefoxOptions"/> class.
/// </summary>
public FirefoxOptions()
: base()
{
this.BrowserName = BrowserNameValue;
this.AddKnownCapabilityName(FirefoxOptions.FirefoxOptionsCapability, "current FirefoxOptions class instance");
this.AddKnownCapabilityName(FirefoxOptions.IsMarionetteCapability, "UseLegacyImplementation property");
this.AddKnownCapabilityName(FirefoxOptions.FirefoxProfileCapability, "Profile property");
this.AddKnownCapabilityName(FirefoxOptions.FirefoxBinaryCapability, "BrowserExecutableLocation property");
this.AddKnownCapabilityName(FirefoxOptions.FirefoxArgumentsCapability, "AddArguments method");
this.AddKnownCapabilityName(FirefoxOptions.FirefoxPrefsCapability, "SetPreference method");
this.AddKnownCapabilityName(FirefoxOptions.FirefoxEnvCapability, "SetEnvironmentVariable method");
this.AddKnownCapabilityName(FirefoxOptions.FirefoxLogCapability, "LogLevel property");
this.AddKnownCapabilityName(FirefoxOptions.FirefoxLegacyProfileCapability, "Profile property");
this.AddKnownCapabilityName(FirefoxOptions.FirefoxLegacyBinaryCapability, "BrowserExecutableLocation property");
this.AddKnownCapabilityName(FirefoxOptions.FirefoxEnableDevToolsProtocolCapability, "EnableDevToolsProtocol property");
// https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/.
// Enable BiDi only
this.SetPreference("remote.active-protocols", 1);
}
/// <summary>
/// Gets or sets the <see cref="FirefoxProfile"/> object to be used with this instance.
/// </summary>
public FirefoxProfile? Profile { get; set; }
/// <summary>
/// Gets or sets the path and file name of the Firefox browser executable.
/// </summary>
public override string? BinaryLocation { get; set; }
/// <summary>
/// Gets or sets the logging level of the Firefox driver.
/// </summary>
public FirefoxDriverLogLevel LogLevel { get; set; } = FirefoxDriverLogLevel.Default;
/// <summary>
/// Gets or sets a value indicating whether to enable the DevTools protocol for the launched browser.
/// </summary>
public bool EnableDevToolsProtocol { get; set; }
/// <summary>
/// Gets or sets the options for automating Firefox on Android.
/// </summary>
public FirefoxAndroidOptions? AndroidOptions { get; set; }
/// <summary>
/// Adds an argument to be used in launching the Firefox browser.
/// </summary>
/// <param name="argumentName">The argument to add.</param>
/// <remarks>Arguments must be preceded by two dashes ("--").</remarks>
/// <exception cref="ArgumentException">If <paramref name="argumentName"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
public void AddArgument(string argumentName)
{
if (string.IsNullOrEmpty(argumentName))
{
throw new ArgumentException("argumentName must not be null or empty", nameof(argumentName));
}
this.AddArguments(argumentName);
}
/// <summary>
/// Adds a list arguments to be used in launching the Firefox browser.
/// </summary>
/// <param name="argumentsToAdd">An array of arguments to add.</param>
/// <remarks>Each argument must be preceded by two dashes ("--").</remarks>
/// <exception cref="ArgumentNullException">If <paramref name="argumentsToAdd"/> is <see langword="null"/>.</exception>
public void AddArguments(params string[] argumentsToAdd)
{
this.AddArguments((IEnumerable<string>)argumentsToAdd);
}
/// <summary>
/// Adds a list arguments to be used in launching the Firefox browser.
/// </summary>
/// <param name="argumentsToAdd">An array of arguments to add.</param>
/// <exception cref="ArgumentNullException">If <paramref name="argumentsToAdd"/> is <see langword="null"/>.</exception>
public void AddArguments(IEnumerable<string> argumentsToAdd)
{
if (argumentsToAdd == null)
{
throw new ArgumentNullException(nameof(argumentsToAdd), "argumentsToAdd must not be null");
}
this.firefoxArguments.AddRange(argumentsToAdd);
}
/// <summary>
/// Sets a preference in the profile used by Firefox.
/// </summary>
/// <param name="preferenceName">Name of the preference to set.</param>
/// <param name="preferenceValue">Value of the preference to set.</param>
/// <exception cref="ArgumentException">If <paramref name="preferenceName"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
public void SetPreference(string preferenceName, bool preferenceValue)
{
this.SetPreferenceValue(preferenceName, preferenceValue);
}
/// <summary>
/// Sets a preference in the profile used by Firefox.
/// </summary>
/// <param name="preferenceName">Name of the preference to set.</param>
/// <param name="preferenceValue">Value of the preference to set.</param>
/// <exception cref="ArgumentException">If <paramref name="preferenceName"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
public void SetPreference(string preferenceName, int preferenceValue)
{
this.SetPreferenceValue(preferenceName, preferenceValue);
}
/// <summary>
/// Sets a preference in the profile used by Firefox.
/// </summary>
/// <param name="preferenceName">Name of the preference to set.</param>
/// <param name="preferenceValue">Value of the preference to set.</param>
/// <exception cref="ArgumentException">If <paramref name="preferenceName"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
public void SetPreference(string preferenceName, long preferenceValue)
{
this.SetPreferenceValue(preferenceName, preferenceValue);
}
/// <summary>
/// Sets a preference in the profile used by Firefox.
/// </summary>
/// <param name="preferenceName">Name of the preference to set.</param>
/// <param name="preferenceValue">Value of the preference to set.</param>
/// <exception cref="ArgumentException">If <paramref name="preferenceName"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
public void SetPreference(string preferenceName, double preferenceValue)
{
this.SetPreferenceValue(preferenceName, preferenceValue);
}
/// <summary>
/// Sets a preference in the profile used by Firefox.
/// </summary>
/// <param name="preferenceName">Name of the preference to set.</param>
/// <param name="preferenceValue">Value of the preference to set.</param>
/// <exception cref="ArgumentException">If <paramref name="preferenceName"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
public void SetPreference(string preferenceName, string preferenceValue)
{
this.SetPreferenceValue(preferenceName, preferenceValue);
}
/// <summary>
/// Sets an environment variable to be set in the operating system's environment under which the Firefox browser is launched.
/// </summary>
/// <param name="variableName">The name of the environment variable.</param>
/// <param name="variableValue">The value of the environment variable.</param>
/// <exception cref="ArgumentException">If <paramref name="variableName"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
public void SetEnvironmentVariable(string variableName, string? variableValue)
{
if (string.IsNullOrEmpty(variableName))
{
throw new ArgumentException("Environment variable name cannot be null or an empty string");
}
this.environmentVariables[variableName] = variableValue ?? string.Empty;
}
/// <summary>
/// Provides a means to add additional capabilities not yet added as type safe options
/// for the Firefox driver.
/// </summary>
/// <param name="optionName">The name of the capability to add.</param>
/// <param name="optionValue">The value of the capability to add.</param>
/// <exception cref="ArgumentException">
/// thrown when attempting to add a capability for which there is already a type safe option, or
/// when <paramref name="optionName"/> is <see langword="null"/> or the empty string.
/// </exception>
/// <remarks>Calling <see cref="AddAdditionalFirefoxOption(string, object)"/>
/// where <paramref name="optionName"/> has already been added will overwrite the
/// existing value with the new value in <paramref name="optionValue"/>.
/// Calling this method adds capabilities to the Firefox-specific options object passed to
/// geckodriver.exe (property name 'moz:firefoxOptions').</remarks>
public void AddAdditionalFirefoxOption(string optionName, object optionValue)
{
this.ValidateCapabilityName(optionName);
this.additionalFirefoxOptions[optionName] = optionValue;
}
/// <summary>
/// Returns DesiredCapabilities for Firefox with these options included as
/// capabilities. This does not copy the options. Further changes will be
/// reflected in the returned capabilities.
/// </summary>
/// <returns>The DesiredCapabilities for Firefox with these options.</returns>
public override ICapabilities ToCapabilities()
{
IWritableCapabilities capabilities = GenerateDesiredCapabilities(true);
Dictionary<string, object> firefoxOptions = this.GenerateFirefoxOptionsDictionary();
capabilities.SetCapability(FirefoxOptionsCapability, firefoxOptions);
if (this.EnableDevToolsProtocol)
{
capabilities.SetCapability(FirefoxEnableDevToolsProtocolCapability, true);
}
return capabilities.AsReadOnly();
}
private Dictionary<string, object> GenerateFirefoxOptionsDictionary()
{
Dictionary<string, object> firefoxOptions = new Dictionary<string, object>();
if (this.Profile != null)
{
firefoxOptions[FirefoxProfileCapability] = this.Profile.ToBase64String();
}
if (!string.IsNullOrEmpty(this.BinaryLocation))
{
firefoxOptions[FirefoxBinaryCapability] = this.BinaryLocation!;
}
if (this.LogLevel != FirefoxDriverLogLevel.Default)
{
Dictionary<string, object> logObject = new Dictionary<string, object>();
logObject["level"] = this.LogLevel.ToString().ToLowerInvariant();
firefoxOptions[FirefoxLogCapability] = logObject;
}
if (this.firefoxArguments.Count > 0)
{
List<object> args = [.. this.firefoxArguments];
firefoxOptions[FirefoxArgumentsCapability] = args;
}
if (this.profilePreferences.Count > 0)
{
firefoxOptions[FirefoxPrefsCapability] = this.profilePreferences;
}
if (this.environmentVariables.Count > 0)
{
firefoxOptions[FirefoxEnvCapability] = this.environmentVariables;
}
if (this.AndroidOptions != null)
{
AddAndroidOptions(this.AndroidOptions, firefoxOptions);
}
foreach (KeyValuePair<string, object> pair in this.additionalFirefoxOptions)
{
firefoxOptions.Add(pair.Key, pair.Value);
}
return firefoxOptions;
}
private void SetPreferenceValue(string preferenceName, object preferenceValue)
{
if (string.IsNullOrEmpty(preferenceName))
{
throw new ArgumentException("Preference name may not be null an empty string.", nameof(preferenceName));
}
this.profilePreferences[preferenceName] = preferenceValue;
}
private static void AddAndroidOptions(FirefoxAndroidOptions androidOptions, Dictionary<string, object> firefoxOptions)
{
firefoxOptions["androidPackage"] = androidOptions.AndroidPackage;
if (!string.IsNullOrEmpty(androidOptions.AndroidDeviceSerial))
{
firefoxOptions["androidDeviceSerial"] = androidOptions.AndroidDeviceSerial!;
}
if (!string.IsNullOrEmpty(androidOptions.AndroidActivity))
{
firefoxOptions["androidActivity"] = androidOptions.AndroidActivity!;
}
if (androidOptions.AndroidIntentArguments.Count > 0)
{
List<object> args = [.. androidOptions.AndroidIntentArguments];
firefoxOptions["androidIntentArguments"] = args;
}
}
}