diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 7c6bcac4..88068f0c 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -471,12 +471,10 @@ pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !voi // It's important to force a reset during the following navigation. self._parse_state = .complete; - { - const parse_arena = try self.getArena(.{ .debug = "about:blank parse" }); - defer self.releaseArena(parse_arena); - var parser = Parser.init(parse_arena, self.document.asNode(), self); - parser.parse(""); - } + self.document.injectBlank(self) catch |err| { + log.err(.browser, "inject blank", .{ .err = err }); + return error.InjectBlankFailed; + }; self.documentIsComplete(); session.notification.dispatch(.page_navigate, &.{ @@ -1029,9 +1027,9 @@ pub fn iframeAddedCallback(self: *Page, iframe: *IFrame) !void { return; } - const src = iframe.asElement().getAttributeSafe(comptime .wrap("src")) orelse return; + var src = iframe.asElement().getAttributeSafe(comptime .wrap("src")) orelse ""; if (src.len == 0) { - return; + src = "about:blank"; } if (iframe._window != null) { @@ -2914,11 +2912,6 @@ fn nodeIsReady(self: *Page, comptime from_parser: bool, node: *Node) !void { return err; }; } else if (node.is(IFrame)) |iframe| { - if ((comptime from_parser == false) and iframe._src.len == 0) { - // iframe was added via JavaScript, but without a src - return; - } - self.iframeAddedCallback(iframe) catch |err| { log.err(.page, "page.nodeIsReady", .{ .err = err, .element = "iframe", .type = self._type, .url = self.url }); return err; diff --git a/src/browser/tests/frames/frames.html b/src/browser/tests/frames/frames.html index 10a22021..4e614de9 100644 --- a/src/browser/tests/frames/frames.html +++ b/src/browser/tests/frames/frames.html @@ -7,46 +7,59 @@ } - + + + + diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index 10af05fc..74a260e8 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -40,6 +40,8 @@ const Selection = @import("Selection.zig"); pub const XMLDocument = @import("XMLDocument.zig"); pub const HTMLDocument = @import("HTMLDocument.zig"); +const IS_DEBUG = @import("builtin").mode == .Debug; + const Document = @This(); _type: Type, @@ -937,6 +939,32 @@ fn validateElementName(name: []const u8) !void { } } +// When a page or frame's URL is about:blank, or as soon as a frame is +// programmatically created, it has this default "blank" content +pub fn injectBlank(self: *Document, page: *Page) error{InjectBlankError}!void { + self._injectBlank(page) catch |err| { + // we wrap _injectBlank like this so that injectBlank can only return an + // InjectBlankError. injectBlank is used in when nodes are inserted + // as since it inserts node itself, Zig can't infer the error set. + log.err(.browser, "inject blank", .{ .err = err }); + return error.InjectBlankError; + }; +} + +fn _injectBlank(self: *Document, page: *Page) !void { + if (comptime IS_DEBUG) { + // should only be called on an empty document + std.debug.assert(self.asNode()._children == null); + } + + const html = try page.createElementNS(.html, "html", null); + const head = try page.createElementNS(.html, "head", null); + const body = try page.createElementNS(.html, "body", null); + try page.appendNode(html, head, .{}); + try page.appendNode(html, body, .{}); + try page.appendNode(self.asNode(), html, .{}); +} + const ReadyState = enum { loading, interactive,