Merge pull request #902 from lightpanda-io/window_DOMContentLoaded
Some checks failed
e2e-test / zig build release (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled

Trigger the DOMContentLoaded on the Window
This commit is contained in:
Karl Seguin
2025-07-19 08:51:12 +08:00
committed by GitHub
4 changed files with 53 additions and 3 deletions

View File

@@ -278,15 +278,17 @@ pub const HTMLDocument = struct {
const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self)));
state.ready_state = .interactive; state.ready_state = .interactive;
const evt = try parser.eventCreate();
defer parser.eventDestroy(evt);
log.debug(.script_event, "dispatch event", .{ log.debug(.script_event, "dispatch event", .{
.type = "DOMContentLoaded", .type = "DOMContentLoaded",
.source = "document", .source = "document",
}); });
const evt = try parser.eventCreate();
defer parser.eventDestroy(evt);
try parser.eventInit(evt, "DOMContentLoaded", .{ .bubbles = true, .cancelable = true }); try parser.eventInit(evt, "DOMContentLoaded", .{ .bubbles = true, .cancelable = true });
_ = try parser.eventTargetDispatchEvent(parser.toEventTarget(parser.DocumentHTML, self), evt); _ = try parser.eventTargetDispatchEvent(parser.toEventTarget(parser.DocumentHTML, self), evt);
try page.window.dispatchForDocumentTarget(evt);
} }
pub fn documentIsComplete(self: *parser.DocumentHTML, page: *Page) !void { pub fn documentIsComplete(self: *parser.DocumentHTML, page: *Page) !void {

View File

@@ -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 { const TimerCallback = struct {
@@ -491,4 +508,21 @@ test "Browser.HTML.Window" {
.{ "var qm = false; window.queueMicrotask(() => {qm = true });", null }, .{ "var qm = false; window.queueMicrotask(() => {qm = true });", null },
.{ "qm", "true" }, .{ "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" },
}, .{});
}
} }

View File

@@ -846,6 +846,14 @@ pub const EventTargetTBase = extern struct {
internal_type_.* = @intFromEnum(self.internal_target_type); internal_type_.* = @intFromEnum(self.internal_target_type);
return c.DOM_NO_ERR; 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 // MouseEvent

View File

@@ -474,6 +474,12 @@ pub const JsRunner = struct {
return err; 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 { const RunnerOpts = struct {