skip null nodes when a create callback fails

xml5ever accepts processing instruction targets that the DOM rejects,
like <?1x?>. createProcessingInstruction then fails and returns null,
but xml5ever still appends the null node, and getNode segfaults.

Make NodeOrText.node optional and skip failed nodes in the append
callbacks. Parser.err is already set, so the XML parse returns a
<parsererror> document.
This commit is contained in:
Pierre Tachoire committed 2026-09-11 12:23:00 +02:00
1 parent 9dc7b1bcb2
commit 76a2ff46ed
3 files changed
+11 -2

No files matched your search

+2
View File
@@ -728,6 +728,7 @@ fn _appendCallback(self: *Parser, parent: *Node, node_or_text: h5e.NodeOrText) !
try self.frame.appendNew(parent, child);
},
.text => |txt| try self.appendTextChunk(parent, txt),
.failed => {},
}
}
@@ -801,6 +802,7 @@ fn _appendBeforeSiblingCallback(self: *Parser, sibling: *Node, node_or_text: h5e
break :blk child;
},
.text => |txt| try Frame.node_factory.createTextNode(self.frame, txt),
.failed => return,
};
try self.frame.insertNodeRelative(parent, node, .{ .before = sibling }, .{});
}
+7 -2
View File
@@ -184,12 +184,15 @@ pub const AttributeIterator = extern struct {
pub const NodeOrText = extern struct {
tag: u8,
node: *anyopaque,
// Null for text. Also null for a node when its create callback failed:
// html5ever still appends the null ref it got back.
node: ?*anyopaque,
text: StringSlice,
pub fn toUnion(self: NodeOrText) Union {
if (self.tag == 0) {
return .{ .node = @ptrCast(@alignCast(self.node)) };
const node = self.node orelse return .failed;
return .{ .node = @ptrCast(@alignCast(node)) };
}
return .{ .text = self.text.slice() };
}
@@ -197,6 +200,8 @@ pub const NodeOrText = extern struct {
const Union = union(enum) {
node: *ParsedNode,
text: []const u8,
// The create callback failed and already set Parser.err.
failed,
};
};
+2
View File
@@ -506,6 +506,8 @@
'<a b="1" b="2"/>', // duplicate attribute
'<x:a>1</x:a>', // undeclared prefix
'<a>&#0;</a>',
'<a><?1x?></a>', // invalid processing instruction target
'<?1x?><a/>',
]) {
testing.expectEqual(bad + ' -> error', bad + (isError(p.parseFromString(bad, 'text/xml')) ? ' -> error' : ' -> ok'));
}