mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
Merge pull request #587 from lightpanda-io/dom-setchildnodes
cdp: dispatch DOM.setChildNodes event for search results
This commit is contained in:
@@ -99,7 +99,8 @@ pub const Element = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_attributes(self: *parser.Element) !*parser.NamedNodeMap {
|
pub fn get_attributes(self: *parser.Element) !*parser.NamedNodeMap {
|
||||||
return try parser.nodeGetAttributes(parser.elementToNode(self));
|
// An element must have non-nil attributes.
|
||||||
|
return try parser.nodeGetAttributes(parser.elementToNode(self)) orelse unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_innerHTML(self: *parser.Element, state: *SessionState) ![]const u8 {
|
pub fn get_innerHTML(self: *parser.Element, state: *SessionState) ![]const u8 {
|
||||||
|
|||||||
@@ -38,18 +38,18 @@ pub fn writeNode(node: *parser.Node, writer: anytype) anyerror!void {
|
|||||||
try writer.writeAll(tag);
|
try writer.writeAll(tag);
|
||||||
|
|
||||||
// write the attributes
|
// write the attributes
|
||||||
const map = try parser.nodeGetAttributes(node);
|
const _map = try parser.nodeGetAttributes(node);
|
||||||
|
if (_map) |map| {
|
||||||
const ln = try parser.namedNodeMapGetLength(map);
|
const ln = try parser.namedNodeMapGetLength(map);
|
||||||
var i: u32 = 0;
|
for (0..ln) |i| {
|
||||||
while (i < ln) {
|
const attr = try parser.namedNodeMapItem(map, @intCast(i)) orelse break;
|
||||||
const attr = try parser.namedNodeMapItem(map, i) orelse break;
|
|
||||||
try writer.writeAll(" ");
|
try writer.writeAll(" ");
|
||||||
try writer.writeAll(try parser.attributeGetName(attr));
|
try writer.writeAll(try parser.attributeGetName(attr));
|
||||||
try writer.writeAll("=\"");
|
try writer.writeAll("=\"");
|
||||||
const attribute_value = try parser.attributeGetValue(attr) orelse "";
|
const attribute_value = try parser.attributeGetValue(attr) orelse "";
|
||||||
try writeEscapedAttributeValue(writer, attribute_value);
|
try writeEscapedAttributeValue(writer, attribute_value);
|
||||||
try writer.writeAll("\"");
|
try writer.writeAll("\"");
|
||||||
i += 1;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try writer.writeAll(">");
|
try writer.writeAll(">");
|
||||||
|
|||||||
@@ -1250,11 +1250,11 @@ pub fn nodeHasAttributes(node: *Node) !bool {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nodeGetAttributes(node: *Node) !*NamedNodeMap {
|
pub fn nodeGetAttributes(node: *Node) !?*NamedNodeMap {
|
||||||
var res: ?*NamedNodeMap = undefined;
|
var res: ?*NamedNodeMap = undefined;
|
||||||
const err = nodeVtable(node).dom_node_get_attributes.?(node, &res);
|
const err = nodeVtable(node).dom_node_get_attributes.?(node, &res);
|
||||||
try DOMErr(err);
|
try DOMErr(err);
|
||||||
return res.?;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nodeGetNamespace(node: *Node) !?[]const u8 {
|
pub fn nodeGetNamespace(node: *Node) !?[]const u8 {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const Node = @This();
|
|||||||
|
|
||||||
id: Id,
|
id: Id,
|
||||||
_node: *parser.Node,
|
_node: *parser.Node,
|
||||||
|
set_child_nodes_event: bool,
|
||||||
|
|
||||||
// Whenever we send a node to the client, we register it here for future lookup.
|
// Whenever we send a node to the client, we register it here for future lookup.
|
||||||
// We maintain a node -> id and id -> node lookup.
|
// We maintain a node -> id and id -> node lookup.
|
||||||
@@ -85,6 +86,7 @@ pub const Registry = struct {
|
|||||||
node.* = .{
|
node.* = .{
|
||||||
._node = n,
|
._node = n,
|
||||||
.id = id,
|
.id = id,
|
||||||
|
.set_child_nodes_event = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
node_lookup_gop.value_ptr.* = node;
|
node_lookup_gop.value_ptr.* = node;
|
||||||
@@ -218,7 +220,7 @@ pub const Writer = struct {
|
|||||||
|
|
||||||
fn toJSON(self: *const Writer, w: anytype) !void {
|
fn toJSON(self: *const Writer, w: anytype) !void {
|
||||||
try w.beginObject();
|
try w.beginObject();
|
||||||
try writeCommon(self.node, false, w);
|
try self.writeCommon(self.node, false, w);
|
||||||
|
|
||||||
{
|
{
|
||||||
var registry = self.registry;
|
var registry = self.registry;
|
||||||
@@ -232,7 +234,7 @@ pub const Writer = struct {
|
|||||||
const child = (try parser.nodeListItem(child_nodes, @intCast(i))) orelse break;
|
const child = (try parser.nodeListItem(child_nodes, @intCast(i))) orelse break;
|
||||||
const child_node = try registry.register(child);
|
const child_node = try registry.register(child);
|
||||||
try w.beginObject();
|
try w.beginObject();
|
||||||
try writeCommon(child_node, true, w);
|
try self.writeCommon(child_node, true, w);
|
||||||
try w.endObject();
|
try w.endObject();
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
@@ -245,7 +247,7 @@ pub const Writer = struct {
|
|||||||
try w.endObject();
|
try w.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeCommon(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 {
|
||||||
try w.objectField("nodeId");
|
try w.objectField("nodeId");
|
||||||
try w.write(node.id);
|
try w.write(node.id);
|
||||||
|
|
||||||
@@ -254,9 +256,24 @@ pub const Writer = struct {
|
|||||||
|
|
||||||
const n = node._node;
|
const n = node._node;
|
||||||
|
|
||||||
// TODO:
|
if (try parser.nodeParentNode(n)) |p| {
|
||||||
// try w.objectField("parentId");
|
const parent_node = try self.registry.register(p);
|
||||||
// try w.write(pid);
|
try w.objectField("parentId");
|
||||||
|
try w.write(parent_node.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const _map = try parser.nodeGetAttributes(n);
|
||||||
|
if (_map) |map| {
|
||||||
|
const attr_count = try parser.namedNodeMapGetLength(map);
|
||||||
|
try w.objectField("attributes");
|
||||||
|
try w.beginArray();
|
||||||
|
for (0..attr_count) |i| {
|
||||||
|
const attr = try parser.namedNodeMapItem(map, @intCast(i)) orelse continue;
|
||||||
|
try w.write(try parser.attributeGetName(attr));
|
||||||
|
try w.write(try parser.attributeGetValue(attr) orelse continue);
|
||||||
|
}
|
||||||
|
try w.endArray();
|
||||||
|
}
|
||||||
|
|
||||||
try w.objectField("nodeType");
|
try w.objectField("nodeType");
|
||||||
try w.write(@intFromEnum(try parser.nodeType(n)));
|
try w.write(@intFromEnum(try parser.nodeType(n)));
|
||||||
@@ -461,6 +478,7 @@ test "cdp Node: Writer" {
|
|||||||
.xmlVersion = "",
|
.xmlVersion = "",
|
||||||
.compatibilityMode = "NoQuirksMode",
|
.compatibilityMode = "NoQuirksMode",
|
||||||
.isScrollable = false,
|
.isScrollable = false,
|
||||||
|
.parentId = 1,
|
||||||
}, .{
|
}, .{
|
||||||
.nodeId = 3,
|
.nodeId = 3,
|
||||||
.backendNodeId = 3,
|
.backendNodeId = 3,
|
||||||
@@ -474,6 +492,7 @@ test "cdp Node: Writer" {
|
|||||||
.xmlVersion = "",
|
.xmlVersion = "",
|
||||||
.compatibilityMode = "NoQuirksMode",
|
.compatibilityMode = "NoQuirksMode",
|
||||||
.isScrollable = false,
|
.isScrollable = false,
|
||||||
|
.parentId = 1,
|
||||||
} },
|
} },
|
||||||
}, json);
|
}, json);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,12 +76,74 @@ fn performSearch(cmd: anytype) !void {
|
|||||||
|
|
||||||
const search = try bc.node_search_list.create(list.nodes.items);
|
const search = try bc.node_search_list.create(list.nodes.items);
|
||||||
|
|
||||||
|
// dispatch setChildNodesEvents to inform the client of the subpart of node
|
||||||
|
// tree covering the results.
|
||||||
|
try dispatchSetChildNodes(cmd, list.nodes.items);
|
||||||
|
|
||||||
return cmd.sendResult(.{
|
return cmd.sendResult(.{
|
||||||
.searchId = search.name,
|
.searchId = search.name,
|
||||||
.resultCount = @as(u32, @intCast(search.node_ids.len)),
|
.resultCount = @as(u32, @intCast(search.node_ids.len)),
|
||||||
}, .{});
|
}, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dispatchSetChildNodes send the setChildNodes event for the whole DOM tree
|
||||||
|
// hierarchy of each nodes.
|
||||||
|
// We dispatch event in the reverse order: from the top level to the direct parents.
|
||||||
|
// We should dispatch a node only if it has never been sent.
|
||||||
|
fn dispatchSetChildNodes(cmd: anytype, nodes: []*parser.Node) !void {
|
||||||
|
const arena = cmd.arena;
|
||||||
|
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||||
|
const session_id = bc.session_id orelse return error.SessionIdNotLoaded;
|
||||||
|
|
||||||
|
var parents: std.ArrayListUnmanaged(*Node) = .{};
|
||||||
|
for (nodes) |_n| {
|
||||||
|
var n = _n;
|
||||||
|
while (true) {
|
||||||
|
const p = try parser.nodeParentNode(n) orelse break;
|
||||||
|
|
||||||
|
// Register the node.
|
||||||
|
const node = try bc.node_registry.register(p);
|
||||||
|
if (node.set_child_nodes_event) break;
|
||||||
|
try parents.append(arena, node);
|
||||||
|
n = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const plen = parents.items.len;
|
||||||
|
if (plen == 0) return;
|
||||||
|
|
||||||
|
var i: usize = plen;
|
||||||
|
// We're going to iterate in reverse order from how we added them.
|
||||||
|
// This ensures that we're emitting the tree of nodes top-down.
|
||||||
|
while (i > 0) {
|
||||||
|
i -= 1;
|
||||||
|
const node = parents.items[i];
|
||||||
|
// Although our above loop won't add an already-sent node to `parents`
|
||||||
|
// this can still be true because two nodes can share the same parent node
|
||||||
|
// so we might have just sent the node a previous iteration of this loop
|
||||||
|
if (node.set_child_nodes_event) continue;
|
||||||
|
|
||||||
|
node.set_child_nodes_event = true;
|
||||||
|
|
||||||
|
// If the node has no parent, it's the root node.
|
||||||
|
// We don't dispatch event for it because we assume the root node is
|
||||||
|
// dispatched via the DOM.getDocument command.
|
||||||
|
const p = try parser.nodeParentNode(node._node) orelse {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieve the parent from the registry.
|
||||||
|
const parent_node = try bc.node_registry.register(p);
|
||||||
|
|
||||||
|
try cmd.sendEvent("DOM.setChildNodes", .{
|
||||||
|
.parentId = parent_node.id,
|
||||||
|
.nodes = .{bc.nodeWriter(node, .{})},
|
||||||
|
}, .{
|
||||||
|
.session_id = session_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-discardSearchResults
|
// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-discardSearchResults
|
||||||
fn discardSearchResults(cmd: anytype) !void {
|
fn discardSearchResults(cmd: anytype) !void {
|
||||||
const params = (try cmd.params(struct {
|
const params = (try cmd.params(struct {
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ const TestContext = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opts.html) |html| {
|
if (opts.html) |html| {
|
||||||
|
if (bc.session_id == null) bc.session_id = "SID-X";
|
||||||
parser.deinit();
|
parser.deinit();
|
||||||
try parser.init();
|
try parser.init();
|
||||||
const page = try bc.session.createPage();
|
const page = try bc.session.createPage();
|
||||||
|
|||||||
Reference in New Issue
Block a user