Skip to content

Commit a44f1d7

Browse files
[dotnet] Annotate nullability on DevTools and event args (#15252)
[dotnet] Annotate nullability `DevTools` and event args
1 parent 8f424e2 commit a44f1d7

32 files changed

+306
-237
lines changed

dotnet/src/webdriver/Chromium/ChromiumOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,9 +641,9 @@ private static Dictionary<string, object> GeneratePerformanceLoggingPreferencesD
641641
return perfLoggingPrefsDictionary;
642642
}
643643

644-
private static Dictionary<string, object> GenerateMobileEmulationSettingsDictionary(ChromiumMobileEmulationDeviceSettings? settings, string? deviceName)
644+
private static Dictionary<string, object?> GenerateMobileEmulationSettingsDictionary(ChromiumMobileEmulationDeviceSettings? settings, string? deviceName)
645645
{
646-
Dictionary<string, object> mobileEmulationSettings = new Dictionary<string, object>();
646+
Dictionary<string, object?> mobileEmulationSettings = new Dictionary<string, object?>();
647647

648648
if (!string.IsNullOrEmpty(deviceName))
649649
{

dotnet/src/webdriver/DevTools/DevToolsSession.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public DevToolsSession(string endpointAddress, DevToolsOptions options)
125125
/// <typeparam name="T">
126126
/// A <see cref="DevToolsSessionDomains"/> object containing the version-specific DevTools Protocol domain implementations.</typeparam>
127127
/// <returns>The version-specific DevTools Protocol domain implementation.</returns>
128+
/// <exception cref="InvalidOperationException">If the provided <typeparamref name="T"/> is not the right protocol version which is running.</exception>
128129
public T GetVersionSpecificDomains<T>() where T : DevToolsSessionDomains
129130
{
130131
if (this.Domains.VersionSpecificDomains is not T versionSpecificDomains)
@@ -146,6 +147,7 @@ public T GetVersionSpecificDomains<T>() where T : DevToolsSessionDomains
146147
/// <param name="millisecondsTimeout">The execution timeout of the command in milliseconds.</param>
147148
/// <param name="throwExceptionIfResponseNotReceived"><see langword="true"/> to throw an exception if a response is not received; otherwise, <see langword="false"/>.</param>
148149
/// <returns>The command response object implementing the <see cref="ICommandResponse{T}"/> interface.</returns>
150+
/// <exception cref="ArgumentNullException">If <paramref name="command"/> is <see langword="null"/>.</exception>
149151
public async Task<ICommandResponse<TCommand>?> SendCommand<TCommand>(TCommand command, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true)
150152
where TCommand : ICommand
151153
{
@@ -214,6 +216,7 @@ public T GetVersionSpecificDomains<T>() where T : DevToolsSessionDomains
214216
/// <param name="millisecondsTimeout">The execution timeout of the command in milliseconds.</param>
215217
/// <param name="throwExceptionIfResponseNotReceived"><see langword="true"/> to throw an exception if a response is not received; otherwise, <see langword="false"/>.</param>
216218
/// <returns>The command response object implementing the <see cref="ICommandResponse{T}"/> interface.</returns>
219+
/// <exception cref="ArgumentNullException">If <paramref name="command"/> is <see langword="null"/>.</exception>
217220
public async Task<TCommandResponse?> SendCommand<TCommand, TCommandResponse>(TCommand command, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true)
218221
where TCommand : ICommand
219222
where TCommandResponse : ICommandResponse<TCommand>
@@ -243,6 +246,7 @@ public T GetVersionSpecificDomains<T>() where T : DevToolsSessionDomains
243246
/// <param name="millisecondsTimeout">The execution timeout of the command in milliseconds.</param>
244247
/// <param name="throwExceptionIfResponseNotReceived"><see langword="true"/> to throw an exception if a response is not received; otherwise, <see langword="false"/>.</param>
245248
/// <returns>The command response object implementing the <see cref="ICommandResponse{T}"/> interface.</returns>
249+
/// <exception cref="ArgumentNullException">If <paramref name="commandName"/> is <see langword="null"/>.</exception>
246250
public async Task<JsonElement?> SendCommand(string commandName, JsonNode commandParameters, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true)
247251
{
248252
if (this.attachedTargetId == null)
@@ -264,6 +268,7 @@ public T GetVersionSpecificDomains<T>() where T : DevToolsSessionDomains
264268
/// <param name="millisecondsTimeout">The execution timeout of the command in milliseconds.</param>
265269
/// <param name="throwExceptionIfResponseNotReceived"><see langword="true"/> to throw an exception if a response is not received; otherwise, <see langword="false"/>.</param>
266270
/// <returns>The command response object implementing the <see cref="ICommandResponse{T}"/> interface.</returns>
271+
/// <exception cref="ArgumentNullException">If <paramref name="commandName"/> is <see langword="null"/>.</exception>
267272
public async Task<JsonElement?> SendCommand(string commandName, string? sessionId, JsonNode commandParameters, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true)
268273
{
269274
millisecondsTimeout ??= Convert.ToInt32(CommandTimeout.TotalMilliseconds);

dotnet/src/webdriver/DevTools/DevToolsVersionInfo.cs

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
using System.Text.Json.Serialization;
2222
using System.Text.RegularExpressions;
2323

24+
#nullable enable
25+
2426
namespace OpenQA.Selenium.DevTools
2527
{
2628
/// <summary>
@@ -33,51 +35,69 @@ public class DevToolsVersionInfo
3335
/// </summary>
3436
[JsonPropertyName("Browser")]
3537
[JsonInclude]
36-
public string Browser { get; internal set; }
38+
public string? Browser { get; internal set; }
3739

3840
/// <summary>
3941
/// Gets the browser version without the preceding browser name.
4042
/// </summary>
4143
[JsonIgnore]
42-
public string BrowserVersion => Regex.Match(Browser, ".*/(.*)").Groups[1].Value;
44+
public string BrowserVersion
45+
{
46+
get
47+
{
48+
if (Browser is null)
49+
{
50+
throw new InvalidOperationException("Browser value is null");
51+
}
52+
53+
return Regex.Match(Browser, ".*/(.*)").Groups[1].Value;
54+
}
55+
}
4356

4457
/// <summary>
4558
/// Gets the browser major version number without the preceding browser name.
4659
/// </summary>
4760
[JsonIgnore]
48-
public string BrowserMajorVersion => Regex.Match(Browser, ".*/(\\d+)\\..*").Groups[1].Value;
61+
public string BrowserMajorVersion
62+
{
63+
get
64+
{
65+
if (Browser is null)
66+
{
67+
throw new InvalidOperationException("Browser value is null");
68+
}
69+
70+
return Regex.Match(Browser, ".*/(\\d+)\\..*").Groups[1].Value;
71+
}
72+
}
4973

5074
/// <summary>
5175
/// Gets the version of the Developer Tools Protocol.
5276
/// </summary>
5377
[JsonPropertyName("Protocol-Version")]
5478
[JsonInclude]
55-
public string ProtocolVersion { get; internal set; }
79+
public string? ProtocolVersion { get; internal set; }
5680

5781
/// <summary>
5882
/// Gets the user agent string.
5983
/// </summary>
6084
[JsonPropertyName("User-Agent")]
6185
[JsonInclude]
62-
public string UserAgent { get; internal set; }
86+
public string? UserAgent { get; internal set; }
6387

6488
/// <summary>
6589
/// Gets the version string for the V8 script engine in use by this version of the browser.
6690
/// </summary>
6791
[JsonPropertyName("V8-Version")]
6892
[JsonInclude]
69-
public string V8Version
70-
{
71-
get;
72-
internal set;
73-
}
93+
public string? V8Version { get; internal set; }
7494

7595
/// <summary>
7696
/// Gets the URL for the WebSocket connection used for communicating via the DevTools Protocol.
7797
/// </summary>
7898
[JsonPropertyName("webSocketDebuggerUrl")]
7999
[JsonInclude]
80-
public string WebSocketDebuggerUrl { get; internal set; }
100+
public string? WebSocketDebuggerUrl { get; internal set; }
81101

82102
/// <summary>
83103
/// Gets the version number of the V8 script engine, stripping values other than the version number.
@@ -88,8 +108,8 @@ public string V8VersionNumber
88108
get
89109
{
90110
//Get the v8 version
91-
var v8VersionMatch = Regex.Match(V8Version, @"^(\d+)\.(\d+)\.(\d+)(\.\d+.*)?");
92-
if (v8VersionMatch.Success == false || v8VersionMatch.Groups.Count < 4)
111+
Match? v8VersionMatch = V8Version is null ? null : Regex.Match(V8Version, @"^(\d+)\.(\d+)\.(\d+)(\.\d+.*)?");
112+
if (v8VersionMatch is null || v8VersionMatch.Success == false || v8VersionMatch.Groups.Count < 4)
93113
{
94114
throw new InvalidOperationException($"Unable to determine v8 version number from v8 version string ({V8Version})");
95115
}
@@ -103,7 +123,7 @@ public string V8VersionNumber
103123
/// </summary>
104124
[JsonPropertyName("WebKit-Version")]
105125
[JsonInclude]
106-
public string WebKitVersion { get; internal set; }
126+
public string? WebKitVersion { get; internal set; }
107127

108128
/// <summary>
109129
/// Gets the hash of the version of WebKit, stripping values other than the hash.
@@ -114,8 +134,8 @@ public string WebKitVersionHash
114134
get
115135
{
116136
//Get the webkit version hash.
117-
var webkitVersionMatch = Regex.Match(WebKitVersion, @"\s\(@(\b[0-9a-f]{5,40}\b)");
118-
if (webkitVersionMatch.Success == false || webkitVersionMatch.Groups.Count != 2)
137+
var webkitVersionMatch = WebKitVersion is null ? null : Regex.Match(WebKitVersion, @"\s\(@(\b[0-9a-f]{5,40}\b)");
138+
if (webkitVersionMatch is null || webkitVersionMatch.Success == false || webkitVersionMatch.Groups.Count != 2)
119139
{
120140
throw new InvalidOperationException($"Unable to determine webkit version hash from webkit version string ({WebKitVersion})");
121141
}

dotnet/src/webdriver/DevTools/ICommand.cs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
// under the License.
1818
// </copyright>
1919

20+
#nullable enable
21+
2022
namespace OpenQA.Selenium.DevTools
2123
{
2224
/// <summary>
@@ -27,24 +29,17 @@ public interface ICommand
2729
/// <summary>
2830
/// Gets the name of the command.
2931
/// </summary>
30-
string CommandName
31-
{
32-
get;
33-
}
32+
string CommandName { get; }
3433
}
3534

3635
/// <summary>
3736
/// Represents a response to a command submitted by the DevTools Remote Interface
3837
///</summary>
39-
public interface ICommandResponse
40-
{
41-
}
38+
public interface ICommandResponse;
4239

4340
/// <summary>
4441
/// Represents a response to a command submitted by the DevTools Remote Interface
4542
///</summary>
4643
public interface ICommandResponse<T> : ICommandResponse
47-
where T : ICommand
48-
{
49-
}
44+
where T : ICommand;
5045
}

dotnet/src/webdriver/DevTools/IDevTools.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
using System;
2121

22+
#nullable enable
23+
2224
namespace OpenQA.Selenium.DevTools
2325
{
2426
/// <summary>
@@ -42,6 +44,7 @@ public interface IDevTools
4244
/// </summary>
4345
/// <param name="options">The options for the DevToolsSession to use.</param>
4446
/// <returns>The active session to use to communicate with the Developer Tools debugging protocol.</returns>
47+
/// <exception cref="ArgumentNullException">If <paramref name="options"/> is <see langword="null"/>.</exception>
4548
DevToolsSession GetDevToolsSession(DevToolsOptions options);
4649

4750
/// <summary>

dotnet/src/webdriver/DevTools/IDevToolsSession.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace OpenQA.Selenium.DevTools
2929
{
3030
/// <summary>
3131
/// Represents a WebSocket connection to a running DevTools instance that can be used to send
32-
/// commands and recieve events.
32+
/// commands and receive events.
3333
///</summary>
3434
public interface IDevToolsSession : IDisposable
3535
{
@@ -50,6 +50,7 @@ public interface IDevToolsSession : IDisposable
5050
/// A <see cref="DevToolsSessionDomains"/> type specific to the version of Developer Tools with which to communicate.
5151
/// </typeparam>
5252
/// <returns>The version-specific domains for this Developer Tools connection.</returns>
53+
/// <exception cref="InvalidOperationException">If the provided <typeparamref name="T"/> is not the right protocol version which is running.</exception>
5354
T GetVersionSpecificDomains<T>() where T : DevToolsSessionDomains;
5455

5556
/// <summary>
@@ -61,6 +62,7 @@ public interface IDevToolsSession : IDisposable
6162
/// <param name="millisecondsTimeout">The execution timeout of the command in milliseconds.</param>
6263
/// <param name="throwExceptionIfResponseNotReceived"><see langword="true"/> to throw an exception if a response is not received; otherwise, <see langword="false"/>.</param>
6364
/// <returns>The command response object implementing the <see cref="ICommandResponse{T}"/> interface.</returns>
65+
/// <exception cref="ArgumentNullException">If <paramref name="command"/> is <see langword="null"/>.</exception>
6466
Task<ICommandResponse<TCommand>?> SendCommand<TCommand>(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived)
6567
where TCommand : ICommand;
6668

@@ -74,6 +76,7 @@ public interface IDevToolsSession : IDisposable
7476
/// <param name="millisecondsTimeout">The execution timeout of the command in milliseconds.</param>
7577
/// <param name="throwExceptionIfResponseNotReceived"><see langword="true"/> to throw an exception if a response is not received; otherwise, <see langword="false"/>.</param>
7678
/// <returns>The command response object implementing the <see cref="ICommandResponse{T}"/> interface.</returns>
79+
/// <exception cref="ArgumentNullException">If <paramref name="command"/> is <see langword="null"/>.</exception>
7780
Task<TCommandResponse?> SendCommand<TCommand, TCommandResponse>(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived)
7881
where TCommand : ICommand
7982
where TCommandResponse : ICommandResponse<TCommand>;
@@ -87,6 +90,7 @@ public interface IDevToolsSession : IDisposable
8790
/// <param name="millisecondsTimeout">The execution timeout of the command in milliseconds.</param>
8891
/// <param name="throwExceptionIfResponseNotReceived"><see langword="true"/> to throw an exception if a response is not received; otherwise, <see langword="false"/>.</param>
8992
/// <returns>The command response object implementing the <see cref="ICommandResponse{T}"/> interface.</returns>
93+
/// <exception cref="ArgumentNullException">If <paramref name="commandName"/> is <see langword="null"/>.</exception>
9094
Task<JsonElement?> SendCommand(string commandName, JsonNode @params, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived);
9195
}
9296
}

dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public JsonEnumMemberConverter()
4444
#endif
4545
foreach (var value in values)
4646
{
47-
var enumMember = type.GetField(value.ToString());
47+
var enumMember = type.GetField(value.ToString())!;
4848
var attr = enumMember.GetCustomAttributes(typeof(EnumMemberAttribute), false)
4949
.Cast<EnumMemberAttribute>()
5050
.FirstOrDefault();

dotnet/src/webdriver/DevTools/WebSocketConnection.cs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
using System.Threading;
2424
using System.Threading.Tasks;
2525

26+
#nullable enable
27+
2628
namespace OpenQA.Selenium.DevTools
2729
{
2830
/// <summary>
@@ -34,9 +36,7 @@ public class WebSocketConnection
3436
private readonly CancellationTokenSource clientTokenSource = new CancellationTokenSource();
3537
private readonly TimeSpan startupTimeout;
3638
private readonly TimeSpan shutdownTimeout;
37-
private readonly int bufferSize = 4096;
38-
private Task dataReceiveTask;
39-
private bool isActive = false;
39+
private Task? dataReceiveTask;
4040
private ClientWebSocket client = new ClientWebSocket();
4141
private readonly SemaphoreSlim sendMethodSemaphore = new SemaphoreSlim(1, 1);
4242

@@ -71,22 +71,22 @@ public WebSocketConnection(TimeSpan startupTimeout, TimeSpan shutdownTimeout)
7171
/// <summary>
7272
/// Occurs when data is received from this connection.
7373
/// </summary>
74-
public event EventHandler<WebSocketConnectionDataReceivedEventArgs> DataReceived;
74+
public event EventHandler<WebSocketConnectionDataReceivedEventArgs>? DataReceived;
7575

7676
/// <summary>
7777
/// Occurs when a log message is emitted from this connection.
7878
/// </summary>
79-
public event EventHandler<DevToolsSessionLogMessageEventArgs> LogMessage;
79+
public event EventHandler<DevToolsSessionLogMessageEventArgs>? LogMessage;
8080

8181
/// <summary>
8282
/// Gets a value indicating whether this connection is active.
8383
/// </summary>
84-
public bool IsActive => this.isActive;
84+
public bool IsActive { get; private set; } = false;
8585

8686
/// <summary>
8787
/// Gets the buffer size for communication used by this connection.
8888
/// </summary>
89-
public int BufferSize => this.bufferSize;
89+
public int BufferSize { get; } = 4096;
9090

9191
/// <summary>
9292
/// Asynchronously starts communication with the remote end of this connection.
@@ -96,6 +96,11 @@ public WebSocketConnection(TimeSpan startupTimeout, TimeSpan shutdownTimeout)
9696
/// <exception cref="TimeoutException">Thrown when the connection is not established within the startup timeout.</exception>
9797
public virtual async Task Start(string url)
9898
{
99+
if (url is null)
100+
{
101+
throw new ArgumentNullException(nameof(url));
102+
}
103+
99104
this.Log($"Opening connection to URL {url}", DevToolsSessionLogLevel.Trace);
100105
bool connected = false;
101106
DateTime timeout = DateTime.Now.Add(this.startupTimeout);
@@ -121,7 +126,7 @@ public virtual async Task Start(string url)
121126
}
122127

123128
this.dataReceiveTask = Task.Run(async () => await this.ReceiveData());
124-
this.isActive = true;
129+
this.IsActive = true;
125130
this.Log($"Connection opened", DevToolsSessionLogLevel.Trace);
126131
}
127132

@@ -159,6 +164,11 @@ public virtual async Task Stop()
159164
/// <returns>The task object representing the asynchronous operation.</returns>
160165
public virtual async Task SendData(string data)
161166
{
167+
if (data is null)
168+
{
169+
throw new ArgumentNullException(nameof(data));
170+
}
171+
162172
ArraySegment<byte> messageBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(data));
163173
this.Log($"SEND >>> {data}");
164174

@@ -236,7 +246,7 @@ private async Task ReceiveData()
236246
try
237247
{
238248
StringBuilder messageBuilder = new StringBuilder();
239-
ArraySegment<byte> buffer = WebSocket.CreateClientBuffer(this.bufferSize, this.bufferSize);
249+
ArraySegment<byte> buffer = WebSocket.CreateClientBuffer(this.BufferSize, this.BufferSize);
240250
while (this.client.State != WebSocketState.Closed && !cancellationToken.IsCancellationRequested)
241251
{
242252
WebSocketReceiveResult receiveResult = await this.client.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false);
@@ -255,7 +265,7 @@ private async Task ReceiveData()
255265
// Display text or binary data
256266
if (this.client.State == WebSocketState.Open && receiveResult.MessageType != WebSocketMessageType.Close)
257267
{
258-
messageBuilder.Append(Encoding.UTF8.GetString(buffer.Array, 0, receiveResult.Count));
268+
messageBuilder.Append(Encoding.UTF8.GetString(buffer.Array!, 0, receiveResult.Count));
259269
if (receiveResult.EndOfMessage)
260270
{
261271
string message = messageBuilder.ToString();
@@ -282,7 +292,7 @@ private async Task ReceiveData()
282292
}
283293
finally
284294
{
285-
this.isActive = false;
295+
this.IsActive = false;
286296
}
287297
}
288298

0 commit comments

Comments
 (0)