Skip to content

Conversation

navin772
Copy link
Member

@navin772 navin772 commented Sep 19, 2025

User description

🔗 Related Issues

💥 What does this PR do?

Adds getData, addDataCollector and removeDataCollector commands as in BiDi spec:

  1. w3c.github.io/webdriver-bidi#command-network-addDataCollector
  2. w3c.github.io/webdriver-bidi#command-network-getData
  3. w3c.github.io/webdriver-bidi#command-network-removeDataCollector

🔧 Implementation Notes

💡 Additional Considerations

🔄 Types of changes

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

PR Type

Enhancement


Description

  • Add BiDi network data collection commands per W3C spec

  • Implement addDataCollector, removeDataCollector, and getData methods

  • Create parameter classes for data collection configuration

  • Add comprehensive test coverage for all new functionality


Diagram Walkthrough

flowchart LR
  A["Network Module"] --> B["addDataCollector"]
  A --> C["removeDataCollector"]
  A --> D["getData"]
  B --> E["AddDataCollectorParameters"]
  D --> F["GetDataParameters"]
  D --> G["BytesValue"]
  E --> H["DataType enum"]
  F --> H
Loading

File Walkthrough

Relevant files
Enhancement
Network.java
Add network data collection methods                                           

java/src/org/openqa/selenium/bidi/module/Network.java

  • Add three new methods: addDataCollector, removeDataCollector, getData
  • Import new parameter classes and JSON handling utilities
  • Implement result mapping for getData command response
+44/-0   
AddDataCollectorParameters.java
Create AddDataCollectorParameters class                                   

java/src/org/openqa/selenium/bidi/network/AddDataCollectorParameters.java

  • New parameter class for addDataCollector command
  • Support for data types, max size, collector type, contexts
  • Builder pattern with fluent API and validation
+78/-0   
DataType.java
Add DataType enum                                                                               

java/src/org/openqa/selenium/bidi/network/DataType.java

  • New enum defining supported data types
  • Currently supports RESPONSE data type
+33/-0   
GetDataParameters.java
Create GetDataParameters class                                                     

java/src/org/openqa/selenium/bidi/network/GetDataParameters.java

  • New parameter class for getData command
  • Support for data type, request ID, collector, disown flag
  • Builder pattern with validation
+59/-0   
Tests
NetworkCommandsTest.java
Add data collection command tests                                               

java/test/org/openqa/selenium/bidi/network/NetworkCommandsTest.java

  • Add comprehensive tests for all new data collection methods
  • Test various parameter combinations and edge cases
  • Verify error handling for invalid scenarios
+157/-0 

@selenium-ci selenium-ci added C-java Java Bindings B-devtools Includes everything BiDi or Chrome DevTools related labels Sep 19, 2025
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

5678 - Partially compliant

Compliant requirements:

Non-compliant requirements:

  • Investigate and resolve ChromeDriver "ConnectFailure" on repeated instantiation.
  • Reproduce and address environment-specific issue.
  • Provide fix or guidance for the error scenario.

Requires further human verification:

  • Validation on the specified OS/Chrome/ChromeDriver/Selenium versions in a real environment.
  • Confirmation that the error no longer appears across multiple driver instantiations.

1234 - Partially compliant

Compliant requirements:

Non-compliant requirements:

  • Ensure click() triggers href JavaScript in Firefox as in 2.47.1.

Requires further human verification:

  • Manual/browser testing across Firefox versions to confirm behavior.
⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Result Mapping

The getData result mapping uses a manual Map read and a second JsonInput to parse BytesValue. Verify schema stability and that "bytes" is always present; consider direct parsing or null-safety to avoid NPE if backend omits "bytes".

private final Function<JsonInput, BytesValue> getDataResultMapper =
    jsonInput -> {
      Map<String, Object> result = jsonInput.read(Map.class);
      @SuppressWarnings("unchecked")
      Map<String, Object> bytesMap = (Map<String, Object>) result.get("bytes");

      try (StringReader reader = new StringReader(JSON.toJson(bytesMap));
          JsonInput bytesInput = JSON.newInput(reader)) {
        return BytesValue.fromJson(bytesInput);
      }
    };
Validation

Only maxEncodedDataSize is validated; allow-empty checks for dataTypes and collectorType values are minimal. Consider enforcing non-empty dataTypes and validating collectorType against supported values to prevent server errors.

public AddDataCollectorParameters(List<DataType> dataTypes, long maxEncodedDataSize) {
  Require.nonNull("Data types", dataTypes);
  if (maxEncodedDataSize <= 0) {
    throw new IllegalArgumentException("Max encoded data size must be positive");
  }

  dataTypes.forEach(dataType -> this.dataTypes.add(dataType.toString()));
  this.maxEncodedDataSize = maxEncodedDataSize;
}

public AddDataCollectorParameters collectorType(String collectorType) {
  this.collectorType = Require.nonNull("Collector type", collectorType);
  return this;
}
Defaults/Flags

The "disown" flag is always included (defaults false). Confirm server tolerates explicit false; if optional, consider omitting when false. Also ensure request and collector IDs format are validated upstream.

public Map<String, Object> toMap() {
  Map<String, Object> map = new HashMap<>();
  map.put("dataType", dataType.toString());
  map.put("request", request);

  if (collector != null) {
    map.put("collector", collector);
  }

  map.put("disown", disown);

  return Map.copyOf(map);
}

Copy link
Contributor

qodo-merge-pro bot commented Sep 19, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Simplify the JSON parsing logic

The JSON parsing for the getData command in Network.java is inefficient due to a
redundant serialize-deserialize cycle. Refactor this to process the nested JSON
map directly.

Examples:

java/src/org/openqa/selenium/bidi/module/Network.java [72-82]
  private final Function<JsonInput, BytesValue> getDataResultMapper =
      jsonInput -> {
        Map<String, Object> result = jsonInput.read(Map.class);
        @SuppressWarnings("unchecked")
        Map<String, Object> bytesMap = (Map<String, Object>) result.get("bytes");

        try (StringReader reader = new StringReader(JSON.toJson(bytesMap));
            JsonInput bytesInput = JSON.newInput(reader)) {
          return BytesValue.fromJson(bytesInput);
        }

 ... (clipped 1 lines)

Solution Walkthrough:

Before:

private final Function<JsonInput, BytesValue> getDataResultMapper =
    jsonInput -> {
      Map<String, Object> result = jsonInput.read(Map.class);
      Map<String, Object> bytesMap = (Map<String, Object>) result.get("bytes");

      try (StringReader reader = new StringReader(JSON.toJson(bytesMap));
          JsonInput bytesInput = JSON.newInput(reader)) {
        return BytesValue.fromJson(bytesInput);
      }
    };

After:

private final Function<JsonInput, BytesValue> getDataResultMapper =
    jsonInput -> {
      jsonInput.beginObject();
      while (jsonInput.hasNext()) {
        if ("bytes".equals(jsonInput.nextName())) {
          return BytesValue.fromJson(jsonInput);
        } else {
          jsonInput.skipValue();
        }
      }
      jsonInput.endObject();
      // Or throw an exception if "bytes" is not found
      return null;
    };
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies an inefficient JSON processing pattern and proposes a valid simplification, which improves performance and code clarity.

Medium
Possible issue
Validate against an empty list

In the AddDataCollectorParameters constructor, add a validation check to ensure
the dataTypes list is not empty, as required by the BiDi specification.

java/src/org/openqa/selenium/bidi/network/AddDataCollectorParameters.java [34-42]

 public AddDataCollectorParameters(List<DataType> dataTypes, long maxEncodedDataSize) {
   Require.nonNull("Data types", dataTypes);
+  Require.nonNull("Max encoded data size", maxEncodedDataSize);
+  if (dataTypes.isEmpty()) {
+    throw new IllegalArgumentException("Data types list must not be empty");
+  }
   if (maxEncodedDataSize <= 0) {
     throw new IllegalArgumentException("Max encoded data size must be positive");
   }
 
   dataTypes.forEach(dataType -> this.dataTypes.add(dataType.toString()));
   this.maxEncodedDataSize = maxEncodedDataSize;
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a missing validation for an empty dataTypes list, which improves API robustness by enforcing a contract from the BiDi specification.

Medium
General
Improve performance by avoiding re-serialization
Suggestion Impact:The commit replaced Map-based parsing and re-serialization with streaming parsing using JsonInput.beginObject/hasNext/nextName/skipValue and directly invoked BytesValue.fromJson, eliminating JSON re-serialization and removing Json/StringReader usage.

code diff:

@@ -71,14 +67,18 @@
 
   private final Function<JsonInput, BytesValue> getDataResultMapper =
       jsonInput -> {
-        Map<String, Object> result = jsonInput.read(Map.class);
-        @SuppressWarnings("unchecked")
-        Map<String, Object> bytesMap = (Map<String, Object>) result.get("bytes");
-
-        try (StringReader reader = new StringReader(JSON.toJson(bytesMap));
-            JsonInput bytesInput = JSON.newInput(reader)) {
-          return BytesValue.fromJson(bytesInput);
+        jsonInput.beginObject();
+        BytesValue bytes = null;
+        while (jsonInput.hasNext()) {
+          String name = jsonInput.nextName();
+          if ("bytes".equals(name)) {
+            bytes = BytesValue.fromJson(jsonInput);
+          } else {
+            jsonInput.skipValue();
+          }
         }
+        jsonInput.endObject();
+        return bytes;
       };

Refactor getDataResultMapper to use the JsonInput streaming API, avoiding
inefficient intermediate object creation and JSON re-serialization when parsing
the response.

java/src/org/openqa/selenium/bidi/module/Network.java [72-82]

 private final Function<JsonInput, BytesValue> getDataResultMapper =
     jsonInput -> {
-      Map<String, Object> result = jsonInput.read(Map.class);
-      @SuppressWarnings("unchecked")
-      Map<String, Object> bytesMap = (Map<String, Object>) result.get("bytes");
-
-      try (StringReader reader = new StringReader(JSON.toJson(bytesMap));
-          JsonInput bytesInput = JSON.newInput(reader)) {
-        return BytesValue.fromJson(bytesInput);
+      BytesValue bytesValue = null;
+      jsonInput.beginObject();
+      while (jsonInput.hasNext()) {
+        if ("bytes".equals(jsonInput.nextName())) {
+          bytesValue = BytesValue.fromJson(jsonInput);
+        } else {
+          jsonInput.skipValue();
+        }
       }
+      jsonInput.endObject();
+      return bytesValue;
     };

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies an inefficient JSON parsing pattern and proposes a more performant, streaming-based approach which improves code quality and efficiency.

Low
  • Update

@navin772 navin772 requested a review from pujagani September 21, 2025 14:10
@navin772 navin772 requested a review from diemol September 24, 2025 10:25
}

@Test
@NeedsFreshDriver
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't the test reuse the driver? Is this a BiDi limitation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added it since other tests were using it too, I will check if its strictly required or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diemol 3 tests fail when @NeedsFreshDriver is removed from all the tests, 2 auth ones and 1 data collector that was added.
Should I remove it for other tests? It does reduce the test execution time by a lot (45sec -> 14sec)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to be able to clear handlers to reuse drivers

@titusfortner
Copy link
Member

So, data collection doesn't make sense outside the context of an interception. You have to pass a request id to get the data, which is only accessible from a handler.

I think this should all be private API. When you register a handler you specify if you need the body, and that method registers the collector and manages it in the background.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-devtools Includes everything BiDi or Chrome DevTools related C-java Java Bindings Review effort 2/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants