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", .{});