Merge pull request #1428 from lightpanda-io/parser_arena_pool

Use ArenaPool when parsing HTML and for TextDecoder (with finalizer)
This commit is contained in:
Karl Seguin
2026-01-29 06:49:14 +08:00
committed by GitHub
5 changed files with 40 additions and 11 deletions

View File

@@ -737,7 +737,10 @@ fn pageDoneCallback(ctx: *anyopaque) !void {
switch (self._parse_state) { switch (self._parse_state) {
.html => |buf| { .html => |buf| {
var parser = Parser.init(self.arena, self.document.asNode(), self); const parse_arena = try self.getArena(.{ .debug = "Page.parse" });
defer self.releaseArena(parse_arena);
var parser = Parser.init(parse_arena, self.document.asNode(), self);
parser.parse(buf.items); parser.parse(buf.items);
self._script_manager.staticScriptsDone(); self._script_manager.staticScriptsDone();
if (self._script_manager.isDone()) { if (self._script_manager.isDone()) {
@@ -749,7 +752,11 @@ fn pageDoneCallback(ctx: *anyopaque) !void {
}, },
.text => |*buf| { .text => |*buf| {
try buf.appendSlice(self.arena, "</pre></body></html>"); try buf.appendSlice(self.arena, "</pre></body></html>");
var parser = Parser.init(self.arena, self.document.asNode(), self);
const parse_arena = try self.getArena(.{ .debug = "Page.parse" });
defer self.releaseArena(parse_arena);
var parser = Parser.init(parse_arena, self.document.asNode(), self);
parser.parse(buf.items); parser.parse(buf.items);
self.documentIsComplete(); self.documentIsComplete();
}, },

View File

@@ -48,6 +48,9 @@ pub fn parseFromString(
@"image/svg+xml", @"image/svg+xml",
}, mime_type) orelse return error.NotSupported; }, mime_type) orelse return error.NotSupported;
const arena = try page.getArena(.{ .debug = "DOMParser.parseFromString" });
defer page.releaseArena(arena);
return switch (target_mime) { return switch (target_mime) {
.@"text/html" => { .@"text/html" => {
// Create a new HTMLDocument // Create a new HTMLDocument
@@ -61,7 +64,7 @@ pub fn parseFromString(
} }
// Parse HTML into the document // Parse HTML into the document
var parser = Parser.init(page.arena, doc.asNode(), page); var parser = Parser.init(arena, doc.asNode(), page);
parser.parse(normalized); parser.parse(normalized);
if (parser.err) |pe| { if (parser.err) |pe| {
@@ -78,7 +81,7 @@ pub fn parseFromString(
// Parse XML into XMLDocument. // Parse XML into XMLDocument.
const doc_node = doc.asNode(); const doc_node = doc.asNode();
var parser = Parser.init(page.arena, doc_node, page); var parser = Parser.init(arena, doc_node, page);
parser.parseXML(html); parser.parseXML(html);
if (parser.err) |pe| { if (parser.err) |pe| {

View File

@@ -648,7 +648,10 @@ pub fn write(self: *Document, text: []const []const u8, page: *Page) !void {
page._parse_mode = .document_write; page._parse_mode = .document_write;
defer page._parse_mode = previous_parse_mode; defer page._parse_mode = previous_parse_mode;
var parser = Parser.init(page.call_arena, fragment_node, page); const arena = try page.getArena(.{ .debug = "Document.write" });
defer page.releaseArena(arena);
var parser = Parser.init(arena, fragment_node, page);
parser.parseFragment(html); parser.parseFragment(html);
// Extract children from wrapper HTML element (html5ever wraps fragments) // Extract children from wrapper HTML element (html5ever wraps fragments)
@@ -661,7 +664,7 @@ pub fn write(self: *Document, text: []const []const u8, page: *Page) !void {
var it = if (first.is(Element.Html.Html) == null) fragment_node.childrenIterator() else first.childrenIterator(); var it = if (first.is(Element.Html.Html) == null) fragment_node.childrenIterator() else first.childrenIterator();
while (it.next()) |child| { while (it.next()) |child| {
try children_to_insert.append(page.call_arena, child); try children_to_insert.append(arena, child);
} }
if (children_to_insert.items.len == 0) { if (children_to_insert.items.len == 0) {

View File

@@ -281,8 +281,11 @@ pub fn insertAdjacentHTML(
}); });
const doc_node = doc.asNode(); const doc_node = doc.asNode();
const arena = try page.getArena(.{ .debug = "HTML.insertAdjacentHTML" });
defer page.releaseArena(arena);
const Parser = @import("../../parser/Parser.zig"); const Parser = @import("../../parser/Parser.zig");
var parser = Parser.init(page.call_arena, doc_node, page); var parser = Parser.init(arena, doc_node, page);
parser.parse(html); parser.parse(html);
// Check if there's parsing error. // Check if there's parsing error.

View File

@@ -25,8 +25,9 @@ const Allocator = std.mem.Allocator;
const TextDecoder = @This(); const TextDecoder = @This();
_fatal: bool, _fatal: bool,
_ignore_bom: bool, _page: *Page,
_arena: Allocator, _arena: Allocator,
_ignore_bom: bool,
_stream: std.ArrayListUnmanaged(u8), _stream: std.ArrayListUnmanaged(u8),
const Label = enum { const Label = enum {
@@ -45,13 +46,23 @@ pub fn init(label_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*TextDecoder {
_ = std.meta.stringToEnum(Label, label) orelse return error.RangeError; _ = std.meta.stringToEnum(Label, label) orelse return error.RangeError;
} }
const arena = try page.getArena(.{ .debug = "TextDecoder" });
errdefer page.releaseArena(arena);
const opts = opts_ orelse InitOpts{}; const opts = opts_ orelse InitOpts{};
return page._factory.create(TextDecoder{ const self = try arena.create(TextDecoder);
._arena = page.arena, self.* = .{
._page = page,
._arena = arena,
._stream = .empty, ._stream = .empty,
._fatal = opts.fatal, ._fatal = opts.fatal,
._ignore_bom = opts.ignoreBOM, ._ignore_bom = opts.ignoreBOM,
}); };
return self;
}
pub fn deinit(self: *TextDecoder, _: bool) void {
self._page.releaseArena(self._arena);
} }
pub fn getEncoding(_: *const TextDecoder) []const u8 { pub fn getEncoding(_: *const TextDecoder) []const u8 {
@@ -103,6 +114,8 @@ pub const JsApi = struct {
pub const name = "TextDecoder"; pub const name = "TextDecoder";
pub const prototype_chain = bridge.prototypeChain(); pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined; pub var class_id: bridge.ClassId = undefined;
pub const weak = true;
pub const finalizer = bridge.finalizer(TextDecoder.deinit);
}; };
pub const constructor = bridge.constructor(TextDecoder.init, .{}); pub const constructor = bridge.constructor(TextDecoder.init, .{});