[BUG] Claude in Chrome: session tabs are created in the background and never activated, so they stay visibilityState "hidden" and screenshots of embedded apps/SPAs come back blank (macOS; same mechanism as #78533)
What's Wrong?
Claude in Chrome creates every Claude Code session's tab as a background tab and never brings it forward. A background tab reports document.visibilityState === "hidden", so its render loop does not run. Page.captureScreenshot then produces a single forced frame. That is enough for simple pages. Apps that need many frames, or that wait until they see visible, come back blank or half-drawn while the tool reports success: Shopify admin embedded apps (a cross-origin app-iframe), Google Business Profile editor child views, and SPA shells.
This is the mechanism reported for Windows in #78533 (closed by the inactivity bot on 2026-09-05, not fixed). Here it is on macOS, traced to the extension's source.
Evidence (extension 1.0.94, read from the installed assets/mcpPermissions-*.js):
- The session group is created with
chrome.windows.getLastFocused({windowTypes:["normal"]})and thenchrome.tabs.create({windowId, url:"chrome://newtab/", active:false}). tabs_create_mcpalso usesactive:false.- No MCP tool calls
chrome.tabs.update({active:true}),chrome.windows.update({focused:true}),Page.bringToFrontorEmulation.setFocusEmulationEnabled. - Screenshots use
Page.captureScreenshot {fromSurface:true, captureBeyondViewport:false}. The extension already recordstarget_visibility_stateper capture, so the hidden rate should be visible in your telemetry.
Why one frame (Chrome 153.0.8010.54 source): content/browser/devtools/protocol/page_handler.cc ~L1426 calls IncrementCapturerCount(..., /*stay_hidden=*/true, ...) only for the duration of the capture ("hidden but painting"). The page keeps reporting hidden and does not run its own frames.
Measured on macOS 26.5.2, Chrome 153.0.8010.54, Claude Code 2.1.283:
| tab state | visibilityState | rAF callbacks in 1.5 s | Shopify embedded app (Messaging) |
|---|---|---|---|
| MCP tab as created (background) | hidden | 0 | frame present (960x536) but blank |
| same tab made the active tab of its window | visible | ~90 | fully drawn |
The same happens with the screen unlocked and the user at the computer. Any other app window covering Chrome, or a screen lock, makes it worse. On macOS, Chrome also marks a fully covered window's active tab as occluded. Only --disable-backgrounding-occluded-windows undoes that, and that switch never affects a background tab.
What Should Happen?
Screenshots and page rendering in the MCP tab should work while the user is using another app or another tab. Suggested fix, in order of preference:
- While a session is driving a tab, call
Emulation.setFocusEmulationEnabled({enabled:true})on it through the existingchrome.debuggersession. In Chrome this callsIncrementCapturerCount(..., /*stay_hidden=*/false, ...)(emulation_handler.cc~L1009). The tab then reportsvisibleand renders continuously without stealing the user's active tab or focus. - Or offer an option to create the session group in its own window with the MCP tab active, which the legacy single-group path already does (
chrome.windows.create({focused:true})). - When a capture's
visibilityStateishidden, say so in the tool result instead of returning a silent success.
Error Messages/Logs
No error. The capture reports success and returns a flat or half-drawn image.
Steps to Reproduce
- On macOS, leave a normal Chrome window open with one tab showing, and connect Claude in Chrome to Claude Code.
- From Claude Code:
tabs_context_mcp {createIfEmpty:true}. The session tab is created in that window as a background tab. - Navigate it to a Shopify admin embedded app page (e.g.
/store/<store>/apps/shopify-messaging). javascript_tool:document.visibilityState→"hidden"; countrequestAnimationFramecallbacks over 1.5 s →0.computer {action:"screenshot"}→ the admin shell with an empty app area.- Make the tab the active tab of its window (click it, or AppleScript
set active tab index). Repeat steps 4–5 →"visible", ~90 callbacks, and the app is fully drawn.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.283 (Claude Code); Claude in Chrome extension 1.0.94
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other
Additional Information
Workaround we use: a script brings the MCP tab to the front of its window through AppleScript (set active tab index). The AppleScript tab id is the same number as the extension's tab id. Chrome is also launched with --disable-backgrounding-occluded-windows, so the tab stays visible while another app covers Chrome. Both belong in the extension, not in each user's setup.
3 Comments
Same on Linux.
--chromeReproduction (30 Sep 2026):
tabs_context_mcpwithcreateIfEmpty: true, thennavigateto an Odoo record URL.document.visibilityStateis"hidden",document.hasFocus()isfalse, and arequestAnimationFramecounter records 0 callbacks in 1.5 s.document.body.children.lengthis 0,get_page_textreturns no text,findsees only one generic element.window.outerWidth/outerHeightreport 0 x 0 for the window the extension created.Page.captureScreenshot), as in #78533; today, with the window at full size, they returned at once.Page.captureScreenshoton the hidden tab draws one frame, and that lets the page advance: with the window at full size and the tab hidden (0 animation-frame callbacks), a click on a button rendered nothing until one screenshot, after which the new dialog was in the DOM. So agents can work around it with a screenshot after every action, which is slow and costs a screenshot per step.Bringing the window to the front makes the page render at once. The extension's bundle has no
Emulation.setFocusEmulationEnabledcall; enabling focus emulation on the session's debugger target (or creating the tab active in its own window) would let the agent work without the user switching windows.Follow-up to my Linux comment above: I tested suggested fix 1 (focus emulation), and it works.
Setup: Linux Mint, Cinnamon on X11, Chrome 154.0.8037.92. A separate Chrome with a fresh profile and no extra flags, driven over
--remote-debugging-port. Each row countsrequestAnimationFramecallbacks in 1.5 s.| tab | visibilityState | rAF in 1.5 s |
|---|---|---|
| background tab (created with
Target.createTarget {background: true}) | hidden | 0 || same, with
--disable-renderer-backgrounding,--disable-backgrounding-occluded-windowsor--disable-background-timer-throttling(each alone, and all three) | hidden | 0 || same tab after
Emulation.setFocusEmulationEnabled {enabled: true}| visible | 47 || same tab after the connection that sent it is closed | hidden | 0 |
| active tab of a minimized window | hidden | 0 |
| active tab of a minimized window, with focus emulation | visible | 47 |
| active tab of a window fully covered by another window (no flags) | visible | 47 |
So on Linux no launch switch helps a background tab. Focus emulation does, for a background tab and for a minimized window. It lasts as long as the debugger session that enabled it stays attached, which fits how the extension already holds
chrome.debuggeron its tab.The Chrome DevTools MCP server (chrome-devtools-mcp 1.10.1) already does this:
McpPage.init()callsemulateFocusedPage(true)on every page, with the comment "We emulate a focused page for all pages to support multi-agent workflows." With it, an Odoo 19 task opened in a background tab (new_pagewithbackground: true) rendered the full form and chatter with no screenshot. On the same machine, in my normal Chrome, the Claude in Chrome tab (extension 1.0.97,tabs_context_mcp {createIfEmpty: true}, thennavigateto the same task) stayedhiddenwith 0 callbacks and an empty body.Confirming on Windows 11 + Microsoft Edge 154.0.4258.62, extension v1.0.99.
In a session driven from the Claude desktop app, the MCP tab reported
document.hidden === trueanddocument.visibilityState === "hidden"right after being opened withtabs_create_mcp/navigate, with the window in the background. Even so,javascript_tool,read_page,find,get_page_text, clicks, typing,form_inputand screenshots on a simple page all returned correct results. In a separate session ascreenshotcall on a background tab failed withScript injection timed out after 5000ms — the page is busy or mid-navigation ....So the hidden state shows up on Windows/Edge too, and the 5000 ms injection timeout is one more way it surfaces besides the blank frames and 30 s capture timeouts described above. This is consistent with #78533 (closed by the inactivity bot, not fixed). The suggested fix of enabling focus emulation (or activating the tab) for the tab a session is driving looks right to me. Failing that, the tool result should say that the tab is hidden instead of only reporting a timeout.