Skip to content

[adr] Install browser extensions from the driver directly - #17817

Open
titusfortner wants to merge 17 commits into
SeleniumHQ:trunkfrom
titusfortner:c/driver-extension-install-adr
Open

[adr] Install browser extensions from the driver directly#17817
titusfortner wants to merge 17 commits into
SeleniumHQ:trunkfrom
titusfortner:c/driver-extension-install-adr

Conversation

@titusfortner

@titusfortner titusfortner commented Jul 24, 2026

Copy link
Copy Markdown
Member

📄 The decision, its rationale, considered options, and consequences are in the record file this PR adds; read it there. The sections below are proposal notes and review logistics.

🔗 Related

Prototype implementation in Ruby: #17879

📝 Proposal notes

  • The pipe/CDP requirement no longer applies. The original implementation of this ADR discussed the need for using Chrome with remote debugging pipe to support extensions, but this requirement no longer applies. Chrome 149 (stable June 2, 2026) dropped the requirement for --remote-debugging-pipe and --enable-unsafe-extension-debugging to install/debug extensions via CDP — crrev.com/c/7765453 ("Remove requirements for --remote-debugging-pipe and --enable-unsafe-extension-debugging for debugging of extensions via CDP"), with the switch removed entirely in the follow-up crrev.com/c/7776996. Extension install now works over the WebSocket, so BiDi extension install and the CDP API coexist — there's no pipe-vs-port choice and no CDP-disabling side effect.
  • Out of scope. This PR is the decision record. Implementing the new methods and deprecating the existing Firefox install methods (Decision 2) is follow-up work, tracked separately.
  • Firefox configurations. Firefox has vendor specific configurations (moz:permanent and moz:allowPrivateBrowsing) that must be supported as well (see Merge vendor cddl files into shared BiDi schema and implement custom Firefox webExtension options #17840).
  • Classic allowPrivateBrowsing support arrived in geckodriver 0.36.0. Before 0.36.0 the classic /moz/addon/install endpoint accepted only temporary; passing allowPrivateBrowsing errors on those older geckodrivers. geckodriver ≥ 0.36.0 honors it (firefox-beta ships this, which is why classic + allowPrivateBrowsing: true grants access rather than failing). Because geckodriver itself errors when the option is unsupported, bindings do not need to validate it on the classic path. The BiDi path is uniform across versions.
  • Known limitation, not a design constraint. Today chromium-bidi cannot accept base64 web extension data ([🐛 Bug]: [python][Chrome] Webextension - no support for archived and base64 extensionData type in Chrome? #16541), so installing over a Grid on Chromium is limited. The record deliberately declares the expected final state — installs work through the Grid via base64 — rather than narrowing the decision around a temporary browser gap; the limitation is tracked and resolves when chromium-bidi catches up.

🗣 Discussion

📌 Tracking

Tracking issue: (linked on acceptance)

@qodo-code-review

qodo-code-review Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Temporary/permanent inverted ✓ Resolved 🐞 Bug ≡ Correctness
Description
The ADR claims Firefox classic addon install supports only temporary “(i.e. permanent)”, but
existing binding APIs define temporary as a temporary (non-permanent) install, so this wording
inverts the intended semantics and can lead to incorrect option mapping/implementation.
Code

docs/decisions/17817-driver-extension-install.md[R93-94]

+- **The classic fallback is reduced-capability.** Firefox's classic endpoint supports only `temporary` 
+  (i.e. `permanent`), not `allowPrivateBrowsing`. With BiDi off, `installExtension` still installs, but a 
Evidence
The ADR equates temporary with permanent, but the existing classic endpoint integrations across
bindings use a temporary boolean explicitly described as temporary install behavior, demonstrating
the polarity is opposite of permanent and should be mapped/inverted rather than treated as
synonymous.

docs/decisions/17817-driver-extension-install.md[93-96]
py/selenium/webdriver/firefox/webdriver.py[105-138]
java/src/org/openqa/selenium/firefox/AddHasExtensions.java[78-100]
dotnet/src/webdriver/Firefox/FirefoxDriver.cs[289-370]

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 ADR currently states that Firefox classic’s endpoint supports only `temporary` “(i.e. `permanent`)”. This is semantically contradictory and likely to cause implementers to invert the `permanent` option when mapping to the classic endpoint.

### Issue Context
Across bindings, the classic `/moz/addon/install` payload uses a boolean field named `temporary`, and the in-repo docs/comments describe it as installing the add-on temporarily for the session.

### Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[93-96]

Suggested wording direction: say the classic fallback supports only the `temporary` boolean flag (and that `temporary = !permanent` if the new API uses `permanent`), and does not support `allowPrivateBrowsing`.

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


2. Pipe described as BiDi-only ✓ Resolved 🐞 Bug ≡ Correctness
Description
The ADR claims Chromium’s pipe transport “carries WebDriver BiDi only” / “not CDP”, but the repo’s
bundled Chrome DevTools Protocol explicitly documents CDP extension commands as available when
connected via --remote-debugging-pipe. This overbroad phrasing can mislead readers/implementers
about what the pipe transport is; if the intent is “CDP is not exposed to the Selenium caller in a
BiDi session”, it should be stated that way.
Code

docs/decisions/17817-driver-extension-install.md[R17-20]

+A Chromium driver reaches the browser over one of two transports: a local TCP debugging port, or a
+pipe inherited from the process that launched the browser. The port is what makes CDP reachable;
+the pipe is private to the driver and carries WebDriver BiDi only. Chrome accepts extension install
+over BiDi only on the pipe.
Evidence
The ADR says the pipe carries BiDi only, but the vendored Chrome DevTools Protocol defines extension
commands (CDP Extensions.*) as available when the client is connected via
--remote-debugging-pipe, demonstrating the pipe is a CDP transport too. Selenium’s own Chromium
options also use --remote-debugging-pipe in the context of extension support, reinforcing that
this flag is associated with remote debugging (CDP) rather than being BiDi-only.

docs/decisions/17817-driver-extension-install.md[17-20]
common/devtools/chromium/v148/browser_protocol.pdl[5637-5652]
py/selenium/webdriver/chromium/options.py[140-148]

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 ADR currently states that the Chromium remote-debugging pipe "carries WebDriver BiDi only" and implies the pipe cannot carry CDP. In Chromium, `--remote-debugging-pipe` is a DevTools/CDP transport; the real intended point appears to be that enabling BiDi should not expose a caller-accessible CDP endpoint/capability.

### Issue Context
This ADR is setting rationale/constraints for future implementation. Overstating the pipe as BiDi-only conflicts with the DevTools protocol docs vendored in this repo and with existing Selenium options that treat the pipe as a remote-debugging (CDP) mechanism.

### Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[17-20]
- docs/decisions/17817-driver-extension-install.md[47-49]

### Suggested change
Reword to distinguish **(a)** the underlying browser<->driver transport (which can carry DevTools/CDP traffic over the pipe) from **(b)** what Selenium exposes to the user (e.g., no `se:cdp` endpoint / no caller-accessible CDP when BiDi is enabled), if that is the intended decision.

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



Remediation recommended

3. Conflicting options surface ⊘ Outdated 🐞 Bug ⚙ Maintainability ⭐ New
Description
The ADR states installWebExtension is exposed on the driver instance and accepts Firefox-specific
options, but later says those options are only available on the vendor driver surface. This
contradiction can drive bindings to ship incompatible method signatures and option-passing
mechanisms, undermining the stated goal of a uniform API.
Code

docs/decisions/17817-driver-extension-install.md[R91-93]

+- **Vendor-specific options are only made available on that vendor's driver.** Options like Firefox's
+  `permanent` and `allowPrivateBrowsing` are exposed only on that vendor's driver surface, so a session
+  for another browser has no way to pass them — there is nothing to silently drop or raise.
Evidence
Decision 1 requires the methods to be on the driver instance and to accept vendor-specific options,
but the Consequences section states those options are only exposed on the vendor’s driver surface,
which is incompatible with a uniform driver-instance API.

docs/decisions/17817-driver-extension-install.md[29-33]
docs/decisions/17817-driver-extension-install.md[91-93]

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 ADR contradicts itself about how callers pass vendor-specific options (e.g., Firefox `permanent`, `allowPrivateBrowsing`). Decision 1 says the methods live on the driver instance and accept vendor-specific options, while Consequences says those options are only exposed on the vendor driver surface.

This ambiguity can cause different language bindings to implement different signatures (e.g., generic-driver method without options vs FirefoxDriver-only overload), reducing cross-binding consistency.

### Issue Context
This PR is an ADR; clarity here affects downstream API design and interoperability across bindings.

### Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[91-93]

### Suggested direction
Update the Consequences bullet to align with Decision 1 by explicitly specifying one consistent approach, e.g.:
- vendor options are accepted via a common options parameter/type on `installWebExtension` but must be validated/raised on non-supporting browsers, **or**
- vendor options are intentionally only available via vendor-specific driver types, and Decision 1 should be updated to reflect that the uniform driver API cannot take vendor-only parameters.

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


4. Chrome scope ambiguous 🐞 Bug ⚙ Maintainability
Description
The ADR’s Context explicitly distinguishes “Branded Chrome” vs “Chrome for Testing and unbranded
Chromium,” but Decision 3 and the “Conditional availability” option refer generically to “Chrome
session,” which does not define whether the BiDi requirement applies to branded Chrome only or
Chromium-family browsers broadly. This ambiguity can lead to bindings implementing different
browser-scoping rules for when to raise/hide the methods.
Code

docs/decisions/17817-driver-extension-install.md[R59-62]

+**Conditional availability**
+- **Expose the method only where it works** — hide it on a Chrome session without BiDi rather than
+  raising. Not taken: Java cannot conditionally implement the interface, and doing it only in a binding
+  that can (Ruby) would make it the odd one out; a uniform surface that raises a clear error is simpler.
Evidence
The document establishes multiple Chromium variants with differing behavior, but later sections use
“Chrome” as if it’s a single, well-defined scope for the BiDi requirement, which it is not in the
ADR’s own Context.

docs/decisions/17817-driver-extension-install.md[11-13]
docs/decisions/17817-driver-extension-install.md[41-43]
docs/decisions/17817-driver-extension-install.md[59-62]

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 ADR uses “Chrome” in Decision 3 and “Conditional availability” without defining whether it means branded Chrome specifically or Chromium-family browsers generally, despite earlier Context text distinguishing branded Chrome from other Chromium variants.

## Issue Context
Context section: “Branded Chrome stopped honoring that path … (Chrome for Testing and unbranded Chromium still do)”. Later, Decision 3 and considered options talk about “Chrome session without BiDi” and “Chrome has no classic install path” without clarifying whether this is branded-Chrome-only or a Chromium-family rule.

## Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[11-13]
- docs/decisions/17817-driver-extension-install.md[41-43]
- docs/decisions/17817-driver-extension-install.md[59-62]

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


5. Uninstall BiDi requirement unspecified ✓ Resolved 🐞 Bug ≡ Correctness
Description
Decision 1 introduces both installWebExtension and uninstallWebExtension, but Decision 3 only
specifies the “raise when BiDi is not enabled” behavior for installWebExtension on Chrome. This
leaves uninstallWebExtension behavior on a non-BiDi Chrome session undefined, encouraging
inconsistent cross-binding behavior.
Code

docs/decisions/17817-driver-extension-install.md[R41-43]

+3. **The methods are always present; on Chrome they require BiDi.** They are never conditionally hidden
+   per session. Chrome has no classic install path, so `installWebExtension` raises there when BiDi is
+   not enabled.
Evidence
The ADR defines two methods in Decision 1, but the Chrome/BiDi unsupported behavior in Decision 3
only mentions installWebExtension, leaving uninstall’s contract unclear.

docs/decisions/17817-driver-extension-install.md[29-35]
docs/decisions/17817-driver-extension-install.md[41-43]

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

## Issue description
Decision 3 states that on Chrome, `installWebExtension` raises when BiDi is not enabled, but it does not state what happens for `uninstallWebExtension` in the same condition.

## Issue Context
Decision 1 defines two new driver-instance methods (`installWebExtension` and `uninstallWebExtension`). Decision 3 is intended to define the always-present/unsupported behavior for Chrome sessions without BiDi, but it currently only names the install method.

## Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[41-43]
- docs/decisions/17817-driver-extension-install.md[29-35]

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


View more (5)
6. Unclear validation boundary ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The ADR states that allowPrivateBrowsing: false “raises” under the Firefox classic fallback, but
the legacy /moz/addon/install payload used by current bindings cannot represent that option, so
the ADR needs to explicitly require bindings to validate and throw before delegating to classic.
Without that clarity, implementers may accidentally ignore the flag and still install with
private-browsing access, violating the documented behavior.
Code

docs/decisions/17817-driver-extension-install.md[R102-104]

+  `allowPrivateBrowsing` is effectively always `true`. `allowPrivateBrowsing: true` (or unspecified) is
+  satisfied; explicitly passing `allowPrivateBrowsing: false` raises rather than silently enabling it
+  anyway.
Evidence
The ADR claims the classic endpoint has “no toggle” yet says allowPrivateBrowsing: false “raises”,
but existing classic addon install code paths show the endpoint payload contains only addon and
temporary, so the server cannot be instructed to disable private browsing via parameters; this
implies any failure must be enforced/validated by the binding itself and should be stated
explicitly.

docs/decisions/17817-driver-extension-install.md[100-104]
javascript/selenium-webdriver/firefox.js[654-668]
javascript/selenium-webdriver/firefox.js[477-479]
py/selenium/webdriver/firefox/webdriver.py[105-139]
py/selenium/webdriver/firefox/remote_connection.py[42-45]

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

### Issue description
ADR Consequences says `allowPrivateBrowsing: false` “raises” in the classic Firefox fallback, but it doesn’t state **who** must raise (binding vs server). Since the classic install endpoint contract (as used today) doesn’t accept such an option, the ADR should explicitly require **client-side validation** in bindings before calling the classic endpoint.

### Issue Context
Current classic Firefox addon install implementations send only `{addon, temporary}` and call `/session/.../moz/addon/install`. There is no `allowPrivateBrowsing` parameter to forward, so classic mode cannot enforce `allowPrivateBrowsing: false` unless bindings reject it themselves.

### Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[100-104]

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


7. Chromium flags tied to BiDi ✓ Resolved 🐞 Bug ≡ Correctness
Description
Decision 3 states bindings will pass remote-debugging-pipe and enable-unsafe-extension-debugging
“when BiDi is enabled,” but the ADR also frames BiDi as a cross-browser capability (Firefox +
Chromium). This phrasing can mislead implementers into applying Chromium-only flags to non-Chromium
BiDi sessions/options instead of scoping them to Chromium-based browsers (and the extension-install
flow).
Code

docs/decisions/17817-driver-extension-install.md[R54-56]

+3. **Enabling BiDi disables the CDP API.** Bindings will pass the arguments for `remote-debugging-pipe`
+   and `enable-unsafe-extension-debugging` when BiDi is enabled to allow users to install web extensions
+   without setting the arguments themselves. This prevents users from accessing the CDP API,
Evidence
The ADR discusses BiDi support across Firefox and Chromium, but Decision 3’s “when BiDi is enabled”
wording is unqualified while referencing Chromium-only flags. Repo docs/code also describe these
flags specifically in Chromium options, reinforcing that they are not a general BiDi setting.

docs/decisions/17817-driver-extension-install.md[8-13]
docs/decisions/17817-driver-extension-install.md[28-30]
docs/decisions/17817-driver-extension-install.md[54-57]
py/selenium/webdriver/chromium/options.py[135-148]

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 ADR currently says bindings pass Chromium-specific flags whenever BiDi is enabled, even though the document discusses BiDi for both Firefox and Chromium. This can be misread as a cross-browser requirement.

## Issue Context
These flags are Chromium-only and should be described as such, otherwise binding implementers may incorrectly try to add them for Firefox (or other non-Chromium) BiDi sessions.

## Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[54-57]

## Suggested change (example wording)
Update Decision 3 to explicitly scope the flags to Chromium-based browsers, e.g.:
- “On Chromium-based browsers, when BiDi is enabled (to support web-extension install/uninstall), bindings will pass …”
And keep the CDP-API-disablement sentence scoped to that same Chromium pipe-transport choice (rather than BiDi universally).

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


8. Flag names missing -- ✗ Dismissed 🐞 Bug ⚙ Maintainability
Description
The ADR refers to Chromium arguments as remote-debugging-pipe and
enable-unsafe-extension-debugging, but the repo consistently documents/uses the literal flags
--remote-debugging-pipe and --enable-unsafe-extension-debugging. This creates ambiguity about
the exact argument strings bindings should pass when implementing the ADR decision.
Code

docs/decisions/17817-driver-extension-install.md[R54-56]

+3. **Enabling BiDi disables the CDP API.** Bindings will pass the arguments for `remote-debugging-pipe` 
+   and `enable-unsafe-extension-debugging` when BiDi is enabled to allow users to install extensions 
+   without setting the arguments themselves. This prevents users from accessing the CDP API, 
Evidence
The ADR omits the -- prefix, while the Python Chromium options implementation and the bundled
Chromium protocol documentation both use the full --flag-name form, indicating the ADR should
match those exact literals for clarity.

docs/decisions/17817-driver-extension-install.md[54-56]
py/selenium/webdriver/chromium/options.py[140-154]
common/devtools/chromium/v148/browser_protocol.pdl[5637-5651]

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 ADR names Chromium command-line switches without their canonical `--` prefixes, which conflicts with how the repo documents/uses them.

### Issue Context
In this repo, both implementation/docs treat these as literal CLI flags (including the `--`). The ADR should be explicit to avoid ambiguity for binding implementers.

### Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[54-56]

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


9. CDP disablement scope unclear ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The ADR’s option (2) says switching Selenium’s “default connection” to the inherited pipe “removes
support for the CDP API”, but later text frames CDP disablement as a consequence of enabling BiDi
and separately notes raw CDP commands remain available via goog/cdp/execute. This makes it unclear
whether CDP is disabled globally, per BiDi-enabled session, or only the higher-level/asynchronous
Selenium CDP API.
Code

docs/decisions/17817-driver-extension-install.md[R22-23]

+2. Switch Selenium's default connection to the inherited pipe, which removes support for the CDP API
+   (Decision 5).
Evidence
The ADR first states that switching the default connection to the pipe removes CDP API support, then
clarifies that CDP commands are still possible via goog/cdp/execute, and later scopes CDP API
disablement to enabling BiDi—together creating ambiguity about what is disabled and under what
conditions.

docs/decisions/17817-driver-extension-install.md[15-26]
docs/decisions/17817-driver-extension-install.md[64-66]
docs/decisions/17817-driver-extension-install.md[97-98]

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 ADR uses ambiguous wording around "default connection" and "CDP API". It should clearly distinguish (a) which default is being changed (e.g., default *BiDi transport* for Chromium) and (b) what is being disabled (Selenium’s higher-level/asynchronous CDP API) versus what remains (the raw `goog/cdp/execute` endpoint).

### Issue Context
The document currently mentions CDP API removal, then immediately notes `goog/cdp/execute` still works (but without events), and later says enabling BiDi disables Selenium’s CDP API. This combination reads inconsistently unless the scope/definitions are explicit.

### Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[15-26]
- docs/decisions/17817-driver-extension-install.md[64-67]
- docs/decisions/17817-driver-extension-install.md[97-98]

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


10. JS BiDi support overstated ✓ Resolved 🐞 Bug ≡ Correctness
Description
The ADR states that bindings already expose a BiDi module for extension install/uninstall, but the
same table indicates the JavaScript binding has no advertised BiDi approach (“none”), which is
contradictory and can mislead readers about cross-binding availability.
Code

docs/decisions/17817-driver-extension-install.md[R22-33]

+WebDriver BiDi specifies extension install and uninstall, which both Firefox and Chromium
+implement. Bindings already expose the BiDi module for it, and pointing users at that module is
+what we advertise today.
+
+| Binding    | Firefox-only method (classic) | Currently advertised BiDi approach |
+|------------|-------------------------------|------------------------------------|
+| Java       | `installExtension` (on `FirefoxDriver`) | `new WebExtension(driver).install(...)` |
+| Python     | `install_addon` | `driver.webextension.install(...)` |
+| Ruby       | `install_addon` (`HasAddons`) | `BiDi::Protocol::WebExtension` (protocol module) |
+| .NET       | `InstallAddOn`, `InstallAddOnFromFile`, `InstallAddOnFromDirectory` | `driver.AsBiDiAsync()` → `BiDi.WebExtension.InstallAsync(...)` |
+| JavaScript | `installAddon` | none |
+
Evidence
The Context section says bindings already expose a BiDi module for extension install/uninstall, but
the JavaScript row in the table immediately below states the JavaScript binding has no advertised
BiDi approach ("none"), creating an internal contradiction in the ADR.

docs/decisions/17817-driver-extension-install.md[22-33]

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 ADR claims that "Bindings already expose the BiDi module" for extension install/uninstall, but then the JavaScript row in the table says there is no advertised BiDi approach ("none"). This contradiction should be resolved by either qualifying the earlier statement (e.g., "Most bindings..." or "All bindings except JavaScript...") or updating the table/statement to match the intended reality.

## Issue Context
This ADR is used as a decision record; contradictory statements in the Context section can lead to incorrect assumptions by readers and implementers.

## Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[22-33]

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



Informational

11. Broken rationale sentence ✗ Dismissed 🐞 Bug ⚙ Maintainability
Description
The sentence "forces users to know which protocol services the command" is missing a verb/object,
making the ADR rationale unclear and easy to misinterpret. This is a documentation correctness issue
that can confuse readers about why the BiDi module indirection is undesirable.
Code

docs/decisions/17817-driver-extension-install.md[63]

+  JavaScript. It also forces users to know which protocol services the command in order to use it.
Evidence
Line 63 contains the incomplete sentence fragment "which protocol services the command in order to
use it", which is missing a verb/object and reads incorrectly.

docs/decisions/17817-driver-extension-install.md[63-63]

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

### Issue description
In the ADR, a sentence in the rationale for rejecting “Keep pointing users at the BiDi module” is grammatically incomplete (missing a verb/object after “the command”), which harms readability and clarity.

### Issue Context
The intent appears to be describing that users must know *which protocol/service provides the command* (or *which protocol the command uses*) in order to invoke it.

### Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[63-63]

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


12. Ambiguous .NET BiDi example ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The ADR’s .NET “Currently advertised BiDi approach” example is ambiguous because it doesn’t show
that WebExtension is accessed from the *instance* returned by driver.AsBiDiAsync(), which can be
misread as BiDi being a static type/member access.
Code

docs/decisions/17817-driver-extension-install.md[R30-32]

+| Ruby       | `install_addon` (`HasAddons`) | `BiDi::Protocol::WebExtension` (protocol module) |
+| .NET       | `InstallAddOn`, `InstallAddOnFromFile`, `InstallAddOnFromDirectory` | `driver.AsBiDiAsync()` → `BiDi.WebExtension.InstallAsync(...)` |
+| JavaScript | `installAddon` | none |
Evidence
The ADR table uses driver.AsBiDiAsync() followed by BiDi.WebExtension.InstallAsync(...), but the
actual .NET usage is instance-based (bidi.WebExtension.InstallAsync(...)) and WebExtension is an
instance property on IBiDi.

docs/decisions/17817-driver-extension-install.md[26-33]
dotnet/src/webdriver/BiDi/IBiDi.cs[34-53]
dotnet/test/webdriver/BiDi/WebExtension/WebExtensionTests.cs[38-45]

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 .NET example should demonstrate instance-based usage explicitly to avoid confusion/compile errors for readers who interpret `BiDi.WebExtension.InstallAsync(...)` as a static call.

## Issue Context
In the .NET binding, `AsBiDiAsync()` returns an `IBiDi` instance which exposes a `WebExtension` module property.

## Fix Focus Areas
- docs/decisions/17817-driver-extension-install.md[30-32]

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


To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Propose ADR: install/uninstall extensions via driver API (BiDi pipe on Chromium)

📝 Documentation 🕐 10-20 Minutes

Grey Divider

AI Description

• Add ADR proposing cross-browser driver-level extension install/uninstall APIs.
• Document Chromium’s BiDi pipe transport requirement and BiDi-vs-CDP session tradeoff.
• Record rationale, alternatives, and consequences for bindings and Grid behavior.
Diagram

graph TD
  A["User test code"] --> B["Driver API"] --> C{"BiDi enabled?"}
  C -->|"Yes"| D["Pipe transport"] --> E["BiDi WebExtension commands"] --> F["Browser"]
  C -->|"No"| G["TCP debug port"] --> H["CDP available"] --> F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Split into separate ADRs (API vs transport vs CDP/BiDi model)
  • ➕ Smaller, more focused decisions with clearer ownership
  • ➕ Lets reviewers accept the driver API without committing to transport defaults
  • ➖ Readers lose the end-to-end rationale needed to understand feasibility
  • ➖ Creates interdependent ADRs that must be read together anyway
2. Driver-level `extensions` namespace instead of flat methods
  • ➕ Extensible surface for future operations (list/enable/disable, etc.)
  • ➕ Consistent with other higher-level namespaces (e.g., network/script)
  • ➖ Extra indirection for a two-verb API
  • ➖ May diverge from existing Firefox mental model and existing method names
3. Keep extension install as a BiDi module feature (status quo)
  • ➕ No new public API surface to maintain across bindings
  • ➕ Direct mapping to the underlying protocol feature
  • ➖ Binding access patterns are inconsistent (and missing in JS)
  • ➖ Public API mirrors an internal implementation detail (per ADR 17670)

Recommendation: Keep the single ADR as proposed: the driver-level method, Chromium pipe default when BiDi is enabled, and the BiDi-vs-CDP exclusivity are tightly coupled and need to be justified together for the decision to be actionable. The document already captures the main alternatives (BiDi module vs driver API, namespacing) and makes the key consequence (no CDP in BiDi sessions) explicit; the main follow-up is ensuring release notes and tracking issues cover the CDP tradeoff and binding rollout.

Files changed (1) +110 / -0

Documentation (1) +110 / -0
17817-driver-extension-install.mdAdd proposed ADR for driver-level extension install/uninstall +110/-0

Add proposed ADR for driver-level extension install/uninstall

• Introduces ADR 17817 describing a cross-browser driver API for installing/uninstalling extensions mid-session. Documents Chromium’s requirement to use BiDi over the pipe transport for extension install and the consequence that sessions are BiDi-only or CDP-only (not both), along with considered alternatives and expected impacts (including Grid).

docs/decisions/17817-driver-extension-install.md

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
Comment thread docs/decisions/17817-driver-extension-install.md
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 551bc46

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit d047e73

Copilot AI left a comment

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.

Pull request overview

This PR adds a proposed Architecture Decision Record (ADR) documenting a cross-binding design decision to support mid-session browser extension installation directly from the driver, using BiDi as the implementation mechanism and adjusting Chromium transport/CDP behavior to make this work without extra user flags.

Changes:

  • Adds ADR 17817 describing a new driver-level installExtension/uninstallExtension surface across bindings.
  • Documents the rationale for using BiDi + pipe transport in Chromium and the resulting CDP API limitations.
  • Captures alternatives considered (API placement, naming, transport default, unsigned extension posture) and their tradeoffs.

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
Comment thread docs/decisions/17817-driver-extension-install.md Outdated
@titusfortner titusfortner changed the title [docs] Propose ADR: the driver installs extensions directly [adr] Install browser extensions from the driver directly Jul 24, 2026
Comment thread docs/decisions/17817-driver-extension-install.md Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit cfdf561

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit b1b9606

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit bfec758

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 4b825a3

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
Comment thread docs/decisions/17817-driver-extension-install.md Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit e894d6a

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 8ac2df1

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 183e0ad

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 192c35b

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
Comment thread docs/decisions/17817-driver-extension-install.md
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 1969b32

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit e23bc4e

Comment thread docs/decisions/17817-driver-extension-install.md Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 2c7d7e8

@qodo-code-review

qodo-code-review Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

No code changes since the last review — review skipped

Qodo Logo

@diemol diemol left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this makes sense, and it is good that we're going to have an alignment across all bindings.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants