Repository navigation
fix(extension): reduce idle overlay and Accessibility overhead - #408
NianJiuZst wants to merge 5 commits into
Conversation
The control overlay animated an inset box-shadow on a full-viewport element forever. box-shadow is not compositor-animatable, so the whole viewport was repainted on every frame for as long as the agent held control, even while it was idle. The animation also ignored prefers-reduced-motion. Paint the resting glow and the brighter glow as two static shadows and fade only the brighter layer's opacity, for two pulses. Under reduced motion only the resting glow is shown. Refs Tencent#391. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZRCYsFEKFsAZe6i8WAc9o
Every capture sent Accessibility.enable to each target but never disabled it, so the renderer kept its accessibility tree live and reported changes for the rest of the debugger attachment. Once a target's AX trees are read, or the capture fails or is cancelled first, disable the domain again on each target that enabled it. The cleanup shares the capture's worker bound, settles before the capture does, only uses the attachment that enabled AX, and is never queued behind a read still pending past its deadline: such a target is skipped, and the read gate now refuses Accessibility.disable behind a pending read. Refs Tencent#391. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZRCYsFEKFsAZe6i8WAc9o
Refs Tencent#391. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZRCYsFEKFsAZe6i8WAc9o
iuyo5678
left a comment
There was a problem hiding this comment.
Thanks for the improvement. I found a reproducible capture regression in the Accessibility cleanup ordering that needs to be fixed before merging. Please address the inline comment and add regression tests to the repository for both root and child cleanup timeouts, while preserving the cancellation and late-reply safeguards.
| }, | ||
| signal, | ||
| ); | ||
| ).finally(releaseAx); |
There was a problem hiding this comment.
[P2] Run Accessibility cleanup after all required capture reads
This finally runs before document-identity verification and geometry collection. If Accessibility.disable exceeds its new 1-second deadline, releaseAccessibility catches the rejection, but CdpReadGate remains closed until the underlying command settles. The subsequent Page.createIsolatedWorld is therefore rejected as phase: "blocked". For the root target this fails the entire capture; for a child target, isolateChildTimeout discards the child's already-collected data. Catching the cleanup error alone does not preserve the capture result.
I reproduced the root failure using the actual captureObservationFacts and CdpReadGate code against an isolated Chrome 155.0.8059.85. Immediately after the final Accessibility.getFullAXTree response, I queued a 1.5-second Runtime.evaluate busy loop and allowed capture to continue without awaiting that loop:
- Base
e8777ab7: capture succeeded with one document in approximately 1,510 ms. - PR
d305f8de: cleanup timed out after approximately 1,002 ms, and capture failed withCdpReadTimeoutErrorforPage.createIsolatedWorld,phase: "blocked".
A separate regression using the existing capture fixture and the real read gate also reproduces loss of the remote document when only the child's cleanup times out.
Please move the bounded cleanup after all required identity and geometry reads, for example into an outer finally covering the capture, so cleanup cannot invalidate an otherwise successful result. Keep cleanup on failure/cancellation, the original-attachment guard, and protection against pending reads.
Please add committed regression tests asserting that:
- A root cleanup timeout does not turn an otherwise successful capture into an error.
- A child cleanup timeout does not remove a fully captured child document.
- Cancellation still settles within the cleanup deadline, and a late cleanup reply releases only its original gate.
The existing tests cover a pending AX read blocking cleanup, but do not cover the effect of cleanup's own timeout on the remaining capture work.
Summary
Fixes #391.
An agent session can keep doing browser work between commands: the control overlay continuously animates a viewport-sized inset shadow, and observation enables Chrome's Accessibility domain without disabling it afterward. This PR makes the control glow settle after two opacity pulses and releases Accessibility on the targets whose capture reads have finished.
Problem and resulting behavior
box-shadowanimation runs for the entire time the overlay is visible.prefers-reduced-motion: reducedisables the pulse and leaves the static control glow.Accessibility.enable, but completed captures leave the domain enabled.Accessibility.disableafter the AX read phase, before document-identity and geometry work.Implementation
Control overlay
ControlOverlay.tsxkeeps the resting inset shadow on the existing overlay and adds a brighter child layer. Thebsk-breathekeyframes now contain only opacity changes, with a finite iteration count of two. The child has a resting opacity of zero, and a reduced-motion media query disables its animation. The control indicator, interruption button, and input-blocking behavior remain available.Accessibility lifetime
capture-coordinator.tsrecords each successfully enabled target together with its debugger attachment ID. Release uses the capture's existing worker helper, with at most four cleanup requests in flight, and is awaited before the capture completes.finallyaround the AX read phase.sendGuardedwith the original attachment ID in production, so an old capture cannot reconnect or send cleanup to a replacement attachment.command-deadline.tsguardsAccessibility.disableagainst earlier pending reads and gives cleanup itself a 1-second deadline. A timed-out disable retains its target gate until Chrome replies or the debugger detaches; late replies cannot clear a replacement attachment.Observation refs retain their backend DOM node IDs and document/attachment identity; the implementation does not depend on Accessibility node IDs surviving between captures. The changelog records both user-visible changes.
Regression coverage
Eight new cases extend three existing test files:
ControlOverlay.test.tsx(2): static shadows, opacity-only keyframes, exactly two pulses, no infinite animation, and the reduced-motion override.capture-coordinator.test.ts(5): release for root and child targets after their AX reads; release before identity/geometry work; dispatch on the original attachment; no cleanup on a replacement attachment; skipping a timed-out child while releasing the root; and cleanup during cancellation. Some cases assert multiple related behaviors.command-deadline.test.ts(1): a pending child-session AX read blocks its own cleanup without blocking root-target cleanup.Validation — 2026-10-20
Merged upstream main
e8777ab71bb851196895515b53eea2481462dd57; current revisiond305f8de8867bc42081d23c765502ab2291ce44b.Accessibility.disablepending. The production read gate returned its timeout after 1,002 ms, rejected another read while pending, and recovered after the original command settled following execution termination.Limits and performance evidence
Thanks to @code-yeongyu for the report, measurements, and original prototype.