feat(cv2-warm): Cb.startNativeSession CDP method for warm-snapshot cold-start #36

Merged
triform-admin merged 4 commits from cv2/warm-snapshot-native-session into main 2026-06-17 10:50:31 +00:00

Summary

Adds Cb.startNativeSession — a CDP method that brings up the cb-chromium native WebRTC signaling session at runtime with per-session params, instead of only reading WEBRTC_SIGNALING_* from env at browser startup.

This is the chromeless half of the warm-snapshot cold-start fix: it lets the isolator restore a WARM cb-chromium golden (chromium already painted, signaling env unset → idle CDP-only target) and inject per-session signaling AFTER restore over CDP — eliminating the ~30s in-guest chromium cold start the config-drive boot path forces.

Changes

  • Extract the native-peer bring-up (lines that built ws_client_/offerer_driver_/dc_host_/transceivers) into MainParts::StartNativeSession(NativeSessionConfig). Boot path calls it from LoadConfigFromEnv() — byte-identical when env is set. When env is unset, the worker no longer early-returns; it stays alive in the message loop as a warm CDP-only target.
  • New Cb.startNativeSession CDP method (cb_devtools_agent), modeled on Cb.startFrameSinkCapture. Parses params (CBOR→JSON via crdtp), maps to the same WsClientConfig+IceConfig the env path builds, reuses ParseIceServersJson/ParseIceTransportPolicy.
  • Wire the callback through content_browser_client.
  • H1: StartNativeSession wraps its body in ScopedAllowBaseSyncPrimitivesForTesting (the signaling-thread BlockingCall would FATAL under a CDP task's DisallowBaseSyncPrimitives; no-op on the env-boot path).

Safety

Purely additive. Cb.startNativeSession is inert unless invoked — only the isolator (behind VMM_WEBRTC_WARM_RESTORE, default OFF) calls it. Existing env-driven sessions and the v5 BeginFrame fix are unaffected. Snapshot frozen in the warm-idle state bakes ZERO WebRTC network state (all peer ptrs nullptr).

Compiles clean (cloud_browser_worker linked, image cr7727-82988a4780b6). Companion isolator change landed to tf-multiverse dev (95f9897).

🤖 Generated with Claude Code

## Summary Adds `Cb.startNativeSession` — a CDP method that brings up the cb-chromium native WebRTC signaling session at runtime with per-session params, instead of only reading `WEBRTC_SIGNALING_*` from env at browser startup. This is the chromeless half of the warm-snapshot cold-start fix: it lets the isolator restore a WARM cb-chromium golden (chromium already painted, signaling env unset → idle CDP-only target) and inject per-session signaling AFTER restore over CDP — eliminating the ~30s in-guest chromium cold start the config-drive boot path forces. ### Changes - Extract the native-peer bring-up (lines that built ws_client_/offerer_driver_/dc_host_/transceivers) into `MainParts::StartNativeSession(NativeSessionConfig)`. Boot path calls it from `LoadConfigFromEnv()` — byte-identical when env is set. When env is unset, the worker no longer early-returns; it stays alive in the message loop as a warm CDP-only target. - New `Cb.startNativeSession` CDP method (cb_devtools_agent), modeled on `Cb.startFrameSinkCapture`. Parses params (CBOR→JSON via crdtp), maps to the same WsClientConfig+IceConfig the env path builds, reuses ParseIceServersJson/ParseIceTransportPolicy. - Wire the callback through content_browser_client. - H1: StartNativeSession wraps its body in ScopedAllowBaseSyncPrimitivesForTesting (the signaling-thread BlockingCall would FATAL under a CDP task's DisallowBaseSyncPrimitives; no-op on the env-boot path). ### Safety Purely additive. `Cb.startNativeSession` is inert unless invoked — only the isolator (behind VMM_WEBRTC_WARM_RESTORE, default OFF) calls it. Existing env-driven sessions and the v5 BeginFrame fix are unaffected. Snapshot frozen in the warm-idle state bakes ZERO WebRTC network state (all peer ptrs nullptr). Compiles clean (cloud_browser_worker linked, image cr7727-82988a4780b6). Companion isolator change landed to tf-multiverse dev (95f9897). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Extracts the native WebRTC signaling bring-up (ws_client + offerer driver +
DataChannel host + input/cursor/clipboard/audio+video transceivers) out of the
inline PreMainMessageLoopRun env path into a reusable
MainParts::StartNativeSession(NativeSessionConfig), and adds a Cb.startNativeSession
CDP method that invokes it at runtime with per-session params.

This lets the isolator restore a WARM cb-chromium golden snapshot (chromium
already launched + first-paint-ready, signaling env UNSET → idle CDP-only
target) and inject per-session signaling AFTER restore over CDP — eliminating
the ~30s in-guest chromium cold-start that the config-drive boot path forces.

Behavior is byte-identical when WEBRTC_SIGNALING_* env is set: the boot path
calls StartNativeSession from LoadConfigFromEnv()+LoadIceConfigFromEnv() exactly
as before. When env is unset, the worker no longer early-returns into the idle
state via a now-deleted `return RESULT_CODE_NORMAL_EXIT` — it falls through to
the same return at the end of PreMainMessageLoopRun (which means "stay alive in
the message loop"), so it idles as a warm CDP target awaiting the CDP call. A
snapshot frozen in that state bakes ZERO WebRTC network state (ws_client_/
offerer_driver_/dc_host_ all nullptr).

H1: StartNativeSession wraps its body in ScopedAllowBaseSyncPrimitivesForTesting
because the signaling-thread BlockingCall (audio transceiver) would otherwise
DCHECK-FATAL under the per-task DisallowBaseSyncPrimitives active in a CDP
HandleCommand task. No-op on the env-boot path. Mirrors the existing
ScopedAllowBlockingForTesting in CbDevToolsManagerDelegate::CreateBrowserContext.

The CDP handler parses params via crdtp CBOR->JSON->base::Value, reuses the env
path's ParseIceServersJson / ParseIceTransportPolicy / BuildDefaultIceServers /
SummariseIceServers so the two paths are semantically identical, and maps an
already-started session to an INVALID_STATE ServerError. No BUILD.gn change
(cb_devtools_agent.cc + main_parts.cc share the embedder source_set, which
already deps cb_ice_config + cb_signaling_ws_client).

Companion isolator change (VMM_WEBRTC_WARM_RESTORE, default off) ships after
this binary is live. Design: memory cv2-warm-snapshot-cold-start-design.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The chromium-rawptr plugin rejects a bare webrtc::AudioDeviceModule* member
(main_parts.h:455). Change adm_for_audio_lifecycle_ to raw_ptr<> + include
base/memory/raw_ptr.h, matching CbAudioLifecycle::adm_debug_ (the consumer)
and the rest of the embedder's non-owning pointer members.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pinned chromium (M147) has no base::Value::Dict spelling reachable here and
JSONReader::Read has no default options arg. Switch to JSONReader::ReadDict(json)
which parses + extracts the top-level object in one call and returns
std::optional<Dict>; bind with auto so the dict type name is never written.
CBOR->JSON via crdtp::json::ConvertCBORToJSON unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(cv2-warm): pass JSON_PARSE_RFC to ReadDict (no default options arg)
Some checks failed
CI / Lint (pull_request) Failing after 40s
CI / Docs link check (pull_request) Failing after 39s
CodeQL / Analyze javascript-typescript (pull_request) Failing after 42s
CodeQL / Analyze go (pull_request) Failing after 54s
CI / Build container image (pull_request) Failing after 59s
CI / Container smoke test (pull_request) Has been skipped
native-peer-gate / Build chromeless:ci for gate (pull_request) Failing after 36s
native-peer-gate / native-peer-gate-scaffold (permissive) (pull_request) Has been skipped
E2E / docker-compose + Playwright (pull_request) Failing after 1m13s
native-peer-gate / native-peer-gate-strict (M7 gate) (pull_request) Has been skipped
82988a4780
ReadDict(json, options, max_depth=...) in branch-heads/7727 has no default for
options. Pass base::JSON_PARSE_RFC (strict; input is machine-generated from
CBOR). Verified DictValue::FindString/FindBool + all crdtp cbor encoders +
JSON_PARSE_RFC against the pinned tree — this was the last API arg-count gap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
triform/chromeless!36
No description provided.