Skip to content

[Feature Request]: Expose Chromium foreground-tab and tab-strip metadata (Chrome 150+) #436

Description

@pirate

Current package version

pydoll-python 2.23.1

Checklist before requesting

  • I searched existing issues and discussions and did not find a duplicate.
  • I checked the current code and documentation; this feature does not exist yet.

Problem Statement

Chrome 150 now exposes the real browser-UI state of tabs through CDP. Pydoll should provide a read-only way to answer questions such as:

  • Which tab is selected in each Chrome window?
  • What is each tab's tab-strip position?
  • Is it pinned or grouped?
  • Which Chrome window contains it?
  • Which Pydoll Tab / page target belongs to it?

This matters when Pydoll connects to a user-owned browser or shares a headed browser with a human. Today ChromiumBase.get_opened_tabs() enumerates "page" targets and returns Tab objects, but a page target is not the browser's tab UI object, and target enumeration order is not tab-strip or focus order.

Before Chrome 150, CDP did not expose authoritative browser-UI tab state. document.hasFocus(), document.visibilityState, focus/blur listeners, or scripts installed with Page.addScriptToEvaluateOnNewDocument only observe or alter renderer state. Page.bringToFront and Target.activateTarget mutate state and may also raise the native browser window; they cannot answer which tab was active without disturbing the user. Backgrounding flags affect scheduling, not tab selection.

That distinction also explains the confusion in #241: tab activation, DOM element focus, and bringing the Chrome application to the OS foreground are different operations.

Proposed Solution

Chrome 150 Stable adds Target.TargetInfo.embedderData for targets of type "tab". A client can request only those targets:

result = await connection.execute_command(
    TargetCommands.get_targets(
        filter=[
            {"type": "tab", "exclude": False},
            {"exclude": True},
        ]
    )
)

Each tab target may contain:

{
    "embedderData": {
        "tabStripIndex": 0,
        "tabActive": True,
        "tabPinned": False,
        "tabGroupId": "...",  # optional
    }
}

Pydoll's TargetInfo model currently omits embedderData, although TargetCommands.get_targets() already accepts a filter. A useful implementation could:

  1. add forward-compatible parsing for TargetInfo.embedderData and a typed TabInfo snapshot;
  2. enumerate "tab" targets and order them by tabStripIndex within each Chrome window;
  3. use Browser.getWindowForTarget for the Chrome window ID;
  4. map a tab target to its related page target(s), without assuming one tab equals one page; and
  5. expose this separately from mutating activation APIs.

Possible public shapes:

tab_info = await tab.get_tab_info()
# TabInfo(active=True, index=0, pinned=False, group_id=None, window_id=1)

tabs = await browser.get_tabs()
active_tabs = await browser.get_active_tabs()  # one selected tab per Chrome window

Another option is to evolve get_opened_tabs() so it can return tab-strip ordering and metadata, but keeping browser-UI tab targets distinct from Pydoll's current page-target-backed Tab abstraction may be less surprising.

The API should gracefully return None / unavailable metadata on Chrome versions before 150 and embedders that omit the field. Unknown future keys in embedderData should not break parsing.

Important protocol details:

  • Metadata belongs to "tab" targets, not "page" targets.
  • A tab can contain multiple page targets under Chromium's multi-page architecture. Target.autoAttachRelated can discover related targets.
  • The data is currently pull-based; metadata-only changes do not emit new Target.targetInfoChanged events.
  • tabActive means selected in its containing Chrome window. It does not mean that Chrome is the OS foreground application.

References:

Alternatives Considered

  • Treating the last result from get_opened_tabs() as current: target order is not tab-strip or focus order.
  • document.hasFocus(), visibilityState, focus events, or an injected tracking/forcing script: renderer-level and not authoritative browser UI state.
  • Page.bringToFront / Target.activateTarget: useful explicit mutations, but they steal/change focus and cannot be used for discovery.
  • Emulation.setFocusEmulationEnabled: changes renderer focus semantics; it does not report the selected tab.
  • --disable-backgrounding-occluded-windows, --disable-renderer-backgrounding, and --disable-background-timer-throttling: mitigate throttling but do not expose tab selection.
  • A Chrome extension using chrome.tabs: works in some deployments but should not be required for a CDP-native library.

Additional Context

Related Pydoll threads:

Pydoll already ships the relevant activation commands and launches Chrome with background timer/occluded-window flags in its test setup. Those mechanisms can remain unchanged. Reading tabActive should be an independent capability, so callers can observe the user's selected tab without forcing focus or changing page behavior.

Importance

Important

Contribution

I could help with parts of the implementation

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions