/dotnet/src/webdriver/Chrome/ChromeOptions.cs

https://github.com/Deitrl/selenium · C# · 528 lines · 290 code · 58 blank · 180 comment · 47 complexity · 66d139f033f454f54b66d9cdcaf8a10a MD5 · raw file

  1. // <copyright file="ChromeOptions.cs" company="WebDriver Committers">
  2. // Copyright 2007-2011 WebDriver committers
  3. // Copyright 2007-2011 Google Inc.
  4. // Portions copyright 2011 Software Freedom Conservancy
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License");
  7. // you may not use this file except in compliance with the License.
  8. // You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. // </copyright>
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Collections.ObjectModel;
  21. using System.Globalization;
  22. using System.IO;
  23. using System.Text;
  24. using Newtonsoft.Json;
  25. using OpenQA.Selenium.Remote;
  26. namespace OpenQA.Selenium.Chrome
  27. {
  28. /// <summary>
  29. /// Class to manage options specific to <see cref="ChromeDriver"/>
  30. /// </summary>
  31. /// <remarks>
  32. /// Used with ChromeDriver.exe v17.0.963.0 and higher.
  33. /// </remarks>
  34. /// <example>
  35. /// <code>
  36. /// ChromeOptions options = new ChromeOptions();
  37. /// options.AddExtensions("\path\to\extension.crx");
  38. /// options.BinaryLocation = "\path\to\chrome";
  39. /// </code>
  40. /// <para></para>
  41. /// <para>For use with ChromeDriver:</para>
  42. /// <para></para>
  43. /// <code>
  44. /// ChromeDriver driver = new ChromeDriver(options);
  45. /// </code>
  46. /// <para></para>
  47. /// <para>For use with RemoteWebDriver:</para>
  48. /// <para></para>
  49. /// <code>
  50. /// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities());
  51. /// </code>
  52. /// </example>
  53. public class ChromeOptions
  54. {
  55. /// <summary>
  56. /// Gets the name of the capability used to store Chrome options in
  57. /// a <see cref="DesiredCapabilities"/> object.
  58. /// </summary>
  59. public static readonly string Capability = "chromeOptions";
  60. private const string ArgumentsChromeOption = "args";
  61. private const string BinaryChromeOption = "binary";
  62. private const string ExtensionsChromeOption = "extensions";
  63. private const string LocalStateChromeOption = "localState";
  64. private const string PreferencesChromeOption = "prefs";
  65. private const string DetachChromeOption = "detach";
  66. private const string DebuggerAddressChromeOption = "debuggerAddress";
  67. private const string ExcludeSwitchesChromeOption = "excludeSwitches";
  68. private const string MinidumpPathChromeOption = "minidumpPath";
  69. private bool leaveBrowserRunning;
  70. private string binaryLocation;
  71. private string debuggerAddress;
  72. private string minidumpPath;
  73. private List<string> arguments = new List<string>();
  74. private List<string> extensionFiles = new List<string>();
  75. private List<string> encodedExtensions = new List<string>();
  76. private List<string> excludedSwitches = new List<string>();
  77. private Dictionary<string, object> additionalCapabilities = new Dictionary<string, object>();
  78. private Dictionary<string, object> additionalChromeOptions = new Dictionary<string, object>();
  79. private Dictionary<string, object> userProfilePreferences;
  80. private Dictionary<string, object> localStatePreferences;
  81. private Proxy proxy;
  82. /// <summary>
  83. /// Gets or sets the location of the Chrome browser's binary executable file.
  84. /// </summary>
  85. public string BinaryLocation
  86. {
  87. get { return this.binaryLocation; }
  88. set { this.binaryLocation = value; }
  89. }
  90. /// <summary>
  91. /// Gets or sets a value indicating whether Chrome should be left running after the
  92. /// ChromeDriver instance is exited. Defaults to <see langword="false"/>.
  93. /// </summary>
  94. public bool LeaveBrowserRunning
  95. {
  96. get { return this.leaveBrowserRunning; }
  97. set { this.leaveBrowserRunning = value; }
  98. }
  99. /// <summary>
  100. /// Gets or sets the proxy to use with this instance of Chrome.
  101. /// </summary>
  102. public Proxy Proxy
  103. {
  104. get { return this.proxy; }
  105. set { this.proxy = value; }
  106. }
  107. /// <summary>
  108. /// Gets the list of arguments appended to the Chrome command line as a string array.
  109. /// </summary>
  110. public ReadOnlyCollection<string> Arguments
  111. {
  112. get { return this.arguments.AsReadOnly(); }
  113. }
  114. /// <summary>
  115. /// Gets the list of extensions to be installed as an array of base64-encoded strings.
  116. /// </summary>
  117. public ReadOnlyCollection<string> Extensions
  118. {
  119. get
  120. {
  121. List<string> allExtensions = new List<string>(this.encodedExtensions);
  122. foreach (string extensionFile in this.extensionFiles)
  123. {
  124. byte[] extensionByteArray = File.ReadAllBytes(extensionFile);
  125. string encodedExtension = Convert.ToBase64String(extensionByteArray);
  126. allExtensions.Add(encodedExtension);
  127. }
  128. return allExtensions.AsReadOnly();
  129. }
  130. }
  131. /// <summary>
  132. /// Gets or sets the address of a Chrome debugger server to connect to.
  133. /// Should be of the form "{hostname|IP address}:port".
  134. /// </summary>
  135. public string DebuggerAddress
  136. {
  137. get { return this.debuggerAddress; }
  138. set { this.debuggerAddress = value; }
  139. }
  140. /// <summary>
  141. /// Gets or sets the directory in which to store minidump files.
  142. /// </summary>
  143. public string MinidumpPath
  144. {
  145. get { return this.minidumpPath; }
  146. set { this.minidumpPath = value; }
  147. }
  148. /// <summary>
  149. /// Adds a single argument to the list of arguments to be appended to the Chrome.exe command line.
  150. /// </summary>
  151. /// <param name="argument">The argument to add.</param>
  152. public void AddArgument(string argument)
  153. {
  154. if (string.IsNullOrEmpty(argument))
  155. {
  156. throw new ArgumentException("argument must not be null or empty", "argument");
  157. }
  158. this.AddArguments(argument);
  159. }
  160. /// <summary>
  161. /// Adds arguments to be appended to the Chrome.exe command line.
  162. /// </summary>
  163. /// <param name="argumentsToAdd">An array of arguments to add.</param>
  164. public void AddArguments(params string[] argumentsToAdd)
  165. {
  166. this.AddArguments(new List<string>(argumentsToAdd));
  167. }
  168. /// <summary>
  169. /// Adds arguments to be appended to the Chrome.exe command line.
  170. /// </summary>
  171. /// <param name="argumentsToAdd">An <see cref="IEnumerable{T}"/> object of arguments to add.</param>
  172. public void AddArguments(IEnumerable<string> argumentsToAdd)
  173. {
  174. if (argumentsToAdd == null)
  175. {
  176. throw new ArgumentNullException("arguments", "arguments must not be null");
  177. }
  178. this.arguments.AddRange(argumentsToAdd);
  179. }
  180. /// <summary>
  181. /// Adds a single argument to be excluded from the list of arguments passed by default
  182. /// to the Chrome.exe command line by chromedriver.exe.
  183. /// </summary>
  184. /// <param name="argument">The argument to exclude.</param>
  185. public void AddExcludedArgument(string argument)
  186. {
  187. if (string.IsNullOrEmpty(argument))
  188. {
  189. throw new ArgumentException("argument must not be null or empty", "argument");
  190. }
  191. this.AddExcludedArguments(argument);
  192. }
  193. /// <summary>
  194. /// Adds arguments to be excluded from the list of arguments passed by default
  195. /// to the Chrome.exe command line by chromedriver.exe.
  196. /// </summary>
  197. /// <param name="arguments">An array of arguments to exclude.</param>
  198. public void AddExcludedArguments(params string[] arguments)
  199. {
  200. this.AddExcludedArguments(new List<string>(arguments));
  201. }
  202. /// <summary>
  203. /// Adds arguments to be excluded from the list of arguments passed by default
  204. /// to the Chrome.exe command line by chromedriver.exe.
  205. /// </summary>
  206. /// <param name="arguments">An <see cref="IEnumerable{T}"/> object of arguments to exclude.</param>
  207. public void AddExcludedArguments(IEnumerable<string> arguments)
  208. {
  209. if (arguments == null)
  210. {
  211. throw new ArgumentNullException("arguments", "arguments must not be null");
  212. }
  213. this.excludedSwitches.AddRange(arguments);
  214. }
  215. /// <summary>
  216. /// Adds a path to a packed Chrome extension (.crx file) to the list of extensions
  217. /// to be installed in the instance of Chrome.
  218. /// </summary>
  219. /// <param name="pathToExtension">The full path to the extension to add.</param>
  220. public void AddExtension(string pathToExtension)
  221. {
  222. if (string.IsNullOrEmpty(pathToExtension))
  223. {
  224. throw new ArgumentException("pathToExtension must not be null or empty", "pathToExtension");
  225. }
  226. this.AddExtensions(pathToExtension);
  227. }
  228. /// <summary>
  229. /// Adds a list of paths to packed Chrome extensions (.crx files) to be installed
  230. /// in the instance of Chrome.
  231. /// </summary>
  232. /// <param name="extensions">An array of full paths to the extensions to add.</param>
  233. public void AddExtensions(params string[] extensions)
  234. {
  235. this.AddExtensions(new List<string>(extensions));
  236. }
  237. /// <summary>
  238. /// Adds a list of paths to packed Chrome extensions (.crx files) to be installed
  239. /// in the instance of Chrome.
  240. /// </summary>
  241. /// <param name="extensions">An <see cref="IEnumerable{T}"/> of full paths to the extensions to add.</param>
  242. public void AddExtensions(IEnumerable<string> extensions)
  243. {
  244. if (extensions == null)
  245. {
  246. throw new ArgumentNullException("extensions", "extensions must not be null");
  247. }
  248. foreach (string extension in extensions)
  249. {
  250. if (!File.Exists(extension))
  251. {
  252. throw new FileNotFoundException("No extension found at the specified path", extension);
  253. }
  254. this.extensionFiles.Add(extension);
  255. }
  256. }
  257. /// <summary>
  258. /// Adds a base64-encoded string representing a Chrome extension to the list of extensions
  259. /// to be installed in the instance of Chrome.
  260. /// </summary>
  261. /// <param name="extension">A base64-encoded string representing the extension to add.</param>
  262. public void AddEncodedExtension(string extension)
  263. {
  264. if (string.IsNullOrEmpty(extension))
  265. {
  266. throw new ArgumentException("extension must not be null or empty", "extension");
  267. }
  268. this.AddExtensions(extension);
  269. }
  270. /// <summary>
  271. /// Adds a list of base64-encoded strings representing Chrome extensions to the list of extensions
  272. /// to be installed in the instance of Chrome.
  273. /// </summary>
  274. /// <param name="extensions">An array of base64-encoded strings representing the extensions to add.</param>
  275. public void AddEncodedExtensions(params string[] extensions)
  276. {
  277. this.AddEncodedExtensions(new List<string>(extensions));
  278. }
  279. /// <summary>
  280. /// Adds a list of base64-encoded strings representing Chrome extensions to be installed
  281. /// in the instance of Chrome.
  282. /// </summary>
  283. /// <param name="extensions">An <see cref="IEnumerable{T}"/> of base64-encoded strings
  284. /// representing the extensions to add.</param>
  285. public void AddEncodedExtensions(IEnumerable<string> extensions)
  286. {
  287. if (extensions == null)
  288. {
  289. throw new ArgumentNullException("extensions", "extensions must not be null");
  290. }
  291. foreach (string extension in extensions)
  292. {
  293. // Run the extension through the base64 converter to test that the
  294. // string is not malformed.
  295. try
  296. {
  297. Convert.FromBase64String(extension);
  298. }
  299. catch (FormatException ex)
  300. {
  301. throw new WebDriverException("Could not properly decode the base64 string", ex);
  302. }
  303. this.encodedExtensions.Add(extension);
  304. }
  305. }
  306. /// <summary>
  307. /// Adds a preference for the user-specific profile or "user data directory."
  308. /// If the specified preference already exists, it will be overwritten.
  309. /// </summary>
  310. /// <param name="preferenceName">The name of the preference to set.</param>
  311. /// <param name="preferenceValue">The value of the preference to set.</param>
  312. public void AddUserProfilePreference(string preferenceName, object preferenceValue)
  313. {
  314. if (this.userProfilePreferences == null)
  315. {
  316. this.userProfilePreferences = new Dictionary<string, object>();
  317. }
  318. this.userProfilePreferences[preferenceName] = preferenceValue;
  319. }
  320. /// <summary>
  321. /// Adds a preference for the local state file in the user's data directory for Chrome.
  322. /// If the specified preference already exists, it will be overwritten.
  323. /// </summary>
  324. /// <param name="preferenceName">The name of the preference to set.</param>
  325. /// <param name="preferenceValue">The value of the preference to set.</param>
  326. public void AddLocalStatePreference(string preferenceName, object preferenceValue)
  327. {
  328. if (this.localStatePreferences == null)
  329. {
  330. this.localStatePreferences = new Dictionary<string, object>();
  331. }
  332. this.localStatePreferences[preferenceName] = preferenceValue;
  333. }
  334. /// <summary>
  335. /// Provides a means to add additional capabilities not yet added as type safe options
  336. /// for the Chrome driver.
  337. /// </summary>
  338. /// <param name="capabilityName">The name of the capability to add.</param>
  339. /// <param name="capabilityValue">The value of the capability to add.</param>
  340. /// <exception cref="ArgumentException">
  341. /// thrown when attempting to add a capability for which there is already a type safe option, or
  342. /// when <paramref name="capabilityName"/> is <see langword="null"/> or the empty string.
  343. /// </exception>
  344. /// <remarks>Calling <see cref="AddAdditionalCapability(System.String, System.Object)"/>
  345. /// where <paramref name="capabilityName"/> has already been added will overwrite the
  346. /// existing value with the new value in <paramref name="capabilityValue"/>.
  347. /// Also, by default, calling this method adds capabilities to the options object passed to
  348. /// chromedriver.exe.</remarks>
  349. public void AddAdditionalCapability(string capabilityName, object capabilityValue)
  350. {
  351. // Add the capability to the chromeOptions object by default. This is to handle
  352. // the 80% case where the chromedriver team adds a new option in chromedriver.exe
  353. // and the bindings have not yet had a type safe option added.
  354. this.AddAdditionalCapability(capabilityName, capabilityValue, false);
  355. }
  356. /// <summary>
  357. /// Provides a means to add additional capabilities not yet added as type safe options
  358. /// for the Chrome driver.
  359. /// </summary>
  360. /// <param name="capabilityName">The name of the capability to add.</param>
  361. /// <param name="capabilityValue">The value of the capability to add.</param>
  362. /// <param name="isGlobalCapability">Indicates whether the capability is to be set as a global
  363. /// capability for the driver instead of a Chrome-specific option.</param>
  364. /// <exception cref="ArgumentException">
  365. /// thrown when attempting to add a capability for which there is already a type safe option, or
  366. /// when <paramref name="capabilityName"/> is <see langword="null"/> or the empty string.
  367. /// </exception>
  368. /// <remarks>Calling <see cref="AddAdditionalCapability(System.String, System.Object, System.Boolean)"/>
  369. /// where <paramref name="capabilityName"/> has already been added will overwrite the
  370. /// existing value with the new value in <paramref name="capabilityValue"/></remarks>
  371. public void AddAdditionalCapability(string capabilityName, object capabilityValue, bool isGlobalCapability)
  372. {
  373. if (capabilityName == ChromeOptions.Capability ||
  374. capabilityName == CapabilityType.Proxy ||
  375. capabilityName == ChromeOptions.ArgumentsChromeOption ||
  376. capabilityName == ChromeOptions.BinaryChromeOption ||
  377. capabilityName == ChromeOptions.ExtensionsChromeOption ||
  378. capabilityName == ChromeOptions.LocalStateChromeOption ||
  379. capabilityName == ChromeOptions.PreferencesChromeOption ||
  380. capabilityName == ChromeOptions.DetachChromeOption ||
  381. capabilityName == ChromeOptions.DebuggerAddressChromeOption ||
  382. capabilityName == ChromeOptions.ExtensionsChromeOption ||
  383. capabilityName == ChromeOptions.ExcludeSwitchesChromeOption ||
  384. capabilityName == ChromeOptions.MinidumpPathChromeOption)
  385. {
  386. string message = string.Format(CultureInfo.InvariantCulture, "There is already an option for the {0} capability. Please use that instead.", capabilityName);
  387. throw new ArgumentException(message, "capabilityName");
  388. }
  389. if (string.IsNullOrEmpty(capabilityName))
  390. {
  391. throw new ArgumentException("Capability name may not be null an empty string.", "capabilityName");
  392. }
  393. if (isGlobalCapability)
  394. {
  395. this.additionalCapabilities[capabilityName] = capabilityValue;
  396. }
  397. else
  398. {
  399. this.additionalChromeOptions[capabilityName] = capabilityValue;
  400. }
  401. }
  402. /// <summary>
  403. /// Returns DesiredCapabilities for Chrome with these options included as
  404. /// capabilities. This does not copy the options. Further changes will be
  405. /// reflected in the returned capabilities.
  406. /// </summary>
  407. /// <returns>The DesiredCapabilities for Chrome with these options.</returns>
  408. public ICapabilities ToCapabilities()
  409. {
  410. Dictionary<string, object> chromeOptions = this.BuildChromeOptionsDictionary();
  411. DesiredCapabilities capabilities = DesiredCapabilities.Chrome();
  412. capabilities.SetCapability(ChromeOptions.Capability, chromeOptions);
  413. if (this.proxy != null)
  414. {
  415. capabilities.SetCapability(CapabilityType.Proxy, this.proxy);
  416. }
  417. foreach (KeyValuePair<string, object> pair in this.additionalCapabilities)
  418. {
  419. capabilities.SetCapability(pair.Key, pair.Value);
  420. }
  421. return capabilities;
  422. }
  423. private Dictionary<string, object> BuildChromeOptionsDictionary()
  424. {
  425. Dictionary<string, object> chromeOptions = new Dictionary<string, object>();
  426. if (this.Arguments.Count > 0)
  427. {
  428. chromeOptions[ArgumentsChromeOption] = this.Arguments;
  429. }
  430. if (!string.IsNullOrEmpty(this.binaryLocation))
  431. {
  432. chromeOptions[BinaryChromeOption] = this.binaryLocation;
  433. }
  434. ReadOnlyCollection<string> extensions = this.Extensions;
  435. if (extensions.Count > 0)
  436. {
  437. chromeOptions[ExtensionsChromeOption] = extensions;
  438. }
  439. if (this.localStatePreferences != null && this.localStatePreferences.Count > 0)
  440. {
  441. chromeOptions[LocalStateChromeOption] = this.localStatePreferences;
  442. }
  443. if (this.userProfilePreferences != null && this.userProfilePreferences.Count > 0)
  444. {
  445. chromeOptions[PreferencesChromeOption] = this.userProfilePreferences;
  446. }
  447. if (this.leaveBrowserRunning)
  448. {
  449. chromeOptions[DetachChromeOption] = this.leaveBrowserRunning;
  450. }
  451. if (!string.IsNullOrEmpty(this.debuggerAddress))
  452. {
  453. chromeOptions[DebuggerAddressChromeOption] = this.debuggerAddress;
  454. }
  455. if (this.excludedSwitches.Count > 0)
  456. {
  457. chromeOptions[ExcludeSwitchesChromeOption] = this.excludedSwitches;
  458. }
  459. if (!string.IsNullOrEmpty(this.minidumpPath))
  460. {
  461. chromeOptions[MinidumpPathChromeOption] = this.minidumpPath;
  462. }
  463. foreach (KeyValuePair<string, object> pair in this.additionalChromeOptions)
  464. {
  465. chromeOptions.Add(pair.Key, pair.Value);
  466. }
  467. return chromeOptions;
  468. }
  469. }
  470. }