Skip to content

[java] Implementing install browser extension from the driver directly - #18036

Open
pujagani wants to merge 1 commit into
SeleniumHQ:trunkfrom
pujagani:web-extension-java
Open

pujagani wants to merge 1 commit into
SeleniumHQ:trunkfrom
pujagani:web-extension-java

Conversation

@pujagani

Copy link
Copy Markdown
Contributor

🔗 Related Issues

Related to #17933

💥 What does this PR do?

Ensures that extension can be installed directly on the driver instance and fall backs on classic for Firefox as required.

🔧 Implementation Notes

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s):
    • What was generated:
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)
  • Breaking change (fix or feature that would cause existing functionality to change)

@selenium-ci selenium-ci added C-java Java Bindings B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related labels Sep 15, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Expose protocol-neutral web extension management on Java drivers

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Adds protocol-neutral extension installation and removal directly on every RemoteWebDriver.
• Routes supported sessions through BiDi, with classic Firefox fallback and Grid directory uploads.
• Adds Firefox options, legacy deprecations, and unit and integration coverage.
Diagram

sequenceDiagram
  actor Client
  participant Driver as RemoteWebDriver
  participant Router as Extension Router
  participant Upload as Grid Upload
  participant BiDi as BiDi Endpoint
  participant Classic as Firefox Classic
  Client->>Driver: Install extension
  Driver->>Router: Delegate source and options
  alt BiDi session
    opt Remote Chromium directory
      Router->>Upload: Upload rooted archive
      Upload-->>Router: Return remote path
    end
    Router->>BiDi: Install extension
    BiDi-->>Router: Return extension id
  else Firefox without BiDi
    Router->>Classic: Install encoded add-on
    Classic-->>Router: Return extension id
  else Unsupported session
    Router-->>Driver: Raise unsupported error
  end
  Router-->>Driver: Create extension handle
  Driver-->>Client: Return WebExtension
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Retain browser-specific augmentation
  • ➕ Avoids adding extension methods directly to RemoteWebDriver.
  • ➕ Keeps classic Firefox behavior isolated in its existing augmentation provider.
  • ➖ Preserves inconsistent APIs across browsers and transports.
  • ➖ Requires callers to understand augmentation and Firefox-specific interfaces.
  • ➖ Does not provide a natural BiDi and Grid abstraction.
2. Support BiDi sessions only
  • ➕ Provides a simpler implementation with one protocol path.
  • ➕ Avoids retaining dependencies on Firefox’s vendor endpoint.
  • ➖ Regresses extension management for Firefox sessions without BiDi.
  • ➖ Forces users to change session configuration despite an available classic capability.
  • ➖ Does not satisfy the stated Firefox fallback requirement.

Recommendation: Keep the PR’s protocol-neutral façade with centralized transport routing. It gives all RemoteWebDriver-derived drivers one API while preserving Firefox compatibility and hiding BiDi, classic endpoint, and Grid upload details. Augmentation-only and BiDi-only designs are simpler locally but create worse compatibility and usability tradeoffs.

Files changed (25) +1210 / -6

Enhancement (10) +684 / -4
InstallExtensionParameters.javaSupport vendor-prefixed BiDi install parameters +23/-1

Support vendor-prefixed BiDi install parameters

• Accepts validated vendor options and serializes them beside 'extensionData'. The merged result is immutable and prevents vendor data from replacing the required extension payload.

java/src/org/openqa/selenium/bidi/webextension/InstallExtensionParameters.java

WebExtension.javaSend complete BiDi extension install parameters +1/-2

Send complete BiDi extension install parameters

• Uses 'InstallExtensionParameters.toMap()' so vendor-prefixed options reach the 'webExtension.install' command.

java/src/org/openqa/selenium/bidi/webextension/WebExtension.java

Zip.javaAdd root-preserving Base64 ZIP creation +24/-0

Add root-preserving Base64 ZIP creation

• Adds an archive helper that retains the input name as the sole top-level entry. This format allows the remote file upload endpoint to resolve an extension directory as one path.

java/src/org/openqa/selenium/io/Zip.java

DriverCommand.javaDefine classic extension command names +5/-0

Define classic extension command names

• Adds shared install and uninstall command constants for Firefox’s registered '/moz/addon' endpoints.

java/src/org/openqa/selenium/remote/DriverCommand.java

RemoteWebDriver.javaExpose web extension management directly +39/-1

Expose web extension management directly

• Implements 'HasWebExtensions' on every 'RemoteWebDriver' and lazily delegates all install and uninstall overloads to 'RemoteWebExtensions'. Derived local and remote drivers therefore expose the API without augmentation.

java/src/org/openqa/selenium/remote/RemoteWebDriver.java

RemoteWebExtensions.javaRoute extension operations across supported transports +301/-0

Route extension operations across supported transports

• Introduces the shared implementation that validates sources and vendor options, selects BiDi or classic Firefox, and returns protocol-neutral extension handles. It also packages Firefox directories, uploads remote Chromium directories, and reports unsupported sessions or malformed responses clearly.

java/src/org/openqa/selenium/remote/RemoteWebExtensions.java

FirefoxWebExtensionOptions.javaAdd immutable Firefox installation options +87/-0

Add immutable Firefox installation options

• Provides fluent options for permanent installation and private-browsing access. Values can be translated to either BiDi vendor parameters or classic Firefox fields.

java/src/org/openqa/selenium/webextension/FirefoxWebExtensionOptions.java

HasWebExtensions.javaDefine the protocol-neutral extension API +95/-0

Define the protocol-neutral extension API

• Adds path- and Base64-based installation overloads, optional vendor settings, and handle-based uninstallation. The interface documents availability and failure behavior across browser sessions.

java/src/org/openqa/selenium/webextension/HasWebExtensions.java

WebExtension.javaAdd an installed extension value handle +69/-0

Add an installed extension value handle

• Wraps the browser-assigned extension identifier with null validation, value equality, hashing, and readable string representation.

java/src/org/openqa/selenium/webextension/WebExtension.java

WebExtensionOptions.javaAdd the cross-browser options base type +40/-0

Add the cross-browser options base type

• Defines an empty base options object that browser-specific extension options can specialize.

java/src/org/openqa/selenium/webextension/WebExtensionOptions.java

Tests (10) +480 / -2
BUILD.bazelRegister web extension API unit tests +2/-0

Register web extension API unit tests

• Adds the extension handle and Firefox options tests to the core small-test suite.

java/test/org/openqa/selenium/BUILD.bazel

BUILD.bazelSeparate BiDi extension unit and browser tests +13/-2

Separate BiDi extension unit and browser tests

• Registers parameter serialization as a small test while retaining browser-backed extension coverage in the large suite.

java/test/org/openqa/selenium/bidi/webextension/BUILD.bazel

InstallExtensionParametersTest.javaTest BiDi vendor parameter serialization +60/-0

Test BiDi vendor parameter serialization

• Verifies default serialization, sibling vendor option merging, and protection of the required 'extensionData' field.

java/test/org/openqa/selenium/bidi/webextension/InstallExtensionParametersTest.java

BUILD.bazelRegister Firefox web extension integration tests +3/-0

Register Firefox web extension integration tests

• Adds the new Firefox extension suite and required core dependencies to the relevant Selenium test targets.

java/test/org/openqa/selenium/firefox/BUILD.bazel

RemoteFirefoxDriverTest.javaVerify direct remote Firefox extension access +16/-0

Verify direct remote Firefox extension access

• Confirms a builder-created remote driver can install and uninstall through 'HasWebExtensions' without augmentation.

java/test/org/openqa/selenium/firefox/RemoteFirefoxDriverTest.java

WebExtensionsTest.javaExercise Firefox extension sources end to end +120/-0

Exercise Firefox extension sources end to end

• Covers installation and removal of XPI, signed and unsigned ZIP, directory, and Base64 sources. It also verifies temporary Firefox installation and observable content-script activation.

java/test/org/openqa/selenium/firefox/WebExtensionsTest.java

ZipTest.javaVerify root-preserving archive layout +14/-0

Verify root-preserving archive layout

• Ensures the new ZIP helper produces exactly one top-level directory while retaining nested extension files.

java/test/org/openqa/selenium/io/ZipTest.java

RemoteWebExtensionsTest.javaTest extension transport routing and validation +146/-0

Test extension transport routing and validation

• Covers classic Firefox installation and removal, option translation, archive reading, unsupported Chromium sessions, invalid inputs, and malformed endpoint responses.

java/test/org/openqa/selenium/remote/RemoteWebExtensionsTest.java

FirefoxWebExtensionOptionsTest.javaTest immutable Firefox extension options +57/-0

Test immutable Firefox extension options

• Verifies default emptiness, configured values, base option behavior, and fluent immutability.

java/test/org/openqa/selenium/webextension/FirefoxWebExtensionOptionsTest.java

WebExtensionTest.javaTest extension handle value semantics +49/-0

Test extension handle value semantics

• Covers identifier access, null rejection, equality, hashing, and string representation.

java/test/org/openqa/selenium/webextension/WebExtensionTest.java

Documentation (1) +26 / -0
package-info.javaDocument the web extension package boundary +26/-0

Document the web extension package boundary

• Describes the package as a null-marked, protocol-neutral API whose transport remains an implementation detail.

java/src/org/openqa/selenium/webextension/package-info.java

Other (4) +20 / -0
BUILD.bazelExport the web extension API package +1/-0

Export the web extension API package

• Adds the new 'webextension' package to the public Selenium Java export target.

java/src/org/openqa/selenium/BUILD.bazel

FirefoxDriver.javaDeprecate Firefox-specific extension methods +14/-0

Deprecate Firefox-specific extension methods

• Marks the legacy install and uninstall methods for removal and directs callers to the protocol-neutral web extension API.

java/src/org/openqa/selenium/firefox/FirefoxDriver.java

HasExtensions.javaDeprecate the Firefox extension interface +4/-0

Deprecate the Firefox extension interface

• Deprecates 'HasExtensions' in favor of 'HasWebExtensions', which works directly on drivers and supports BiDi and Grid.

java/src/org/openqa/selenium/firefox/HasExtensions.java

BUILD.bazelAdd the BiDi web extension dependency +1/-0

Add the BiDi web extension dependency

• Allows the remote driver implementation to construct and send BiDi web extension commands.

java/src/org/openqa/selenium/remote/BUILD.bazel

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Action required

1. Extension routing can regress unseen 📘 Rule violation ☼ Reliability
Description
The new install branch sends every negotiated bidirectional session through installOverBiDi, but
no focused test constructs such a session. Firefox vendor serialization, Chromium directory path or
upload selection, result validation, and uninstall dispatch can therefore change without the
small-test suite detecting it.
Code

java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[R105-107]

+    if (driver.maybeGetBiDi().isPresent()) {
+      LOG.fine("Installing web extension over BiDi");
+      return installOverBiDi(source, firefoxOptions);
Evidence
Compliance rule 4 requires focused regression coverage for new behavior. The implementation
introduces bidirectional dispatch and several browser-specific branches, while the focused remote
tests cover classic Firefox, non-bidirectional Chromium rejection, and input validation only.

AGENTS.md: Provide Focused Automated Tests for Code Changes
java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[105-176]
java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[215-224]
java/test/org/openqa/selenium/remote/RemoteWebExtensionsTest.java[48-145]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new driver-level web-extension implementation contains untested bidirectional routing, including browser-specific source conversion, vendor options, response validation, and uninstall behavior.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[84-176]
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[215-224]
- java/test/org/openqa/selenium/remote/RemoteWebExtensionsTest.java[48-145]

## Recommended Fix
Extend the focused remote web-extension tests with a controllable bidirectional-session fixture. Verify Firefox vendor parameters and uninstall commands, Chromium local directory paths and remote uploads, and rejection of malformed install results without relying on browser-level tests.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Firefox installs persist unexpectedly 🐞 Bug ≡ Correctness
Description
installOverClassic omits temporary unless callers explicitly set permanent, even though the
classic Firefox endpoint treats an omitted value as permanent while the API and BiDi path default to
temporary installation. On non-BiDi Firefox, a signed extension installed with default options or
only private-browsing permission therefore remains in a reused custom profile after the session,
whereas the same call through BiDi is removed automatically.
Code

java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[R181-184]

+    Map<String, Object> params = new HashMap<>();
+    params.put("addon", source.toBase64());
+    if (firefoxOptions != null) {
+      firefoxOptions.isPermanent().ifPresent(permanent -> params.put("temporary", !permanent));
Evidence
The classic request construction adds temporary only when permanent is explicitly present, so
requests with no options or only private-browsing permission omit the field despite permanence being
opt-in. Geckodriver's classic installer defaults an omitted temporary argument to false, while
Firefox's BiDi install parameters and the repository's vendor schema define moz:permanent as false
by default, proving that the two transports give the same API call opposite persistence semantics.

java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[179-195]
common/bidi/webextension-install-extensions.cddl[4-6]
java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[101-113]
java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[179-188]
java/src/org/openqa/selenium/webextension/FirefoxWebExtensionOptions.java[55-63]
🌐 Firefox documents moz:permanent as false by default and says temporary installation is the default behavior.
🌐 The classic Marionette add-on installer defines temporary = false, making an omitted value permanent.
🌐 Geckodriver documents that an omitted temporary field for the classic add-on install endpoint installs the add-on permanently.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Firefox extension installation has different persistence semantics depending on transport: the classic request omits `temporary` unless `permanent(...)` is explicitly set, and geckodriver interprets that omission as a permanent installation, while the API and BiDi Firefox behavior default to temporary installation. This also affects option sets that only configure private-browsing access.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[179-188]
- java/test/org/openqa/selenium/remote/RemoteWebExtensionsTest.java[48-72]

## Recommended Fix
Always add the classic `temporary` parameter, deriving it as the inverse of `firefoxOptions.isPermanent().orElse(false)`. This makes absent options and options that only set private-browsing access send `temporary: true`, while `permanent(true)` sends `temporary: false`; update the tests to verify both default temporary installation and explicit permanent installation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Kubernetes Grid cannot install folders 🐞 Bug ≡ Correctness
Description
extensionData routes remote Chromium directories through uploadDirectory, which assumes the
file-upload command returns a response containing the extracted remote path. Grid dispatches that
request to OneShotNode.uploadFile, which returns null, so Chromium directory installation on a
Kubernetes one-shot node fails before the BiDi install command is sent.
Code

java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[R171-175]

+    Path directory = requireNonNullPath(source);
+    String resolved =
+        browserSharesFilesystem()
+            ? directory.toAbsolutePath().toString()
+            : uploadDirectory(directory);
Evidence
Remote Chromium sessions upload an unpacked directory whenever the client and browser do not share a
filesystem. The Grid route delegates uploads to each node implementation, but the Kubernetes
one-shot implementation returns null while this new client code immediately dereferences the
response.

java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[169-176]
java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[215-224]
java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[235-238]
java/src/org/openqa/selenium/grid/node/Node.java[168-170]
java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java[339-343]
java/src/org/openqa/selenium/grid/node/local/LocalNode.java[1080-1116]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Remote Chromium directory installation depends on the Grid file-upload endpoint, but Kubernetes one-shot nodes return no upload response and the client cannot obtain a browser-visible directory path.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[169-176]
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[215-224]
- java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java[339-343]

## Recommended Fix
Implement `OneShotNode.uploadFile` with the same single-root extraction and path response semantics used by `LocalNode`, or forward the upload to the environment that owns the browser. Add an integration test covering a Chromium directory installation through this node type.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Unreadable folders become partial uploads 🐞 Bug ☼ Reliability
Description
zipToBase64PreservingRoot delegates traversal to addToZip, which catches directory-enumeration
failures, logs them, and continues instead of propagating the declared IOException. When any
extension subtree is unreadable, Grid receives a successful but incomplete archive and the eventual
install fails remotely with missing manifest or resource data rather than identifying the local read
failure.
Code

java/src/org/openqa/selenium/io/Zip.java[R74-77]

+    try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+      try (ZipOutputStream zos = new ZipOutputStream(bos)) {
+        addToZip(parent.getAbsolutePath(), zos, absolute);
+      }
Evidence
The added method promises an IOException when input cannot be read, but the helper it invokes
catches that exact failure around newDirectoryStream and returns normally. The resulting Base64
archive is therefore indistinguishable from a successfully completed upload to its caller.

java/src/org/openqa/selenium/io/Zip.java[58-78]
java/src/org/openqa/selenium/io/Zip.java[82-110]
java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[215-224]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new root-preserving zip operation can silently produce incomplete extension uploads because its shared traversal helper suppresses directory read failures.

## Fix Focus Areas
- java/src/org/openqa/selenium/io/Zip.java[68-78]
- java/src/org/openqa/selenium/io/Zip.java[82-93]
- java/test/org/openqa/selenium/io/ZipTest.java[95-107]

## Recommended Fix
Remove the catch that suppresses `IOException` from directory enumeration and let traversal failures propagate through `zipToBase64PreservingRoot`. Add a test demonstrating that an unreadable subtree fails the operation instead of producing a partial archive.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Web pages:
  +22 more
Review mode: 🧠 Deep: This is a broad, behavior-changing API and transport implementation spanning BiDi, classic Firefox fallback, remote uploads, zipping, driver integration, and multiple independent code paths, creating substantial opportunity for subtle defects.

Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +105 to +107
if (driver.maybeGetBiDi().isPresent()) {
LOG.fine("Installing web extension over BiDi");
return installOverBiDi(source, firefoxOptions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

1. Extension routing can regress unseen 📘 Rule violation ☼ Reliability

The new install branch sends every negotiated bidirectional session through installOverBiDi, but
no focused test constructs such a session. Firefox vendor serialization, Chromium directory path or
upload selection, result validation, and uninstall dispatch can therefore change without the
small-test suite detecting it.
Agent Prompt
## Issue description
The new driver-level web-extension implementation contains untested bidirectional routing, including browser-specific source conversion, vendor options, response validation, and uninstall behavior.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[84-176]
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[215-224]
- java/test/org/openqa/selenium/remote/RemoteWebExtensionsTest.java[48-145]

## Recommended Fix
Extend the focused remote web-extension tests with a controllable bidirectional-session fixture. Verify Firefox vendor parameters and uninstall commands, Chromium local directory paths and remote uploads, and rejection of malformed install results without relying on browser-level tests.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +181 to +184
Map<String, Object> params = new HashMap<>();
params.put("addon", source.toBase64());
if (firefoxOptions != null) {
firefoxOptions.isPermanent().ifPresent(permanent -> params.put("temporary", !permanent));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

2. Firefox installs persist unexpectedly 🐞 Bug ≡ Correctness

installOverClassic omits temporary unless callers explicitly set permanent, even though the
classic Firefox endpoint treats an omitted value as permanent while the API and BiDi path default to
temporary installation. On non-BiDi Firefox, a signed extension installed with default options or
only private-browsing permission therefore remains in a reused custom profile after the session,
whereas the same call through BiDi is removed automatically.
Agent Prompt
## Issue description
Firefox extension installation has different persistence semantics depending on transport: the classic request omits `temporary` unless `permanent(...)` is explicitly set, and geckodriver interprets that omission as a permanent installation, while the API and BiDi Firefox behavior default to temporary installation. This also affects option sets that only configure private-browsing access.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[179-188]
- java/test/org/openqa/selenium/remote/RemoteWebExtensionsTest.java[48-72]

## Recommended Fix
Always add the classic `temporary` parameter, deriving it as the inverse of `firefoxOptions.isPermanent().orElse(false)`. This makes absent options and options that only set private-browsing access send `temporary: true`, while `permanent(true)` sends `temporary: false`; update the tests to verify both default temporary installation and explicit permanent installation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +171 to +175
Path directory = requireNonNullPath(source);
String resolved =
browserSharesFilesystem()
? directory.toAbsolutePath().toString()
: uploadDirectory(directory);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

3. Kubernetes grid cannot install folders 🐞 Bug ≡ Correctness

extensionData routes remote Chromium directories through uploadDirectory, which assumes the
file-upload command returns a response containing the extracted remote path. Grid dispatches that
request to OneShotNode.uploadFile, which returns null, so Chromium directory installation on a
Kubernetes one-shot node fails before the BiDi install command is sent.
Agent Prompt
## Issue description
Remote Chromium directory installation depends on the Grid file-upload endpoint, but Kubernetes one-shot nodes return no upload response and the client cannot obtain a browser-visible directory path.

## Fix Focus Areas
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[169-176]
- java/src/org/openqa/selenium/remote/RemoteWebExtensions.java[215-224]
- java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java[339-343]

## Recommended Fix
Implement `OneShotNode.uploadFile` with the same single-root extraction and path response semantics used by `LocalNode`, or forward the upload to the environment that owns the browser. Add an integration test covering a Chromium directory installation through this node type.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +74 to +77
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
try (ZipOutputStream zos = new ZipOutputStream(bos)) {
addToZip(parent.getAbsolutePath(), zos, absolute);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remediation recommended

4. Unreadable folders become partial uploads 🐞 Bug ☼ Reliability

zipToBase64PreservingRoot delegates traversal to addToZip, which catches directory-enumeration
failures, logs them, and continues instead of propagating the declared IOException. When any
extension subtree is unreadable, Grid receives a successful but incomplete archive and the eventual
install fails remotely with missing manifest or resource data rather than identifying the local read
failure.
Agent Prompt
## Issue description
The new root-preserving zip operation can silently produce incomplete extension uploads because its shared traversal helper suppresses directory read failures.

## Fix Focus Areas
- java/src/org/openqa/selenium/io/Zip.java[68-78]
- java/src/org/openqa/selenium/io/Zip.java[82-93]
- java/test/org/openqa/selenium/io/ZipTest.java[95-107]

## Recommended Fix
Remove the catch that suppresses `IOException` from directory enumeration and let traversal failures propagate through `zipToBase64PreservingRoot`. Add a test demonstrating that an unreadable subtree fails the operation instead of producing a partial archive.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

This branch has not been deployed

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

Labels

B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related C-java Java Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants