diff --git a/src/browser/dom/node.zig b/src/browser/dom/node.zig index 08c8f42e..3a2f47be 100644 --- a/src/browser/dom/node.zig +++ b/src/browser/dom/node.zig @@ -111,6 +111,9 @@ pub const Node = struct { // -------- // Read-only attributes + pub fn get_baseURI(_: *parser.Node, page: *Page) ![]const u8 { + return page.url.raw; + } pub fn get_firstChild(self: *parser.Node) !?Union { const res = try parser.nodeFirstChild(self); @@ -737,6 +740,10 @@ test "Browser.DOM.node" { .{ "link.normalize()", "undefined" }, }, .{}); + try runner.testCases(&.{ + .{ "link.baseURI", "https://lightpanda.io/opensource-browser/" }, + }, .{}); + try runner.testCases(&.{ .{ "content.removeChild(append) !== undefined", "true" }, .{ "last_child.__proto__.constructor.name !== 'HTMLHeadingElement'", "true" }, diff --git a/src/browser/page.zig b/src/browser/page.zig index eff09152..ba57167b 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -141,8 +141,13 @@ pub const Page = struct { 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. - 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| { // raw_data was set if the document was not HTML, dump the data content only. 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. 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 tag after + // . + // If 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 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 { diff --git a/src/main.zig b/src/main.zig index cb99d227..4ca6db09 100644 --- a/src/main.zig +++ b/src/main.zig @@ -134,7 +134,10 @@ fn run(alloc: Allocator) !void { // dump if (opts.dump) { - try page.dump(.{ .exclude_scripts = opts.noscript }, std.io.getStdOut()); + try page.dump(.{ + .exclude_scripts = opts.noscript, + .with_base = opts.withbase, + }, std.io.getStdOut()); } }, else => unreachable, @@ -213,6 +216,7 @@ const Command = struct { dump: bool = false, common: Common, noscript: bool = false, + withbase: bool = false, }; const Common = struct { @@ -277,6 +281,7 @@ const Command = struct { \\--dump Dumps document to stdout. \\ Defaults to false. \\--noscript Exclude