Path: blob/trunk/dotnet/src/webdriver/Firefox/FirefoxOptions.cs
2885 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 path and file name of the Firefox browser executable. /// </summary> [Obsolete("Use BinaryLocation property instead of BrowserExecutableLocation. This one will be removed soon.")] public string? BrowserExecutableLocation { get => this.BinaryLocation; set => this.BinaryLocation = value; } /// <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; } } }