Skip to content

Commit 6d361a4

Browse files
committed
Making Marionette the default for .NET FirefoxDriver in 3.0
1 parent 6066e42 commit 6d361a4

File tree

7 files changed

+227
-78
lines changed

7 files changed

+227
-78
lines changed

dotnet/src/webdriver/Firefox/FirefoxDriver.cs

Lines changed: 101 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,11 @@ public class FirefoxDriver : RemoteWebDriver
9898
/// </summary>
9999
public static readonly bool AssumeUntrustedCertificateIssuer = true;
100100

101-
private FirefoxBinary binary;
102-
103-
private FirefoxProfile profile;
104-
105101
/// <summary>
106102
/// Initializes a new instance of the <see cref="FirefoxDriver"/> class.
107103
/// </summary>
108104
public FirefoxDriver()
109-
: this(new FirefoxBinary(), null)
105+
: this(new FirefoxOptions(null, null))
110106
{
111107
}
112108

@@ -116,7 +112,7 @@ public FirefoxDriver()
116112
/// <param name="profile">A <see cref="FirefoxProfile"/> object representing the profile settings
117113
/// to be used in starting Firefox.</param>
118114
public FirefoxDriver(FirefoxProfile profile)
119-
: this(new FirefoxBinary(), profile)
115+
: this(new FirefoxOptions(profile, null))
120116
{
121117
}
122118

@@ -125,8 +121,9 @@ public FirefoxDriver(FirefoxProfile profile)
125121
/// </summary>
126122
/// <param name="capabilities">The <see cref="ICapabilities"/> object containing the desired
127123
/// capabilities of this FirefoxDriver.</param>
124+
[Obsolete("FirefoxDriver should not be constructed with a raw ICapabilities or DesiredCapabilities object. Use FirefoxOptions instead. This constructor will be removed before the 3.0 release.")]
128125
public FirefoxDriver(ICapabilities capabilities)
129-
: this(ExtractBinary(capabilities), ExtractProfile(capabilities), capabilities, RemoteWebDriver.DefaultCommandTimeout)
126+
: this(CreateOptionsFromCapabilities(capabilities))
130127
{
131128
}
132129

@@ -137,8 +134,9 @@ public FirefoxDriver(ICapabilities capabilities)
137134
/// environmental settings used when running Firefox.</param>
138135
/// <param name="profile">A <see cref="FirefoxProfile"/> object representing the profile settings
139136
/// to be used in starting Firefox.</param>
137+
[Obsolete("FirefoxDriver should not be constructed with a FirefoxBinary object. Use FirefoxOptions instead. This constructor will be removed before the 3.0 release.")]
140138
public FirefoxDriver(FirefoxBinary binary, FirefoxProfile profile)
141-
: this(binary, profile, RemoteWebDriver.DefaultCommandTimeout)
139+
: this(new FirefoxOptions(profile, binary))
142140
{
143141
}
144142

@@ -150,8 +148,9 @@ public FirefoxDriver(FirefoxBinary binary, FirefoxProfile profile)
150148
/// <param name="profile">A <see cref="FirefoxProfile"/> object representing the profile settings
151149
/// to be used in starting Firefox.</param>
152150
/// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
151+
[Obsolete("FirefoxDriver should not be constructed with a FirefoxBinary object. Use FirefoxOptions instead. This constructor will be removed before the 3.0 release.")]
153152
public FirefoxDriver(FirefoxBinary binary, FirefoxProfile profile, TimeSpan commandTimeout)
154-
: this(binary, profile, DesiredCapabilities.Firefox(), commandTimeout)
153+
: this(null, new FirefoxOptions(profile, binary), commandTimeout)
155154
{
156155
}
157156

@@ -180,15 +179,8 @@ public FirefoxDriver(FirefoxDriverService service)
180179
/// <param name="options">The <see cref="FirefoxOptions"/> to be used with the Firefox driver.</param>
181180
/// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
182181
public FirefoxDriver(FirefoxDriverService service, FirefoxOptions options, TimeSpan commandTimeout)
183-
: base(new DriverServiceCommandExecutor(service, commandTimeout), ConvertOptionsToCapabilities(options))
184-
{
185-
}
186-
187-
private FirefoxDriver(FirefoxBinary binary, FirefoxProfile profile, ICapabilities capabilities, TimeSpan commandTimeout)
188-
: base(CreateExtensionConnection(binary, profile, commandTimeout), RemoveUnneededCapabilities(capabilities))
182+
: base(CreateExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options))
189183
{
190-
this.binary = binary;
191-
this.profile = profile;
192184
}
193185

194186
/// <summary>
@@ -218,22 +210,6 @@ public bool IsMarionette
218210
get { return this.IsSpecificationCompliant; }
219211
}
220212

221-
/// <summary>
222-
/// Gets the FirefoxBinary and its details for subclasses
223-
/// </summary>
224-
protected FirefoxBinary Binary
225-
{
226-
get { return this.binary; }
227-
}
228-
229-
/// <summary>
230-
/// Gets the FirefoxProfile that is currently in use by subclasses
231-
/// </summary>
232-
protected FirefoxProfile Profile
233-
{
234-
get { return this.profile; }
235-
}
236-
237213
/// <summary>
238214
/// In derived classes, the <see cref="PrepareEnvironment"/> method prepares the environment for test execution.
239215
/// </summary>
@@ -252,6 +228,98 @@ protected override RemoteWebElement CreateElement(string elementId)
252228
return new FirefoxWebElement(this, elementId);
253229
}
254230

231+
private static ICommandExecutor CreateExecutor(FirefoxDriverService service, FirefoxOptions options, TimeSpan commandTimeout)
232+
{
233+
ICommandExecutor executor = null;
234+
if (options.UseLegacyImplementation)
235+
{
236+
// Note: If BrowserExecutableLocation is null or empty, the legacy driver
237+
// will still do the right thing, and find Firefox in the default location.
238+
FirefoxBinary binary = new FirefoxBinary(options.BrowserExecutableLocation);
239+
240+
FirefoxProfile profile = options.Profile;
241+
if (profile == null)
242+
{
243+
profile = new FirefoxProfile();
244+
}
245+
246+
executor = CreateExtensionConnection(binary, profile, commandTimeout);
247+
}
248+
else
249+
{
250+
if (service == null)
251+
{
252+
throw new ArgumentNullException("service", "You requested a service-based implementation, but passed in a null service object.");
253+
}
254+
255+
return new DriverServiceCommandExecutor(service, commandTimeout);
256+
}
257+
258+
return executor;
259+
}
260+
261+
private static ICommandExecutor CreateExtensionConnection(FirefoxBinary binary, FirefoxProfile profile, TimeSpan commandTimeout)
262+
{
263+
FirefoxProfile profileToUse = profile;
264+
265+
string suggestedProfile = Environment.GetEnvironmentVariable("webdriver.firefox.profile");
266+
if (profileToUse == null && suggestedProfile != null)
267+
{
268+
profileToUse = new FirefoxProfileManager().GetProfile(suggestedProfile);
269+
}
270+
else if (profileToUse == null)
271+
{
272+
profileToUse = new FirefoxProfile();
273+
}
274+
275+
FirefoxDriverCommandExecutor executor = new FirefoxDriverCommandExecutor(binary, profileToUse, "localhost", commandTimeout);
276+
return executor;
277+
}
278+
279+
private static ICapabilities ConvertOptionsToCapabilities(FirefoxOptions options)
280+
{
281+
if (options == null)
282+
{
283+
throw new ArgumentNullException("options", "options must not be null");
284+
}
285+
286+
ICapabilities capabilities = options.ToCapabilities();
287+
if (options.UseLegacyImplementation)
288+
{
289+
capabilities = RemoveUnneededCapabilities(capabilities);
290+
}
291+
292+
return capabilities;
293+
}
294+
295+
private static ICapabilities RemoveUnneededCapabilities(ICapabilities capabilities)
296+
{
297+
DesiredCapabilities caps = capabilities as DesiredCapabilities;
298+
caps.CapabilitiesDictionary.Remove(FirefoxDriver.ProfileCapabilityName);
299+
caps.CapabilitiesDictionary.Remove(FirefoxDriver.BinaryCapabilityName);
300+
return caps;
301+
}
302+
303+
private static FirefoxOptions CreateOptionsFromCapabilities(ICapabilities capabilities)
304+
{
305+
// This is awkward and hacky. To be removed when the legacy driver is retired.
306+
FirefoxBinary binary = ExtractBinary(capabilities);
307+
FirefoxProfile profile = ExtractProfile(capabilities);
308+
DesiredCapabilities desiredCaps = RemoveUnneededCapabilities(capabilities) as DesiredCapabilities;
309+
310+
FirefoxOptions options = new FirefoxOptions(profile, binary);
311+
if (desiredCaps != null)
312+
{
313+
Dictionary<string, object> capsDictionary = desiredCaps.ToDictionary();
314+
foreach (KeyValuePair<string, object> capability in capsDictionary)
315+
{
316+
options.AddAdditionalCapability(capability.Key, capability.Value);
317+
}
318+
}
319+
320+
return options;
321+
}
322+
255323
private static FirefoxBinary ExtractBinary(ICapabilities capabilities)
256324
{
257325
if (capabilities.GetCapability(BinaryCapabilityName) != null)
@@ -314,41 +382,5 @@ private static FirefoxProfile ExtractProfile(ICapabilities capabilities)
314382

315383
return profile;
316384
}
317-
318-
private static ICommandExecutor CreateExtensionConnection(FirefoxBinary binary, FirefoxProfile profile, TimeSpan commandTimeout)
319-
{
320-
FirefoxProfile profileToUse = profile;
321-
322-
string suggestedProfile = Environment.GetEnvironmentVariable("webdriver.firefox.profile");
323-
if (profileToUse == null && suggestedProfile != null)
324-
{
325-
profileToUse = new FirefoxProfileManager().GetProfile(suggestedProfile);
326-
}
327-
else if (profileToUse == null)
328-
{
329-
profileToUse = new FirefoxProfile();
330-
}
331-
332-
FirefoxDriverCommandExecutor executor = new FirefoxDriverCommandExecutor(binary, profileToUse, "localhost", commandTimeout);
333-
return executor;
334-
}
335-
336-
private static ICapabilities RemoveUnneededCapabilities(ICapabilities capabilities)
337-
{
338-
DesiredCapabilities caps = capabilities as DesiredCapabilities;
339-
caps.CapabilitiesDictionary.Remove(FirefoxDriver.ProfileCapabilityName);
340-
caps.CapabilitiesDictionary.Remove(FirefoxDriver.BinaryCapabilityName);
341-
return caps;
342-
}
343-
344-
private static ICapabilities ConvertOptionsToCapabilities(FirefoxOptions options)
345-
{
346-
if (options == null)
347-
{
348-
throw new ArgumentNullException("options", "options must not be null");
349-
}
350-
351-
return options.ToCapabilities();
352-
}
353385
}
354386
}

dotnet/src/webdriver/Firefox/FirefoxDriverService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public sealed class FirefoxDriverService : DriverService
3131
{
3232
private const string FirefoxDriverServiceFileName = "geckodriver.exe";
3333
private static readonly Uri FirefoxDriverDownloadUrl = new Uri("https://github.com/mozilla/geckodriver/releases");
34-
private string browserBinaryPath = @"C:\Program Files (x86)\Nightly\firefox.exe";
34+
private string browserBinaryPath = string.Empty;
3535
private int browserCommunicationPort = -1;
3636

3737
/// <summary>

dotnet/src/webdriver/Firefox/FirefoxOptions.cs

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
// </copyright>
1818

1919
using System;
20+
using System.Collections.Generic;
21+
using System.Globalization;
2022
using OpenQA.Selenium.Remote;
2123

2224
namespace OpenQA.Selenium.Firefox
@@ -46,17 +48,79 @@ namespace OpenQA.Selenium.Firefox
4648
/// </example>
4749
public class FirefoxOptions : DriverOptions
4850
{
51+
private const string IsMarionetteCapability = "marionette";
52+
private const string FirefoxProfileCapability = "firefox_profile";
53+
private const string FirefoxBinaryCapability = "firefox_binary";
54+
4955
private bool isMarionette = true;
56+
private string browserBinaryLocation;
57+
private FirefoxProfile profile;
58+
private Dictionary<string, object> additionalCapabilities = new Dictionary<string, object>();
59+
60+
/// <summary>
61+
/// Initializes a new instance of the <see cref="FirefoxOptions"/> class.
62+
/// </summary>
63+
public FirefoxOptions()
64+
: base()
65+
{
66+
}
67+
68+
/// <summary>
69+
/// Initializes a new instance of the <see cref="FirefoxOptions"/> class for the given profile and binary.
70+
/// </summary>
71+
/// <param name="profile">The <see cref="FirefoxProfile"/> to use in the options.</param>
72+
/// <param name="binary">The <see cref="FirefoxBinary"/> to use in the options.</param>
73+
internal FirefoxOptions(FirefoxProfile profile, FirefoxBinary binary)
74+
{
75+
if (profile != null)
76+
{
77+
this.profile = profile;
78+
}
79+
80+
if (binary != null)
81+
{
82+
this.browserBinaryLocation = binary.BinaryExecutable.ExecutablePath;
83+
}
84+
}
5085

5186
/// <summary>
5287
/// Gets or sets a value indicating whether or not to use the Mozilla-provided Marionette implementation.
88+
/// Defaults to <see langword="true"/>.
5389
/// </summary>
90+
[Obsolete("Use the UseLegacyImplementation property instead. This property will be removed before the 3.0 release.")]
5491
public bool IsMarionette
5592
{
5693
get { return this.isMarionette; }
5794
set { this.isMarionette = value; }
5895
}
5996

97+
/// <summary>
98+
/// Gets or sets a value indicating whether to use the legacy driver implementation.
99+
/// </summary>
100+
public bool UseLegacyImplementation
101+
{
102+
get { return !this.isMarionette; }
103+
set { this.isMarionette = !value; }
104+
}
105+
106+
/// <summary>
107+
/// Gets or sets the <see cref="FirefoxProfile"/> object to be used with this instance.
108+
/// </summary>
109+
public FirefoxProfile Profile
110+
{
111+
get { return this.profile; }
112+
set { this.profile = value; }
113+
}
114+
115+
/// <summary>
116+
/// Gets or sets the path and file name of the Firefox browser executable.
117+
/// </summary>
118+
public string BrowserExecutableLocation
119+
{
120+
get { return this.browserBinaryLocation; }
121+
set { this.browserBinaryLocation = value; }
122+
}
123+
60124
/// <summary>
61125
/// Provides a means to add additional capabilities not yet added as type safe options
62126
/// for the Firefox driver.
@@ -72,6 +136,20 @@ public bool IsMarionette
72136
/// change in the future.</remarks>
73137
public override void AddAdditionalCapability(string capabilityName, object capabilityValue)
74138
{
139+
if (capabilityName == IsMarionetteCapability ||
140+
capabilityName == FirefoxProfileCapability ||
141+
capabilityName == FirefoxBinaryCapability)
142+
{
143+
string message = string.Format(CultureInfo.InvariantCulture, "There is already an option for the {0} capability. Please use that instead.", capabilityName);
144+
throw new ArgumentException(message, "capabilityName");
145+
}
146+
147+
if (string.IsNullOrEmpty(capabilityName))
148+
{
149+
throw new ArgumentException("Capability name may not be null an empty string.", "capabilityName");
150+
}
151+
152+
this.additionalCapabilities[capabilityName] = capabilityValue;
75153
}
76154

77155
/// <summary>
@@ -83,7 +161,27 @@ public override void AddAdditionalCapability(string capabilityName, object capab
83161
public override ICapabilities ToCapabilities()
84162
{
85163
DesiredCapabilities capabilities = DesiredCapabilities.Firefox();
86-
capabilities.SetCapability("marionette", this.isMarionette);
164+
capabilities.SetCapability(IsMarionetteCapability, this.isMarionette);
165+
166+
if (this.profile != null)
167+
{
168+
capabilities.SetCapability(FirefoxProfileCapability, this.profile.ToBase64String());
169+
}
170+
171+
if (!string.IsNullOrEmpty(this.browserBinaryLocation))
172+
{
173+
capabilities.SetCapability(FirefoxBinaryCapability, this.browserBinaryLocation);
174+
}
175+
else
176+
{
177+
capabilities.SetCapability(FirefoxBinaryCapability, new FirefoxBinary().BinaryExecutable.ExecutablePath);
178+
}
179+
180+
foreach (KeyValuePair<string, object> pair in this.additionalCapabilities)
181+
{
182+
capabilities.SetCapability(pair.Key, pair.Value);
183+
}
184+
87185
return capabilities;
88186
}
89187
}

0 commit comments

Comments
 (0)