Add RC support to NodeList

Most importantly, this allows the Selector.List to be self-contained with
an arena from the ArenaPool. Selector.List can be both relatively large
and relatively common, so moving it off the page.arena is a nice win.

Also applied this to ChildNodes, which is much smaller but could also be
called often.

I was initially going to hook into the v8::Object's internal fields to store
the referencing v8::Object. So the v8::Object representing the Iterator
would store the v8::Object representing the NodeList inside of its internal
field - which the GC would trace/detect/respect. And that is probably the
fastest and most v8-ish solution, but I couldn't come up with an elegant
solution. The best I had was having a "afterCreate" callback which passed
the v8 object (this is similar to the old postAttach callback we had, but
used for a different purpose). However, since "acquireRef" was recently
added to events, re-using that was much simpler and worked well.
This commit is contained in:
Karl Seguin
2026-02-27 10:17:30 +08:00
parent a14ad6f700
commit 315c9a2d92
10 changed files with 131 additions and 31 deletions

View File

@@ -400,20 +400,32 @@ test "cdp Node: search list" {
defer page._session.removePage();
var doc = page.window._document;
const s1 = try search_list.create((try doc.querySelectorAll(.wrap("a"), page))._nodes);
try testing.expectEqual("1", s1.name);
try testing.expectEqualSlices(u32, &.{ 1, 2 }, s1.node_ids);
{
const l1 = try doc.querySelectorAll(.wrap("a"), page);
defer l1.deinit(page);
const s1 = try search_list.create(l1._nodes);
try testing.expectEqual("1", s1.name);
try testing.expectEqualSlices(u32, &.{ 1, 2 }, s1.node_ids);
}
try testing.expectEqual(2, registry.lookup_by_id.count());
try testing.expectEqual(2, registry.lookup_by_node.count());
const s2 = try search_list.create((try doc.querySelectorAll(.wrap("#a1"), page))._nodes);
try testing.expectEqual("2", s2.name);
try testing.expectEqualSlices(u32, &.{1}, s2.node_ids);
{
const l2 = try doc.querySelectorAll(.wrap("#a1"), page);
defer l2.deinit(page);
const s2 = try search_list.create(l2._nodes);
try testing.expectEqual("2", s2.name);
try testing.expectEqualSlices(u32, &.{1}, s2.node_ids);
}
const s3 = try search_list.create((try doc.querySelectorAll(.wrap("#a2"), page))._nodes);
try testing.expectEqual("3", s3.name);
try testing.expectEqualSlices(u32, &.{2}, s3.node_ids);
{
const l3 = try doc.querySelectorAll(.wrap("#a2"), page);
defer l3.deinit(page);
const s3 = try search_list.create(l3._nodes);
try testing.expectEqual("3", s3.name);
try testing.expectEqualSlices(u32, &.{2}, s3.node_ids);
}
try testing.expectEqual(2, registry.lookup_by_id.count());
try testing.expectEqual(2, registry.lookup_by_node.count());

View File

@@ -98,6 +98,8 @@ fn performSearch(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
const list = try Selector.querySelectorAll(page.window._document.asNode(), params.query, page);
defer list.deinit(page);
const search = try bc.node_search_list.create(list._nodes);
// dispatch setChildNodesEvents to inform the client of the subpart of node
@@ -247,6 +249,8 @@ fn querySelectorAll(cmd: anytype) !void {
};
const selected_nodes = try Selector.querySelectorAll(node.dom, params.selector, page);
defer selected_nodes.deinit(page);
const nodes = selected_nodes._nodes;
const node_ids = try cmd.arena.alloc(Node.Id, nodes.len);