mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-31 17:55:59 -04:00
Improve gating around nodeIsReadySubtree to prevent unnecessary calls
Free temp DocumentFragment when possible
This commit is contained in:
@@ -2563,16 +2563,17 @@ pub fn moveAllChildren(self: *Frame, source: *Node, parent: *Node, ref_node: ?*N
|
||||
// Nodes moved into a not-yet-started script run the script's
|
||||
// children-changed steps first (whatwg/html#10188), then each inserted
|
||||
// node's own ready work runs, in tree order, with everything in place.
|
||||
// Ready work only happens on becoming connected, so a detached
|
||||
// destination has none.
|
||||
if (parent.isConnected()) {
|
||||
if (parent.is(Element.Html.Script)) |script| {
|
||||
if (!script._executed) {
|
||||
try self.nodeIsReady(false, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (moved.items) |child| {
|
||||
try self.nodeIsReadySubtree(child);
|
||||
for (moved.items) |child| {
|
||||
try self.nodeIsReadySubtree(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2681,17 +2682,17 @@ pub fn _insertNodeRelative(self: *Frame, comptime from_parser: bool, parent: *No
|
||||
// Inserting into a not-yet-started script runs the script's
|
||||
// children-changed steps first, then the inserted nodes' own ready work
|
||||
// (whatwg/html#10188: the outer script executes before an inner one).
|
||||
// Ready work only happens on becoming connected, so a detached parent
|
||||
// has none.
|
||||
if (opts.run_ready and parent_is_connected) {
|
||||
if (parent.is(Element.Html.Script)) |script| {
|
||||
if (!script._executed) {
|
||||
try self.nodeIsReady(false, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nodeIsReady resolves the node's owning frame itself (only for the few node
|
||||
// types that have ready work), so pass the incumbent `self`.
|
||||
if (opts.run_ready) {
|
||||
// nodeIsReady resolves the node's owning frame itself (only for the few node
|
||||
// types that have ready work), so pass the incumbent `self`.
|
||||
try self.nodeIsReadySubtree(child);
|
||||
}
|
||||
|
||||
@@ -2931,9 +2932,16 @@ fn nodeIsReadySubtree(self: *Frame, node: *Node) !void {
|
||||
if (node._type != .element or node.firstChild() == null) {
|
||||
return self.nodeIsReady(false, node);
|
||||
}
|
||||
|
||||
// Scripts can mutate the tree. Safe to do this since nodeIsReady re-checks
|
||||
// connectivity.
|
||||
var elements: std.ArrayList(*Node) = .empty;
|
||||
var tw = @import("webapi/TreeWalker.zig").Full.Elements.init(node, .{});
|
||||
while (tw.next()) |el| {
|
||||
try self.nodeIsReady(false, el.asNode());
|
||||
try elements.append(self.call_arena, el.asNode());
|
||||
}
|
||||
for (elements.items) |el| {
|
||||
try self.nodeIsReady(false, el);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1429,8 +1429,6 @@ pub fn getElementsByClassName(self: *Node, class_name: []const u8, frame: *Frame
|
||||
}, frame);
|
||||
}
|
||||
|
||||
/// Shared implementation of replaceChildren for Element, Document, and DocumentFragment.
|
||||
/// Validates all nodes, removes existing children, then appends new children.
|
||||
// ParentNode.append/prepend with several nodes convert them into a fragment
|
||||
// first, so the insertion happens as one operation: an earlier script must
|
||||
// observe its later siblings inserted (and can remove them before they run).
|
||||
@@ -1440,29 +1438,40 @@ pub fn appendNodes(self: *Node, nodes: []const NodeOrText, frame: *Frame) !void
|
||||
_ = try self.appendChild(child, frame);
|
||||
return;
|
||||
}
|
||||
const fragment = (try DocumentFragment.init(frame)).asNode();
|
||||
const fragment = try DocumentFragment.init(frame);
|
||||
const fragment_node = fragment.asNode();
|
||||
// The fragment is internal — JS never sees it, and no mutation record
|
||||
// targets it — so it can be reclaimed once its children have moved out.
|
||||
// If conversion or insertion failed, nodes left inside stay parented to
|
||||
// it (per spec), so it must live on.
|
||||
defer if (fragment_node.firstChild() == null) frame._factory.destroy(fragment);
|
||||
for (nodes) |node_or_text| {
|
||||
const child = try node_or_text.toNode(frame);
|
||||
_ = try fragment.appendChild(child, frame);
|
||||
_ = try fragment_node.appendChild(child, frame);
|
||||
}
|
||||
_ = try self.appendChild(fragment, frame);
|
||||
_ = try self.appendChild(fragment_node, frame);
|
||||
}
|
||||
|
||||
pub fn prependNodes(self: *Node, nodes: []const NodeOrText, frame: *Frame) !void {
|
||||
const reference = self.firstChild();
|
||||
if (nodes.len == 1) {
|
||||
const child = try nodes[0].toNode(frame);
|
||||
_ = try self.insertBefore(child, reference, frame);
|
||||
_ = try self.insertBefore(child, self.firstChild(), frame);
|
||||
return;
|
||||
}
|
||||
const fragment = (try DocumentFragment.init(frame)).asNode();
|
||||
const fragment = try DocumentFragment.init(frame);
|
||||
const fragment_node = fragment.asNode();
|
||||
defer if (fragment_node.firstChild() == null) frame._factory.destroy(fragment);
|
||||
for (nodes) |node_or_text| {
|
||||
const child = try node_or_text.toNode(frame);
|
||||
_ = try fragment.appendChild(child, frame);
|
||||
_ = try fragment_node.appendChild(child, frame);
|
||||
}
|
||||
_ = try self.insertBefore(fragment, reference, frame);
|
||||
// The reference child is evaluated after converting nodes into the
|
||||
// fragment: one of the arguments may be the current first child.
|
||||
_ = try self.insertBefore(fragment_node, self.firstChild(), frame);
|
||||
}
|
||||
|
||||
/// Shared implementation of replaceChildren for Element, Document, and DocumentFragment.
|
||||
/// Validates all nodes, removes existing children, then appends new children.
|
||||
pub fn replaceChildren(self: *Node, nodes: []const NodeOrText, frame: *Frame) !void {
|
||||
// First pass: validate all nodes and collect them
|
||||
// We need to collect because DocumentFragments contribute their children, not themselves
|
||||
|
||||
Reference in New Issue
Block a user