3 Getting-Started
Virgil edited this page 2026-02-19 16:54:36 +00:00

Getting Started

This page covers how to connect to a Chrome browser instance and configure the Webview client.

See also: Home | DOM-Queries | Actions | Console-Monitoring | Angular-Testing

Prerequisites

You need a running Chrome or Chromium instance with remote debugging enabled. Launch it from the command line:

# Headless (no visible window)
google-chrome --remote-debugging-port=9222 --headless

# With visible window (useful during development)
google-chrome --remote-debugging-port=9222

# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222

# Chromium
chromium --remote-debugging-port=9222

The --remote-debugging-port flag starts an HTTP server on the specified port that exposes the DevTools Protocol. The library connects to this endpoint, discovers available page targets, and opens a WebSocket connection to the first available page.

Installation

go get forge.lthn.ai/core/go-webview

Creating a Webview

The New constructor accepts functional options and returns a configured *Webview:

wv, err := webview.New(
    webview.WithDebugURL("http://localhost:9222"),
)
if err != nil {
    log.Fatal(err)
}
defer wv.Close()

WithDebugURL is required. It connects to the Chrome DevTools HTTP endpoint, queries /json for available page targets, and establishes a WebSocket connection to the first page target found. If no page target exists, it creates one via /json/new.

Configuration Options

WithDebugURL

Sets the Chrome DevTools HTTP endpoint. This is the only required option.

webview.WithDebugURL("http://localhost:9222")

WithTimeout

Sets the default timeout for all operations. Defaults to 30 seconds.

webview.WithTimeout(10 * time.Second)

Every method on Webview creates a child context from the instance context with this timeout. If an operation (navigation, DOM query, screenshot, etc.) exceeds the timeout, it returns a context deadline error.

WithConsoleLimit

Sets the maximum number of console messages retained in the built-in buffer. Defaults to 1000. When the limit is reached, the oldest messages are pruned.

webview.WithConsoleLimit(5000)

Connection Lifecycle

When New is called:

  1. WithDebugURL sends GET /json to discover page targets
  2. The first target of type "page" with a WebSocket URL is selected
  3. If no page targets are found, GET /json/new creates a fresh tab
  4. A WebSocket connection is established using gorilla/websocket
  5. A background goroutine (readLoop) begins reading CDP messages
  6. The Runtime, Page, and DOM domains are enabled
  7. A console event handler is registered on Runtime.consoleAPICalled

When Close is called:

  1. The instance context is cancelled
  2. The read loop exits
  3. The WebSocket connection is closed

Low-Level CDPClient

The CDPClient is the WebSocket transport layer. You normally do not use it directly, but it is available for advanced use cases.

// Create a standalone CDP client
client, err := webview.NewCDPClient("http://localhost:9222")
if err != nil {
    log.Fatal(err)
}
defer client.Close()

// Send a CDP command
ctx := context.Background()
result, err := client.Call(ctx, "Page.navigate", map[string]any{
    "url": "https://example.com",
})

// Subscribe to events
client.OnEvent("Page.loadEventFired", func(params map[string]any) {
    log.Println("Page loaded")
})

// Fire-and-forget message (no response)
client.Send("Runtime.enable", nil)

CDPClient Utilities

// List all targets (pages, workers, service workers, etc.)
targets, err := webview.ListTargets("http://localhost:9222")

// Get Chrome version information
version, err := webview.GetVersion("http://localhost:9222")

// Create a new tab
newClient, err := client.NewTab("https://example.com")
defer newClient.Close()

// Close the current tab
err = client.CloseTab()

Error Handling

All methods return errors with context. A typical pattern:

wv, err := webview.New(webview.WithDebugURL("http://localhost:9222"))
if err != nil {
    // Connection failed -- Chrome may not be running
    log.Fatalf("failed to connect: %v", err)
}
defer wv.Close()

if err := wv.Navigate("https://example.com"); err != nil {
    // Navigation failed or timed out
    log.Printf("navigation error: %v", err)
}

Errors are wrapped with fmt.Errorf and preserve the underlying cause, so you can use errors.Is and errors.As for inspection.

Next Steps