mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
events: create an EventHandlerData struct
It simplifies the EventHandlerFunc creation and allows to insert user's data.
This commit is contained in:
@@ -73,9 +73,9 @@ pub const EventTarget = struct {
|
||||
self,
|
||||
alloc,
|
||||
eventType,
|
||||
cbk,
|
||||
capture orelse false,
|
||||
EventHandler,
|
||||
.{ .cbk = cbk },
|
||||
capture orelse false,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -241,31 +241,23 @@ pub fn testExecFn(
|
||||
}
|
||||
|
||||
pub const EventHandler = struct {
|
||||
fn handle(event: ?*parser.Event, data: ?*anyopaque) callconv(.C) void {
|
||||
if (data) |d| {
|
||||
const func = parser.event_handler_cbk(d);
|
||||
fn handle(event: ?*parser.Event, data: parser.EventHandlerData) void {
|
||||
// TODO get the allocator by another way?
|
||||
var res = CallbackResult.init(data.cbk.nat_ctx.alloc);
|
||||
defer res.deinit();
|
||||
|
||||
// TODO get the allocator by another way?
|
||||
var res = CallbackResult.init(func.nat_ctx.alloc);
|
||||
defer res.deinit();
|
||||
if (event) |evt| {
|
||||
data.cbk.trycall(.{
|
||||
Event.toInterface(evt) catch unreachable,
|
||||
}, &res) catch |e| log.err("event handler error: {any}", .{e});
|
||||
} else {
|
||||
data.cbk.trycall(.{event}, &res) catch |e| log.err("event handler error: {any}", .{e});
|
||||
}
|
||||
|
||||
if (event) |evt| {
|
||||
func.trycall(.{
|
||||
Event.toInterface(evt) catch unreachable,
|
||||
}, &res) catch |e| log.err("event handler error: {any}", .{e});
|
||||
} else {
|
||||
func.trycall(.{event}, &res) catch |e| log.err("event handler error: {any}", .{e});
|
||||
}
|
||||
|
||||
// in case of function error, we log the result and the trace.
|
||||
if (!res.success) {
|
||||
log.info("event handler error: {s}", .{res.result orelse "unknown"});
|
||||
log.debug("{s}", .{res.stack orelse "no stack trace"});
|
||||
}
|
||||
|
||||
// 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
|
||||
// in case of function error, we log the result and the trace.
|
||||
if (!res.success) {
|
||||
log.info("event handler error: {s}", .{res.result orelse "unknown"});
|
||||
log.debug("{s}", .{res.stack orelse "no stack trace"});
|
||||
}
|
||||
}
|
||||
}.handle;
|
||||
|
||||
@@ -522,12 +522,6 @@ pub const EventType = enum(u8) {
|
||||
progress_event = 1,
|
||||
};
|
||||
|
||||
// EventHandler
|
||||
pub fn event_handler_cbk(data: *anyopaque) *Callback {
|
||||
const ptr: *align(@alignOf(*Callback)) anyopaque = @alignCast(data);
|
||||
return @as(*Callback, @ptrCast(ptr));
|
||||
}
|
||||
|
||||
// EventListener
|
||||
pub const EventListener = c.dom_event_listener;
|
||||
const EventListenerEntry = c.listener_entry;
|
||||
@@ -587,10 +581,9 @@ pub fn eventTargetHasListener(
|
||||
// and capture property,
|
||||
// let's check if the callback handler is the same
|
||||
defer c.dom_event_listener_unref(listener);
|
||||
const data = eventListenerGetData(listener);
|
||||
if (data) |d| {
|
||||
const cbk = event_handler_cbk(d);
|
||||
if (cbk_id == cbk.id()) {
|
||||
const ehd = EventHandlerDataInternal.fromListener(listener);
|
||||
if (ehd) |d| {
|
||||
if (cbk_id == d.data.cbk.id()) {
|
||||
return lst;
|
||||
}
|
||||
}
|
||||
@@ -608,29 +601,99 @@ pub fn eventTargetHasListener(
|
||||
return null;
|
||||
}
|
||||
|
||||
const EventHandler = fn (event: ?*Event, data: ?*anyopaque) callconv(.C) void;
|
||||
// EventHandlerFunc is a zig function called when the event is dispatched to a
|
||||
// listener.
|
||||
// The EventHandlerFunc is responsible to call the callback included into the
|
||||
// EventHandlerData.
|
||||
pub const EventHandlerFunc = *const fn (event: ?*Event, data: EventHandlerData) void;
|
||||
|
||||
// EventHandler implements the function exposed in C and called by libdom.
|
||||
// It retrieves the EventHandlerInternalData and call the EventHandlerFunc with
|
||||
// the EventHandlerData in parameter.
|
||||
const EventHandler = struct {
|
||||
fn handle(event: ?*Event, data: ?*anyopaque) callconv(.C) void {
|
||||
if (data) |d| {
|
||||
const ehd = EventHandlerDataInternal.get(d);
|
||||
ehd.handler(event, ehd.data);
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}.handle;
|
||||
|
||||
// EventHandlerData contains a JS callback and the data associated to the
|
||||
// handler.
|
||||
// If given, deinitFunc is called with the data pointer to allow the creator to
|
||||
// clean memory.
|
||||
// The callback is deinit by EventHandlerDataInternal. It must NOT be deinit
|
||||
// into deinitFunc.
|
||||
pub const EventHandlerData = struct {
|
||||
cbk: Callback,
|
||||
data: ?*anyopaque = null,
|
||||
// deinitFunc implements the data deinitialization.
|
||||
deinitFunc: ?DeinitFunc = null,
|
||||
|
||||
pub const DeinitFunc = *const fn (data: ?*anyopaque, alloc: std.mem.Allocator) void;
|
||||
};
|
||||
|
||||
// EventHandlerDataInternal groups the EventHandlerFunc and the EventHandlerData.
|
||||
const EventHandlerDataInternal = struct {
|
||||
data: EventHandlerData,
|
||||
handler: EventHandlerFunc,
|
||||
|
||||
fn init(alloc: std.mem.Allocator, handler: EventHandlerFunc, data: EventHandlerData) !*EventHandlerDataInternal {
|
||||
const ptr = try alloc.create(EventHandlerDataInternal);
|
||||
ptr.* = .{
|
||||
.data = data,
|
||||
.handler = handler,
|
||||
};
|
||||
return ptr;
|
||||
}
|
||||
|
||||
fn deinit(self: *EventHandlerDataInternal, alloc: std.mem.Allocator) void {
|
||||
if (self.data.deinitFunc) |d| d(self.data.data, alloc);
|
||||
self.data.cbk.deinit(alloc);
|
||||
alloc.destroy(self);
|
||||
}
|
||||
|
||||
fn get(data: *anyopaque) *EventHandlerDataInternal {
|
||||
const ptr: *align(@alignOf(*EventHandlerDataInternal)) anyopaque = @alignCast(data);
|
||||
return @as(*EventHandlerDataInternal, @ptrCast(ptr));
|
||||
}
|
||||
|
||||
// retrieve a EventHandlerDataInternal from a listener.
|
||||
fn fromListener(lst: *EventListener) ?*EventHandlerDataInternal {
|
||||
const data = eventListenerGetData(lst);
|
||||
// free cbk allocation made on eventTargetAddEventListener
|
||||
if (data == null) return null;
|
||||
|
||||
return get(data.?);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn eventTargetAddEventListener(
|
||||
et: *EventTarget,
|
||||
alloc: std.mem.Allocator,
|
||||
typ: []const u8,
|
||||
cbk: Callback,
|
||||
handlerFunc: EventHandlerFunc,
|
||||
data: EventHandlerData,
|
||||
capture: bool,
|
||||
handler: EventHandler,
|
||||
) !void {
|
||||
// this allocation will be removed either on
|
||||
// eventTargetRemoveEventListener or eventTargetRemoveAllEventListeners
|
||||
const cbk_ptr = try alloc.create(Callback);
|
||||
cbk_ptr.* = cbk;
|
||||
const ehd = try EventHandlerDataInternal.init(alloc, handlerFunc, data);
|
||||
errdefer ehd.deinit(alloc);
|
||||
|
||||
// When a function is used as an event handler, its this parameter is bound
|
||||
// to the DOM element on which the listener is placed.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#this_in_dom_event_handlers
|
||||
try cbk_ptr.setThisArg(et);
|
||||
try ehd.data.cbk.setThisArg(et);
|
||||
|
||||
const ctx = @as(*anyopaque, @ptrCast(cbk_ptr));
|
||||
const ctx = @as(*anyopaque, @ptrCast(ehd));
|
||||
var listener: ?*EventListener = undefined;
|
||||
const errLst = c.dom_event_listener_create(handler, ctx, &listener);
|
||||
const errLst = c.dom_event_listener_create(EventHandler, ctx, &listener);
|
||||
try DOMErr(errLst);
|
||||
defer c.dom_event_listener_unref(listener);
|
||||
|
||||
@@ -646,13 +709,9 @@ pub fn eventTargetRemoveEventListener(
|
||||
lst: *EventListener,
|
||||
capture: bool,
|
||||
) !void {
|
||||
const data = eventListenerGetData(lst);
|
||||
// free cbk allocation made on eventTargetAddEventListener
|
||||
if (data) |d| {
|
||||
const cbk_ptr = event_handler_cbk(d);
|
||||
cbk_ptr.deinit(alloc);
|
||||
alloc.destroy(cbk_ptr);
|
||||
}
|
||||
// free data allocation made on eventTargetAddEventListener
|
||||
const ehd = EventHandlerDataInternal.fromListener(lst);
|
||||
if (ehd) |d| d.deinit(alloc);
|
||||
|
||||
const s = try strFromData(typ);
|
||||
const err = eventTargetVtable(et).remove_event_listener.?(et, s, lst, capture);
|
||||
@@ -680,13 +739,10 @@ pub fn eventTargetRemoveAllEventListeners(
|
||||
|
||||
if (lst) |listener| {
|
||||
defer c.dom_event_listener_unref(listener);
|
||||
const data = eventListenerGetData(listener);
|
||||
if (data) |d| {
|
||||
// free cbk allocation made on eventTargetAddEventListener
|
||||
const cbk = event_handler_cbk(d);
|
||||
cbk.deinit(alloc);
|
||||
alloc.destroy(cbk);
|
||||
}
|
||||
|
||||
const ehd = EventHandlerDataInternal.fromListener(listener);
|
||||
if (ehd) |d| d.deinit(alloc);
|
||||
|
||||
const err = eventTargetVtable(et).remove_event_listener.?(
|
||||
et,
|
||||
null,
|
||||
|
||||
@@ -52,9 +52,9 @@ pub const XMLHttpRequestEventTarget = struct {
|
||||
@as(*parser.EventTarget, @ptrCast(self)),
|
||||
alloc,
|
||||
typ,
|
||||
cbk,
|
||||
false,
|
||||
EventHandler,
|
||||
.{ .cbk = cbk },
|
||||
false,
|
||||
);
|
||||
}
|
||||
fn unregister(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, typ: []const u8, cbk: Callback) !void {
|
||||
|
||||
Reference in New Issue
Block a user