Skip to content

Conversation

nvborisenko
Copy link
Member

@nvborisenko nvborisenko commented Aug 16, 2025

User description

https://w3c.github.io/webdriver-bidi/#command-network-addDataCollector
https://w3c.github.io/webdriver-bidi/#command-network-getData
https://w3c.github.io/webdriver-bidi/#command-network-removeDataCollector

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

PR Type

Enhancement


Description

  • Add network data collectors for BiDi protocol

  • Implement response body retrieval functionality

  • Support collector lifecycle management with disposal

  • Add JSON serialization for new network commands


Diagram Walkthrough

flowchart LR
  A["Network Module"] --> B["Add Data Collector"]
  B --> C["Collector Instance"]
  C --> D["Get Response Data"]
  C --> E["Remove Collector"]
  F["JSON Converters"] --> G["Serialization Support"]
  H["Tests"] --> I["Validate Functionality"]
Loading

File Walkthrough

Relevant files
Configuration changes
2 files
Broker.cs
Register CollectorConverter for JSON serialization             
+1/-0     
BiDiJsonSerializerContext.cs
Add JSON serialization attributes for data collector commands
+5/-0     
Enhancement
7 files
CollectorConverter.cs
Create JSON converter for Collector objects                           
+47/-0   
AddDataCollectorCommand.cs
Implement add data collector command and parameters           
+49/-0   
BytesValue.cs
Add explicit string conversion operator                                   
+10/-0   
Collector.cs
Create Collector class with disposal support                         
+58/-0   
GetDataCommand.cs
Implement get data command for retrieving response bodies
+36/-0   
NetworkModule.cs
Add data collector and get data methods                                   
+25/-0   
RemoveDataCollectorCommand.cs
Implement remove data collector command                                   
+29/-0   
Tests
1 files
NetworkTest.cs
Add tests for data collector functionality                             
+35/-0   
Miscellaneous
1 files
StableChannelChromeDriver.cs
Remove hardcoded Chrome version constraint                             
+1/-1     

@selenium-ci selenium-ci added the C-dotnet .NET Bindings label Aug 16, 2025
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Casting Behavior

The explicit cast to string decodes Base64 using UTF-8. Verify this is correct for all expected response bodies (binary data or non-UTF-8 encodings may throw or corrupt data). Consider exposing a byte[] accessor or encoding-parameterized decode to avoid misuse.

public static explicit operator string(BytesValue value) => value switch
{
    StringBytesValue stringBytesValue => stringBytesValue.Value,
    Base64BytesValue base64BytesValue => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(base64BytesValue.Value)),
    _ => throw new InvalidCastException($"Cannot cast '{value.GetType()}' to '{typeof(string)}'.")
};
Dispose Exceptions

DisposeAsync calls RemoveAsync without try/catch and without ConfigureAwait. If removal fails (e.g., session closed), disposal could throw during using/await using. Consider swallowing/logging expected disposal-time errors to keep disposal idempotent and resilient.

public async ValueTask DisposeAsync()
{
    await RemoveAsync();
}
Null Handling

Converter assumes JSON token is a non-null string and uses null-forgiving operator. Add validation to guard against null/invalid token types to avoid NullReferenceException at runtime.

public override Collector? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    var id = reader.GetString();

    return new Collector(_bidi, id!);
}

@nvborisenko nvborisenko marked this pull request as draft August 16, 2025 13:48
Copy link
Contributor

qodo-merge-pro bot commented Aug 16, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Validate JSON token for id

Validate the JSON token type before reading to prevent invalid JSON from causing
unexpected behavior. Throw a JsonException when the token is not a string or
when the id is null/empty.

dotnet/src/webdriver/BiDi/Communication/Json/Converters/CollectorConverter.cs [36-41]

 public override Collector? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 {
+    if (reader.TokenType != JsonTokenType.String)
+    {
+        throw new JsonException("Expected string token for Collector id.");
+    }
+
     var id = reader.GetString();
+    if (string.IsNullOrEmpty(id))
+    {
+        throw new JsonException("Collector id cannot be null or empty.");
+    }
 
-    return new Collector(_bidi, id!);
+    return new Collector(_bidi, id);
 }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion significantly improves the robustness of the JSON converter by adding validation for the token type and for a null or empty id, preventing potential exceptions from malformed JSON.

Medium
Make dispose safe and idempotent

Dispose should be resilient and idempotent. Wrap the removal in a try/catch to
avoid throwing during dispose paths and guard against double-dispose to prevent
multiple removal attempts.

dotnet/src/webdriver/BiDi/Network/Collector.cs [42-45]

+private bool _disposed;
+
 public async ValueTask DisposeAsync()
 {
-    await RemoveAsync();
+    if (_disposed) return;
+    _disposed = true;
+    try
+    {
+        await RemoveAsync().ConfigureAwait(false);
+    }
+    catch
+    {
+        // Suppress exceptions on dispose
+    }
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly implements the standard disposable pattern, making DisposeAsync idempotent and preventing exceptions from being thrown, which is a crucial best practice for resource management.

Medium
General
Add null-safe explicit cast

Handle null inputs defensively to avoid a NullReferenceException during explicit
cast. Also use the Encoding property directly and consider malformed base64 data
to avoid hard crashes.

dotnet/src/webdriver/BiDi/Network/BytesValue.cs [33-38]

-public static explicit operator string(BytesValue value) => value switch
+public static explicit operator string?(BytesValue? value) => value switch
 {
+    null => null,
     StringBytesValue stringBytesValue => stringBytesValue.Value,
     Base64BytesValue base64BytesValue => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(base64BytesValue.Value)),
     _ => throw new InvalidCastException($"Cannot cast '{value.GetType()}' to '{typeof(string)}'.")
 };
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly improves the robustness of the explicit cast operator by handling null input, which aligns with standard C# casting behavior and prevents potential NullReferenceException.

Low
Learned
best practice
Validate constructor parameters

Validate constructor arguments to prevent invalid state and clearer errors.
Check for null bidi and null/empty id and throw ArgumentNullException or
ArgumentException.

dotnet/src/webdriver/BiDi/Network/Collector.cs [29-33]

 internal Collector(BiDi bidi, string id)
 {
-    _bidi = bidi;
-    Id = id;
+    _bidi = bidi ?? throw new ArgumentNullException(nameof(bidi));
+    Id = !string.IsNullOrEmpty(id) ? id : throw new ArgumentException("Collector id cannot be null or empty.", nameof(id));
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Add null checks and validation for parameters before using them to prevent runtime errors.

Low
  • More

@nvborisenko nvborisenko marked this pull request as ready for review September 17, 2025 15:37
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Casting API Design

The new explicit cast to string on BytesValue throws for non-string implementations, but there is no equivalent cast/accessor for Base64BytesValue. Consider adding a safe API (e.g., TryGetString/ToStringUnsafe or a byte[] accessor) to avoid misuse and clarify expectations.

public static explicit operator string(BytesValue value)
{
    if (value is StringBytesValue stringBytesValue)
    {
        return stringBytesValue.Value;
    }

    throw new InvalidCastException($"Cannot cast '{value.GetType()}' to '{typeof(string)}'.");
}
Test Environment Change

DefaultOptions no longer pins a specific Chrome BrowserVersion. This may increase test flakiness across environments. Validate that CI matrices remain stable or reintroduce pinning where necessary.

public StableChannelChromeDriver(ChromeDriverService service, ChromeOptions options)
    : base(service, options)
{
}

public static ChromeOptions DefaultOptions
{
    get { return new ChromeOptions(); }
}
Dispose Error Handling

DisposeAsync awaits RemoveAsync but does not handle exceptions; disposal during shutdown might throw. Consider swallowing/logging specific errors or providing a cancellation/timeout to make disposal resilient.

public async ValueTask DisposeAsync()
{
    await RemoveAsync();
}

Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Add null check for deserialized ID

In the Read method, add a null check for the id returned by reader.GetString()
and throw a JsonException if it is null to prevent potential runtime errors.

dotnet/src/webdriver/BiDi/Communication/Json/Converters/CollectorConverter.cs [36-41]

 public override Collector? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 {
     var id = reader.GetString();
 
-    return new Collector(_bidi, id!);
+    if (id is null)
+    {
+        throw new JsonException("Collector ID cannot be null.");
+    }
+
+    return new Collector(_bidi, id);
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that reader.GetString() can return null, and the use of the null-forgiving operator (!) could lead to a runtime exception. Adding a null check improves the robustness of the JSON deserialization logic.

Medium
General
Improve event handler robustness in test

To improve test robustness, replace SetResult with TrySetResult and wrap the
logic in a try...catch block to safely handle exceptions on the
TaskCompletionSource.

dotnet/test/common/BiDi/Network/NetworkTest.cs [233-239]

 await using var _ = await bidi.Network.OnResponseCompletedAsync(async e =>
 {
     if (e.Response.Url.Contains("simpleTest.html"))
     {
-        responseBodyCompletionSource.SetResult((string)await bidi.Network.GetDataAsync(DataType.Response, e.Request.Request));
+        try
+        {
+            var data = await bidi.Network.GetDataAsync(DataType.Response, e.Request.Request);
+            responseBodyCompletionSource.TrySetResult((string)data);
+        }
+        catch (Exception ex)
+        {
+            responseBodyCompletionSource.TrySetException(ex);
+        }
     }
 });
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies two potential issues in the test code: a race condition that could cause test flakiness and unhandled exceptions in an async event handler. The proposed changes make the test significantly more robust.

Medium
Learned
best practice
Make disposal idempotent and safe

Guard against multiple DisposeAsync calls and avoid throwing if underlying
removal fails; make disposal idempotent and resilient (e.g., track disposed
state and swallow expected errors).

dotnet/src/webdriver/BiDi/Network/Collector.cs [42-45]

+private bool _disposed;
 public async ValueTask DisposeAsync()
 {
-    await RemoveAsync();
+    if (_disposed) return;
+    _disposed = true;
+    try
+    {
+        await RemoveAsync().ConfigureAwait(false);
+    }
+    catch
+    {
+        // best-effort cleanup during dispose
+    }
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Always close or dispose resources; ensure disposals are safe and idempotent to prevent leaks or double-dispose errors.

Low
Add null check to cast

Validate input and provide clearer error paths by handling nulls defensively and
using ArgumentNullException for null input.

dotnet/src/webdriver/BiDi/Network/BytesValue.cs [33-41]

 public static explicit operator string(BytesValue value)
 {
+    if (value is null)
+    {
+        throw new ArgumentNullException(nameof(value));
+    }
     if (value is StringBytesValue stringBytesValue)
     {
         return stringBytesValue.Value;
     }
-
     throw new InvalidCastException($"Cannot cast '{value.GetType()}' to '{typeof(string)}'.");
 }
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why:
Relevant best practice - Add precise validation and defensive checks around parsing and conversions to avoid null/invalid casts.

Low
  • More

@nvborisenko
Copy link
Member Author

Thanks for the contribution, finally long awaited feature is available for all us.

@nvborisenko nvborisenko merged commit fc28c02 into SeleniumHQ:trunk Sep 17, 2025
10 checks passed
@nvborisenko nvborisenko deleted the dotnet-bidi-getdata branch September 17, 2025 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants