Repository navigation
[adr] Install browser extensions from the driver directly #17817
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
titusfortner
merged 19 commits into
SeleniumHQ:trunk
from
titusfortner:c/driver-extension-install-adr
Aug 21, 2026
+92
β0
Merged
Changes from 2 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
3537c7b
[docs] Propose ADR: the driver installs extensions directly
titusfortner 551bc46
[docs] Number ADR 17817 and link its PR
titusfortner d047e73
[docs] ADR 17817: sharpen the CDP-access framing
titusfortner cfdf561
[docs] ADR 17817: address review copyedits
titusfortner b1b9606
[docs] ADR 17817: restructure decisions and trim considered options
titusfortner bfec758
[docs] ADR 17817: tighten decisions to read as decisions
titusfortner 4b825a3
[docs] ADR 17817: note the classic fallback is reduced-capability
titusfortner d28dd8d
[docs] ADR 17817: fix inverted temporary/permanent wording
titusfortner e894d6a
[docs] ADR 17817: adopt installWebExtension naming and the web-extensβ¦
titusfortner 8ac2df1
[docs] ADR 17817: scope the CDP-pipe flags to Chromium sessions
titusfortner 183e0ad
[docs] ADR 17817: correct the classic private-browsing behavior
titusfortner 192c35b
[docs] ADR 17817: require bindings to reject allowPrivateBrowsing:falβ¦
titusfortner 8119540
[docs] ADR 17817: drop the CDP tradeoff, obsoleted by Chrome 149 remoβ¦
titusfortner 1969b32
[docs] ADR 17817: require BiDi for installWebExtension on Chrome
titusfortner e23bc4e
[docs] ADR 17817: split backwards-compat from the raise-when-unsupporβ¦
titusfortner 2c7d7e8
[docs] ADR 17817: scope vendor options to the vendor driver, drop theβ¦
titusfortner f224e6e
[docs] ADR 17817: drop the vendor-option consequence, Decision 1 alreβ¦
titusfortner fdffcad
[docs] ADR 17817: make the Grid consequence transport-neutral
titusfortner 2c90f41
[docs] ADR 17817: clarify remote-end delivery and Chromium scoping
titusfortner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # 17817. The driver installs extensions directly | ||
|
|
||
| - Status: Proposed | ||
| - Discussion: https://github.com/SeleniumHQ/selenium/pull/17817 | ||
|
|
||
| ## Context | ||
|
|
||
| Installing a browser extension is a routine automation need. Firefox can already install one | ||
| mid-session, through a WebDriver-classic endpoint β but exposed under a different name in every | ||
| binding and hanging off a browser-specific type rather than the driver. | ||
|
|
||
| Chromium has traditionally taken extensions through capabilities, applied when the session is | ||
| created. Branded production Chrome stopped honoring that path in Chrome 137; Chrome for Testing and | ||
| unbranded Chromium builds still honor it. Installing an extension after the session is created is | ||
| therefore now a requirement, not a convenience. | ||
|
|
||
| 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. | ||
|
qodo-code-review[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| 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 | | ||
|
qodo-code-review[bot] marked this conversation as resolved.
|
||
|
|
||
|
qodo-code-review[bot] marked this conversation as resolved.
Outdated
|
||
| ## Decision | ||
|
|
||
| 1. **Installing an extension is supported directly on the driver.** Every binding exposes, on the | ||
| driver instance itself, a cross-browser implementation of `installExtension` β taking an unpacked | ||
| directory, a packed archive, or base64 bytes β and `uninstallExtension`, taking the id returned by | ||
| install. It must also support any browser-specific install options the browser exposes. The | ||
| existing Firefox methods route through it when BiDi is enabled. | ||
|
|
||
| 2. **Extension installation works with default settings when BiDi is enabled.** Enabling BiDi | ||
| switches Chromium to the pipe transport and permits unsigned extensions, so installing an | ||
| extension requires no additional flags from the user, signed or not. Firefox already exposes BiDi | ||
| natively. | ||
|
|
||
| 3. **A session provides BiDi or CDP, not both.** The pipe carries BiDi but not CDP, so a BiDi | ||
| session has no CDP available. No configuration provides both β a single transport carries one or | ||
| the other β so a caller who needs CDP does not enable BiDi. | ||
|
|
||
| ## Considered options | ||
|
|
||
| **Where the method lives** | ||
| - **On the driver instance** (Accepted) β an installed extension is session state, and the driver is | ||
| the object every binding already hands the user; it is the one shape that works identically in all | ||
| five bindings. | ||
| - **Keep pointing users at the BiDi module** (Rejected) β the status quo, and what we advertise | ||
| today. Per [ADR 17670](17670-bidi-implementation-boundaries.md) that module is the internal implementation: protocol-shaped, outside the | ||
| deprecation policy, and reached differently in every binding β direct construction in Java, an | ||
| accessor in Python, a protocol class in Ruby, through a BiDi object in .NET, and absent in | ||
| JavaScript. It also makes users know which protocol services the command in order to use it. | ||
|
titusfortner marked this conversation as resolved.
Outdated
|
||
| - **A dedicated `extensions` namespace on the driver** (Rejected) β the strongest alternative: | ||
| consistent with the high-level `network` / `script` surfaces, available in every binding, and with | ||
| room for later operations such as listing or enabling. Rejected because the surface is two verbs, | ||
| which does not earn the extra indirection, and a flat method matches the Firefox install methods | ||
| it replaces. Worth revisiting if extension operations grow. | ||
|
|
||
| **Naming of the method** | ||
| - **Extend `install_addon` cross-browser** (Rejected) β "add-on" is Mozilla terminology that | ||
| misleads for Chromium extensions, and the classic name already differs across bindings, so there | ||
| is no single name to preserve. | ||
| - **`installWebExtension`** (Rejected) β "web extension" is the BiDi module's noun; per ADR 17670 the | ||
| supported surface should not mirror the protocol. | ||
| - **`installExtension` / "extension" concept** (Accepted) β neutral, the word users say, and matches | ||
| Java's existing `installExtension`. | ||
|
|
||
| **Signed and unsigned extensions** | ||
|
|
||
| Signed extensions install over the pipe with no additional browser flags. Unsigned extensions β the | ||
| common case for locally built or test extensions β additionally require Chrome's unsigned-extension | ||
| flag, which must be set when the browser launches and cannot be added once the session is running. | ||
|
titusfortner marked this conversation as resolved.
Outdated
|
||
|
|
||
| - **Allow unsigned extensions by default** (Accepted) β automation routinely loads locally built, | ||
| unsigned extensions, and the flag has no effect unless the user's own code installs one. Because | ||
| it must be set at launch, an opt-in would force the choice before the user knows whether they will | ||
| need it, turning a late discovery into a session restart. | ||
| - **Leave unsigned extensions to an explicit opt-in** (Rejected) β a more conservative browser | ||
| posture, but the restriction exists to stop malicious software installing extensions into a | ||
| browser someone actually browses with. That does not describe a session the automation itself | ||
| launched, so the opt-in adds a step without buying protection. | ||
|
|
||
| **Default transport** | ||
| - **Keep the port as the default** (Rejected) β the method would then require the user to pass | ||
| specific Chrome flags before it works at all, which is the friction this decision exists to | ||
| remove. | ||
| - **Switch to the pipe when BiDi is enabled** (Accepted). | ||
|
|
||
| ## Consequences | ||
|
|
||
| - **Extensions can be added and removed mid-session**, on both Firefox and Chromium, without | ||
| preparing capabilities before the session starts. | ||
| - **CDP is unavailable in a BiDi session.** Users who need `execute_cdp_cmd`, port-based DevTools, | ||
| or Grid `se:cdp` do not enable BiDi for that session. This needs a release-note call-out. | ||
| - **Chromium BiDi sessions are more secure** β no localhost CDP control port for other local | ||
| processes to attach to. The gain is largest on shared, containerized, or CI Grid nodes. | ||
| - **Grid needs no code changes.** BiDi over Grid is proxied through chromedriver's own | ||
| `webSocketUrl` and never used the CDP port; with the pipe, a session that would have exposed | ||
| `se:cdp` simply comes back BiDi-only. | ||
| - **Firefox is unaffected by the transport change** β it exposes BiDi natively and gates nothing on | ||
| transport. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.