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..4a675e6b 100644 --- a/src/browser/webapi/element/html/Image.zig +++ b/src/browser/webapi/element/html/Image.zig @@ -5,6 +5,8 @@ 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 Image = @This(); _proto: *HtmlElement, @@ -47,6 +49,41 @@ pub fn getSrc(self: *const Image, page: *Page) ![]const u8 { pub fn setSrc(self: *Image, value: []const u8, page: *Page) !void { try self.asElement().setAttributeSafe(comptime .wrap("src"), .wrap(value), page); + + const event_target = self.asNode().asEventTarget(); + + // Have to do this since `Scheduler` only allow passing a single arg. + const SetSrcCallback = struct { + page: *Page, + event_target: *@import("../../EventTarget.zig"), + }; + const args = try page._factory.create(SetSrcCallback{ + .page = page, + .event_target = event_target, + }); + errdefer page._factory.destroy(args); + + // We don't actually fetch the media, here we fake the load call. + try page.scheduler.add( + args, + struct { + fn wrap(raw: *anyopaque) anyerror!?u32 { + const _args: *SetSrcCallback = @ptrCast(@alignCast(raw)); + const _page = _args.page; + defer _page._factory.destroy(_args); + // Dispatch. + const event = try Event.initTrusted("load", .{}, _page); + try _page._event_manager.dispatch(_args.event_target, event); + + return null; + } + }.wrap, + 25, + .{ + .low_priority = false, + .name = "Image.setSrc", + }, + ); } pub fn getAlt(self: *const Image) []const u8 { @@ -115,6 +152,40 @@ pub const JsApi = struct { pub const loading = bridge.accessor(Image.getLoading, Image.setLoading, .{}); }; +/// Argument passed to `dispatchLoadEvent`. +const CallbackParams = struct { page: *Page, element: *Element }; + +/// Callback passed to `Scheduler` to execute load listeners. +fn dispatchLoadEvent(raw: *anyopaque) !?u32 { + const _args: *CallbackParams = @ptrCast(@alignCast(raw)); + const _page = _args.page; + defer _page._factory.destroy(_args); + + const _element = _args.element; + const _img = _element.as(Image); + const event_target = _element.asEventTarget(); + const event = try Event.initTrusted("load", .{}, _page); + + // If onload provided, dispatch with it. + if (_img.getOnLoad(_page)) |_on_load| { + var ls: js.Local.Scope = undefined; + _page.js.localScope(&ls); + defer ls.deinit(); + + try _page._event_manager.dispatchWithFunction( + event_target, + event, + _on_load.local(&ls.local), + .{ .context = "Image.onload" }, + ); + return null; + } + + // Dispatch to addEventListener listeners. + try _page._event_manager.dispatch(event_target, event); + return null; +} + const testing = @import("../../../../testing.zig"); test "WebApi: HTML.Image" { try testing.htmlRunner("element/html/image.html", .{});