Current package version
playwright 1.61.1
🚀 Feature Request
Chrome 150 now exposes the real browser-UI state of tabs through CDP. I would like Playwright to expose a read-only way to answer questions such as:
- Which tab is active in each Chrome window?
- Where is a tab in the tab strip?
- Is it pinned or grouped?
- Which Chrome window contains it?
- Which Playwright
Page or pages belong to that tab?
This is especially useful when Playwright is connected to a user-owned browser, or when a human and automation coexist in the same headed browser. It is also useful for MCP/browser-agent integrations, where assuming that the newest page is the foreground page is unreliable.
This request is specifically about reading the real Chrome tab-strip state. It is separate from:
- activating a tab with
page.bringToFront() / Target.activateTarget;
- raising or focusing the native Chrome application/window at the OS level;
- emulating DOM focus with
Emulation.setFocusEmulationEnabled; and
- disabling renderer throttling for background or occluded pages.
Why this was not possible before
Historically, CDP only exposed page/renderer targets. It did not expose the browser UI metadata needed to know which tab a human was actually viewing.
In particular:
- The order returned by
Target.getTargets is not tab-strip order or focus history.
- The most recently created page is not necessarily the active tab.
document.hasFocus(), document.visibilityState, focus/blur listeners, and injected scripts are renderer-level signals rather than authoritative tab-strip state.
- Calling
page.bringToFront() mutates state, may steal user focus, and cannot be used as a read operation.
- A browser extension can query
chrome.tabs, but requiring an extension is not a general Playwright solution.
Playwright also intentionally makes Chromium pages behave as focused by sending:
Emulation.setFocusEmulationEnabled({ enabled: true })
That behavior was introduced to make focus/blur events and automation deterministic and to avoid the timer, RAF, rendering, and screenshot problems associated with background pages. As a result, page-JavaScript checks cannot reliably identify the real foreground tab in a Playwright-controlled browser.
Several previous issues therefore correctly concluded that Playwright could not answer this question at the time.
What changed in Chrome 150
Chrome 150, now in the Stable channel, adds an embedderData object to Target.TargetInfo for targets of type "tab".
A client can now request tab targets directly:
const { targetInfos } = await cdp.send("Target.getTargets", {
filter: [
{ type: "tab", exclude: false },
{ exclude: true },
],
});
Each Chrome tab target can include:
type ChromeTabEmbedderData = {
tabStripIndex: number;
tabActive: boolean;
tabPinned: boolean;
tabGroupId?: string;
};
The existing browserContextId identifies the browser context, and Browser.getWindowForTarget can return the containing Chrome window ID.
Playwright current Chromium protocol types already include the new TargetInfo.embedderData field as a generic object, so the protocol definition has reached the repository. The remaining work is to consume the tab targets, associate them with Playwright pages, and expose an appropriate public API.
The Chromium implementation and background are documented here:
A few protocol details:
- This metadata belongs to
"tab" targets, not "page" targets, a tab can contain multiple page targets because of Chromium's multi-page architecture, so implementations should not assume a one-tab-to-one-page relationship.
Target.autoAttachRelated can be used to discover the page targets related to a tab target.
- The result is currently pull-based. Chrome does not emit new
Target.targetInfoChanged events when only this tab metadata changes.
tabActive means selected in its containing Chrome window. It does not by itself say that the native Chrome window is the OS foreground window.
Possible Playwright API shapes
I do not have a strong preference about the exact public shape. A few possibilities:
1. Minimal page query
await page.isActive(): Promise<boolean | null>
null could mean unavailable on this browser/version or that the page cannot be mapped to a tab.
2. Page tab snapshot
type TabInfo = {
active: boolean;
index: number;
pinned: boolean;
groupId: string | null;
windowId?: string;
};
await page.tabInfo(): Promise<TabInfo | null>
Making this asynchronous communicates that it is a current snapshot rather than a live handle.
3. Browser-context tab inventory
const tabs = await context.tabs();
for (const tab of tabs) {
console.log(
tab.active,
tab.index,
tab.pinned,
tab.groupId,
tab.windowId,
tab.pages(),
);
}
This may fit the underlying model best because a Chrome tab is a browser-UI container and can own multiple page targets.
4. Chromium-specific API first
If a cross-browser Playwright API is premature, this could initially be exposed as a Chromium-specific/experimental API while preserving a shape that Firefox and WebKit could implement later if equivalent browser-level data becomes available.
For Chrome versions before 150 and for embedders that do not provide the metadata, the API could return null, omit tab metadata, or throw a clearly documented unsupported-operation error.
Interaction with Playwright focus emulation
Reading tabActive does not require Playwright to remove its existing focus emulation.
A low-risk first implementation could:
- preserve
Emulation.setFocusEmulationEnabled({enabled: true}) by default;
- expose the real tab-strip state independently through
tabInfo(), isActive(), or context.tabs();
- avoid changing
document.hasFocus(), document.visibilityState, timer throttling, or existing test behavior; and
- keep
page.bringToFront() as an explicit mutating operation.
Separately, Playwright could eventually offer an opt-in such as a context option or page method to disable focus emulation for tests that specifically need real blur/visibility/background behavior. That would solve a different part of the older requests and would need careful handling because it reintroduces browser throttling and rendering differences.
In other words, exposing real tab metadata can be implemented without changing Playwright's deterministic focus defaults. Real focus/background emulation can remain a separate design decision.
Related issues and PRs
Reading the active/user-selected tab
Background, visibility, and focus behavior
Activation and native-window focus
The new Chrome metadata directly addresses the read/query side of #31890, #8090, and part of #3570. It does not by itself solve background-state emulation or OS-level focus stealing, but it lets Playwright stop relying on guesses or page-JavaScript heuristics when it needs to know the real foreground tab.
Motivation
Playwright is increasingly used beyond QA in hybrid environments where automation, browser agents, MCP clients, and humans share a headed browser. In those environments, reliable foreground-tab tracking is essential to avoid working on the wrong page. This is especially important for https://github.com/microsoft/Webwright and https://github.com/microsoft/playwright-mcp where agent focus and browser tab focus often need to be kept in sync so the user can follow what an agent is doing.
Current package version
playwright1.61.1🚀 Feature Request
Chrome 150 now exposes the real browser-UI state of tabs through CDP. I would like Playwright to expose a read-only way to answer questions such as:
Pageor pages belong to that tab?This is especially useful when Playwright is connected to a user-owned browser, or when a human and automation coexist in the same headed browser. It is also useful for MCP/browser-agent integrations, where assuming that the newest page is the foreground page is unreliable.
This request is specifically about reading the real Chrome tab-strip state. It is separate from:
page.bringToFront()/Target.activateTarget;Emulation.setFocusEmulationEnabled; andWhy this was not possible before
Historically, CDP only exposed page/renderer targets. It did not expose the browser UI metadata needed to know which tab a human was actually viewing.
In particular:
Target.getTargetsis not tab-strip order or focus history.document.hasFocus(),document.visibilityState, focus/blur listeners, and injected scripts are renderer-level signals rather than authoritative tab-strip state.page.bringToFront()mutates state, may steal user focus, and cannot be used as a read operation.chrome.tabs, but requiring an extension is not a general Playwright solution.Playwright also intentionally makes Chromium pages behave as focused by sending:
That behavior was introduced to make focus/blur events and automation deterministic and to avoid the timer, RAF, rendering, and screenshot problems associated with background pages. As a result, page-JavaScript checks cannot reliably identify the real foreground tab in a Playwright-controlled browser.
Several previous issues therefore correctly concluded that Playwright could not answer this question at the time.
What changed in Chrome 150
Chrome 150, now in the Stable channel, adds an
embedderDataobject toTarget.TargetInfofor targets of type"tab".A client can now request tab targets directly:
Each Chrome tab target can include:
The existing
browserContextIdidentifies the browser context, andBrowser.getWindowForTargetcan return the containing Chrome window ID.Playwright current Chromium protocol types already include the new
TargetInfo.embedderDatafield as a generic object, so the protocol definition has reached the repository. The remaining work is to consume the tab targets, associate them with Playwright pages, and expose an appropriate public API.The Chromium implementation and background are documented here:
A few protocol details:
"tab"targets, not"page"targets, atabcan contain multiplepagetargets because of Chromium's multi-page architecture, so implementations should not assume a one-tab-to-one-page relationship.Target.autoAttachRelatedcan be used to discover the page targets related to a tab target.Target.targetInfoChangedevents when only this tab metadata changes.tabActivemeans selected in its containing Chrome window. It does not by itself say that the native Chrome window is the OS foreground window.Possible Playwright API shapes
I do not have a strong preference about the exact public shape. A few possibilities:
1. Minimal page query
nullcould mean unavailable on this browser/version or that the page cannot be mapped to a tab.2. Page tab snapshot
Making this asynchronous communicates that it is a current snapshot rather than a live handle.
3. Browser-context tab inventory
This may fit the underlying model best because a Chrome tab is a browser-UI container and can own multiple page targets.
4. Chromium-specific API first
If a cross-browser Playwright API is premature, this could initially be exposed as a Chromium-specific/experimental API while preserving a shape that Firefox and WebKit could implement later if equivalent browser-level data becomes available.
For Chrome versions before 150 and for embedders that do not provide the metadata, the API could return
null, omit tab metadata, or throw a clearly documented unsupported-operation error.Interaction with Playwright focus emulation
Reading
tabActivedoes not require Playwright to remove its existing focus emulation.A low-risk first implementation could:
Emulation.setFocusEmulationEnabled({enabled: true})by default;tabInfo(),isActive(), orcontext.tabs();document.hasFocus(),document.visibilityState, timer throttling, or existing test behavior; andpage.bringToFront()as an explicit mutating operation.Separately, Playwright could eventually offer an opt-in such as a context option or page method to disable focus emulation for tests that specifically need real blur/visibility/background behavior. That would solve a different part of the older requests and would need careful handling because it reintroduces browser throttling and rendering differences.
In other words, exposing real tab metadata can be implemented without changing Playwright's deterministic focus defaults. Real focus/background emulation can remain a separate design decision.
Related issues and PRs
Reading the active/user-selected tab
page.isActive()visibilitychangeBackground, visibility, and focus behavior
document.visibilityStatedocument.hasFocus()behavior under forced focus emulationActivation and native-window focus
page.bringToFront()createPagesInBackgroundoption, closed unmergedThe new Chrome metadata directly addresses the read/query side of #31890, #8090, and part of #3570. It does not by itself solve background-state emulation or OS-level focus stealing, but it lets Playwright stop relying on guesses or page-JavaScript heuristics when it needs to know the real foreground tab.
Motivation
Playwright is increasingly used beyond QA in hybrid environments where automation, browser agents, MCP clients, and humans share a headed browser. In those environments, reliable foreground-tab tracking is essential to avoid working on the wrong page. This is especially important for https://github.com/microsoft/Webwright and https://github.com/microsoft/playwright-mcp where agent focus and browser tab focus often need to be kept in sync so the user can follow what an agent is doing.