From c3ba39c80f52aa81be4f610be299a39eecdaf0ee Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Sun, 16 Nov 2025 07:53:55 +0800 Subject: [PATCH] add reparent_children html5ever callback --- src/browser/Page.zig | 11 ++--------- src/browser/parser/Parser.zig | 15 ++++++++++++++- src/browser/parser/html5ever.zig | 3 +++ src/html5ever/lib.rs | 6 ++++++ src/html5ever/sink.rs | 7 ++++--- src/html5ever/types.rs | 2 ++ 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/browser/Page.zig b/src/browser/Page.zig index a4feb165..a3b10986 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -1159,22 +1159,15 @@ pub fn appendNode(self: *Page, parent: *Node, child: *Node, opts: InsertNodeOpts return self._insertNodeRelative(false, parent, child, .append, opts); } -// Currently only called when appending DocumentFragment children, -// so optimized for that case. pub fn appendAllChildren(self: *Page, parent: *Node, target: *Node) !void { - // DocumentFragments are never connected so we set child_already_connected - // to false. This assertion exists to protect against any future use of this - // function where the parent is connected (and thus the hard-coded false - // must be changed) - std.debug.assert(!parent.isConnected()); - self.domChanged(); + const is_connected = parent.isConnected(); const dest_connected = target.isConnected(); var it = parent.childrenIterator(); while (it.next()) |child| { self.removeNode(parent, child, .{ .will_be_reconnected = dest_connected }); - try self.appendNode(target, child, .{ .child_already_connected = false }); + try self.appendNode(target, child, .{ .child_already_connected = is_connected }); } } diff --git a/src/browser/parser/Parser.zig b/src/browser/parser/Parser.zig index 28649758..e2c154dd 100644 --- a/src/browser/parser/Parser.zig +++ b/src/browser/parser/Parser.zig @@ -68,6 +68,7 @@ const Error = struct { add_attrs_if_missing, get_template_content, remove_from_parent, + reparent_children, }; }; @@ -87,6 +88,7 @@ pub fn parse(self: *Parser, html: []const u8) void { addAttrsIfMissingCallback, getTemplateContentsCallback, removeFromParentCallback, + reparentChildrenCallback, ); } @@ -106,6 +108,7 @@ pub fn parseFragment(self: *Parser, html: []const u8) void { addAttrsIfMissingCallback, getTemplateContentsCallback, removeFromParentCallback, + reparentChildrenCallback, ); } @@ -142,6 +145,7 @@ pub const Streaming = struct { addAttrsIfMissingCallback, getTemplateContentsCallback, removeFromParentCallback, + reparentChildrenCallback, ) orelse return error.ParserCreationFailed; } @@ -308,12 +312,21 @@ fn removeFromParentCallback(ctx: *anyopaque, target_ref: *anyopaque) callconv(.c self.err = .{ .err = err, .source = .remove_from_parent }; }; } - fn _removeFromParentCallback(self: *Parser, node: *Node) !void { const parent = node.parentNode() orelse return; _ = try parent.removeChild(node, self.page); } +fn reparentChildrenCallback(ctx: *anyopaque, node_ref: *anyopaque, new_parent_ref: *anyopaque) callconv(.c) void { + const self: *Parser = @ptrCast(@alignCast(ctx)); + self._reparentChildrenCallback(getNode(node_ref), getNode(new_parent_ref)) catch |err| { + self.err = .{ .err = err, .source = .reparent_children }; + }; +} +fn _reparentChildrenCallback(self: *Parser, node: *Node, new_parent: *Node) !void { + try self.page.appendAllChildren(node, new_parent); +} + fn getNode(ref: *anyopaque) *Node { const pn: *ParsedNode = @ptrCast(@alignCast(ref)); return pn.node; diff --git a/src/browser/parser/html5ever.zig b/src/browser/parser/html5ever.zig index bec5ebcf..727d9a73 100644 --- a/src/browser/parser/html5ever.zig +++ b/src/browser/parser/html5ever.zig @@ -33,6 +33,7 @@ pub extern "c" fn html5ever_parse_document( addAttrsIfMissingCallback: *const fn (ctx: *anyopaque, target_ref: *anyopaque, AttributeIterator) callconv(.c) void, getTemplateContentsCallback: *const fn (ctx: *anyopaque, target_ref: *anyopaque) callconv(.c) ?*anyopaque, removeFromParentCallback: *const fn (ctx: *anyopaque, target_ref: *anyopaque) callconv(.c) void, + reparentChildrenCallback: *const fn (ctx: *anyopaque, node_ref: *anyopaque, new_parent_ref: *anyopaque) callconv(.c) void, ) void; pub extern "c" fn html5ever_parse_fragment( @@ -50,6 +51,7 @@ pub extern "c" fn html5ever_parse_fragment( addAttrsIfMissingCallback: *const fn (ctx: *anyopaque, target_ref: *anyopaque, AttributeIterator) callconv(.c) void, getTemplateContentsCallback: *const fn (ctx: *anyopaque, target_ref: *anyopaque) callconv(.c) ?*anyopaque, removeFromParentCallback: *const fn (ctx: *anyopaque, target_ref: *anyopaque) callconv(.c) void, + reparentChildrenCallback: *const fn (ctx: *anyopaque, node_ref: *anyopaque, new_parent_ref: *anyopaque) callconv(.c) void, ) void; pub extern "c" fn html5ever_attribute_iterator_next(ctx: *anyopaque) Nullable(Attribute); @@ -76,6 +78,7 @@ pub extern "c" fn html5ever_streaming_parser_create( addAttrsIfMissingCallback: *const fn (ctx: *anyopaque, target_ref: *anyopaque, AttributeIterator) callconv(.c) void, getTemplateContentsCallback: *const fn (ctx: *anyopaque, target_ref: *anyopaque) callconv(.c) ?*anyopaque, removeFromParentCallback: *const fn (ctx: *anyopaque, target_ref: *anyopaque) callconv(.c) void, + reparentChildrenCallback: *const fn (ctx: *anyopaque, node_ref: *anyopaque, new_parent_ref: *anyopaque) callconv(.c) void, ) ?*anyopaque; pub extern "c" fn html5ever_streaming_parser_feed( diff --git a/src/html5ever/lib.rs b/src/html5ever/lib.rs index 7c74df00..992b00fd 100644 --- a/src/html5ever/lib.rs +++ b/src/html5ever/lib.rs @@ -47,6 +47,7 @@ pub extern "C" fn html5ever_parse_document( add_attrs_if_missing_callback: AddAttrsIfMissingCallback, get_template_contents_callback: GetTemplateContentsCallback, remove_from_parent_callback: RemoveFromParentCallback, + reparent_children_callback: ReparentChildrenCallback, ) -> () { if html.is_null() || len == 0 { return (); @@ -69,6 +70,7 @@ pub extern "C" fn html5ever_parse_document( add_attrs_if_missing_callback: add_attrs_if_missing_callback, get_template_contents_callback: get_template_contents_callback, remove_from_parent_callback: remove_from_parent_callback, + reparent_children_callback: reparent_children_callback, }; let bytes = unsafe { std::slice::from_raw_parts(html, len) }; @@ -93,6 +95,7 @@ pub extern "C" fn html5ever_parse_fragment( add_attrs_if_missing_callback: AddAttrsIfMissingCallback, get_template_contents_callback: GetTemplateContentsCallback, remove_from_parent_callback: RemoveFromParentCallback, + reparent_children_callback: ReparentChildrenCallback, ) -> () { if html.is_null() || len == 0 { return (); @@ -115,6 +118,7 @@ pub extern "C" fn html5ever_parse_fragment( add_attrs_if_missing_callback: add_attrs_if_missing_callback, get_template_contents_callback: get_template_contents_callback, remove_from_parent_callback: remove_from_parent_callback, + reparent_children_callback: reparent_children_callback, }; let bytes = unsafe { std::slice::from_raw_parts(html, len) }; @@ -198,6 +202,7 @@ pub extern "C" fn html5ever_streaming_parser_create( add_attrs_if_missing_callback: AddAttrsIfMissingCallback, get_template_contents_callback: GetTemplateContentsCallback, remove_from_parent_callback: RemoveFromParentCallback, + reparent_children_callback: ReparentChildrenCallback, ) -> *mut c_void { let arena = Box::new(typed_arena::Arena::new()); @@ -223,6 +228,7 @@ pub extern "C" fn html5ever_streaming_parser_create( add_attrs_if_missing_callback: add_attrs_if_missing_callback, get_template_contents_callback: get_template_contents_callback, remove_from_parent_callback: remove_from_parent_callback, + reparent_children_callback: reparent_children_callback, }; // Create a parser which implements TendrilSink for streaming parsing diff --git a/src/html5ever/sink.rs b/src/html5ever/sink.rs index f947e1f1..fbad55a5 100644 --- a/src/html5ever/sink.rs +++ b/src/html5ever/sink.rs @@ -58,6 +58,7 @@ pub struct Sink<'arena> { pub add_attrs_if_missing_callback: AddAttrsIfMissingCallback, pub get_template_contents_callback: GetTemplateContentsCallback, pub remove_from_parent_callback: RemoveFromParentCallback, + pub reparent_children_callback: ReparentChildrenCallback, } impl<'arena> TreeSink for Sink<'arena> { @@ -235,8 +236,8 @@ impl<'arena> TreeSink for Sink<'arena> { } fn reparent_children(&self, node: &Ref, new_parent: &Ref) { - _ = node; - _ = new_parent; - panic!("reparent_children"); + unsafe { + (self.reparent_children_callback)(self.ctx, *node, *new_parent); + } } } diff --git a/src/html5ever/types.rs b/src/html5ever/types.rs index 4c7b364d..ef078b87 100644 --- a/src/html5ever/types.rs +++ b/src/html5ever/types.rs @@ -61,6 +61,8 @@ pub type GetTemplateContentsCallback = unsafe extern "C" fn(ctx: Ref, target: Re pub type RemoveFromParentCallback = unsafe extern "C" fn(ctx: Ref, target: Ref) -> (); +pub type ReparentChildrenCallback = unsafe extern "C" fn(ctx: Ref, node: Ref, new_parent: Ref) -> (); + pub type Ref = *const c_void; #[repr(C)]