# How Lightpanda Implements JavaScript Execution via the CDP Runtime Domain

> Learn how Lightpanda executes JavaScript using the CDP Runtime domain. Discover the V8 inspector session and Zig based Local.compileAndRun function for efficient script compilation and execution.

- Repository: [Lightpanda/browser](https://github.com/lightpanda-io/browser)
- Tags: how-to-guide
- Published: 2026-03-14

---

**Lightpanda executes JavaScript through the CDP Runtime domain by routing `Runtime.evaluate` commands from the CDP dispatcher through a V8 inspector session to the Zig-based `Local.compileAndRun` function, which compiles and executes scripts using V8's `ScriptCompiler` API.**

Lightpanda is a lightweight, Zig-based browser engine that implements the Chrome DevTools Protocol (CDP) to enable remote debugging and automation. Its JavaScript execution capabilities are exposed through the CDP **Runtime** domain, providing full compatibility with standard DevTools clients while leveraging the V8 JavaScript engine embedded within its architecture.

## Architecture Overview of Lightpanda JavaScript Execution

The implementation spans multiple layers, from CDP message parsing to V8 script compilation.

### CDP Runtime Domain Entry Point

The `Runtime` domain dispatcher resides in `src/cdp/domains/runtime.zig`. The `processMessage` function maps incoming action strings (such as `"evaluate"`) to internal enums. For actions other than `runIfWaitingForDebugger`, it invokes `sendInspector` to forward the request.

### Inspector Session Routing

The `sendInspector` function retrieves the current `BrowserContext` and forwards the raw CDP JSON payload to the inspector session via `bc.callInspector`. This function, defined in `src/cdp/cdp.zig`, writes the message to the V8 inspector and immediately triggers `runMicrotasks()` to process the command.

### Tool Handler Integration

The actual JavaScript execution is handled as a **tool** in `src/mcp/tools.zig`. The `handleEvaluate` function receives the `EvaluateParams`, obtains the current page's JavaScript context, and prepares the execution environment before invoking the V8 binding layer.

## Execution Flow from CDP to V8

The complete execution path transforms a CDP command into a V8 script evaluation:

1. **Client Request**: The DevTools client sends a `Runtime.evaluate` command:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "Runtime.evaluate",
  "params": {
    "expression": "2 + 2"
  }
}

```

2. **CDP Parsing**: `runtime.processMessage` in `src/cdp/domains/runtime.zig` identifies the action as `.evaluate` and calls `sendInspector`.

3. **Inspector Forwarding**: The `callInspector` method in `src/cdp/cdp.zig` passes the JSON to the V8 inspector session and runs microtasks.

4. **Tool Dispatch**: The inspector routes to `handleEvaluate` in `src/mcp/tools.zig`, which:
   - Parses `EvaluateParams` (script and optional URL)
   - Optionally navigates to a URL first (`performGoto`)
   - Retrieves the current page (`server.session.currentPage`)
   - Creates a `js.Local.Scope` for the page's JavaScript context
   - Sets up `js.TryCatch` to capture V8 exceptions

5. **V8 Compilation**: `handleEvaluate` calls `ls.local.compileAndRun(args.script, null)` defined in `src/browser/js/Local.zig`. This function:
   - Builds a V8 `ScriptOrigin` and `ScriptCompilerSource`
   - Calls `v8__ScriptCompiler__Compile` to compile the source
   - Executes via `v8__Script__Run`
   - Returns a `js.Value` wrapping the V8 handle

6. **Module Evaluation**: For ES modules, `src/browser/js/Module.zig` provides `module.evaluate()`, used internally by the `Local` APIs when a script imports modules.

7. **Response Serialization**: The result is converted to a string (`js_result.toStringSliceWithAlloc`) and wrapped in a CDP-compliant JSON response:

```json
{
  "id": 1,
  "result": {
    "value": "4"
  }
}

```

## Error Handling and Exception Management

Lightpanda captures JavaScript exceptions using **V8's `TryCatch` mechanism** before they propagate back to the CDP client.

In `src/mcp/tools.zig`, the `handleEvaluate` function wraps the `compileAndRun` call with `js.TryCatch`. If V8 throws an exception during compilation or execution, `caught.format` serializes the exception details. The response is then sent with `.isError = true` and the serialized error message, maintaining CDP compatibility for runtime exceptions.

## Key Source Files and Functions

| Purpose | File Path | Key Functions |
|---------|-----------|---------------|
| CDP Runtime domain dispatcher | `src/cdp/domains/runtime.zig` | `processMessage`, `sendInspector` |
| Inspector session forwarding | `src/cdp/cdp.zig` | `callInspector` |
| Tool handling for evaluation | `src/mcp/tools.zig` | `handleEvaluate`, `EvaluateParams` |
| V8 script compilation/execution | `src/browser/js/Local.zig` | `compileAndRun` |
| ES module evaluation | `src/browser/js/Module.zig` | `evaluate` |
| Page context management | `src/browser/Page.zig` | Context retrieval for JS scope |
| Browser environment | `src/browser/Browser.zig` | Inspector and microtask management |

## Summary

Lightpanda's JavaScript execution via the CDP Runtime domain follows a tightly integrated pipeline:

- **CDP Runtime domain** in `src/cdp/domains/runtime.zig` parses incoming `Runtime.evaluate` requests and forwards them to the V8 inspector session.
- **Inspector session** in `src/cdp/cdp.zig` routes commands to the embedded V8 engine and triggers immediate microtask processing.
- **Tool handler** in `src/mcp/tools.zig` prepares the page context, sets up exception handling, and invokes the V8 binding layer.
- **V8 binding** in `src/browser/js/Local.zig` compiles scripts using `ScriptCompiler` and executes them, returning results or capturing exceptions via `js.TryCatch`.

This architecture enables Lightpanda to provide full CDP-compatible JavaScript evaluation, including ES module support, while maintaining the performance benefits of Zig and V8 integration.

## Frequently Asked Questions

### How does Lightpanda handle JavaScript exceptions during Runtime.evaluate?

Lightpanda wraps the V8 execution in a `js.TryCatch` block within the `handleEvaluate` function in `src/mcp/tools.zig`. If the script throws an exception, `caught.format` serializes the error details, and the response is sent with `.isError = true` to maintain CDP compatibility for error reporting.

### What is the difference between Local.zig and Module.zig in Lightpanda's JavaScript execution?

`src/browser/js/Local.zig` provides the `compileAndRun` function for traditional script evaluation using V8's `ScriptCompiler`, while `src/browser/js/Module.zig` implements `module.evaluate()` for ES module execution. The Local APIs internally use Module functionality when scripts contain ES module imports.

### How does Lightpanda ensure CDP compatibility for the Runtime domain?

Lightpanda implements the CDP Runtime domain by parsing standard CDP messages in `src/cdp/domains/runtime.zig`, forwarding them through the V8 inspector session in `src/cdp/cdp.zig`, and formatting responses according to the CDP specification in `src/mcp/tools.zig`. This ensures compatibility with standard DevTools clients and automation tools.

### Where does the actual V8 script compilation happen in Lightpanda?

The actual V8 compilation occurs in `src/browser/js/Local.zig` within the `compileAndRun` function. This function constructs a V8 `ScriptOrigin` and `ScriptCompilerSource`, calls `v8__ScriptCompiler__Compile` to compile the source code, and then executes it via `v8__Script__Run`.