mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 23:23:28 +00:00
Add support for CDP's DOM.requestChildNodes
https://github.com/lightpanda-io/browser/issues/866
This commit is contained in:
143
src/cdp/Node.zig
143
src/cdp/Node.zig
@@ -201,49 +201,69 @@ pub const Search = struct {
|
|||||||
// (For now, we only support direct children)
|
// (For now, we only support direct children)
|
||||||
|
|
||||||
pub const Writer = struct {
|
pub const Writer = struct {
|
||||||
opts: Opts,
|
depth: i32,
|
||||||
node: *const Node,
|
exclude_root: bool,
|
||||||
|
root: *const Node,
|
||||||
registry: *Registry,
|
registry: *Registry,
|
||||||
|
|
||||||
pub const Opts = struct {};
|
pub const Opts = struct {
|
||||||
|
depth: i32 = 0,
|
||||||
|
exclude_root: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn jsonStringify(self: *const Writer, w: anytype) !void {
|
pub fn jsonStringify(self: *const Writer, w: anytype) !void {
|
||||||
self.toJSON(w) catch |err| {
|
if (self.exclude_root) {
|
||||||
// The only error our jsonStringify method can return is
|
_ = self.writeChildren(self.root, 0, w) catch |err| {
|
||||||
// @TypeOf(w).Error. In other words, our code can't return its own
|
log.err(.cdp, "node writeChildren", .{ .err = err });
|
||||||
// error, we can only return a writer error. Kinda sucks.
|
return error.OutOfMemory;
|
||||||
log.err(.cdp, "json stringify", .{ .err = err });
|
};
|
||||||
return error.OutOfMemory;
|
} else {
|
||||||
};
|
self.toJSON(self.root, 0, w) catch |err| {
|
||||||
|
// The only error our jsonStringify method can return is
|
||||||
|
// @TypeOf(w).Error. In other words, our code can't return its own
|
||||||
|
// error, we can only return a writer error. Kinda sucks.
|
||||||
|
log.err(.cdp, "node toJSON stringify", .{ .err = err });
|
||||||
|
return error.OutOfMemory;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toJSON(self: *const Writer, w: anytype) !void {
|
fn toJSON(self: *const Writer, node: *const Node, depth: usize, w: anytype) !void {
|
||||||
try w.beginObject();
|
try w.beginObject();
|
||||||
try self.writeCommon(self.node, false, w);
|
try self.writeCommon(node, false, w);
|
||||||
|
|
||||||
{
|
try w.objectField("children");
|
||||||
var registry = self.registry;
|
const child_count = try self.writeChildren(node, depth, w);
|
||||||
const child_nodes = try parser.nodeGetChildNodes(self.node._node);
|
try w.objectField("childNodeCount");
|
||||||
const child_count = try parser.nodeListLength(child_nodes);
|
try w.write(child_count);
|
||||||
|
|
||||||
var i: usize = 0;
|
try w.endObject();
|
||||||
try w.objectField("children");
|
}
|
||||||
try w.beginArray();
|
|
||||||
for (0..child_count) |_| {
|
fn writeChildren(self: *const Writer, node: *const Node, depth: usize, w: anytype) anyerror!usize {
|
||||||
const child = (try parser.nodeListItem(child_nodes, @intCast(i))) orelse break;
|
var registry = self.registry;
|
||||||
const child_node = try registry.register(child);
|
const child_nodes = try parser.nodeGetChildNodes(node._node);
|
||||||
|
const child_count = try parser.nodeListLength(child_nodes);
|
||||||
|
const full_child = self.depth < 0 or self.depth < depth;
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
try w.beginArray();
|
||||||
|
for (0..child_count) |_| {
|
||||||
|
const child = (try parser.nodeListItem(child_nodes, @intCast(i))) orelse break;
|
||||||
|
const child_node = try registry.register(child);
|
||||||
|
if (full_child) {
|
||||||
|
try self.toJSON(child_node, depth + 1, w);
|
||||||
|
} else {
|
||||||
try w.beginObject();
|
try w.beginObject();
|
||||||
try self.writeCommon(child_node, true, w);
|
try self.writeCommon(child_node, true, w);
|
||||||
try w.endObject();
|
try w.endObject();
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
try w.endArray();
|
|
||||||
|
|
||||||
try w.objectField("childNodeCount");
|
i += 1;
|
||||||
try w.write(i);
|
|
||||||
}
|
}
|
||||||
|
try w.endArray();
|
||||||
|
|
||||||
try w.endObject();
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeCommon(self: *const Writer, node: *const Node, include_child_count: bool, w: anytype) !void {
|
fn writeCommon(self: *const Writer, node: *const Node, include_child_count: bool, w: anytype) !void {
|
||||||
@@ -400,14 +420,15 @@ test "cdp Node: Writer" {
|
|||||||
var registry = Registry.init(testing.allocator);
|
var registry = Registry.init(testing.allocator);
|
||||||
defer registry.deinit();
|
defer registry.deinit();
|
||||||
|
|
||||||
var doc = try testing.Document.init("<a id=a1></a><a id=a2></a>");
|
var doc = try testing.Document.init("<a id=a1></a><div id=d2><a id=a2></a></div>");
|
||||||
defer doc.deinit();
|
defer doc.deinit();
|
||||||
|
|
||||||
{
|
{
|
||||||
const node = try registry.register(doc.asNode());
|
const node = try registry.register(doc.asNode());
|
||||||
const json = try std.json.stringifyAlloc(testing.allocator, Writer{
|
const json = try std.json.stringifyAlloc(testing.allocator, Writer{
|
||||||
.node = node,
|
.root = node,
|
||||||
.opts = .{},
|
.depth = 0,
|
||||||
|
.exclude_root = false,
|
||||||
.registry = ®istry,
|
.registry = ®istry,
|
||||||
}, .{});
|
}, .{});
|
||||||
defer testing.allocator.free(json);
|
defer testing.allocator.free(json);
|
||||||
@@ -445,8 +466,9 @@ test "cdp Node: Writer" {
|
|||||||
{
|
{
|
||||||
const node = registry.lookup_by_id.get(1).?;
|
const node = registry.lookup_by_id.get(1).?;
|
||||||
const json = try std.json.stringifyAlloc(testing.allocator, Writer{
|
const json = try std.json.stringifyAlloc(testing.allocator, Writer{
|
||||||
.node = node,
|
.root = node,
|
||||||
.opts = .{},
|
.depth = 0,
|
||||||
|
.exclude_root = false,
|
||||||
.registry = ®istry,
|
.registry = ®istry,
|
||||||
}, .{});
|
}, .{});
|
||||||
defer testing.allocator.free(json);
|
defer testing.allocator.free(json);
|
||||||
@@ -495,4 +517,61 @@ test "cdp Node: Writer" {
|
|||||||
} },
|
} },
|
||||||
}, json);
|
}, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const node = registry.lookup_by_id.get(1).?;
|
||||||
|
const json = try std.json.stringifyAlloc(testing.allocator, Writer{
|
||||||
|
.root = node,
|
||||||
|
.depth = -1,
|
||||||
|
.exclude_root = true,
|
||||||
|
.registry = ®istry,
|
||||||
|
}, .{});
|
||||||
|
defer testing.allocator.free(json);
|
||||||
|
|
||||||
|
try testing.expectJson(&.{ .{
|
||||||
|
.nodeId = 2,
|
||||||
|
.backendNodeId = 2,
|
||||||
|
.nodeType = 1,
|
||||||
|
.nodeName = "HEAD",
|
||||||
|
.localName = "head",
|
||||||
|
.nodeValue = "",
|
||||||
|
.childNodeCount = 0,
|
||||||
|
.documentURL = null,
|
||||||
|
.baseURL = null,
|
||||||
|
.xmlVersion = "",
|
||||||
|
.compatibilityMode = "NoQuirksMode",
|
||||||
|
.isScrollable = false,
|
||||||
|
.parentId = 1,
|
||||||
|
}, .{
|
||||||
|
.nodeId = 3,
|
||||||
|
.backendNodeId = 3,
|
||||||
|
.nodeType = 1,
|
||||||
|
.nodeName = "BODY",
|
||||||
|
.localName = "body",
|
||||||
|
.nodeValue = "",
|
||||||
|
.childNodeCount = 2,
|
||||||
|
.documentURL = null,
|
||||||
|
.baseURL = null,
|
||||||
|
.xmlVersion = "",
|
||||||
|
.compatibilityMode = "NoQuirksMode",
|
||||||
|
.isScrollable = false,
|
||||||
|
.children = &.{ .{
|
||||||
|
.nodeId = 4,
|
||||||
|
.localName = "a",
|
||||||
|
.childNodeCount = 0,
|
||||||
|
.parentId = 3,
|
||||||
|
}, .{
|
||||||
|
.nodeId = 5,
|
||||||
|
.localName = "div",
|
||||||
|
.childNodeCount = 1,
|
||||||
|
.parentId = 3,
|
||||||
|
.children = &.{ .{
|
||||||
|
.nodeId = 6,
|
||||||
|
.localName = "a",
|
||||||
|
.childNodeCount = 0,
|
||||||
|
.parentId = 5,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
} } }, json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -402,10 +402,11 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
|||||||
return &self.isolated_world.?;
|
return &self.isolated_world.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nodeWriter(self: *Self, node: *const Node, opts: Node.Writer.Opts) Node.Writer {
|
pub fn nodeWriter(self: *Self, root: *const Node, opts: Node.Writer.Opts) Node.Writer {
|
||||||
return .{
|
return .{
|
||||||
.node = node,
|
.root = root,
|
||||||
.opts = opts,
|
.depth = opts.depth,
|
||||||
|
.exclude_root = opts.exclude_root,
|
||||||
.registry = &self.node_registry,
|
.registry = &self.node_registry,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ pub fn processMessage(cmd: anytype) !void {
|
|||||||
scrollIntoViewIfNeeded,
|
scrollIntoViewIfNeeded,
|
||||||
getContentQuads,
|
getContentQuads,
|
||||||
getBoxModel,
|
getBoxModel,
|
||||||
|
requestChildNodes,
|
||||||
}, cmd.input.action) orelse return error.UnknownMethod;
|
}, cmd.input.action) orelse return error.UnknownMethod;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
@@ -53,6 +54,7 @@ pub fn processMessage(cmd: anytype) !void {
|
|||||||
.scrollIntoViewIfNeeded => return scrollIntoViewIfNeeded(cmd),
|
.scrollIntoViewIfNeeded => return scrollIntoViewIfNeeded(cmd),
|
||||||
.getContentQuads => return getContentQuads(cmd),
|
.getContentQuads => return getContentQuads(cmd),
|
||||||
.getBoxModel => return getBoxModel(cmd),
|
.getBoxModel => return getBoxModel(cmd),
|
||||||
|
.requestChildNodes => return requestChildNodes(cmd),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,6 +435,29 @@ fn getBoxModel(cmd: anytype) !void {
|
|||||||
} }, .{});
|
} }, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn requestChildNodes(cmd: anytype) !void {
|
||||||
|
const params = (try cmd.params(struct {
|
||||||
|
nodeId: Node.Id,
|
||||||
|
depth: i32 = 1,
|
||||||
|
pierce: bool = false,
|
||||||
|
})) orelse return error.InvalidParams;
|
||||||
|
|
||||||
|
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||||
|
const session_id = bc.session_id orelse return error.SessionIdNotLoaded;
|
||||||
|
const node = bc.node_registry.lookup_by_id.get(params.nodeId) orelse {
|
||||||
|
return error.InvalidNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
try cmd.sendEvent("DOM.setChildNodes", .{
|
||||||
|
.parentId = node.id,
|
||||||
|
.nodes = bc.nodeWriter(node, .{.depth = params.depth, .exclude_root = true}),
|
||||||
|
}, .{
|
||||||
|
.session_id = session_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return cmd.sendResult(null, .{});
|
||||||
|
}
|
||||||
|
|
||||||
const testing = @import("../testing.zig");
|
const testing = @import("../testing.zig");
|
||||||
|
|
||||||
test "cdp.dom: getSearchResults unknown search id" {
|
test "cdp.dom: getSearchResults unknown search id" {
|
||||||
|
|||||||
Reference in New Issue
Block a user