Merge pull request #1308 from lightpanda-io/axtree-backport

cdp: add AXTree
This commit is contained in:
Pierre Tachoire
2026-01-16 17:56:40 +01:00
committed by GitHub
6 changed files with 1208 additions and 3 deletions

View File

@@ -1367,7 +1367,7 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const
Element.Html.Quote, Element.Html.Quote,
namespace, namespace,
attribute_iterator, attribute_iterator,
.{ ._proto = undefined, ._tag_name = String.init(undefined, "q", .{}) catch unreachable, ._tag = .unknown }, .{ ._proto = undefined, ._tag_name = String.init(undefined, "q", .{}) catch unreachable, ._tag = .quote },
), ),
's' => return self.createHtmlElementT( 's' => return self.createHtmlElementT(
Element.Html.Generic, Element.Html.Generic,
@@ -1523,7 +1523,7 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const
Element.Html.TableCol, Element.Html.TableCol,
namespace, namespace,
attribute_iterator, attribute_iterator,
.{ ._proto = undefined, ._tag_name = String.init(undefined, "col", .{}) catch unreachable, ._tag = .unknown }, .{ ._proto = undefined, ._tag_name = String.init(undefined, "col", .{}) catch unreachable, ._tag = .col },
), ),
asUint("dir") => return self.createHtmlElementT( asUint("dir") => return self.createHtmlElementT(
Element.Html.Directory, Element.Html.Directory,
@@ -1926,7 +1926,7 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const
Element.Html.TableCol, Element.Html.TableCol,
namespace, namespace,
attribute_iterator, attribute_iterator,
.{ ._proto = undefined, ._tag_name = String.init(undefined, "colgroup", .{}) catch unreachable, ._tag = .unknown }, .{ ._proto = undefined, ._tag_name = String.init(undefined, "colgroup", .{}) catch unreachable, ._tag = .colgroup },
), ),
asUint("fieldset") => return self.createHtmlElementT( asUint("fieldset") => return self.createHtmlElementT(
Element.Html.FieldSet, Element.Html.FieldSet,
@@ -1952,6 +1952,12 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const
attribute_iterator, attribute_iterator,
.{ ._proto = undefined }, .{ ._proto = undefined },
), ),
asUint("noscript") => return self.createHtmlElementT(
Element.Html.Generic,
namespace,
attribute_iterator,
.{ ._proto = undefined, ._tag_name = String.init(undefined, "noscript", .{}) catch unreachable, ._tag = .noscript },
),
else => {}, else => {},
}, },
10 => switch (@as(u80, @bitCast(name[0..10].*))) { 10 => switch (@as(u80, @bitCast(name[0..10].*))) {

View File

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
</head>
<body>
<h1>Test Page</h1>
<nav>
<a href="/page1" id="link1">First Link</a>
<a href="/page2" id="link2">Second Link</a>
</nav>
<form id="testForm" action="/submit" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" placeholder="Enter username">
<label for="email">Email:</label>
<input type="email" id="email" name="email" placeholder="Enter email">
<label for="password">Password:</label>
<input type="password" id="password" name="password">
<button type="submit">Submit</button>
</form>
</body>
</html>

View File

@@ -1219,6 +1219,8 @@ pub const Tag = enum {
caption, caption,
circle, circle,
code, code,
col,
colgroup,
custom, custom,
data, data,
datalist, datalist,
@@ -1265,20 +1267,26 @@ pub const Tag = enum {
meta, meta,
meter, meter,
nav, nav,
noscript,
object,
ol, ol,
optgroup, optgroup,
option, option,
output,
p, p,
path, path,
param,
polygon, polygon,
polyline, polyline,
progress, progress,
quote,
rect, rect,
s, s,
script, script,
section, section,
select, select,
slot, slot,
source,
span, span,
strong, strong,
style, style,
@@ -1298,6 +1306,7 @@ pub const Tag = enum {
thead, thead,
title, title,
tr, tr,
track,
ul, ul,
video, video,
unknown, unknown,

1130
src/cdp/AXNode.zig Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -289,6 +289,7 @@ pub fn CDPT(comptime TypeProvider: type) type {
pub fn BrowserContext(comptime CDP_T: type) type { pub fn BrowserContext(comptime CDP_T: type) type {
const Node = @import("Node.zig"); const Node = @import("Node.zig");
const AXNode = @import("AXNode.zig");
return struct { return struct {
id: []const u8, id: []const u8,
@@ -465,6 +466,16 @@ pub fn BrowserContext(comptime CDP_T: type) type {
}; };
} }
pub fn axnodeWriter(self: *Self, root: *const Node, opts: AXNode.Writer.Opts) !AXNode.Writer {
const page = self.session.currentPage() orelse return error.PageNotLoaded;
_ = opts;
return .{
.page = page,
.root = root,
.registry = &self.node_registry,
};
}
pub fn getURL(self: *const Self) ?[:0]const u8 { pub fn getURL(self: *const Self) ?[:0]const u8 {
const page = self.session.currentPage() orelse return null; const page = self.session.currentPage() orelse return null;
const url = page.url; const url = page.url;

View File

@@ -22,11 +22,13 @@ pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
disable, disable,
getFullAXTree,
}, cmd.input.action) orelse return error.UnknownMethod; }, cmd.input.action) orelse return error.UnknownMethod;
switch (action) { switch (action) {
.enable => return enable(cmd), .enable => return enable(cmd),
.disable => return disable(cmd), .disable => return disable(cmd),
.getFullAXTree => return getFullAXTree(cmd),
} }
} }
fn enable(cmd: anytype) !void { fn enable(cmd: anytype) !void {
@@ -36,3 +38,25 @@ fn enable(cmd: anytype) !void {
fn disable(cmd: anytype) !void { fn disable(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn getFullAXTree(cmd: anytype) !void {
const params = (try cmd.params(struct {
depth: ?i32 = null,
frameId: ?[]const u8 = null,
})) orelse return error.InvalidParams;
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
if (params.frameId) |frameId| {
const target_id = bc.target_id orelse return error.TargetNotLoaded;
if (std.mem.eql(u8, target_id, frameId) == false) {
return cmd.sendError(-32000, "Frame with the given id does not belong to the target.", .{});
}
}
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
const doc = page.window._document.asNode();
const node = try bc.node_registry.register(doc);
return cmd.sendResult(.{ .nodes = try bc.axnodeWriter(node, .{}) }, .{});
}