How caveman-browse Captures Accessibility Snapshots via Chrome DevTools Protocol

caveman-browse leverages the chromedp Go library to execute the Accessibility.getFullAXTree Chrome DevTools Protocol command, retrieving a slice of *accessibility.Node structs that represent the page's complete accessibility hierarchy.

The caveman repository provides a headless browsing utility designed to extract semantic page structures for AI agents. To obtain accurate, up-to-date accessibility data, the project implements a CDP client in browse/cdp.go that queries the browser's internal accessibility engine directly via the Chrome DevTools Protocol.

CDP Integration Architecture

caveman-browse relies on the chromedp library to manage a headless Chrome instance. The library establishes a CDP session over WebSocket, allowing Go code to send commands and receive events from the browser's DevTools API. This architecture separates the high-level browsing logic from the low-level protocol serialization, ensuring reliable communication with Chromium-based browsers.

Context Management

Every CDP operation requires a valid context. The code constructs an actionCtx (context.Context) that carries deadlines and cancellation signals. This context is passed to the accessibility query to ensure the snapshot request respects timeout limits and can be interrupted if the page becomes unresponsive.

The Accessibility Snapshot Implementation in browse/cdp.go

The core snapshot logic resides in browse/cdp.go, where the package imports the CDP accessibility domain and executes the full tree retrieval.

Importing the Accessibility Domain

At line 13, the file imports the protocol definitions:

import "github.com/chromedp/cdproto/accessibility"

This import provides Go structs that map 1:1 to the CDP Accessibility domain types, ensuring type-safe interaction with the browser's native accessibility data. By using these generated types, caveman-browse avoids manual JSON unmarshaling and gains compile-time verification of protocol structures.

Retrieving the Full Accessibility Tree

At line 151, the code executes the snapshot:

nodes, err = accessibility.GetFullAXTree().Do(actionCtx)

The GetFullAXTree() function constructs a CDP command that maps to the Accessibility.getFullAXTree protocol method. Calling .Do(actionCtx) dispatches the command over the CDP WebSocket and blocks until the browser returns the complete accessibility tree or an error occurs.

Handling the Node Slice

Before the call at line 141, the code declares a variable to receive the result:

var nodes []*accessibility.Node

Each *accessibility.Node contains fields such as NodeID, Role, Name, and Children, representing a single accessible element in the DOM. The resulting slice captures every accessible object on the page, from the root document down to individual controls, providing a comprehensive semantic map.

Practical Example: Capturing an Accessibility Snapshot

Below is a runnable example that mirrors caveman-browse's approach. It launches a headless Chrome instance, navigates to a target, and extracts the accessibility tree.

package main

import (
    "context"
    "log"
    "time"

    "github.com/chromedp/cdproto/accessibility"
    "github.com/chromedp/chromedp"
)

func main() {
    ctx, cancel := chromedp.NewContext(
        context.Background(),
        chromedp.WithHeadless(true),
    )
    defer cancel()

    // Set a 30-second timeout for the entire operation
    ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
    defer cancel()

    var axNodes []*accessibility.Node

    err := chromedp.Run(ctx,
        chromedp.Navigate("https://example.com"),
        chromedp.ActionFunc(func(actionCtx context.Context) error {
            var err error
            // Capture the full accessibility tree via CDP
            axNodes, err = accessibility.GetFullAXTree().Do(actionCtx)
            return err
        }),
    )
    if err != nil {
        log.Fatalf("failed to capture accessibility snapshot: %v", err)
    }

    log.Printf("Captured %d accessibility nodes", len(axNodes))
    // Process axNodes further...
}

This code demonstrates the exact pattern used in browse/cdp.go: creating an action function that receives the CDP context, invoking accessibility.GetFullAXTree().Do(actionCtx), and handling the returned slice.

Error Handling and Timeouts

Any failure during the CDP call—such as a disconnected browser, timeout, or internal accessibility engine error—is returned from the .Do() method. The calling code in caveman-browse checks err immediately after line 151 and propagates it upward, allowing the session manager to retry the navigation or terminate the browsing task gracefully.

Summary

  • caveman-browse uses the chromedp Go library to communicate with Chrome via the Chrome DevTools Protocol.
  • In browse/cdp.go, the code imports github.com/chromedp/cdproto/accessibility to access the CDP Accessibility domain.
  • The snapshot is captured by calling accessibility.GetFullAXTree().Do(actionCtx), which returns a slice of *accessibility.Node structs.
  • This approach yields a complete, accurate accessibility tree directly from the browser's rendering engine, enabling precise semantic analysis of web content.

Frequently Asked Questions

What is the Chrome DevTools Protocol (CDP) used for in caveman-browse?

The Chrome DevTools Protocol is a set of APIs that allow external tools to inspect, debug, and control Chromium-based browsers. caveman-browse uses CDP to programmatically query the browser's internal accessibility tree, bypassing the need for HTML parsing and instead retrieving the semantic structure that assistive technologies use.

Why does caveman-browse use GetFullAXTree instead of scraping the DOM?

GetFullAXTree returns the computed accessibility tree, which includes semantic information that raw DOM elements lack, such as ARIA role mappings, accessible names, and visibility states. This provides a more accurate representation of how screen readers perceive the page compared to raw HTML scraping.

How does chromedp differ from Puppeteer or Playwright?

chromedp is a native Go library that communicates directly with Chrome via the CDP WebSocket without requiring Node.js or external browser drivers. Puppeteer and Playwright are Node.js-based alternatives that offer similar CDP access but require a JavaScript runtime, making chromedp preferable for Go-based systems like caveman.

Can the accessibility snapshot capture hidden or off-screen elements?

Yes. The GetFullAXTree command returns all nodes in the accessibility tree, including elements with CSS visibility: hidden or positioned off-screen, as long as they are part of the accessibility hierarchy. The browser's accessibility engine determines inclusion based on semantic relevance rather than visual viewport presence.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →