From 2faf584d367752b98d42ea11c1e9c0ea2df20476 Mon Sep 17 00:00:00 2001 From: Francis Bouvier Date: Thu, 18 Jan 2024 22:00:36 +0100 Subject: [PATCH] Updates, fix and Event properties Signed-off-by: Francis Bouvier --- src/dom/event_target.zig | 36 +++++++++++++-- src/events/event.zig | 73 ++++++++++++++++++++++++++++-- src/netsurf.zig | 98 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 194 insertions(+), 13 deletions(-) diff --git a/src/dom/event_target.zig b/src/dom/event_target.zig index 9a49585d..6fbfbd47 100644 --- a/src/dom/event_target.zig +++ b/src/dom/event_target.zig @@ -8,12 +8,23 @@ const checkCases = jsruntime.test_utils.checkCases; const parser = @import("../netsurf.zig"); const DOMException = @import("exceptions.zig").DOMException; +const Nod = @import("node.zig"); +// EventTarget interfaces +pub const Union = Nod.Union; + +// EventTarget implementation pub const EventTarget = struct { pub const Self = parser.EventTarget; pub const Exception = DOMException; pub const mem_guarantied = true; + pub fn toInterface(et: *parser.EventTarget) !Union { + // NOTE: for now we state that all EventTarget are Nodes + // TODO: handle other types (eg. Window) + return Nod.Node.toInterface(@as(*parser.Node, @ptrCast(et))); + } + // JS funcs // -------- @@ -22,11 +33,14 @@ pub const EventTarget = struct { alloc: std.mem.Allocator, eventType: []const u8, cbk: Callback, + capture: ?bool, + // TODO: hanle EventListenerOptions + // see #https://github.com/lightpanda-io/jsruntime-lib/issues/114 ) !void { // TODO: when can we free this allocation? const cbk_ptr = try alloc.create(Callback); cbk_ptr.* = cbk; - try parser.eventTargetAddEventListener(self, eventType, cbk_ptr); + try parser.eventTargetAddEventListener(self, eventType, cbk_ptr, capture orelse false); } pub fn _dispatchEvent(self: *parser.EventTarget, event: *parser.Event) !bool { @@ -46,9 +60,25 @@ pub fn testExecFn( var basic = [_]Case{ .{ .src = "let event = new Event('myEvent')", .ex = "undefined" }, .{ .src = "let content = document.getElementById('content')", .ex = "undefined" }, - .{ .src = "var nb = 0; content.addEventListener('myEvent', function(event) {nb ++;})", .ex = "undefined" }, + .{ .src = + \\var nb = 0; + \\var evt = undefined; + \\var phase = undefined; + \\var cur = undefined; + \\content.addEventListener('myEvent', + \\function(event) { + \\evt = event; + \\phase = event.eventPhase; + \\cur = event.currentTarget; + \\nb ++; + \\}) + , .ex = "undefined" }, .{ .src = "content.dispatchEvent(event)", .ex = "true" }, - .{ .src = "nb", .ex = "2" }, // 2 because the callback is called twice + .{ .src = "nb", .ex = "1" }, + .{ .src = "evt instanceof Event", .ex = "true" }, + .{ .src = "evt.type", .ex = "myEvent" }, + .{ .src = "phase", .ex = "2" }, + .{ .src = "cur.localName", .ex = "div" }, }; try checkCases(js_env, &basic); } diff --git a/src/events/event.zig b/src/events/event.zig index a336a700..89576cbb 100644 --- a/src/events/event.zig +++ b/src/events/event.zig @@ -5,20 +5,85 @@ const generate = @import("../generate.zig"); const parser = @import("../netsurf.zig"); const DOMException = @import("../dom/exceptions.zig").DOMException; +const EventTarget = @import("../dom/event_target.zig").EventTarget; +const EventTargetUnion = @import("../dom/event_target.zig").Union; +// https://dom.spec.whatwg.org/#event pub const Event = struct { pub const Self = parser.Event; pub const Exception = DOMException; pub const mem_guarantied = true; - // JS funcs - // -------- + pub const EventInit = parser.EventInit; - pub fn constructor(eventType: []const u8) !*parser.Event { + // JS + // -- + + pub const _CAPTURING_PHASE = 1; + pub const _AT_TARGET = 2; + pub const _BUBBLING_PHASE = 3; + + pub fn constructor(eventType: []const u8, opts: ?EventInit) !*parser.Event { const event = try parser.eventCreate(); - try parser.eventInit(event, eventType); + try parser.eventInit(event, eventType, opts orelse EventInit{}); return event; } + + // Getters + + pub fn get_type(self: *parser.Event) ![]const u8 { + return try parser.eventType(self); + } + + pub fn get_target(self: *parser.Event) !?EventTargetUnion { + const et = try parser.eventTarget(self); + if (et == null) return null; + return try EventTarget.toInterface(et.?); + } + + pub fn get_currentTarget(self: *parser.Event) !?EventTargetUnion { + const et = try parser.eventCurrentTarget(self); + if (et == null) return null; + return try EventTarget.toInterface(et.?); + } + + pub fn get_eventPhase(self: *parser.Event) !u8 { + return try parser.eventPhase(self); + } + + pub fn get_bubbles(self: *parser.Event) !bool { + return try parser.eventBubbles(self); + } + + pub fn get_cancelable(self: *parser.Event) !bool { + return try parser.eventCancelable(self); + } + + pub fn get_defaultPrevented(self: *parser.Event) !bool { + return try parser.eventDefaultPrevented(self); + } + + pub fn get_isTrusted(self: *parser.Event) !bool { + return try parser.eventIsTrusted(self); + } + + pub fn get_timestamp(self: *parser.Event) !u32 { + return try parser.eventTimestamp(self); + } + + // Methods + + pub fn get_stopPropagation(self: *parser.Event) !void { + return try parser.eventStopPropagation(self); + } + + pub fn get_stopImmediatePropagation(self: *parser.Event) !void { + return try parser.eventStopImmediatePropagation(self); + } + + pub fn get_preventDefault(self: *parser.Event) !void { + return try parser.eventPreventDefault(self); + } }; // Event interfaces diff --git a/src/netsurf.zig b/src/netsurf.zig index 863ccc1d..b609768b 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -353,9 +353,93 @@ pub fn eventCreate() !*Event { return evt.?; } -pub fn eventInit(evt: *Event, eventType: []const u8) !void { - const s = try strFromData(eventType); - const err = c._dom_event_init(evt, s, false, false); +pub const EventInit = struct { + bubbles: bool = false, + cancelable: bool = false, + composed: bool = false, +}; + +pub fn eventInit(evt: *Event, typ: []const u8, opts: EventInit) !void { + const s = try strFromData(typ); + const err = c._dom_event_init(evt, s, opts.bubbles, opts.cancelable); + try DOMErr(err); +} + +pub fn eventType(evt: *Event) ![]const u8 { + var s: ?*String = undefined; + const err = c._dom_event_get_type(evt, &s); + try DOMErr(err); + return strToData(s.?); +} + +pub fn eventTarget(evt: *Event) !?*EventTarget { + var et: ?*EventTarget = undefined; + const err = c._dom_event_get_target(evt, &et); + try DOMErr(err); + return et; +} + +pub fn eventCurrentTarget(evt: *Event) !?*EventTarget { + var et: ?*EventTarget = undefined; + const err = c._dom_event_get_current_target(evt, &et); + try DOMErr(err); + return et; +} + +pub fn eventPhase(evt: *Event) !u8 { + var phase: c.dom_event_flow_phase = undefined; + const err = c._dom_event_get_event_phase(evt, &phase); + try DOMErr(err); + return @as(u8, @intCast(phase)); +} + +pub fn eventBubbles(evt: *Event) !bool { + var res: bool = undefined; + const err = c._dom_event_get_bubbles(evt, &res); + try DOMErr(err); + return res; +} + +pub fn eventCancelable(evt: *Event) !bool { + var res: bool = undefined; + const err = c._dom_event_get_cancelable(evt, &res); + try DOMErr(err); + return res; +} + +pub fn eventDefaultPrevented(evt: *Event) !bool { + var res: bool = undefined; + const err = c._dom_event_is_default_prevented(evt, &res); + try DOMErr(err); + return res; +} + +pub fn eventIsTrusted(evt: *Event) !bool { + var res: bool = undefined; + const err = c._dom_event_get_is_trusted(evt, &res); + try DOMErr(err); + return res; +} + +pub fn eventTimestamp(evt: *Event) !u32 { + var ts: c_uint = undefined; + const err = c._dom_event_get_timestamp(evt, &ts); + try DOMErr(err); + return @as(u32, @intCast(ts)); +} + +pub fn eventStopPropagation(evt: *Event) !void { + const err = c._dom_event_stop_propagation(evt); + try DOMErr(err); +} + +pub fn eventStopImmediatePropagation(evt: *Event) !void { + const err = c._dom_event_stop_immediate_propagation(evt); + try DOMErr(err); +} + +pub fn eventPreventDefault(evt: *Event) !void { + const err = c._dom_event_prevent_default(evt); try DOMErr(err); } @@ -383,20 +467,22 @@ fn eventTargetVtable(et: *EventTarget) c.dom_event_target_vtable { pub fn eventTargetAddEventListener( et: *EventTarget, - eventType: []const u8, + typ: []const u8, cbk_ptr: *Callback, + capture: bool, ) !void { - const s = try strFromData(eventType); + const s = try strFromData(typ); const ctx = @as(*anyopaque, @ptrCast(cbk_ptr)); var listener: ?*c.dom_event_listener = undefined; const errLst = c.dom_event_listener_create(event_handler, ctx, &listener); try DOMErr(errLst); - const err = eventTargetVtable(et).add_event_listener.?(et, s, listener, true); + const err = eventTargetVtable(et).add_event_listener.?(et, s, listener, capture); try DOMErr(err); } pub fn eventTargetDispatchEvent(et: *EventTarget, event: *Event) !bool { var res: bool = undefined; + // const err = c.dom_event_target_dispatch_event(et, event, &res); const err = eventTargetVtable(et).dispatch_event.?(et, event, &res); try DOMErr(err); return res;