diff --git a/src/events/event.zig b/src/events/event.zig index f619891d..f192430d 100644 --- a/src/events/event.zig +++ b/src/events/event.zig @@ -13,6 +13,16 @@ const DOMException = @import("../dom/exceptions.zig").DOMException; const EventTarget = @import("../dom/event_target.zig").EventTarget; const EventTargetUnion = @import("../dom/event_target.zig").Union; +const xhr = @import("../xhr/xhr.zig"); + +// Event interfaces +pub const Interfaces = generate.Tuple(.{ + Event, + xhr.ProgressEvent, +}); +const Generated = generate.Union.compile(Interfaces); +pub const Union = Generated._union; + // https://dom.spec.whatwg.org/#event pub const Event = struct { pub const Self = parser.Event; @@ -28,6 +38,13 @@ pub const Event = struct { pub const _AT_TARGET = 2; pub const _BUBBLING_PHASE = 3; + pub fn toInterface(evt: *parser.Event) !Union { + return switch (try parser.eventGetInternalType(evt)) { + .event => .{ .Event = evt }, + .progress_event => .{ .ProgressEvent = @as(*xhr.ProgressEvent, @ptrCast(evt)).* }, + }; + } + pub fn constructor(eventType: []const u8, opts: ?EventInit) !*parser.Event { const event = try parser.eventCreate(); try parser.eventInit(event, eventType, opts orelse EventInit{}); @@ -104,13 +121,6 @@ pub const Event = struct { } }; -// Event interfaces -pub const Interfaces = generate.Tuple(.{ - Event, -}); -const Generated = generate.Union.compile(Interfaces); -pub const Union = Generated._union; - pub fn testExecFn( _: std.mem.Allocator, js_env: *jsruntime.Env, @@ -200,7 +210,7 @@ pub fn testExecFn( try checkCases(js_env, &legacy); var remove = [_]Case{ - .{ .src = "var nb = 0; function cbk(event) { nb ++; }", .ex = "undefined" }, + .{ .src = "var nb = 0; var evt = null; function cbk(event) { nb ++; evt=event; }", .ex = "undefined" }, .{ .src = "document.addEventListener('count', cbk)", .ex = "undefined" }, .{ .src = "document.removeEventListener('count', cbk)", .ex = "undefined" }, .{ .src = "document.dispatchEvent(new Event('count'))", .ex = "true" }, diff --git a/src/netsurf.zig b/src/netsurf.zig index 5ecbb155..57d071e2 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -8,6 +8,7 @@ const c = @cImport({ }); const Callback = @import("jsruntime").Callback; +const EventToInterface = @import("events/event.zig").Event.toInterface; // Vtable // ------ @@ -449,6 +450,23 @@ pub fn eventPreventDefault(evt: *Event) !void { try DOMErr(err); } +pub fn eventGetInternalType(evt: *Event) !EventType { + var res: u32 = undefined; + const err = c._dom_event_get_internal_type(evt, &res); + try DOMErr(err); + return @enumFromInt(res); +} + +pub fn eventSetInternalType(evt: *Event, internal_type: EventType) !void { + const err = c._dom_event_set_internal_type(evt, @intFromEnum(internal_type)); + try DOMErr(err); +} + +pub const EventType = enum(u8) { + event = 0, + progress_event = 1, +}; + // EventHandler fn event_handler_cbk(data: *anyopaque) *Callback { const ptr: *align(@alignOf(*Callback)) anyopaque = @alignCast(data); @@ -459,7 +477,14 @@ const event_handler = struct { fn handle(event: ?*Event, data: ?*anyopaque) callconv(.C) void { if (data) |d| { const func = event_handler_cbk(d); - func.call(.{event}) catch unreachable; + + if (event) |evt| { + func.call(.{ + EventToInterface(evt) catch unreachable, + }) catch unreachable; + } else { + func.call(.{event}) catch unreachable; + } // NOTE: we can not call func.deinit here // b/c the handler can be called several times // either on this dispatch event or in anoter one @@ -652,7 +677,12 @@ pub const EventTargetTBase = struct { pub fn dispatch_event(et: [*c]c.dom_event_target, evt: ?*c.struct_dom_event, res: [*c]bool) callconv(.C) c.dom_exception { const self = @as(*Self, @ptrCast(et)); - return c._dom_event_target_dispatch(et, &self.eti, evt, c.DOM_BUBBLING_PHASE, res); + // Set the event target to the target dispatched. + const e = c._dom_event_set_target(evt, et); + if (e != c.DOM_NO_ERR) { + return e; + } + return c._dom_event_target_dispatch(et, &self.eti, evt, c.DOM_AT_TARGET, res); } pub fn remove_event_listener(et: [*c]c.dom_event_target, t: [*c]c.dom_string, l: ?*c.struct_dom_event_listener, capture: bool) callconv(.C) c.dom_exception { diff --git a/src/xhr/xhr.zig b/src/xhr/xhr.zig index 85059887..d0dc7fb1 100644 --- a/src/xhr/xhr.zig +++ b/src/xhr/xhr.zig @@ -28,7 +28,6 @@ pub const Interfaces = generate.Tuple(.{ XMLHttpRequestEventTarget, XMLHttpRequestUpload, XMLHttpRequest, - ProgressEvent, }); pub const XMLHttpRequestEventTarget = struct { @@ -144,6 +143,7 @@ pub const ProgressEvent = struct { const event = try parser.eventCreate(); defer parser.eventDestroy(event); try parser.eventInit(event, eventType, .{}); + try parser.eventSetInternalType(event, .progress_event); const o = opts orelse EventInit{}; @@ -476,7 +476,6 @@ pub const XMLHttpRequest = struct { self.dispatchEvt("readystatechange"); // dispatch a progress event load. - self.dispatchEvt("load"); self.dispatchProgressEvent("load", .{ .loaded = loaded, .total = total }); // dispatch a progress event loadend. self.dispatchProgressEvent("loadend", .{ .loaded = loaded, .total = total }); @@ -585,7 +584,9 @@ pub fn testExecFn( // Each case executed waits for all loop callaback calls. // So the url has been retrieved. .{ .src = "nb", .ex = "1" }, - // .{ .src = "evt.__proto__.constructor.name", .ex = "ProgressEvent" }, + .{ .src = "evt.type", .ex = "load" }, + .{ .src = "evt.loaded > 0", .ex = "true" }, + .{ .src = "evt instanceof ProgressEvent", .ex = "true" }, .{ .src = "req.status", .ex = "200" }, .{ .src = "req.statusText", .ex = "OK" }, .{ .src = "req.getResponseHeader('Content-Type')", .ex = "text/html; charset=UTF-8" }, @@ -597,7 +598,12 @@ pub fn testExecFn( var progress_event = [_]Case{ .{ .src = "let pevt = new ProgressEvent('foo');", .ex = "undefined" }, .{ .src = "pevt.loaded", .ex = "0" }, - .{ .src = "pevt.__proto__.constructor.name", .ex = "ProgressEvent" }, + .{ .src = "pevt instanceof ProgressEvent", .ex = "true" }, + .{ .src = "var nnb = 0; var eevt = null; function ccbk(event) { nnb ++; eevt = event; }", .ex = "undefined" }, + .{ .src = "document.addEventListener('foo', ccbk)", .ex = "undefined" }, + .{ .src = "document.dispatchEvent(pevt)", .ex = "true" }, + .{ .src = "eevt.type", .ex = "foo" }, + .{ .src = "eevt instanceof ProgressEvent", .ex = "true" }, }; try checkCases(js_env, &progress_event); }