Browser Runtime Architecture

Owner: bridge, browsers Related: browser-abstraction.md, routing-contract.md

Overview

Handlers have zero chromedp/cdproto imports. All browser operations go through BridgeAPI (~40 methods). CDP usage is contained in the bridge layer and the cdptk shared toolkit. Each browser provider owns its post-launch runtime behavior via the RuntimeInstance interface.

Layer diagram

Architecture DiagramASCII
  Handlers (54 files)                                                                                               Zero chromedp imports. Zero cdproto imports.              All operations via bridge.BridgeAPI.                      TabContext() returns *TabHandle (opaque).                                                                          screenshot  Bridge.CaptureScreenshot(ctx, ...)          screencast  Bridge.StartScreencast(ctx, opts)           record      captureFrame closure (injected at init)     evaluate    Bridge.Evaluate(ctx, expr, &out, opts)      cookies     Bridge.GetCookies(ctx) / SetCookie(ctx)     DOM         Bridge.CallFunctionOnNode(ctx, ...)         download    Bridge.DownloadURL(ctx, url, opts)          emulation   Bridge.SetViewport / SetGeolocation / ...   navigation  Bridge.CurrentURL / CurrentTitle            actions     Bridge.ExecuteAction(ctx, kind, req)                                  BridgeAPI (domain types, no CDP types)                             Bridge (BridgeAPI  ~40 methods)                                                                                  Owns: lifecycle, tab routing, locks, auto-close,               network monitoring, CDP connection                                                                          Holds a RuntimeInstance for provider-specific behavior.                                                            Visual:     CaptureScreenshot, StartScreencast           Evaluate:   Evaluate, CallFunctionOnNode,                            EvaluateInFrame                               DOM:        DescribeNode, ResolveSelectorToNodeID,                    SetFileInputFiles                             Cookies:    GetCookies, SetCookie                         Emulation:  SetViewport, SetGeolocation,                              SetEmulatedMedia                              Network:    SetNetworkConditions, SetExtraHTTPHeaders,                EnableNetwork, ListenNetworkEvents             Navigation: CurrentURL, CurrentTitle, GoBack,                         GoForward, Reload                             Download:   DownloadURL (Fetch interception + Network)    Auth:       EnableFetchWithAuth                           PDF:        PrintToPDF                                    Stealth:    SetUserAgentOverride,                                     AddScriptToEvaluateOnNewDocument              Tabs:       ListTargets  []TabTarget (bridge type)                                                                BridgeAPI signatures use domain types, not CDP types.     CDP types never appear in BridgeAPI signatures.                                     RuntimeInstance + chromedp (internal)                             RuntimeInstance (per-provider runtime)                                                                             20-method interface in internal/browsers/.                Each provider owns HOW it controls the browser.          CDP is an implementation detail, not a contract.                                                                  Chrome instance   uses cdptk (shared toolkit)           Cloak instance    embeds Chrome, forces polling                            screencast (no Page.startScreencast)   Ghost-chrome      embeds Chrome (pure delegation)       Future: Firefox   WebDriver BiDi (no CDP at all)                                                              internal/cdptk/ (shared CDP toolkit)                                                                              Pure functions. No state. No browser ownership.          Takes a chromedp context, returns data.                                                                           cdptk.CaptureScreenshot(ctx, format, quality, clip)      cdptk.ClipForNode(ctx, backendNodeID)  *ScreenshotClip  cdptk.ScreencastRepaintLoop(ctx) (start/stop)            cdptk.AnnotatedScreenshot(ctx, ...)  []byte                                                                      Used by RuntimeInstance impls. Never by handlers.      

Browser interface (pre-launch)

The Browser interface handles pre-launch concerns: ID, DisplayName, BuildLaunchArgs, CanHandle, DiscoverBinary, DoctorChecks, GeoAlignment, Capabilities, ValidateTarget. It also provides NewRuntimeInstance() to create the post-launch runtime.

Each provider package exports both:

// internal/browsers/chrome/chrome.go
type Browser struct{}           // implements browsers.Browser (pre-launch)

// internal/browsers/chrome/instance.go
type Instance struct{}          // implements browsers.RuntimeInstance (post-launch)

The Bridge calls browser.NewRuntimeInstance(browserCtx, headless) during EnsureBrowser() and holds the returned RuntimeInstance for the session lifetime.

TabHandle

TabContext() returns *TabHandle instead of context.Context. TabHandle implements context.Context (Deadline/Done/Err/Value all delegate to the underlying CDP context) but the return type signals to handlers that this context should only be passed to Bridge methods.

Capability-based routing

Browser capabilities (CapabilitySet) drive runtime behavior. For example, CapEventScreencast controls screencast strategy:

  • Chrome declares CapEventScreencast → event-driven screencast
  • Cloak omits it → polling-based screencast (same as headless path)

shouldUsePollingScreencast() checks both Config.Headless and the browser’s capability set.

Key patterns

  • BridgeAPI signatures use domain types, not CDP types. Return []byte for screenshots, not *page.CaptureScreenshotReturns.

  • CallFunctionOnNode centralizes the DOM.resolveNode → Runtime.callFunctionOn pattern. Used by attr, box, checked, enabled, visible, value, inspect, and text handlers.

  • ScreencastStream returns *ScreencastStream with Frames <-chan []byte. Two strategies selected by capability.

  • DownloadURL encapsulates the full Fetch interception + Network monitoring state machine.

  • captureFrame closure — the recorder receives a captureFrame function injected at construction time, bound to Bridge.CaptureScreenshot.

  • TabTarget — bridge-level type replaces *target.Info to avoid cdproto imports leaking into handlers.

CDP usage by layer

LayerCDP Domains UsedPurpose
bridge/Page, DOM, Runtime, Network, Fetch, Emulation, Input, TargetAll browser operations delegated from handlers
cdptk/Page, DOM, RuntimeShared pure-function CDP wrappers
browsers/chrome/Page, DOM, Runtime, Network, Fetch, EmulationRuntimeInstance implementation
handlers/NoneAll operations via BridgeAPI

Non-goals

  • Replacing chromedp as the CDP client library.
  • Supporting non-Chromium browsers in v1 (but the architecture does not prevent it).