Skip to content

fix(targets): read tab URLs from Target.getTargets, not the renderer - #1

Merged
dovidgef merged 3 commits into
masterfrom
fix/tab-enumeration-sleeping-renderers
Aug 20, 2026
Merged

dovidgef merged 3 commits into
masterfrom
fix/tab-enumeration-sleeping-renderers

Conversation

@dovidgef

Copy link
Copy Markdown
Owner

Problem

Tab enumeration read each tab's URL via await tab.current_url, which evaluates location.href inside the page. Browsers that sleep background tabs (Wavebox, Edge, Chrome Memory Saver) discard the renderer while leaving the target attachable — so that evaluate is accepted and never answered. It burns the full 60s per-command timeout, sequentially, for every sleeping tab before selection can reach a live one.

Measured on a Wavebox with 36 tabs, 11 of them slept: tabs list and session start --tab-url never completed (~11 min of stacked timeouts); --tab N hit the same sweep.

Fix

CDP already reports url and title in TargetInfo, so one browser-level Target.getTargets call replaces N renderer round-trips — faster in the healthy case too, where it was N sequential evaluates.

  • New pydoll_cli/targets.py helpers: target_info_map, tab_url, tab_title, visible_tabs_with_urls. In-page reads survive only as a time-boxed (5s) fallback for a target Target.getTargets didn't report.
  • async_runner, session, and commands/tabs all route through them, so tabs list indices stay in sync with what --tab N resolves to.

Both paths now return in ~0.5s.

Tests

tests/unit/test_tab_filtering.py gains a _SleepingTab fake whose current_url/title sleep for an hour and assert-fail if ever awaited, plus a _run_bounded helper so a regression fails instead of hanging the suite. Five new cases cover visible_tabs, --tab-url matching a slept tab, --tab-url reaching a live tab behind a slept one, _find_tab_matching, and tabs list output.

289 unit tests pass; ruff, ruff format, and mypy clean.

Also

A README note in Development: uv keys its build cache on the version string, so with __version__ unchanged uv tool install --force . re-installs the previously cached wheel and working-tree edits silently never ship. Use uv cache clean pydoll-cli && uv tool install --force --no-cache .. This bit during development of this very fix — a mid-edit snapshot of commands/tabs.py stayed installed and kept raising NameError: visible_tabs from tabs close / tabs focus long after the tree was correct.

🤖 Generated with Claude Code

dovidgef and others added 3 commits August 17, 2026 14:31
Tab enumeration read each tab's URL via `await tab.current_url`, which
evaluates `location.href` inside the page. Browsers that sleep background
tabs (Wavebox, Edge, Chrome Memory Saver) discard the renderer while
leaving the target attachable, so that evaluate is accepted and never
answered — it burns the full 60s per-command timeout, sequentially, for
every sleeping tab before selection can reach a live one.

Measured on a Wavebox with 36 tabs, 11 of them slept: `tabs list` and
`session start --tab-url` never completed (~11 min of stacked timeouts);
`--tab N` hit the same sweep. Both now return in ~0.5s.

CDP already reports url and title in TargetInfo, so one browser-level
Target.getTargets call replaces N renderer round-trips — faster in the
healthy case too, where it was N sequential evaluates.

- targets: add target_info_map / tab_url / tab_title /
  visible_tabs_with_urls; resolve from TargetInfo with a bounded 5s
  in-page fallback for targets getTargets doesn't report
- session, async_runner, commands.tabs: take URLs from the enumeration
  instead of re-reading each tab

Tabs with no resolvable URL are still dropped, so --tab N indices keep
their meaning. Sleeping tabs are not in that category — CDP reports their
URL, so they stay listed and selectable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
uv keys its build cache on the version string, so with __version__
unchanged `uv tool install --force .` re-installs the previously cached
wheel. Working-tree edits then never reach the installed console script
and it keeps raising from code that is already fixed — which is how a
mid-edit snapshot of commands/tabs.py (visible_tabs used but no longer
imported) stayed installed and broke `tabs close` / `tabs focus` long
after the tree was correct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repo keeps an [Unreleased] section between releases; this branch
shipped a user-visible fix without one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dovidgef
dovidgef merged commit e8e000d into master Aug 20, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant