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
8 changes: 5 additions & 3 deletions src/browser/html/document.zig
Original file line number Diff line number Diff line change
Expand Up @@ -278,15 +278,17 @@ pub const HTMLDocument = struct {
const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self)));
state.ready_state = .interactive;

const evt = try parser.eventCreate();
defer parser.eventDestroy(evt);

log.debug(.script_event, "dispatch event", .{
.type = "DOMContentLoaded",
.source = "document",
});

const evt = try parser.eventCreate();
defer parser.eventDestroy(evt);
try parser.eventInit(evt, "DOMContentLoaded", .{ .bubbles = true, .cancelable = true });
_ = try parser.eventTargetDispatchEvent(parser.toEventTarget(parser.DocumentHTML, self), evt);

try page.window.dispatchForDocumentTarget(evt);
}

pub fn documentIsComplete(self: *parser.DocumentHTML, page: *Page) !void {
Expand Down
34 changes: 34 additions & 0 deletions src/browser/html/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,23 @@ pub const Window = struct {
);
}
}

// libdom's document doesn't have a parent, which is correct, but
// breaks the event bubbling that happens for many events from
// document -> window.
// We need to force dispatch this event on the window, with the
// document target.
// In theory, we should do this for a lot of events and might need
// to come up with a good way to solve this more generically. But
// this specific event, and maybe a few others in the near future,
// are blockers.
// Worth noting that NetSurf itself appears to do something similar:
// https://github.com/netsurf-browser/netsurf/blob/a32e1a03e1c91ee9f0aa211937dbae7a96831149/content/handlers/html/html.c#L380
pub fn dispatchForDocumentTarget(self: *Window, evt: *parser.Event) !void {
// we assume that this evt has already been dispatched on the document
// and thus the target has already been set to the document.
return self.base.redispatchEvent(evt);
}
};

const TimerCallback = struct {
Expand Down Expand Up @@ -491,4 +508,21 @@ test "Browser.HTML.Window" {
.{ "var qm = false; window.queueMicrotask(() => {qm = true });", null },
.{ "qm", "true" },
}, .{});

{
try runner.testCases(&.{
.{
\\ let dcl = false;
\\ window.addEventListener('DOMContentLoaded', (e) => {
\\ dcl = e.target == document;
\\ });
,
null,
},
}, .{});
try runner.dispatchDOMContentLoaded();
try runner.testCases(&.{
.{ "dcl", "true" },
}, .{});
}
}
8 changes: 8 additions & 0 deletions src/browser/netsurf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,14 @@ pub const EventTargetTBase = extern struct {
internal_type_.* = @intFromEnum(self.internal_target_type);
return c.DOM_NO_ERR;
}

// Called to simulate bubbling from a libdom node (e.g. the Document) to a
// Zig instance (e.g. the Window).
pub fn redispatchEvent(self: *EventTargetTBase, evt: *Event) !void {
var res: bool = undefined;
const err = c._dom_event_target_dispatch(@ptrCast(self), &self.eti, evt, c.DOM_BUBBLING_PHASE, &res);
try DOMErr(err);
}
};

// MouseEvent
Expand Down
6 changes: 6 additions & 0 deletions src/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,12 @@ pub const JsRunner = struct {
return err;
};
}

pub fn dispatchDOMContentLoaded(self: *JsRunner) !void {
const HTMLDocument = @import("browser/html/document.zig").HTMLDocument;
const html_doc = self.page.window.document;
try HTMLDocument.documentIsLoaded(html_doc, self.page);
}
};

const RunnerOpts = struct {
Expand Down