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 |
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.
permanent and allowPrivateBrowsing). The implementation must work with the Grid. The method returns a WebExtension object which wraps the id.WebExtension object rather than a raw id.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.
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.
These are the alternatives considered and not taken; the accepted choice is the decision above.
Where the method lives
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 directlyConditional availability
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
webExtension.uninstall() — out of scope for nowThe legacy installAddon methods
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 methodinstallAddon 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