-
-
Notifications
You must be signed in to change notification settings - Fork 8.7k
[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 all 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
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,92 @@ | ||
| # 17817. The driver installs web extensions directly | ||
|
|
||
| - Status: Proposed | ||
| - Discussion: https://github.com/SeleniumHQ/selenium/pull/17817 | ||
|
|
||
| ## Context | ||
|
|
||
| Firefox can install a web extension mid-session through a WebDriver-classic endpoint, but every binding | ||
| hangs it off a browser-specific type rather than the driver. | ||
|
|
||
| Chromium takes web extensions through capabilities applied at session creation. Branded Chrome stopped | ||
| honoring that path in Chrome 137 (Chrome for Testing and unbranded Chromium still do), so installing | ||
| after the session starts is now a requirement. | ||
|
|
||
| WebDriver BiDi specifies web extension install and uninstall, which both Firefox and Chromium | ||
| implement. Most 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` | `(await driver.AsBiDiAsync()).WebExtension.InstallAsync(...)` | | ||
| | JavaScript | `installAddon` | none | | ||
|
|
||
| ## Decision | ||
|
|
||
| 1. **Add two methods to the driver instance.** Every binding exposes `installWebExtension` and | ||
| `uninstallWebExtension` on the driver instance itself, not on a browser-specific type or the BiDi | ||
| module. | ||
| * **Install behavior:** accepts an archive, a directory, or base64, as well as vendor-specific options | ||
| (on Firefox, `permanent` and `allowPrivateBrowsing`). The implementation must work with the Grid. | ||
| The method returns a `WebExtension` object which wraps the id. | ||
| * **Uninstall behavior:** accepts the `WebExtension` object rather than a raw id. | ||
|
|
||
| 2. **Backwards compatible**. On Firefox these methods fall back to the WebDriver-Classic endpoint when | ||
| BiDi is not enabled, so no existing capability is lost; the existing classic install methods and | ||
| parameters are deprecated in favor of them. | ||
|
|
||
| 3. **Raise when the target cannot honor the request.** When a browser or transport cannot fulfill a | ||
| request, it raises rather than silently doing less β such as `installWebExtension` on Chromium without | ||
| BiDi. | ||
|
|
||
| ## Considered options | ||
|
|
||
| These are the alternatives considered and not taken; the accepted choice is the decision above. | ||
|
|
||
| **Where the method lives** | ||
| - **Re-implement the existing methods instead of adding a new one** β the direction the project is | ||
| generally moving is to give existing methods new behavior rather than grow the API surface. It | ||
| cannot deliver this capability on its own: the existing methods are Firefox-only and inconsistently | ||
| named, and Chromium has none to re-implement, so there is no uniform method to carry it. A new, | ||
| uniformly-named method is what makes it cross-browser. | ||
| - **A dedicated `webExtensions` namespace** β consistent with `network` / `script`, with room to grow, | ||
| but two methods make that seem excessive, especially when the precedent from Firefox is to have the | ||
| method on the driver directly | ||
|
|
||
| **Conditional availability** | ||
| - **Expose the method only where it works** β hide it on a Chromium 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. | ||
|
qodo-code-review[bot] marked this conversation as resolved.
|
||
|
|
||
| **Naming** | ||
| - **`installExtension` returning `Extension`** β shorter and reuses the name Java already ships, but | ||
| "extension" is overloaded, and reusing Java's classic name collides by return type with its | ||
| `installExtension(Path)`, forcing a distinct-parameter workaround. `installWebExtension` models the | ||
| standard "web extension" noun and keeps the deprecation uniform across all five bindings; the public | ||
| `WebExtension` type sits in its own package, separate from the internal BiDi `WebExtension` module. | ||
|
|
||
| **Return type** | ||
| - **A raw id string** β untyped; the signature would accept any string. | ||
| - **Self acting object with `webExtension.uninstall()`** β out of scope for now | ||
|
|
||
| **The legacy `installAddon` methods** | ||
| - **Redirect `installAddon` to `installWebExtension` when BiDi is enabled** β keeps the legacy name | ||
| working as an alias instead of steering users to `installWebExtension`, so the two names persist | ||
| rather than converge, but if the point is to move to a new common name we shouldn't extend the old method | ||
| - **Keep it completely separate**. `installAddon` is always classic implementation and | ||
| `installWebExtension` is always BiDi implementation. This isn't how we plan to manage other | ||
| transitions, and we want to converge on a single common method. | ||
|
|
||
| **Unsigned web extensions** | ||
| - **Require an explicit opt-in** β the browser restriction guards a profile someone actually browses with, | ||
| not an automation-launched session, so it adds a step without buying protection. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - Because the remote end may run on a different host than the client (for example a Grid node), the | ||
| implementation cannot pass a client-local filesystem path; it must deliver the extension to the remote | ||
| end and reference it in a form the remote end can resolve β inline content, or a location obtained by | ||
| uploading to the remote end first. | ||
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.