Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/browser/dom/comment.zig
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub const Comment = struct {

pub fn constructor(data: ?[]const u8, state: *const SessionState) !*parser.Comment {
return parser.documentCreateComment(
parser.documentHTMLToDocument(state.window.document.?),
parser.documentHTMLToDocument(state.window.document),
data orelse "",
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/browser/dom/document.zig
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ pub const Document = struct {

pub fn constructor(state: *const SessionState) !*parser.DocumentHTML {
const doc = try parser.documentCreateDocument(
try parser.documentHTMLGetTitle(state.window.document.?),
try parser.documentHTMLGetTitle(state.window.document),
);

// we have to work w/ document instead of html document.
const ddoc = parser.documentHTMLToDocument(doc);
const ccur = parser.documentHTMLToDocument(state.window.document.?);
const ccur = parser.documentHTMLToDocument(state.window.document);
try parser.documentSetDocumentURI(ddoc, try parser.documentGetDocumentURI(ccur));
try parser.documentSetInputEncoding(ddoc, try parser.documentGetInputEncoding(ccur));

Expand Down
2 changes: 1 addition & 1 deletion src/browser/dom/document_fragment.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub const DocumentFragment = struct {

pub fn constructor(state: *const SessionState) !*parser.DocumentFragment {
return parser.documentCreateDocumentFragment(
parser.documentHTMLToDocument(state.window.document.?),
parser.documentHTMLToDocument(state.window.document),
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/browser/dom/element.zig
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ pub const Element = struct {
pub fn _getBoundingClientRect(self: *parser.Element, state: *SessionState) !DOMRect {
// Since we are lazy rendering we need to do this check. We could store the renderer in a viewport such that it could cache these, but it would require tracking changes.
const root = try parser.nodeGetRootNode(parser.elementToNode(self));
if (root != parser.documentToNode(parser.documentHTMLToDocument(state.window.document.?))) {
if (root != parser.documentToNode(parser.documentHTMLToDocument(state.window.document))) {
return DOMRect{ .x = 0, .y = 0, .width = 0, .height = 0 };
}
return state.renderer.getRect(self);
Expand All @@ -381,7 +381,7 @@ pub const Element = struct {
// Returns an empty array if the element is eventually detached from the main window
pub fn _getClientRects(self: *parser.Element, state: *SessionState) ![]DOMRect {
const root = try parser.nodeGetRootNode(parser.elementToNode(self));
if (root != parser.documentToNode(parser.documentHTMLToDocument(state.window.document.?))) {
if (root != parser.documentToNode(parser.documentHTMLToDocument(state.window.document))) {
return &.{};
}
const heap_ptr = try state.call_arena.create(DOMRect);
Expand Down
4 changes: 2 additions & 2 deletions src/browser/dom/intersection_observer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub const IntersectionObserver = struct {
// new IntersectionObserver(callback, options) [not supported yet]
pub fn constructor(callback: Env.Callback, options_: ?IntersectionObserverOptions, state: *SessionState) !IntersectionObserver {
var options = IntersectionObserverOptions{
.root = parser.documentToNode(parser.documentHTMLToDocument(state.window.document.?)),
.root = parser.documentToNode(parser.documentHTMLToDocument(state.window.document)),
.rootMargin = "0px 0px 0px 0px",
.threshold = &.{0.0},
};
Expand Down Expand Up @@ -142,7 +142,7 @@ pub const IntersectionObserverEntry = struct {
// Returns a DOMRectReadOnly for the intersection observer's root.
pub fn get_rootBounds(self: *const IntersectionObserverEntry) !Element.DOMRect {
const root = self.options.root.?;
if (@intFromPtr(root) == @intFromPtr(self.state.window.document.?)) {
if (@intFromPtr(root) == @intFromPtr(self.state.window.document)) {
return self.state.renderer.boundingRect();
}

Expand Down
2 changes: 1 addition & 1 deletion src/browser/dom/processing_instruction.zig
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub const ProcessingInstruction = struct {
// a simple workaround.
pub fn _cloneNode(self: *parser.ProcessingInstruction, _: ?bool, state: *SessionState) !*parser.ProcessingInstruction {
return try parser.documentCreateProcessingInstruction(
@ptrCast(state.window.document.?),
@ptrCast(state.window.document),
try get_target(self),
(try get_data(self)) orelse "",
);
Expand Down
2 changes: 1 addition & 1 deletion src/browser/dom/text.zig
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub const Text = struct {

pub fn constructor(data: ?[]const u8, state: *const SessionState) !*parser.Text {
return parser.documentCreateTextNode(
parser.documentHTMLToDocument(state.window.document.?),
parser.documentHTMLToDocument(state.window.document),
data orelse "",
);
}
Expand Down
8 changes: 4 additions & 4 deletions src/browser/html/document.zig
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,10 @@ pub const HTMLDocument = struct {
// Thus we can add the HtmlHtmlElement and it's child HTMLBodyElement to the returned list.
// TBD Should we instead return every parent that is an element? Note that a child does not physically need to be overlapping the parent.
// Should we do a render pass on demand?
const doc_elem = try parser.documentGetDocumentElement(parser.documentHTMLToDocument(state.window.document.?)) orelse {
const doc_elem = try parser.documentGetDocumentElement(parser.documentHTMLToDocument(state.window.document)) orelse {
return list.items;
};
if (try parser.documentHTMLBody(state.window.document.?)) |body| {
if (try parser.documentHTMLBody(state.window.document)) |body| {
list.appendAssumeCapacity(try Element.toInterface(parser.bodyToElement(body)));
}
list.appendAssumeCapacity(try Element.toInterface(doc_elem));
Expand Down Expand Up @@ -383,12 +383,12 @@ test "Browser.HTML.Document" {
.{ "document.readyState", "loading" },
}, .{});

try HTMLDocument.documentIsLoaded(runner.window.document.?, &runner.state);
try HTMLDocument.documentIsLoaded(runner.window.document, &runner.state);
try runner.testCases(&.{
.{ "document.readyState", "interactive" },
}, .{});

try HTMLDocument.documentIsComplete(runner.window.document.?, &runner.state);
try HTMLDocument.documentIsComplete(runner.window.document, &runner.state);
try runner.testCases(&.{
.{ "document.readyState", "complete" },
}, .{});
Expand Down
12 changes: 8 additions & 4 deletions src/browser/html/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub const Window = struct {
// Extend libdom event target for pure zig struct.
base: parser.EventTargetTBase = parser.EventTargetTBase{},

document: ?*parser.DocumentHTML = null,
document: *parser.DocumentHTML,
target: []const u8 = "",
history: History = .{},
location: Location = .{},
Expand All @@ -60,7 +60,13 @@ pub const Window = struct {
performance: Performance,

pub fn create(target: ?[]const u8, navigator: ?Navigator) !Window {
var fbs = std.io.fixedBufferStream("");
const html_doc = try parser.documentHTMLParse(fbs.reader(), "utf-8");
const doc = parser.documentHTMLToDocument(html_doc);
try parser.documentSetDocumentURI(doc, "about:blank");

return .{
.document = html_doc,
.target = target orelse "",
.navigator = navigator orelse .{},
.performance = .{ .time_origin = try std.time.Timer.start() },
Expand All @@ -69,9 +75,7 @@ pub const Window = struct {

pub fn replaceLocation(self: *Window, loc: Location) !void {
self.location = loc;
if (self.document) |doc| {
try parser.documentHTMLSetLocation(Location, doc, &self.location);
}
try parser.documentHTMLSetLocation(Location, self.document, &self.location);
}

pub fn replaceDocument(self: *Window, doc: *parser.DocumentHTML) !void {
Expand Down
56 changes: 28 additions & 28 deletions src/browser/page.zig
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ pub const Page = struct {
// Serves are the root object of our JavaScript environment
window: Window,

doc: ?*parser.Document,

// The URL of the page
url: URL,

Expand All @@ -87,7 +85,6 @@ pub const Page = struct {
self.* = .{
.window = try Window.create(null, null),
.arena = arena,
.doc = null,
.raw_data = null,
.url = URL.empty,
.session = session,
Expand Down Expand Up @@ -121,15 +118,14 @@ pub const Page = struct {

// dump writes the page content into the given file.
pub fn dump(self: *const Page, out: std.fs.File) !void {
// if no HTML document pointer available, dump the data content only.
if (self.doc == null) {
// no data loaded, nothing to do.
if (self.raw_data == null) return;
return try out.writeAll(self.raw_data.?);
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);
}

// if the page has a pointer to a document, dumps the HTML.
try Dump.writeHTML(self.doc.?, out);
const doc = parser.documentHTMLToDocument(self.window.document);
try Dump.writeHTML(doc, out);
}

pub fn fetchModuleSource(ctx: *anyopaque, specifier: []const u8) !?[]const u8 {
Expand Down Expand Up @@ -183,6 +179,11 @@ pub const Page = struct {

// if the url is about:blank, nothing to do.
if (std.mem.eql(u8, "about:blank", request_url.raw)) {
var fbs = std.io.fixedBufferStream("");
try self.loadHTMLDoc(fbs.reader(), "utf-8");
// We do not processHTMLDoc here as we know we don't have any scripts
// This assumption may be false when CDP Page.addScriptToEvaluateOnNewDocument is implemented
try HTMLDocument.documentIsComplete(self.window.document, &self.state);
return;
}

Expand Down Expand Up @@ -224,7 +225,9 @@ pub const Page = struct {
} orelse .unknown;

if (mime.isHTML()) {
self.raw_data = null;
try self.loadHTMLDoc(&response, mime.charset orelse "utf-8");
try self.processHTMLDoc();
} else {
log.info("non-HTML document: {s}", .{content_type orelse "null"});
var arr: std.ArrayListUnmanaged(u8) = .{};
Expand All @@ -243,29 +246,13 @@ pub const Page = struct {

// https://html.spec.whatwg.org/#read-html
fn loadHTMLDoc(self: *Page, reader: anytype, charset: []const u8) !void {
const arena = self.arena;

log.debug("parse html with charset {s}", .{charset});

const ccharset = try arena.dupeZ(u8, charset);
const ccharset = try self.arena.dupeZ(u8, charset);

const html_doc = try parser.documentHTMLParse(reader, ccharset);
const doc = parser.documentHTMLToDocument(html_doc);

// save a document's pointer in the page.
self.doc = doc;

const document_element = (try parser.documentGetDocumentElement(doc)) orelse return error.DocumentElementError;
try parser.eventTargetAddEventListener(
parser.toEventTarget(parser.Element, document_element),
"click",
&self.window_clicked_event_node,
false,
);

// TODO set document.readyState to interactive
// https://html.spec.whatwg.org/#reporting-document-loading-status

// inject the URL to the document including the fragment.
try parser.documentSetDocumentURI(doc, self.url.raw);

Expand All @@ -274,6 +261,19 @@ pub const Page = struct {
self.window.setStorageShelf(
try self.session.storage_shed.getOrPut(try self.origin(self.arena)),
);
}

fn processHTMLDoc(self: *Page) !void {
const html_doc = self.window.document;
const doc = parser.documentHTMLToDocument(html_doc);

const document_element = (try parser.documentGetDocumentElement(doc)) orelse return error.DocumentElementError;
try parser.eventTargetAddEventListener(
parser.toEventTarget(parser.Element, document_element),
"click",
&self.window_clicked_event_node,
false,
);

// https://html.spec.whatwg.org/#read-html

Expand Down Expand Up @@ -320,12 +320,12 @@ pub const Page = struct {
// > parsing and evaluated as soon as it is available.
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#async
if (script.is_async) {
try async_scripts.append(arena, script);
try async_scripts.append(self.arena, script);
continue;
}

if (script.is_defer) {
try defer_scripts.append(arena, script);
try defer_scripts.append(self.arena, script);
continue;
}

Expand Down
4 changes: 2 additions & 2 deletions src/cdp/domains/dom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ fn getDocument(cmd: anytype) !void {

const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
const doc = page.doc orelse return error.DocumentNotLoaded;
const doc = parser.documentHTMLToDocument(page.window.document);

const node = try bc.node_registry.register(parser.documentToNode(doc));
return cmd.sendResult(.{ .root = bc.nodeWriter(node, .{}) }, .{});
Expand All @@ -74,7 +74,7 @@ fn performSearch(cmd: anytype) !void {

const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
const doc = page.doc orelse return error.DocumentNotLoaded;
const doc = parser.documentHTMLToDocument(page.window.document);

const allocator = cmd.cdp.allocator;
var list = try css.querySelectorAll(allocator, parser.documentToNode(doc), params.query);
Expand Down
2 changes: 1 addition & 1 deletion src/cdp/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ const TestContext = struct {
if (bc.session_id == null) bc.session_id = "SID-X";
parser.deinit();
const page = try bc.session.createPage();
page.doc = (try Document.init(html)).doc;
page.window.document = (try Document.init(html)).doc;
}
return bc;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main_wpt.zig
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ fn run(arena: Allocator, test_file: []const u8, loader: *FileLoader, err_out: *?
try polyfill.load(arena, runner.scope);

// loop over the scripts.
const doc = parser.documentHTMLToDocument(runner.state.window.document.?);
const doc = parser.documentHTMLToDocument(runner.state.window.document);
const scripts = try parser.documentGetElementsByTagName(doc, "script");
const script_count = try parser.nodeListLength(scripts);
for (0..script_count) |i| {
Expand Down
6 changes: 3 additions & 3 deletions src/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ pub const Random = struct {
};

pub const Document = struct {
doc: *parser.Document,
doc: *parser.DocumentHTML,
arena: std.heap.ArenaAllocator,

pub fn init(html: []const u8) !Document {
Expand All @@ -219,7 +219,7 @@ pub const Document = struct {

return .{
.arena = std.heap.ArenaAllocator.init(allocator),
.doc = parser.documentHTMLToDocument(html_doc),
.doc = html_doc,
};
}

Expand All @@ -240,7 +240,7 @@ pub const Document = struct {
}

pub fn asNode(self: *const Document) *parser.Node {
return parser.documentToNode(self.doc);
return parser.documentHTMLToNode(self.doc);
}
};

Expand Down
1 change: 1 addition & 0 deletions src/url.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub const URL = struct {
raw: []const u8,

pub const empty = URL{ .uri = .{ .scheme = "" }, .raw = "" };
pub const about_blank = URL{ .uri = .{ .scheme = "" }, .raw = "about:blank" };

// We assume str will last as long as the URL
// In some cases, this is safe to do, because we know the URL is short lived.
Expand Down