diff --git a/src/browser/Page.zig b/src/browser/Page.zig
index c50de6d3..5f23cc20 100644
--- a/src/browser/Page.zig
+++ b/src/browser/Page.zig
@@ -41,8 +41,10 @@ const Parser = @import("parser/Parser.zig");
const URL = @import("URL.zig");
const Node = @import("webapi/Node.zig");
const Event = @import("webapi/Event.zig");
+const EventTarget = @import("webapi/EventTarget.zig");
const CData = @import("webapi/CData.zig");
const Element = @import("webapi/Element.zig");
+const HtmlElement = @import("webapi/element/Html.zig");
const Window = @import("webapi/Window.zig");
const Location = @import("webapi/Location.zig");
const Document = @import("webapi/Document.zig");
@@ -124,6 +126,10 @@ _element_assigned_slots: Element.AssignedSlotLookup = .{},
/// ```
_element_attr_listeners: GlobalEventHandlersLookup = .{},
+/// `load` events that'll be fired before window's `load` event.
+/// A call to `documentIsComplete` (which calls `_documentIsComplete`) resets it.
+_to_load: std.ArrayList(*Element) = .{},
+
_script_manager: ScriptManager,
// List of active MutationObservers
@@ -335,6 +341,8 @@ fn reset(self: *Page, comptime initializing: bool) !void {
self._element_attr_listeners = .{};
+ self._to_load = .{};
+
self._notified_network_idle = .init;
self._notified_network_almost_idle = .init;
@@ -690,15 +698,34 @@ pub fn documentIsComplete(self: *Page) void {
fn _documentIsComplete(self: *Page) !void {
self.document._ready_state = .complete;
- // dispatch window.load event
const event = try Event.initTrusted("load", .{}, self);
- // this event is weird, it's dispatched directly on the window, but
- // with the document as the target
var ls: JS.Local.Scope = undefined;
self.js.localScope(&ls);
defer ls.deinit();
+ // Dispatch `_to_load` events before window.load.
+ for (self._to_load.items) |element| {
+ const maybe_inline_listener = self.getAttrListener(element, .onload);
+
+ try self._event_manager.dispatchWithFunction(
+ element.asEventTarget(),
+ event,
+ ls.toLocal(maybe_inline_listener),
+ .{ .context = "Page dispatch load events" },
+ );
+
+ if (comptime IS_DEBUG) {
+ log.debug(.page, "load event for element", .{ .element = element });
+ }
+ }
+
+ // `_to_load` can be cleaned here.
+ self._to_load.clearAndFree(self.arena);
+
+ // Dispatch window.load event.
+ // This event is weird, it's dispatched directly on the window, but
+ // with the document as the target.
event._target = self.document.asEventTarget();
try self._event_manager.dispatchWithFunction(
self.window.asEventTarget(),
diff --git a/src/browser/tests/element/html/image.html b/src/browser/tests/element/html/image.html
index b9cb8153..b834aebf 100644
--- a/src/browser/tests/element/html/image.html
+++ b/src/browser/tests/element/html/image.html
@@ -97,3 +97,62 @@
testing.expectEqual('lazy', img.getAttribute('loading'));
}
+
+
+
+
+
+
diff --git a/src/browser/webapi/element/html/Image.zig b/src/browser/webapi/element/html/Image.zig
index 41482ec9..10a37ce7 100644
--- a/src/browser/webapi/element/html/Image.zig
+++ b/src/browser/webapi/element/html/Image.zig
@@ -5,6 +5,10 @@ const URL = @import("../../../URL.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
+const Event = @import("../../Event.zig");
+const log = @import("../../../../log.zig");
+
+const IS_DEBUG = @import("builtin").mode == .Debug;
const Image = @This();
_proto: *HtmlElement,
@@ -115,6 +119,19 @@ pub const JsApi = struct {
pub const loading = bridge.accessor(Image.getLoading, Image.setLoading, .{});
};
+pub const Build = struct {
+ pub fn created(node: *Node, page: *Page) !void {
+ const self = node.as(Image);
+ const image = self.asElement();
+ // Exit if src not set.
+ // TODO: We might want to check if src point to valid image.
+ _ = image.getAttributeSafe(comptime .wrap("src")) orelse return;
+
+ // Push to `_to_load` to dispatch load event just before window load event.
+ return page._to_load.append(page.arena, image);
+ }
+};
+
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.Image" {
try testing.htmlRunner("element/html/image.html", .{});
diff --git a/src/browser/webapi/element/html/Style.zig b/src/browser/webapi/element/html/Style.zig
index b9372bd3..483d07f7 100644
--- a/src/browser/webapi/element/html/Style.zig
+++ b/src/browser/webapi/element/html/Style.zig
@@ -96,6 +96,15 @@ pub const JsApi = struct {
pub const sheet = bridge.accessor(Style.getSheet, null, .{});
};
+pub const Build = struct {
+ pub fn created(node: *Node, page: *Page) !void {
+ const self = node.as(Style);
+ const style = self.asElement();
+ // Push to `_to_load` to dispatch load event just before window load event.
+ return page._to_load.append(page.arena, style);
+ }
+};
+
const testing = @import("../../../../testing.zig");
test "WebApi: Style" {
try testing.htmlRunner("element/html/style.html", .{});