fix(cv2-capture): self-heal FrameSink capture across cross-doc nav RVH swap #38
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "cv2-capture-rearm-land"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The one true cold-boot frozen-video bug. Cold-boot cb-chromium guest arms FrameSink capture on about:blank; nav to real content swaps the RenderViewHost (+FrameSinkId) but the capturer is NEVER re-pointed at the new sink → RENDERER-STARVED, frames_encoded=0, frozen video. Warm-restore is immune (snapshotted already-on-content). CbActiveWebContentsResolver gains an optional SetRecaptureOnRvhSwapCallback; RenderViewHostChanged POSTs it; RearmCaptureAfterRvhSwap re-resolves WC→RWHV→RWH→GetFrameSinkId, forces view SHOWING, re-StartCapture (capturer.cc ChangeTarget re-points, verified not a no-op), bounded 5x/50ms retry. Adversarial review 8/8 SAFE (no UAF, no re-entrancy, threading OK). Optional callback → legacy invalidate-only when unset (tests unaffected).
Deploy: guest change → cr7727- build → cb-rootfs bake → isolator roll.
THE root cause of cold-boot frozen video. A cold-boot cb-chromium guest starts on about:blank; capture is armed on that page's FrameSinkId; then the navigation to real content swaps the RenderViewHost (and its FrameSinkId), but nothing re-points the capturer at the new sink. It stays bound to the dead pre-nav sink -> VERDICT=RENDERER-STARVED, frames_encoded=0, no video. Warm-restored golden sessions dodge this (they resume already-on-content with no about:blank->nav transition), which is why cold-boot was the sole frozen path -- and why the isolator cold-boot fallback, though correct at the allocation layer, produced a connected-but-frozen stream. Fix: make capture self-healing at the layer that owns both the observer and the capturer. CbActiveWebContentsResolver already observes the captured WebContents and receives RenderViewHostChanged, but is content-layer glue with no capturer handle (the FrameSink capturer is deliberately content-agnostic -- FrameSinkIds only). So the resolver gains an optional re-arm closure (SetRecaptureOnRvhSwapCallback) that CloudBrowserBrowserMainParts supplies; on an RVH swap of the active capture the resolver POSTs it (the new RWH's view/FSID may not be the WC's live primary synchronously at RenderViewHostChanged -- a same-turn UI PostTask runs after the swap settles). The owner (RearmCaptureAfterRvhSwap) re-resolves WC->RWHV->RWH->GetFrameSinkId, forces the fresh view SHOWING (the post-nav RWH boots HIDDEN -> renderer throttled, same gating Cb.startFrameSinkCapture applies), calls cb_track_source_->StartCapture on the new sink, and refreshes the resolver's active target. capturer.cc:138-164 confirms StartCapture on a NEW FrameSinkId re-targets (ChangeTarget), not a no-op -- so the re-arm is effective. For a terminal nav whose fresh RWHV isn't attached by the task turn (no later swap to retrigger), the owner re-posts itself up to 5x/50ms (bounded) rather than strand the capturer. Stale "Start is a no-op" comments in cb_devtools_agent.cc + cb_framesink_video_track_source.{h,cc} that predated the retarget path are corrected so this fix can't be mistakenly reverted as dead code. Layering preserved (capturer stays content-agnostic; re-arm lives in build-integration where content deps already exist). Optional callback: unset -> legacy invalidate-only behaviour, so tests/no-capturer builds are unaffected. Adversarially reviewed (lifetime/UAF, re-entrancy, threading, StartCapture-idempotency, includes, Show() walk) -- no correctness bugs. Composes with the isolator cold-boot fallback (now a COMPLETE fix, not frozen) and the per-language warm-slot fix. Guest change -- needs a cb-chromium rootfs bake to deploy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>