Skip to content

fix(browser): own child tabs, make Tab move focus, #21 follow-ups - #22

Merged
sheehanmunim merged 2 commits into
mainfrom
fix/child-tabs-and-tab-key
Oct 8, 2026
Merged

sheehanmunim merged 2 commits into
mainfrom
fix/child-tabs-and-tab-key

Conversation

@sheehanmunim

Copy link
Copy Markdown
Member

Fixes #14. Fixes #20. Follow-ups to #21 and #17.

#14: tabs the agent's clicks open

A tab whose opener is one of a task's tabs, created within 10 s of an agent click or key press in that opener, becomes the task's tab. It joins the task's group (a popup window, which cannot be grouped, stays where it is) and the task's cleanup closes it. A tab opened from an agent tab without a recent agent action stays the user's.

#20: Tab in a background tab

Chrome runs focus traversal only in a focused page, and a background agent tab never is one. The keystroke reached the page, focus stayed put, and the next browser_type went into the original field. Now, if the keystroke does not move focus and the page did not preventDefault it, the extension moves focus to the next element in sequential focus order: positive tabindex first, a radio group counts as one stop, hidden/disabled/inert elements are skipped, and a text field's contents are selected as with a real Tab. The result names the focused element. If focus cannot move, the call fails instead of reporting success.

Follow-ups to #21

  • Regression: modal scoping dropped options rendered in a portal (Radix/MUI/Headless UI selects inside a dialog). Now in scope: roots referenced by the modal's aria-controls/aria-owns, open popovers, and controls painted on top of the modal.
  • Security: a wait_for_selector such as input[type=password][value^="a"] returned met or timeout, which reveals a React-controlled password one character at a time. Selectors that test the value attribute are now refused, after undoing CSS escapes and comments.
  • null for an optional argument means absent again.
  • A transient evaluate error (context destroyed by a navigation) is retried until the deadline.
  • When an extension older than the server ignores the new arguments, both hosts say so.
  • fix(windows): exit native host on backend disconnect #17's relay exit now applies on Linux too, which had the same hang. The Swift relay already exited on EOF.
  • e2e: prompt buttons are clicked after the evaluation has answered, because the click closes the window. When no prompt answers, the error lists the live targets. The macOS "closing the sign-in window" check had been failing on main since Chrome 155.

Verified locally

  • node chrome-extension/background.test.mjs: 56 passed.
  • scripts/snapshot-dom.test.mjs against Chrome 155: all scenarios, including the new portal ones.
  • scripts/e2e-browser.mjs against Chrome for Testing 155: 18/18, three runs.
  • The Tab focus script on a real page in headless Chrome.
  • Tool parity and the tool-defs round trip pass; swift build passes.
  • Rust is not built locally; CI covers it.

🤖 Generated with Claude Code

A target=_blank link or window.open popup the agent triggers now belongs
to the task that triggered it: a tab whose opener is one of the task's
tabs, created within 10 s of an agent click or key press there, joins the
task's tabs and group and is closed by its cleanup. Tabs opened from an
agent tab without a recent agent action stay the user's. (#14)

browser_press_key Tab dispatched the keystroke, but Chrome runs focus
traversal only in a focused page, and a background agent tab never is.
Focus stayed put, the tool reported success, and the next browser_type
landed in the original field. When the keystroke does not move focus and
the page did not handle it, move focus to the next element in sequential
focus order; report where focus went, and fail when it cannot move. (#20)
- Snapshot modal scoping kept only controls inside the dialog, so a
  select or combobox whose options render in a portal at the end of
  <body> (Radix, MUI, Headless UI) lost every option. Roots the modal's
  controls point at via aria-controls/aria-owns, open popovers, and
  controls painted on top of the modal stay in scope.
- wait_for_selector refuses selectors that test the value attribute.
  React mirrors controlled inputs' values there, password fields
  included, so met/timeout answered a password one guessed character at
  a time. Escapes and comments are undone before the check.
- null for an optional argument means absent again, as it did before
  offset/limit/wait_for_selector existed.
- A transient evaluate error (a navigation destroying the context)
  polls again until the deadline instead of ending the wait.
- When an extension older than the server ignores the new arguments,
  both hosts say so and ask for a reload instead of printing nothing.
- The native-host relay exits on backend EOF on Linux too (#16): it
  blocks on Chrome's stdin the same way the Windows relay did.
- e2e: click a prompt's button after the evaluation has answered, since
  the click closes the window; report the live targets when no prompt
  answers.
- browser_click / browser_press_key / browser_close_all_tabs
  descriptions cover child tabs and Tab's focus report.
Copilot AI balanced review requested due to automatic review settings October 8, 2026 21:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sheehanmunim
sheehanmunim merged commit 9a634ef into main Oct 8, 2026
4 checks passed
@sheehanmunim
sheehanmunim deleted the fix/child-tabs-and-tab-key branch October 8, 2026 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants