[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)

Status Open
Reported on v2.1.283
Maintainer reply None cached
Activity 3 comments · opened Sep 28, 2026

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 then chrome.tabs.create({windowId, url:"chrome://newtab/", active:false}).
  • tabs_create_mcp also uses active:false.
  • No MCP tool calls chrome.tabs.update({active:true}), chrome.windows.update({focused:true}), Page.bringToFront or Emulation.setFocusEmulationEnabled.
  • Screenshots use Page.captureScreenshot {fromSurface:true, captureBeyondViewport:false}. The extension already records target_visibility_state per 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:

  1. While a session is driving a tab, call Emulation.setFocusEmulationEnabled({enabled:true}) on it through the existing chrome.debugger session. In Chrome this calls IncrementCapturerCount(..., /*stay_hidden=*/false, ...) (emulation_handler.cc ~L1009). The tab then reports visible and renders continuously without stealing the user's active tab or focus.
  2. 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})).
  3. When a capture's visibilityState is hidden, 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

  1. On macOS, leave a normal Chrome window open with one tab showing, and connect Claude in Chrome to Claude Code.
  2. From Claude Code: tabs_context_mcp {createIfEmpty:true}. The session tab is created in that window as a background tab.
  3. Navigate it to a Shopify admin embedded app page (e.g. /store/<store>/apps/shopify-messaging).
  4. javascript_tool: document.visibilityState → "hidden"; count requestAnimationFrame callbacks over 1.5 s → 0.
  5. computer {action:"screenshot"} → the admin shell with an empty app area.
  6. 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.

View original on GitHub ↗

3 Comments

jcfernandez-890825 · 14 days ago

Same on Linux.

  • OS: Linux Mint, Cinnamon desktop on X11
  • Chrome 153.0.8010.52, Claude in Chrome extension 1.0.97, Claude Code CLI with --chrome
  • App: Odoo 19 (OWL front end)

Reproduction (30 Sep 2026):

  1. tabs_context_mcp with createIfEmpty: true, then navigate to an Odoo record URL.
  2. In the page, after 4 s: document.visibilityState is "hidden", document.hasFocus() is false, and a requestAnimationFrame counter records 0 callbacks in 1.5 s.
  3. The page never mounts: document.body.children.length is 0, get_page_text returns no text, find sees only one generic element. window.outerWidth/outerHeight report 0 x 0 for the window the extension created.
  4. On earlier days, screenshots of such tabs sometimes timed out after 30 s (Page.captureScreenshot), as in #78533; today, with the window at full size, they returned at once.
  5. Each Page.captureScreenshot on 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.setFocusEmulationEnabled call; 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.

jcfernandez-890825 · 13 days ago

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 counts requestAnimationFrame callbacks 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-windows or --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.debugger on its tab.

The Chrome DevTools MCP server (chrome-devtools-mcp 1.10.1) already does this: McpPage.init() calls emulateFocusedPage(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_page with background: 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}, then navigate to the same task) stayed hidden with 0 callbacks and an empty body.

Don-Enone · 6 days ago

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 === true and document.visibilityState === "hidden" right after being opened with tabs_create_mcp/navigate, with the window in the background. Even so, javascript_tool, read_page, find, get_page_text, clicks, typing, form_input and screenshots on a simple page all returned correct results. In a separate session a screenshot call on a background tab failed with Script 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.