Skip to content

Commit e639050

Browse files
[dotnet] Annotate nullability on platform-specific WebDrivers (SeleniumHQ#15236)
* [dotnet] Annotate nullability on platform-specific WebDrivers * Use new Response not-null helper
1 parent a44f1d7 commit e639050

File tree

7 files changed

+167
-75
lines changed

7 files changed

+167
-75
lines changed

dotnet/src/webdriver/Chrome/ChromeDriver.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
using System.Collections.Generic;
2424
using System.Collections.ObjectModel;
2525

26+
#nullable enable
27+
2628
namespace OpenQA.Selenium.Chrome
2729
{
2830
/// <summary>
@@ -60,7 +62,7 @@ namespace OpenQA.Selenium.Chrome
6062
/// </example>
6163
public class ChromeDriver : ChromiumDriver
6264
{
63-
private static Dictionary<string, CommandInfo> chromeCustomCommands = new Dictionary<string, CommandInfo>()
65+
private static readonly Dictionary<string, CommandInfo> chromeCustomCommands = new Dictionary<string, CommandInfo>()
6466
{
6567
{ ExecuteCdp, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cdp/execute") },
6668
{ GetCastSinksCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_sinks") },
@@ -83,6 +85,7 @@ public ChromeDriver()
8385
/// Initializes a new instance of the <see cref="ChromeDriver"/> class using the specified options.
8486
/// </summary>
8587
/// <param name="options">The <see cref="ChromeOptions"/> to be used with the Chrome driver.</param>
88+
/// <exception cref="ArgumentNullException">If <paramref name="options"/> is <see langword="null"/>.</exception>
8689
public ChromeDriver(ChromeOptions options)
8790
: this(ChromeDriverService.CreateDefaultService(), options, RemoteWebDriver.DefaultCommandTimeout)
8891
{
@@ -92,6 +95,7 @@ public ChromeDriver(ChromeOptions options)
9295
/// Initializes a new instance of the <see cref="ChromeDriver"/> class using the specified driver service.
9396
/// </summary>
9497
/// <param name="service">The <see cref="ChromeDriverService"/> used to initialize the driver.</param>
98+
/// <exception cref="ArgumentNullException">If <paramref name="service"/> is <see langword="null"/>.</exception>
9599
public ChromeDriver(ChromeDriverService service)
96100
: this(service, new ChromeOptions())
97101
{
@@ -113,6 +117,7 @@ public ChromeDriver(string chromeDriverDirectory)
113117
/// </summary>
114118
/// <param name="chromeDriverDirectory">The full path to the directory containing ChromeDriver.exe.</param>
115119
/// <param name="options">The <see cref="ChromeOptions"/> to be used with the Chrome driver.</param>
120+
/// <exception cref="ArgumentNullException">If <paramref name="options"/> is <see langword="null"/>.</exception>
116121
public ChromeDriver(string chromeDriverDirectory, ChromeOptions options)
117122
: this(chromeDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout)
118123
{
@@ -125,6 +130,7 @@ public ChromeDriver(string chromeDriverDirectory, ChromeOptions options)
125130
/// <param name="chromeDriverDirectory">The full path to the directory containing ChromeDriver.exe.</param>
126131
/// <param name="options">The <see cref="ChromeOptions"/> to be used with the Chrome driver.</param>
127132
/// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
133+
/// <exception cref="ArgumentNullException">If <paramref name="options"/> is <see langword="null"/>.</exception>
128134
public ChromeDriver(string chromeDriverDirectory, ChromeOptions options, TimeSpan commandTimeout)
129135
: this(ChromeDriverService.CreateDefaultService(chromeDriverDirectory), options, commandTimeout)
130136
{
@@ -136,6 +142,7 @@ public ChromeDriver(string chromeDriverDirectory, ChromeOptions options, TimeSpa
136142
/// </summary>
137143
/// <param name="service">The <see cref="ChromeDriverService"/> to use.</param>
138144
/// <param name="options">The <see cref="ChromeOptions"/> used to initialize the driver.</param>
145+
/// <exception cref="ArgumentNullException">If <paramref name="service"/> or <paramref name="options"/> are <see langword="null"/>.</exception>
139146
public ChromeDriver(ChromeDriverService service, ChromeOptions options)
140147
: this(service, options, RemoteWebDriver.DefaultCommandTimeout)
141148
{
@@ -147,6 +154,7 @@ public ChromeDriver(ChromeDriverService service, ChromeOptions options)
147154
/// <param name="service">The <see cref="ChromeDriverService"/> to use.</param>
148155
/// <param name="options">The <see cref="ChromeOptions"/> to be used with the Chrome driver.</param>
149156
/// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
157+
/// <exception cref="ArgumentNullException">If <paramref name="service"/> or <paramref name="options"/> are <see langword="null"/>.</exception>
150158
public ChromeDriver(ChromeDriverService service, ChromeOptions options, TimeSpan commandTimeout)
151159
: base(service, options, commandTimeout)
152160
{

dotnet/src/webdriver/Chromium/ChromiumDriver.cs

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@
2222
using System;
2323
using System.Collections.Generic;
2424
using System.Collections.ObjectModel;
25+
using System.Diagnostics.CodeAnalysis;
2526
using System.IO;
2627
using System.Threading.Tasks;
2728

29+
#nullable enable
30+
2831
namespace OpenQA.Selenium.Chromium
2932
{
3033
/// <summary>
@@ -108,9 +111,9 @@ public class ChromiumDriver : WebDriver, ISupportsLogs, IDevTools
108111
public static readonly string SetPermissionCommand = "setPermission";
109112

110113
private readonly string optionsCapabilityName;
111-
private DevToolsSession devToolsSession;
114+
private DevToolsSession? devToolsSession;
112115

113-
private static Dictionary<string, CommandInfo> chromiumCustomCommands = new Dictionary<string, CommandInfo>()
116+
private static readonly Dictionary<string, CommandInfo> chromiumCustomCommands = new Dictionary<string, CommandInfo>()
114117
{
115118
{ GetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/chromium/network_conditions") },
116119
{ SetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/network_conditions") },
@@ -127,19 +130,18 @@ public class ChromiumDriver : WebDriver, ISupportsLogs, IDevTools
127130
/// <param name="service">The <see cref="ChromiumDriverService"/> to use.</param>
128131
/// <param name="options">The <see cref="ChromiumOptions"/> to be used with the ChromiumDriver.</param>
129132
/// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
133+
/// <exception cref="ArgumentNullException">If <paramref name="service"/> or <paramref name="options"/> are <see langword="null"/>.</exception>
134+
/// <exception cref="ArgumentException">If the Chromium options capability name is <see langword="null"/>.</exception>
130135
protected ChromiumDriver(ChromiumDriverService service, ChromiumOptions options, TimeSpan commandTimeout)
131136
: base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options))
132137
{
133-
this.optionsCapabilityName = options.CapabilityName;
138+
this.optionsCapabilityName = options.CapabilityName ?? throw new ArgumentException("No chromium options capability name specified", nameof(options));
134139
}
135140

136141
/// <summary>
137142
/// Gets the dictionary of custom Chromium commands registered with the driver.
138143
/// </summary>
139-
protected static IReadOnlyDictionary<string, CommandInfo> ChromiumCustomCommands
140-
{
141-
get { return new ReadOnlyDictionary<string, CommandInfo>(chromiumCustomCommands); }
142-
}
144+
protected static IReadOnlyDictionary<string, CommandInfo> ChromiumCustomCommands => new ReadOnlyDictionary<string, CommandInfo>(chromiumCustomCommands);
143145

144146
/// <summary>
145147
/// Uses DriverFinder to set Service attributes if necessary when creating the command executor
@@ -150,6 +152,16 @@ protected static IReadOnlyDictionary<string, CommandInfo> ChromiumCustomCommands
150152
/// <returns></returns>
151153
private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout)
152154
{
155+
if (service is null)
156+
{
157+
throw new ArgumentNullException(nameof(service));
158+
}
159+
160+
if (options is null)
161+
{
162+
throw new ArgumentNullException(nameof(options));
163+
}
164+
153165
if (service.DriverServicePath == null)
154166
{
155167
DriverFinder finder = new DriverFinder(options);
@@ -177,27 +189,31 @@ private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverServi
177189
/// in conjunction with a standalone WebDriver server.</remarks>
178190
public override IFileDetector FileDetector
179191
{
180-
get { return base.FileDetector; }
192+
get => base.FileDetector;
181193
set { }
182194
}
183195

184196
/// <summary>
185197
/// Gets a value indicating whether a DevTools session is active.
186198
/// </summary>
187-
public bool HasActiveDevToolsSession
188-
{
189-
get { return this.devToolsSession != null; }
190-
}
199+
[MemberNotNullWhen(true, nameof(devToolsSession))]
200+
public bool HasActiveDevToolsSession => this.devToolsSession != null;
191201

192202
/// <summary>
193203
/// Gets or sets the network condition emulation for Chromium.
194204
/// </summary>
205+
/// <exception cref="ArgumentNullException">If the value is set to <see langword="null"/>.</exception>
195206
public ChromiumNetworkConditions NetworkConditions
196207
{
197208
get
198209
{
199210
Response response = this.Execute(GetNetworkConditionsCommand, null);
200-
return ChromiumNetworkConditions.FromDictionary(response.Value as Dictionary<string, object>);
211+
if (response.Value is not Dictionary<string, object?> responseDictionary)
212+
{
213+
throw new WebDriverException($"GetNetworkConditions command returned successfully, but data was not an object: {response.Value}");
214+
}
215+
216+
return ChromiumNetworkConditions.FromDictionary(responseDictionary);
201217
}
202218

203219
set
@@ -209,6 +225,7 @@ public ChromiumNetworkConditions NetworkConditions
209225

210226
Dictionary<string, object> parameters = new Dictionary<string, object>();
211227
parameters["network_conditions"] = value;
228+
212229
this.Execute(SetNetworkConditionsCommand, parameters);
213230
}
214231
}
@@ -217,6 +234,7 @@ public ChromiumNetworkConditions NetworkConditions
217234
/// Launches a Chromium based application.
218235
/// </summary>
219236
/// <param name="id">ID of the chromium app to launch.</param>
237+
/// <exception cref="ArgumentNullException">If <paramref name="id"/> is <see langword="null"/>.</exception>
220238
public void LaunchApp(string id)
221239
{
222240
if (id == null)
@@ -226,6 +244,7 @@ public void LaunchApp(string id)
226244

227245
Dictionary<string, object> parameters = new Dictionary<string, object>();
228246
parameters["id"] = id;
247+
229248
this.Execute(LaunchAppCommand, parameters);
230249
}
231250

@@ -234,6 +253,7 @@ public void LaunchApp(string id)
234253
/// </summary>
235254
/// <param name="permissionName">Name of item to set the permission on.</param>
236255
/// <param name="permissionValue">Value to set the permission to.</param>
256+
/// <exception cref="ArgumentNullException">If <paramref name="permissionName"/> or <paramref name="permissionValue"/> are <see langword="null"/>.</exception>
237257
public void SetPermission(string permissionName, string permissionValue)
238258
{
239259
if (permissionName == null)
@@ -260,7 +280,8 @@ public void SetPermission(string permissionName, string permissionValue)
260280
/// <param name="commandName">Name of the command to execute.</param>
261281
/// <param name="commandParameters">Parameters of the command to execute.</param>
262282
/// <returns>An object representing the result of the command, if applicable.</returns>
263-
public object ExecuteCdpCommand(string commandName, Dictionary<string, object> commandParameters)
283+
/// <exception cref="ArgumentNullException">If <paramref name="commandName"/> is <see langword="null"/>.</exception>
284+
public object? ExecuteCdpCommand(string commandName, Dictionary<string, object> commandParameters)
264285
{
265286
if (commandName == null)
266287
{
@@ -296,21 +317,20 @@ public DevToolsSession GetDevToolsSession(DevToolsOptions options)
296317
throw new WebDriverException("Cannot find " + this.optionsCapabilityName + " capability for driver");
297318
}
298319

299-
Dictionary<string, object> optionsCapability = this.Capabilities.GetCapability(this.optionsCapabilityName) as Dictionary<string, object>;
300-
if (optionsCapability == null)
320+
object? optionsCapabilityObject = this.Capabilities.GetCapability(this.optionsCapabilityName);
321+
if (optionsCapabilityObject is not Dictionary<string, object?> optionsCapability)
301322
{
302-
throw new WebDriverException("Found " + this.optionsCapabilityName + " capability, but is not an object");
323+
throw new WebDriverException($"Found {this.optionsCapabilityName} capability, but is not an object: {optionsCapabilityObject}");
303324
}
304325

305-
if (!optionsCapability.ContainsKey("debuggerAddress"))
326+
if (!optionsCapability.TryGetValue("debuggerAddress", out object? debuggerAddress))
306327
{
307328
throw new WebDriverException("Did not find debuggerAddress capability in " + this.optionsCapabilityName);
308329
}
309330

310-
string debuggerAddress = optionsCapability["debuggerAddress"].ToString();
311331
try
312332
{
313-
DevToolsSession session = new DevToolsSession(debuggerAddress, options);
333+
DevToolsSession session = new DevToolsSession(debuggerAddress?.ToString(), options);
314334
Task.Run(async () => await session.StartSession()).GetAwaiter().GetResult();
315335
this.devToolsSession = session;
316336
}
@@ -341,7 +361,7 @@ public void CloseDevToolsSession()
341361
{
342362
if (this.devToolsSession != null)
343363
{
344-
Task.Run(async () => await this.devToolsSession.StopSession(true)).GetAwaiter().GetResult();
364+
Task.Run(async () => await this.devToolsSession.StopSession(manualDetach: true)).GetAwaiter().GetResult();
345365
}
346366
}
347367

@@ -361,18 +381,16 @@ public List<Dictionary<string, string>> GetCastSinks()
361381
{
362382
List<Dictionary<string, string>> returnValue = new List<Dictionary<string, string>>();
363383
Response response = this.Execute(GetCastSinksCommand, null);
364-
object[] responseValue = response.Value as object[];
365-
if (responseValue != null)
384+
if (response.Value is object?[] responseValue)
366385
{
367-
foreach (object entry in responseValue)
386+
foreach (object? entry in responseValue)
368387
{
369-
Dictionary<string, object> entryValue = entry as Dictionary<string, object>;
370-
if (entryValue != null)
388+
if (entry is Dictionary<string, object?> entryValue)
371389
{
372390
Dictionary<string, string> sink = new Dictionary<string, string>();
373-
foreach (KeyValuePair<string, object> pair in entryValue)
391+
foreach (KeyValuePair<string, object?> pair in entryValue)
374392
{
375-
sink[pair.Key] = pair.Value.ToString();
393+
sink[pair.Key] = pair.Value!.ToString()!;
376394
}
377395

378396
returnValue.Add(sink);
@@ -434,10 +452,10 @@ public void StartDesktopMirroring(string deviceName)
434452
/// Returns the error message if there is any issue in a Cast session.
435453
/// </summary>
436454
/// <returns>An error message.</returns>
437-
public String GetCastIssueMessage()
455+
public string? GetCastIssueMessage()
438456
{
439457
Response response = this.Execute(GetCastIssueMessageCommand, null);
440-
return (string)response.Value;
458+
return (string?)response.Value;
441459
}
442460

443461
/// <summary>

dotnet/src/webdriver/Chromium/ChromiumNetworkConditions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ public long UploadThroughput
9191
/// </summary>
9292
/// <param name="dictionary">The dictionary to use to create the object.</param>
9393
/// <returns>The ChromiumNetworkConditions object created from the dictionary.</returns>
94-
public static ChromiumNetworkConditions FromDictionary(Dictionary<string, object> dictionary)
94+
public static ChromiumNetworkConditions FromDictionary(Dictionary<string, object?> dictionary)
9595
{
9696
ChromiumNetworkConditions conditions = new ChromiumNetworkConditions();
9797
if (dictionary.TryGetValue("offline", out object? offline))
9898
{
99-
conditions.IsOffline = (bool)offline;
99+
conditions.IsOffline = (bool)offline!;
100100
}
101101

102102
if (dictionary.TryGetValue("latency", out object? latency))
@@ -106,12 +106,12 @@ public static ChromiumNetworkConditions FromDictionary(Dictionary<string, object
106106

107107
if (dictionary.TryGetValue("upload_throughput", out object? uploadThroughput))
108108
{
109-
conditions.UploadThroughput = (long)uploadThroughput;
109+
conditions.UploadThroughput = (long)uploadThroughput!;
110110
}
111111

112112
if (dictionary.TryGetValue("download_throughput", out object? downloadThroughput))
113113
{
114-
conditions.DownloadThroughput = (long)downloadThroughput;
114+
conditions.DownloadThroughput = (long)downloadThroughput!;
115115
}
116116

117117
return conditions;

0 commit comments

Comments
 (0)