Repository navigation
[Regression]: Firefox cannot move focus from an iframe to its parent since r1553 #43280
Description
Last Good Version
1.63.0. With the Playwright client fixed at 1.64.0, Firefox browser build r1552 is the last good build in the bisect below.
First Bad Version
1.64.0, the latest stable release as of October 11, 2026. Firefox browser build r1553 is the first bad build.
Steps to reproduce
Minimal reproduction: https://github.com/diegohaz/playwright-firefox-iframe-focus-repro
git clone https://github.com/diegohaz/playwright-firefox-iframe-focus-repro.git
cd playwright-firefox-iframe-focus-repro
npm ci
npx playwright install firefox
npm test
npm test -- --headedThe repository has one test file, plain HTML, and only @playwright/test as a dependency. Each test gets a fresh page. It checks three ways to move focus from an iframe back to its parent:
- Click the iframe input, then press Tab to reach the next parent input.
- In a button's click handler, focus the iframe input, then call
parentInput.focus({ preventScroll: true })in a microtask. - Repeat the second case with
iframe.contentWindow.focus()before focusing the iframe input.
Expected behavior
The parent input receives focus, and expect(page.locator('#outer')).toBeFocused() passes in all three tests, in both headed and headless mode.
Actual behavior
All three tests fail in both modes with Playwright 1.64.0's bundled Firefox r1555 (Firefox 157.0):
Error: expect(locator).toBeFocused() failed
Locator: locator('#outer')
Expected: focused
Received: inactive
In the original diagnostic fixture, the parent document's activeElement remains the iframe instead of the parent input. The failure also occurs with native DOM .focus() calls; it does not require Playwright keyboard input.
This surfaced while updating Ariakit's browser tests in ariakit/ariakit#7847. It prevents tests from exercising focus restoration and keyboard navigation after focus enters an iframe. The reproduction does not use Ariakit or any other UI library.
Additional context
I bisected the official macOS arm64 Firefox archives while keeping the Playwright client at 1.64.0 and changing only executablePath:
| Firefox build | Firefox version | Headless | Headed |
|---|---|---|---|
| r1551 | 156.0 | 3 pass | 3 pass |
| r1552 | 156.0 | 3 pass | 3 pass |
| r1553 | 156.0 | 3 fail | 3 fail |
| r1555 (bundled with 1.64.0) | 157.0 | 3 fail | 3 fail |
Last good: r1552. First bad: r1553. #42958 rolled Firefox from r1551 to r1553 and changed its focus-emulation patches. All three builds around the boundary use Firefox 156.0, so this boundary does not coincide with a Firefox version change.
The reproduction README includes the archive URLs and commands to repeat the bisect. The original per-scenario results and archive SHA-256 checksums are also included. The smaller test file published in the repository was checked separately against all four builds in both modes, with retries disabled.
Playwright 1.63.0's bundled Firefox r1543 (Firefox 155.0) passed all three original scenarios in both modes. A manual plain-HTML test with the same focus sequences also passed in ordinary Firefox 157.0.1 on the release channel, on this Mac. Updating the bundled Firefox version from 156 to 157 did not remove the regression.
The focus-emulation changes in #42958 are a likely cause. In particular, ShouldOverrideHasFocus() now inherits the root docshell's override, which is also used by the existing early return in nsFocusManager::BlurImpl when leaving a document. This is a source-level hypothesis; I have not confirmed the exact C++ change with a revert and rebuild. Related context: #42933 and its regression test in #42969.
Environment
The output of npx envinfo --preset playwright is below, with home-directory paths shortened to ~. The tests used Node.js 24.21.0.
System:
OS: macOS 26.6.2
CPU: (14) arm64 Apple M4 Pro
Memory: 2.97 GB / 48.00 GB
Binaries:
Node: 24.21.0 - ~/.codex/worktrees/a9b9/ariakit/node_modules/.pnpm/node@runtime+24.21.0/node_modules/node/bin/node
npm: 12.0.1 - ~/Library/pnpm/bin/npm
pnpm: 12.3.0 - ~/Library/pnpm/bin/pnpm
bun: 1.0.0 - ~/.bun/bin/bun
IDEs:
VSCode: 1.137.0 - /usr/local/bin/code
Cursor: 3.20.17 - /usr/local/bin/cursor
Claude Code: 2.1.296 - ~/.local/bin/claude
Codex: 0.160.0 - ~/.local/bin/codex
Languages:
Bash: 3.2.57 - /bin/bash
npmPackages:
@playwright/test: 1.64.0 => 1.64.0
Activity
pavelfeldman commentedon Oct 12, 2026
Hi, the Playwright bot looked into this on my behalf and reproduced it locally. Thanks for the bisect, it points at the right change.
Reproduced on tip-of-tree (sha
d9f2fd3, Firefox r1559), Firefox only, headless and headed. All three of your scenarios fail: Tab from the iframe input, a programmaticfocus()after focusing the iframe input, and the same from a click handler. Chromium passes all three.The cause is in our Firefox focus-emulation patch, as you suspected.
nsFocusManager::BlurImplreturns early, without blurring, when a document is being left andShouldOverrideHasFocus()is true; that's how we keep the page focused when another page takes focus. In r1553,ShouldOverrideHasFocus()started returning the root docshell's override (for #42933, so iframes inside an emulated-focused page report focus). Moving focus from an iframe to its parent also "leaves" the iframe's document, so for any frame the early return now fires and focus stays in the iframe. The fix belongs in the Firefox patches: theBlurImplearly return should only apply when focus leaves the page, not when it moves between frames of the same page. #42969's test has to keep passing alongside it.Minimal repro
What the bot ran
d9f2fd3e2, Linux; Firefox r1559 (157.0), Chromium 156, WebKit 27.2#outereither, which looks like a separate WebKit difference and wasn't investigated furtherbrowser_patches/firefox/patches/bootstrap.diff,nsDocShell::ShouldOverrideHasFocus()now returnsGetRootDocShell()->mOverrideHasFocus(changed in feat(firefox): roll to r1553 #42958), used by the early return innsFocusManager::BlurImplwhenaIsLeavingDocumentis setInvestigated locally by the Playwright bot