DOM Domain
The DOM domain provides methods for querying and manipulating the Document Object Model.
Import
const cdp = @import("cdp");
const DOM = cdp.DOM;Initialization
var session = try browser.newPage();
var dom = DOM.init(session);
try dom.enable();Methods
enable / disable
Enable or disable the DOM domain.
pub fn enable(self: *DOM) !void
pub fn disable(self: *DOM) !voidgetDocument
Get the document root node.
pub fn getDocument(self: *DOM, allocator: Allocator, options: GetDocumentOptions) !NodeOptions:
| Field | Type | Description |
|---|---|---|
depth | ?i32 | Maximum depth of children to return (null = all) |
pierce | bool | Whether to pierce shadow DOM and include shadow roots (default: false) |
Returns: Node (caller must deinit)
Example:
// Basic usage
const doc = try dom.getDocument(allocator, .{ .depth = 1 });
defer {
var d = doc;
d.deinit(allocator);
}
std.debug.print("Document node ID: {}\n", .{doc.node_id});
// With shadow DOM piercing
const full_doc = try dom.getDocument(allocator, .{ .depth = -1, .pierce = true });querySelector
Find a single element matching a CSS selector.
pub fn querySelector(self: *DOM, node_id: i64, selector: []const u8) !i64Parameters:
node_id- Starting node ID (usually document node)selector- CSS selector
Returns: Node ID of matching element
Example:
const doc = try dom.getDocument(allocator, 1);
const h1_id = try dom.querySelector(doc.node_id, "h1");querySelectorAll
Find all elements matching a CSS selector.
pub fn querySelectorAll(
self: *DOM,
allocator: Allocator,
node_id: i64,
selector: []const u8,
) ![]i64Returns: Slice of node IDs (caller must free)
Example:
const links = try dom.querySelectorAll(allocator, doc.node_id, "a");
defer allocator.free(links);
for (links) |link_id| {
const href = try dom.getAttributes(allocator, link_id);
defer allocator.free(href);
}getOuterHTML
Get the outer HTML of an element.
pub fn getOuterHTML(self: *DOM, allocator: Allocator, node_id: i64) ![]const u8Returns: HTML string (caller must free)
Example:
const html = try dom.getOuterHTML(allocator, node_id);
defer allocator.free(html);
std.debug.print("HTML: {s}\n", .{html});setOuterHTML
Replace an element's outer HTML.
pub fn setOuterHTML(self: *DOM, node_id: i64, outer_html: []const u8) !voidExample:
try dom.setOuterHTML(node_id, "<div class=\"new\">Updated content</div>");getAttributes
Get element attributes.
pub fn getAttributes(self: *DOM, allocator: Allocator, node_id: i64) ![]const u8Returns: Flattened attribute string
setAttributeValue
Set an attribute value.
pub fn setAttributeValue(self: *DOM, node_id: i64, name: []const u8, value: []const u8) !voidExample:
try dom.setAttributeValue(node_id, "class", "highlighted");removeAttribute
Remove an attribute.
pub fn removeAttribute(self: *DOM, node_id: i64, name: []const u8) !voidremoveNode
Remove a node from the DOM.
pub fn removeNode(self: *DOM, node_id: i64) !voidfocus
Focus an element.
pub fn focus(self: *DOM, node_id: i64) !voidsetFileInputFiles
Set files for a file input element. Used for file uploads.
pub fn setFileInputFiles(self: *DOM, node_id: i64, files: []const []const u8) !voidParameters:
node_id- Node ID of the file input elementfiles- Array of absolute file paths
Note: File paths must be absolute paths on the local filesystem. This method only sets the files on the input element - it does not submit any form.
Example:
const file_input_id = try dom.querySelector(doc.node_id, "input[type=file]");
try dom.setFileInputFiles(file_input_id, &[_][]const u8{
"/path/to/document.pdf",
"/path/to/image.png",
});getBoxModel
Get element's box model dimensions.
pub fn getBoxModel(self: *DOM, allocator: Allocator, node_id: i64) !BoxModelReturns: BoxModel with content, padding, border, margin quads
resolveNode
Resolve a node to a Runtime remote object.
pub fn resolveNode(self: *DOM, allocator: Allocator, node_id: i64) !RemoteObjectdescribeNode
Get detailed information about a node, including shadow roots and content documents.
pub fn describeNode(self: *DOM, allocator: Allocator, options: DescribeNodeOptions) !NodeDescriptionOptions:
| Field | Type | Description |
|---|---|---|
node_id | ?i64 | Node ID to describe |
backend_node_id | ?i64 | Backend node ID |
object_id | ?[]const u8 | Remote object ID |
depth | ?i32 | Maximum depth (-1 for entire subtree) |
pierce | bool | Whether to pierce shadow DOM (default: false) |
Returns: NodeDescription (caller must deinit)
Example:
var desc = try dom.describeNode(allocator, .{
.node_id = element_id,
.depth = 1,
.pierce = true,
});
defer desc.deinit(allocator);
// Check shadow root type
if (desc.shadow_root_type) |srt| {
switch (srt) {
.open => std.debug.print("Open shadow root\n", .{}),
.closed => std.debug.print("Closed shadow root\n", .{}),
.user_agent => std.debug.print("User-agent shadow root\n", .{}),
}
}
// Access shadow roots
if (desc.shadow_roots) |roots| {
for (roots) |root| {
std.debug.print("Shadow root node: {}\n", .{root.node_id});
}
}getShadowRoot
Get the shadow root of a node (if it has one). This is a convenience wrapper around describeNode.
pub fn getShadowRoot(self: *DOM, allocator: Allocator, node_id: i64) !?NodeDescriptionReturns: ?NodeDescription - The shadow root, or null if the node has no shadow root.
Example:
if (try dom.getShadowRoot(allocator, host_node_id)) |shadow| {
defer {
var s = shadow;
s.deinit(allocator);
}
// Query within shadow root
const inner_id = try dom.querySelector(shadow.node_id, ".inner-element");
}requestNode
Get a node ID from a remote object ID.
pub fn requestNode(self: *DOM, object_id: []const u8) !i64Types
Node
pub const Node = struct {
node_id: i64,
node_type: i64, // 1=Element, 3=Text, 9=Document, etc.
node_name: []const u8, // Tag name or #text, #document
node_value: []const u8, // Text content or empty
children: ?[]Node = null,
attributes: ?[]const u8 = null,
document_url: ?[]const u8 = null,
base_url: ?[]const u8 = null,
pub fn deinit(self: *Node, allocator: Allocator) void;
};BoxModel
pub const BoxModel = struct {
content: [8]f64, // 4 corners × 2 coordinates
padding: [8]f64,
border: [8]f64,
margin: [8]f64,
width: i64,
height: i64,
};ShadowRootType
pub const ShadowRootType = enum {
user_agent, // Browser internal shadow DOM (e.g., <input>, <video>)
open, // Accessible via element.shadowRoot
closed, // Not accessible via JavaScript
};NodeDescription
Detailed node information returned by describeNode.
pub const NodeDescription = struct {
node_id: i64,
backend_node_id: i64,
node_type: i64,
node_name: []const u8,
local_name: ?[]const u8 = null,
node_value: []const u8,
frame_id: ?[]const u8 = null,
/// Shadow root type (if this is a shadow root)
shadow_root_type: ?ShadowRootType = null,
/// Shadow roots of this element
shadow_roots: ?[]NodeDescription = null,
/// Content document for iframes
content_document: ?*NodeDescription = null,
pub fn deinit(self: *NodeDescription, allocator: Allocator) void;
};Complete Example
const std = @import("std");
const cdp = @import("cdp");
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
var browser = try cdp.Browser.launch(.{
.headless = .new,
.allocator = allocator,
.io = init.io,
});
defer browser.close();
var session = try browser.newPage();
defer session.detach() catch {};
var page = cdp.Page.init(session);
var dom = cdp.DOM.init(session);
try page.enable();
try dom.enable();
_ = try page.navigate(allocator, "https://example.com");
// Wait for page load
var i: u32 = 0;
while (i < 500000) : (i += 1) {
std.atomic.spinLoopHint();
}
// Get document
const doc = try dom.getDocument(allocator, 1);
defer {
var d = doc;
d.deinit(allocator);
}
// Find heading
const h1_id = try dom.querySelector(doc.node_id, "h1");
const h1_html = try dom.getOuterHTML(allocator, h1_id);
defer allocator.free(h1_html);
std.debug.print("H1: {s}\n", .{h1_html});
// Find all links
const links = try dom.querySelectorAll(allocator, doc.node_id, "a[href]");
defer allocator.free(links);
std.debug.print("Found {} links\n", .{links.len});
// Get box model
const box = try dom.getBoxModel(allocator, h1_id);
std.debug.print("H1 size: {}x{}\n", .{box.width, box.height});
}