mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-05 06:47:11 +00:00
EventManager: add hasListener
Not sure if this should be in `EventTarget` or `EventManager`, here goes nothing. `Image`: dispatch `load` event when `src` set add load event test remove `hasListener` let `Scheduler` dispatch `load` event Simulates async nature. update test free `args` when done implement `load` event dispatch for `<img>` tags This dispatches `load` events tied to `EventManager` but not the `onload` for some reason... `"load"` event must be dispatched even if `onload` not set Resolves the bug that cause event listeners added through `EventTarget` not executing if `onload` not set. add `onload` getter/setter for `Image` prefer `attributeChange` to run side-effects This should give more consistent results than using `setSrc`. add inline `<img src="..." />` test `Image`: prefer `inline_lookup` for `onload` remove incorrect URL check + prefer 0ms in `Scheduler` change after rebase
This commit is contained in:
@@ -97,3 +97,62 @@
|
|||||||
testing.expectEqual('lazy', img.getAttribute('loading'));
|
testing.expectEqual('lazy', img.getAttribute('loading'));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id="load-trigger-event">
|
||||||
|
{
|
||||||
|
const img = document.createElement("img");
|
||||||
|
let count = 0;
|
||||||
|
img.addEventListener("load", ({ bubbles, cancelBubble, cancelable, composed, isTrusted, target }) => {
|
||||||
|
testing.expectEqual(true, count < 3);
|
||||||
|
count++;
|
||||||
|
|
||||||
|
testing.expectEqual(false, bubbles);
|
||||||
|
testing.expectEqual(false, cancelBubble);
|
||||||
|
testing.expectEqual(false, cancelable);
|
||||||
|
testing.expectEqual(false, composed);
|
||||||
|
testing.expectEqual(true, isTrusted);
|
||||||
|
testing.expectEqual(img, target);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
img.src = "https://cdn.lightpanda.io/website/assets/images/docs/hn.png";
|
||||||
|
testing.expectEqual("https://cdn.lightpanda.io/website/assets/images/docs/hn.png", img.src);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure count is incremented asynchronously.
|
||||||
|
testing.expectEqual(0, count);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<img
|
||||||
|
id="inline-img"
|
||||||
|
src="https://cdn.lightpanda.io/website/assets/images/docs/hn.png"
|
||||||
|
onload="(() => testing.expectEqual(true, true))()"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<script id="inline-on-load">
|
||||||
|
{
|
||||||
|
const img = document.getElementById("inline-img");
|
||||||
|
testing.expectEqual(true, img.onload instanceof Function);
|
||||||
|
// Also call inline to double check.
|
||||||
|
img.onload();
|
||||||
|
|
||||||
|
// Make sure ones attached with `addEventListener` also executed.
|
||||||
|
testing.async(async () => {
|
||||||
|
const result = await new Promise(resolve => {
|
||||||
|
img.addEventListener("load", ({ bubbles, cancelBubble, cancelable, composed, isTrusted, target }) => {
|
||||||
|
testing.expectEqual(false, bubbles);
|
||||||
|
testing.expectEqual(false, cancelBubble);
|
||||||
|
testing.expectEqual(false, cancelable);
|
||||||
|
testing.expectEqual(false, composed);
|
||||||
|
testing.expectEqual(true, isTrusted);
|
||||||
|
testing.expectEqual(img, target);
|
||||||
|
|
||||||
|
return resolve(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
testing.expectEqual(true, result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ const URL = @import("../../../URL.zig");
|
|||||||
const Node = @import("../../Node.zig");
|
const Node = @import("../../Node.zig");
|
||||||
const Element = @import("../../Element.zig");
|
const Element = @import("../../Element.zig");
|
||||||
const HtmlElement = @import("../Html.zig");
|
const HtmlElement = @import("../Html.zig");
|
||||||
|
const Event = @import("../../Event.zig");
|
||||||
|
const log = @import("../../../../log.zig");
|
||||||
|
|
||||||
const Image = @This();
|
const Image = @This();
|
||||||
_proto: *HtmlElement,
|
_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 {
|
pub fn setSrc(self: *Image, value: []const u8, page: *Page) !void {
|
||||||
try self.asElement().setAttributeSafe(comptime .wrap("src"), .wrap(value), page);
|
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 {
|
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, .{});
|
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");
|
const testing = @import("../../../../testing.zig");
|
||||||
test "WebApi: HTML.Image" {
|
test "WebApi: HTML.Image" {
|
||||||
try testing.htmlRunner("element/html/image.html", .{});
|
try testing.htmlRunner("element/html/image.html", .{});
|
||||||
|
|||||||
Reference in New Issue
Block a user