add <base> in the DOM tree

This commit is contained in:
Pierre Tachoire
2025-08-08 18:31:08 +02:00
parent 5e30a3997e
commit dc23a74e7b
3 changed files with 34 additions and 12 deletions

View File

@@ -23,7 +23,6 @@ const Walker = @import("dom/walker.zig").WalkerChildren;
pub const Opts = struct { pub const Opts = struct {
exclude_scripts: bool = false, exclude_scripts: bool = false,
include_base: ?[]const u8 = null,
}; };
// writer must be a std.io.Writer // writer must be a std.io.Writer
@@ -92,14 +91,6 @@ pub fn writeNode(node: *parser.Node, opts: Opts, writer: anytype) anyerror!void
// void elements can't have any content. // void elements can't have any content.
if (try isVoid(parser.nodeToElement(node))) return; if (try isVoid(parser.nodeToElement(node))) return;
// If we wrote the <head> and we want to include a <base>, add it
// now.
if (opts.include_base != null and tag_type == .head) {
try writer.writeAll("<base href=\"");
try writer.writeAll(opts.include_base.?);
try writer.writeAll("\">");
}
if (tag_type == .script) { if (tag_type == .script) {
try writer.writeAll(try parser.nodeTextContent(node) orelse ""); try writer.writeAll(try parser.nodeTextContent(node) orelse "");
} else { } else {

View File

@@ -141,8 +141,13 @@ pub const Page = struct {
repeat_delay.* = 100 * std.time.ns_per_ms; repeat_delay.* = 100 * std.time.ns_per_ms;
} }
pub const DumpOpts = struct {
exclude_scripts: bool = false,
with_base: bool = false,
};
// dump writes the page content into the given file. // dump writes the page content into the given file.
pub fn dump(self: *const Page, opts: Dump.Opts, out: std.fs.File) !void { pub fn dump(self: *const Page, opts: DumpOpts, out: std.fs.File) !void {
if (self.raw_data) |raw_data| { if (self.raw_data) |raw_data| {
// raw_data was set if the document was not HTML, dump the data content only. // raw_data was set if the document was not HTML, dump the data content only.
return try out.writeAll(raw_data); return try out.writeAll(raw_data);
@@ -150,7 +155,33 @@ pub const Page = struct {
// if the page has a pointer to a document, dumps the HTML. // if the page has a pointer to a document, dumps the HTML.
const doc = parser.documentHTMLToDocument(self.window.document); const doc = parser.documentHTMLToDocument(self.window.document);
try Dump.writeHTML(doc, opts, out);
// if the base si requested, add the base's node in the document's headers.
if (opts.with_base) {
try self.addDOMTreeBase();
}
try Dump.writeHTML(doc, .{
.exclude_scripts = opts.exclude_scripts,
}, out);
}
// addDOMTreeBase modifies the page's document to add a <base> tag after
// <head>.
// If <head> is missing, the function returns silently.
fn addDOMTreeBase(self: *const Page) !void {
const doc = parser.documentHTMLToDocument(self.window.document);
std.debug.assert(doc.is_html);
// find <head> tag
const list = try parser.documentGetElementsByTagName(doc, "head");
const head = try parser.nodeListItem(list, 0) orelse return;
const base = try parser.documentCreateElement(doc, "base");
try parser.elementSetAttribute(base, "href", self.url.raw);
const Node = @import("dom/node.zig").Node;
try Node.prepend(head, &[_]Node.NodeOrText{.{ .node = parser.elementToNode(base) }});
} }
pub fn fetchModuleSource(ctx: *anyopaque, src: []const u8) !?[]const u8 { pub fn fetchModuleSource(ctx: *anyopaque, src: []const u8) !?[]const u8 {

View File

@@ -136,7 +136,7 @@ fn run(alloc: Allocator) !void {
if (opts.dump) { if (opts.dump) {
try page.dump(.{ try page.dump(.{
.exclude_scripts = opts.noscript, .exclude_scripts = opts.noscript,
.include_base = if (opts.withbase) page.url.raw else null, .with_base = opts.withbase,
}, std.io.getStdOut()); }, std.io.getStdOut());
} }
}, },