#8007: Firefox browser support
stale
Cluster:
Browser Enhancements and Fixes
## Summary
- Add Firefox browser support via Playwright's **juggler** protocol, enabling Firefox as an alternative browser engine for browser profiles
- Introduce `engine: "firefox"` config option for browser profiles alongside the existing Chromium path
- Auto-resolve Playwright's bundled (juggler‑patched) Firefox from the Playwright cache, since stock Firefox doesn't support the juggler protocol
## Changes
### New files
- `src/browser/firefox.ts` — Firefox lifecycle management (launch, stop, reachability check) using Playwright persistent contexts
- `src/browser/firefox.executables.ts` — Cross-platform Firefox executable discovery (macOS, Linux, Windows) with Playwright cache scanner
- `src/browser/firefox.executables.test.ts` — Tests for executable resolution across all platforms
- `src/browser/firefox.test.ts` — Basic Firefox helper tests
- `src/browser/browser-process.ts` — Shared `RunningBrowser` type extracted to support both Chrome and Firefox engines
### Modified files
- `src/browser/config.ts` / `config.test.ts` — Add `engine` field to `ResolvedBrowserProfile`; validate that Firefox rejects the extension driver
- `src/browser/profiles-service.ts` / `profiles-service.test.ts` — Persist `engine` field on profile creation; reject Firefox + extension driver combo
- `src/config/types.browser.ts` — Add `engine` to `BrowserProfileConfig`
- `src/config/zod-schema.ts` — Add `engine` to the config schema validation
- `src/browser/server-context.ts` — Wire Firefox launch/stop/reachability into the profile lifecycle (launch, tab management, focus, close, status)
- `src/browser/pw-session.ts` — Firefox page routing:
- Synthetic tab IDs (`ff-1`, `ff-2`, …) since Firefox lacks CDP target IDs
- Firefox code paths for:
- `getPageForTargetId`
- `listPagesViaPlaywright`
- `createPageViaPlaywright`
- `closePageByTargetIdViaPlaywright`
- `focusPageByTargetIdViaPlaywright`
- `src/browser/pw-tools-core.snapshot.ts` — Firefox fallback for ARIA snapshots (Playwright `ariaSnapshot()` instead of CDP `Accessibility.enable`)
- `src/browser/pw-tools-core.state.ts` — Guard CDP-only features (locale/timezone override, device UA emulation) for Firefox; viewport works cross‑browser
- `src/browser/pw-tools-core.interactions.ts` — Thread `engine` / `profileName` through interaction tool functions
- `src/browser/routes/agent.act.ts`
- `src/browser/routes/agent.snapshot.ts`
- `src/browser/routes/agent.storage.ts`
- `src/browser/routes/agent.debug.ts`
- `src/browser/routes/basic.ts` — Pass engine options through all route handlers
- `docs/tools/browser.md` — Add Docker Playwright install instructions
## Design decisions
- **Playwright persistent context**
Firefox doesn't expose CDP, so we use `firefox.launchPersistentContext()` to manage the browser lifecycle. This provides a `BrowserContext` that stays alive for the session.
- **Synthetic tab IDs**
Since Firefox has no CDP target IDs, we assign stable synthetic IDs (`ff-1`, `ff-2`, …) via a `WeakMap` so tab references work consistently across tool calls.
- **Bundled Firefox preferred**
Playwright's bundled Firefox includes juggler patches required for automation. The resolver checks `~/.cache/ms-playwright` (or `PLAYWRIGHT_BROWSERS_PATH`) first, falling back to system Firefox installs.
- **Extension driver guard**
The Chrome extension relay is inherently Chromium-only, so `engine: "firefox"` + `driver: "extension"` is rejected at config resolution and profile creation time.
- **Graceful degradation**
CDP-only features (locale override, timezone override, device UA spoofing) throw a clear error for Firefox profiles; viewport and most Playwright-level operations work cross‑browser.
## Test plan
- Unit tests for Firefox executable discovery across macOS/Linux/Windows
- Unit tests for config resolution with `engine: "firefox"`
- Unit tests for profile creation with Firefox engine (and rejection of Firefox + extension)
- Unit tests for Firefox helper functions (`isFirefoxReachable`, `getFirefoxContext`)
- Manual: create a Firefox profile (`openclaw config set browser.profiles.ff.engine firefox`), launch, navigate, take snapshots
- Manual: verify Playwright-bundled Firefox is preferred over system Firefox
- Manual: verify extension driver + Firefox is rejected with a clear error
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR adds a new `engine` setting for browser profiles (`chromium` default, `firefox` optional) and wires Firefox support through the browser server lifecycle. Firefox is implemented using Playwright’s `firefox.launchPersistentContext()` and a per-profile in-memory context map, with synthetic `ff-*` tab IDs to replace CDP target IDs. Tooling is updated to route page/tab operations through Playwright for Firefox (and for remote profiles), and CDP-only features are guarded/disabled for Firefox.
Key areas touched:
- Profile config/schema/type updates (`engine` + validation rejecting `firefox` + `extension`).
- New Firefox launch/stop/reachability helpers and executable discovery with Playwright cache scanning.
- `server-context` integration for launching and tab ops.
- `pw-session` Playwright tab listing/creation/close/focus updated to support Firefox contexts and synthetic IDs.
- Snapshot/state tools updated to handle Firefox limitations (no CDP).
<h3>Confidence Score: 3/5</h3>
- This PR is generally safe to merge, but Firefox support has a few correctness gaps that will likely surface in real usage.
- The overall integration approach is consistent (engine flag, Playwright persistent context, synthetic tab IDs), and tests cover several paths, but there are some concrete issues: Windows Playwright-cache Firefox resolution is missing, `stopOpenClawFirefox` can pick the wrong profile due to substring matching, and Firefox ARIA snapshot refs are not wired into the existing ref-resolution mechanism. These can lead to using the wrong browser binary, stopping the wrong session, or producing unusable refs for later actions.
- src/browser/firefox.executables.ts, src/browser/firefox.ts, src/browser/pw-tools-core.snapshot.ts, src/browser/browser-process.ts
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#14944: fix(browser): prefer openclaw profile in headless/noSandbox environ...
by BenediktSchackenberg · 2026-02-12
74.6%
#23363: Browser: fallback to managed profile and improve runtime diagnostics
by isdoho · 2026-02-22
74.4%
#15304: feat(browser): add browser-use no-relay profile driver
by rajanrengasamy · 2026-02-13
74.2%
#10367: CLI/Ops: resilient browser fill + failover hardening + operations t...
by cluster2600 · 2026-02-06
73.5%
#19823: fix(browser): stability improvements for headless Chrome
by Milofax · 2026-02-18
72.8%
#11692: feat: add standalone browser CLI for Linux users
by shipitirl · 2026-02-08
72.7%
#12378: feat: expose download and waitForDownload actions in browser agent ...
by vabole · 2026-02-09
72.4%
#9020: fix(browser): skip port ownership and WS reachability checks for re...
by yepack · 2026-02-04
72.3%
#4663: fix: per-profile browser caching to allow parallel connections
by tsukhani · 2026-01-30
72.1%
#20207: fix(browser): use profile driver instead of hardcoded name
by mahsumaktas · 2026-02-18
72.1%