mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
Merge pull request #577 from lightpanda-io/unified_intrusive_events
Unify the Zig and JS events using an intrusive node.
This commit is contained in:
@@ -229,6 +229,8 @@ pub const Page = struct {
|
||||
|
||||
renderer: FlatRenderer,
|
||||
|
||||
window_clicked_event_node: parser.EventNode,
|
||||
|
||||
scope: *Env.Scope,
|
||||
|
||||
// current_script is the script currently evaluated by the page.
|
||||
@@ -245,6 +247,7 @@ pub const Page = struct {
|
||||
.url = URL.empty,
|
||||
.session = session,
|
||||
.renderer = FlatRenderer.init(arena),
|
||||
.window_clicked_event_node = .{ .func = windowClicked },
|
||||
.state = .{
|
||||
.arena = arena,
|
||||
.document = null,
|
||||
@@ -400,12 +403,10 @@ pub const Page = struct {
|
||||
self.doc = doc;
|
||||
|
||||
const document_element = (try parser.documentGetDocumentElement(doc)) orelse return error.DocumentElementError;
|
||||
try parser.eventTargetAddZigListener(
|
||||
try parser.eventTargetAddEventListener(
|
||||
parser.toEventTarget(parser.Element, document_element),
|
||||
arena,
|
||||
"click",
|
||||
windowClicked,
|
||||
self,
|
||||
&self.window_clicked_event_node,
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -675,8 +676,8 @@ pub const Page = struct {
|
||||
_ = try parser.elementDispatchEvent(element, @ptrCast(event));
|
||||
}
|
||||
|
||||
fn windowClicked(ctx: *anyopaque, event: *parser.Event) void {
|
||||
const self: *Page = @alignCast(@ptrCast(ctx));
|
||||
fn windowClicked(node: *parser.EventNode, event: *parser.Event) void {
|
||||
const self: *Page = @fieldParentPtr("window_clicked_event_node", node);
|
||||
self._windowClicked(event) catch |err| {
|
||||
log.err("window click handler: {}", .{err});
|
||||
};
|
||||
|
||||
@@ -46,7 +46,7 @@ pub const EventTarget = struct {
|
||||
|
||||
pub fn _addEventListener(
|
||||
self: *parser.EventTarget,
|
||||
eventType: []const u8,
|
||||
typ: []const u8,
|
||||
cbk: Env.Callback,
|
||||
capture: ?bool,
|
||||
state: *SessionState,
|
||||
@@ -56,7 +56,7 @@ pub const EventTarget = struct {
|
||||
// check if event target has already this listener
|
||||
const lst = try parser.eventTargetHasListener(
|
||||
self,
|
||||
eventType,
|
||||
typ,
|
||||
capture orelse false,
|
||||
cbk.id,
|
||||
);
|
||||
@@ -64,29 +64,28 @@ pub const EventTarget = struct {
|
||||
return;
|
||||
}
|
||||
|
||||
const eh = try EventHandler.init(state.arena, try cbk.withThis(self));
|
||||
|
||||
try parser.eventTargetAddEventListener(
|
||||
self,
|
||||
state.arena,
|
||||
eventType,
|
||||
EventHandler,
|
||||
.{ .cbk = cbk },
|
||||
typ,
|
||||
&eh.node,
|
||||
capture orelse false,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn _removeEventListener(
|
||||
self: *parser.EventTarget,
|
||||
eventType: []const u8,
|
||||
typ: []const u8,
|
||||
cbk: Env.Callback,
|
||||
capture: ?bool,
|
||||
state: *SessionState,
|
||||
// TODO: hanle EventListenerOptions
|
||||
// see #https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
||||
) !void {
|
||||
// check if event target has already this listener
|
||||
const lst = try parser.eventTargetHasListener(
|
||||
self,
|
||||
eventType,
|
||||
typ,
|
||||
capture orelse false,
|
||||
cbk.id,
|
||||
);
|
||||
@@ -97,8 +96,7 @@ pub const EventTarget = struct {
|
||||
// remove listener
|
||||
try parser.eventTargetRemoveEventListener(
|
||||
self,
|
||||
state.arena,
|
||||
eventType,
|
||||
typ,
|
||||
lst.?,
|
||||
capture orelse false,
|
||||
);
|
||||
|
||||
@@ -59,56 +59,45 @@ pub const MutationObserver = struct {
|
||||
.node = node,
|
||||
.options = options,
|
||||
.mutation_observer = self,
|
||||
.event_node = .{ .id = self.cbk.id, .func = Observer.handle },
|
||||
};
|
||||
|
||||
const arena = self.arena;
|
||||
|
||||
// register node's events
|
||||
if (options.childList or options.subtree) {
|
||||
try parser.eventTargetAddZigListener(
|
||||
try parser.eventTargetAddEventListener(
|
||||
parser.toEventTarget(parser.Node, node),
|
||||
arena,
|
||||
"DOMNodeInserted",
|
||||
Observer.handle,
|
||||
observer,
|
||||
&observer.event_node,
|
||||
false,
|
||||
);
|
||||
try parser.eventTargetAddZigListener(
|
||||
try parser.eventTargetAddEventListener(
|
||||
parser.toEventTarget(parser.Node, node),
|
||||
arena,
|
||||
"DOMNodeRemoved",
|
||||
Observer.handle,
|
||||
observer,
|
||||
&observer.event_node,
|
||||
false,
|
||||
);
|
||||
}
|
||||
if (options.attr()) {
|
||||
try parser.eventTargetAddZigListener(
|
||||
try parser.eventTargetAddEventListener(
|
||||
parser.toEventTarget(parser.Node, node),
|
||||
arena,
|
||||
"DOMAttrModified",
|
||||
Observer.handle,
|
||||
observer,
|
||||
&observer.event_node,
|
||||
false,
|
||||
);
|
||||
}
|
||||
if (options.cdata()) {
|
||||
try parser.eventTargetAddZigListener(
|
||||
try parser.eventTargetAddEventListener(
|
||||
parser.toEventTarget(parser.Node, node),
|
||||
arena,
|
||||
"DOMCharacterDataModified",
|
||||
Observer.handle,
|
||||
observer,
|
||||
&observer.event_node,
|
||||
false,
|
||||
);
|
||||
}
|
||||
if (options.subtree) {
|
||||
try parser.eventTargetAddZigListener(
|
||||
try parser.eventTargetAddEventListener(
|
||||
parser.toEventTarget(parser.Node, node),
|
||||
arena,
|
||||
"DOMSubtreeModified",
|
||||
Observer.handle,
|
||||
observer,
|
||||
&observer.event_node,
|
||||
false,
|
||||
);
|
||||
}
|
||||
@@ -221,6 +210,8 @@ const Observer = struct {
|
||||
// and batch the mutation records.
|
||||
mutation_observer: *MutationObserver,
|
||||
|
||||
event_node: parser.EventNode,
|
||||
|
||||
fn appliesTo(o: *const Observer, target: *parser.Node) bool {
|
||||
// mutation on any target is always ok.
|
||||
if (o.options.subtree) {
|
||||
@@ -250,9 +241,9 @@ const Observer = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn handle(ctx: *anyopaque, event: *parser.Event) void {
|
||||
// retrieve the observer from the data.
|
||||
var self: *Observer = @alignCast(@ptrCast(ctx));
|
||||
fn handle(en: *parser.EventNode, event: *parser.Event) void {
|
||||
const self: *Observer = @fieldParentPtr("event_node", en);
|
||||
|
||||
var mutation_observer = self.mutation_observer;
|
||||
|
||||
const node = blk: {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const parser = @import("../netsurf.zig");
|
||||
const Callback = @import("../env.zig").Callback;
|
||||
@@ -136,14 +137,35 @@ pub const Event = struct {
|
||||
};
|
||||
|
||||
pub const EventHandler = struct {
|
||||
fn handle(event: ?*parser.Event, data: *const parser.JSEventHandlerData) void {
|
||||
callback: Callback,
|
||||
node: parser.EventNode,
|
||||
|
||||
pub fn init(allocator: Allocator, callback: Callback) !*EventHandler {
|
||||
const eh = try allocator.create(EventHandler);
|
||||
eh.* = .{
|
||||
.callback = callback,
|
||||
.node = .{
|
||||
.id = callback.id,
|
||||
.func = handle,
|
||||
},
|
||||
};
|
||||
return eh;
|
||||
}
|
||||
|
||||
fn handle(node: *parser.EventNode, event: *parser.Event) void {
|
||||
const ievent = Event.toInterface(event) catch |err| {
|
||||
log.err("Event.toInterface: {}", .{err});
|
||||
return;
|
||||
};
|
||||
|
||||
const self: *EventHandler = @fieldParentPtr("node", node);
|
||||
var result: Callback.Result = undefined;
|
||||
data.cbk.tryCall(.{if (event) |evt| Event.toInterface(evt) catch unreachable else null}, &result) catch {
|
||||
self.callback.tryCall(.{ievent}, &result) catch {
|
||||
log.err("event handler error: {s}", .{result.exception});
|
||||
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
||||
};
|
||||
}
|
||||
}.handle;
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "Browser.Event" {
|
||||
|
||||
@@ -29,9 +29,6 @@ const c = @cImport({
|
||||
|
||||
const mimalloc = @import("mimalloc.zig");
|
||||
|
||||
const Callback = @import("env.zig").Callback;
|
||||
const SessionState = @import("env.zig").SessionState;
|
||||
|
||||
// init initializes netsurf lib.
|
||||
// init starts a mimalloc heap arena for the netsurf session. The caller must
|
||||
// call deinit() to free the arena memory.
|
||||
@@ -587,11 +584,61 @@ pub inline fn toEventTarget(comptime T: type, v: *T) *EventTarget {
|
||||
return @as(*EventTarget, @ptrCast(et_aligned));
|
||||
}
|
||||
|
||||
// The way we implement events is a lot like how Zig implements linked lists.
|
||||
// A Zig struct contains an `EventNode` field, i.e.:
|
||||
// node: parser.EventNode,
|
||||
//
|
||||
// When eventTargetAddEventListener is called, we pass in `&self.node`.
|
||||
// This is the pointer that's stored in the netsurf listener and it's the data
|
||||
// we can get back from the listener. We can call the node's `func` function,
|
||||
// passing the node itself, and the receiving function will know how to turn
|
||||
// that node into the our "self", i..e by using @fieldParentPtr.
|
||||
// https://www.openmymind.net/Zigs-New-LinkedList-API/
|
||||
pub const EventNode = struct {
|
||||
// Event id, used for removing. Internal Zig events won't have an id.
|
||||
// This is normally set to the callback.id for a JavaScript event.
|
||||
id: ?usize = null,
|
||||
|
||||
func: *const fn (node: *EventNode, event: *Event) void,
|
||||
|
||||
fn idFromListener(lst: *EventListener) ?usize {
|
||||
const ctx = eventListenerGetData(lst) orelse return null;
|
||||
const node: *EventNode = @alignCast(@ptrCast(ctx));
|
||||
return node.id;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn eventTargetAddEventListener(
|
||||
et: *EventTarget,
|
||||
typ: []const u8,
|
||||
node: *EventNode,
|
||||
capture: bool,
|
||||
) !void {
|
||||
const event_handler = struct {
|
||||
fn handle(event_: ?*Event, ptr_: ?*anyopaque) callconv(.C) void {
|
||||
const ptr = ptr_ orelse return;
|
||||
const event = event_ orelse return;
|
||||
|
||||
const node_: *EventNode = @alignCast(@ptrCast(ptr));
|
||||
node_.func(node_, event);
|
||||
}
|
||||
}.handle;
|
||||
|
||||
var listener: ?*EventListener = undefined;
|
||||
const errLst = c.dom_event_listener_create(event_handler, node, &listener);
|
||||
try DOMErr(errLst);
|
||||
defer c.dom_event_listener_unref(listener);
|
||||
|
||||
const s = try strFromData(typ);
|
||||
const err = eventTargetVtable(et).add_event_listener.?(et, s, listener, capture);
|
||||
try DOMErr(err);
|
||||
}
|
||||
|
||||
pub fn eventTargetHasListener(
|
||||
et: *EventTarget,
|
||||
typ: []const u8,
|
||||
capture: bool,
|
||||
cbk_id: usize,
|
||||
id: usize,
|
||||
) !?*EventListener {
|
||||
const str = try strFromData(typ);
|
||||
|
||||
@@ -616,12 +663,9 @@ pub fn eventTargetHasListener(
|
||||
// and capture property,
|
||||
// let's check if the callback handler is the same
|
||||
defer c.dom_event_listener_unref(listener);
|
||||
if (EventHandlerData.fromListener(listener)) |ehd| {
|
||||
switch (ehd.*) {
|
||||
.js => |js| if (cbk_id == js.data.cbk.id) {
|
||||
return lst;
|
||||
},
|
||||
.zig => {},
|
||||
if (EventNode.idFromListener(listener)) |node_id| {
|
||||
if (node_id == id) {
|
||||
return lst;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -638,144 +682,18 @@ pub fn eventTargetHasListener(
|
||||
return null;
|
||||
}
|
||||
|
||||
// The *anyopque that get stored in the libdom listener, which we'll retrieve
|
||||
// when then event is dispatched so that we can execute the JS or Zig callback.
|
||||
const EventHandlerData = union(enum) {
|
||||
js: JS,
|
||||
zig: Zig,
|
||||
|
||||
const JS = struct {
|
||||
data: JSEventHandlerData,
|
||||
func: JSEventHandlerFunc,
|
||||
};
|
||||
|
||||
const Zig = struct {
|
||||
ctx: *anyopaque,
|
||||
func: ZigEventHandlerFunc,
|
||||
};
|
||||
|
||||
// retrieve a EventHandlerDataInternal from a listener.
|
||||
fn fromListener(lst: *EventListener) ?*EventHandlerData {
|
||||
const ctx = eventListenerGetData(lst) orelse return null;
|
||||
const ehd: *EventHandlerData = @alignCast(@ptrCast(ctx));
|
||||
return ehd;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *EventHandlerData, alloc: std.mem.Allocator) void {
|
||||
switch (self.*) {
|
||||
.js => |*js| {
|
||||
const js_data = &js.data;
|
||||
if (js_data.deinitFunc) |df| {
|
||||
df(js_data.ctx, alloc);
|
||||
}
|
||||
},
|
||||
.zig => {},
|
||||
}
|
||||
alloc.destroy(self);
|
||||
}
|
||||
|
||||
pub fn handle(self: *EventHandlerData, event: ?*Event) void {
|
||||
switch (self.*) {
|
||||
.js => |*js| js.func(event, &js.data),
|
||||
.zig => |zig| zig.func(zig.ctx, event.?),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const JSEventHandlerData = struct {
|
||||
cbk: Callback,
|
||||
ctx: ?*anyopaque = null,
|
||||
// deinitFunc implements the data deinitialization.
|
||||
deinitFunc: ?DeinitFunc = null,
|
||||
|
||||
pub const DeinitFunc = *const fn (data: ?*anyopaque, alloc: std.mem.Allocator) void;
|
||||
};
|
||||
|
||||
const JSEventHandlerFunc = *const fn (event: ?*Event, data: *JSEventHandlerData) void;
|
||||
const ZigEventHandlerFunc = *const fn (ctx: *anyopaque, event: *Event) void;
|
||||
|
||||
pub fn eventTargetAddEventListener(
|
||||
et: *EventTarget,
|
||||
alloc: std.mem.Allocator,
|
||||
typ: []const u8,
|
||||
func: JSEventHandlerFunc,
|
||||
data: JSEventHandlerData,
|
||||
capture: bool,
|
||||
) !void {
|
||||
// this allocation will be removed either on
|
||||
// eventTargetRemoveEventListener or eventTargetRemoveAllEventListeners
|
||||
const ehd = try alloc.create(EventHandlerData);
|
||||
errdefer alloc.destroy(ehd);
|
||||
ehd.* = .{ .js = .{ .data = data, .func = func } };
|
||||
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 ehd.js.data.cbk.setThis(et);
|
||||
|
||||
return addEventTargetListener(et, typ, ehd, capture);
|
||||
}
|
||||
|
||||
pub fn eventTargetAddZigListener(
|
||||
et: *EventTarget,
|
||||
alloc: std.mem.Allocator,
|
||||
typ: []const u8,
|
||||
func: ZigEventHandlerFunc,
|
||||
ctx: *anyopaque,
|
||||
capture: bool,
|
||||
) !void {
|
||||
const ehd = try alloc.create(EventHandlerData);
|
||||
errdefer alloc.destroy(ehd);
|
||||
ehd.* = .{ .zig = .{ .ctx = ctx, .func = func } };
|
||||
return addEventTargetListener(et, typ, ehd, capture);
|
||||
}
|
||||
|
||||
fn addEventTargetListener(et: *EventTarget, typ: []const u8, data: *anyopaque, capture: bool) !void {
|
||||
// event_handler implements the function exposed in C and called by libdom.
|
||||
// It retrieves the EventHandler and calls the appropriate (JS or Zig)
|
||||
// handler function with the corresponding data.
|
||||
const event_handler = struct {
|
||||
fn handle(event: ?*Event, ptr_: ?*anyopaque) callconv(.C) void {
|
||||
const ptr = ptr_ orelse return;
|
||||
@as(*EventHandlerData, @alignCast(@ptrCast(ptr))).handle(event);
|
||||
// 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;
|
||||
|
||||
var listener: ?*EventListener = undefined;
|
||||
const errLst = c.dom_event_listener_create(event_handler, data, &listener);
|
||||
try DOMErr(errLst);
|
||||
defer c.dom_event_listener_unref(listener);
|
||||
|
||||
const s = try strFromData(typ);
|
||||
const err = eventTargetVtable(et).add_event_listener.?(et, s, listener, capture);
|
||||
try DOMErr(err);
|
||||
}
|
||||
|
||||
pub fn eventTargetRemoveEventListener(
|
||||
et: *EventTarget,
|
||||
alloc: std.mem.Allocator,
|
||||
typ: []const u8,
|
||||
lst: *EventListener,
|
||||
capture: bool,
|
||||
) !void {
|
||||
// free data allocation made on eventTargetAddEventListener
|
||||
if (EventHandlerData.fromListener(lst)) |ehd| {
|
||||
ehd.deinit(alloc);
|
||||
}
|
||||
|
||||
const s = try strFromData(typ);
|
||||
const err = eventTargetVtable(et).remove_event_listener.?(et, s, lst, capture);
|
||||
try DOMErr(err);
|
||||
}
|
||||
|
||||
pub fn eventTargetRemoveAllEventListeners(
|
||||
et: *EventTarget,
|
||||
alloc: std.mem.Allocator,
|
||||
) !void {
|
||||
pub fn eventTargetRemoveAllEventListeners(et: *EventTarget) !void {
|
||||
var next: ?*EventListenerEntry = undefined;
|
||||
var lst: ?*EventListener = undefined;
|
||||
|
||||
@@ -792,15 +710,8 @@ pub fn eventTargetRemoveAllEventListeners(
|
||||
try DOMErr(errIter);
|
||||
|
||||
if (lst) |listener| {
|
||||
defer c.dom_event_listener_unref(listener);
|
||||
|
||||
if (EventHandlerData.fromListener(listener)) |ehd| {
|
||||
if (ehd.* == .zig) {
|
||||
// we don't remove Zig listeners
|
||||
continue;
|
||||
}
|
||||
|
||||
ehd.deinit(alloc);
|
||||
if (EventNode.idFromListener(listener) != null) {
|
||||
defer c.dom_event_listener_unref(listener);
|
||||
const err = eventTargetVtable(et).remove_event_listener.?(
|
||||
et,
|
||||
null,
|
||||
|
||||
@@ -48,25 +48,25 @@ pub const XMLHttpRequestEventTarget = struct {
|
||||
typ: []const u8,
|
||||
cbk: Callback,
|
||||
) !void {
|
||||
const target = @as(*parser.EventTarget, @ptrCast(self));
|
||||
const eh = try EventHandler.init(alloc, try cbk.withThis(target));
|
||||
try parser.eventTargetAddEventListener(
|
||||
@as(*parser.EventTarget, @ptrCast(self)),
|
||||
alloc,
|
||||
target,
|
||||
typ,
|
||||
EventHandler,
|
||||
.{ .cbk = cbk },
|
||||
&eh.node,
|
||||
false,
|
||||
);
|
||||
}
|
||||
fn unregister(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, typ: []const u8, cbk: Callback) !void {
|
||||
fn unregister(self: *XMLHttpRequestEventTarget, typ: []const u8, cbk_id: usize) !void {
|
||||
const et = @as(*parser.EventTarget, @ptrCast(self));
|
||||
// check if event target has already this listener
|
||||
const lst = try parser.eventTargetHasListener(et, typ, false, cbk.id);
|
||||
const lst = try parser.eventTargetHasListener(et, typ, false, cbk_id);
|
||||
if (lst == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove listener
|
||||
try parser.eventTargetRemoveEventListener(et, alloc, typ, lst.?, false);
|
||||
try parser.eventTargetRemoveEventListener(et, typ, lst.?, false);
|
||||
}
|
||||
|
||||
pub fn get_onloadstart(self: *XMLHttpRequestEventTarget) ?Callback {
|
||||
@@ -89,39 +89,33 @@ pub const XMLHttpRequestEventTarget = struct {
|
||||
}
|
||||
|
||||
pub fn set_onloadstart(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
||||
const arena = state.arena;
|
||||
if (self.onloadstart_cbk) |cbk| try self.unregister(arena, "loadstart", cbk);
|
||||
try self.register(arena, "loadstart", handler);
|
||||
if (self.onloadstart_cbk) |cbk| try self.unregister("loadstart", cbk.id);
|
||||
try self.register(state.arena, "loadstart", handler);
|
||||
self.onloadstart_cbk = handler;
|
||||
}
|
||||
pub fn set_onprogress(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
||||
const arena = state.arena;
|
||||
if (self.onprogress_cbk) |cbk| try self.unregister(arena, "progress", cbk);
|
||||
try self.register(arena, "progress", handler);
|
||||
if (self.onprogress_cbk) |cbk| try self.unregister("progress", cbk.id);
|
||||
try self.register(state.arena, "progress", handler);
|
||||
self.onprogress_cbk = handler;
|
||||
}
|
||||
pub fn set_onabort(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
||||
const arena = state.arena;
|
||||
if (self.onabort_cbk) |cbk| try self.unregister(arena, "abort", cbk);
|
||||
try self.register(arena, "abort", handler);
|
||||
if (self.onabort_cbk) |cbk| try self.unregister("abort", cbk.id);
|
||||
try self.register(state.arena, "abort", handler);
|
||||
self.onabort_cbk = handler;
|
||||
}
|
||||
pub fn set_onload(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
||||
const arena = state.arena;
|
||||
if (self.onload_cbk) |cbk| try self.unregister(arena, "load", cbk);
|
||||
try self.register(arena, "load", handler);
|
||||
if (self.onload_cbk) |cbk| try self.unregister("load", cbk.id);
|
||||
try self.register(state.arena, "load", handler);
|
||||
self.onload_cbk = handler;
|
||||
}
|
||||
pub fn set_ontimeout(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
||||
const arena = state.arena;
|
||||
if (self.ontimeout_cbk) |cbk| try self.unregister(arena, "timeout", cbk);
|
||||
try self.register(arena, "timeout", handler);
|
||||
if (self.ontimeout_cbk) |cbk| try self.unregister("timeout", cbk.id);
|
||||
try self.register(state.arena, "timeout", handler);
|
||||
self.ontimeout_cbk = handler;
|
||||
}
|
||||
pub fn set_onloadend(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
||||
const arena = state.arena;
|
||||
if (self.onloadend_cbk) |cbk| try self.unregister(arena, "loadend", cbk);
|
||||
try self.register(arena, "loadend", handler);
|
||||
if (self.onloadend_cbk) |cbk| try self.unregister("loadend", cbk.id);
|
||||
try self.register(state.arena, "loadend", handler);
|
||||
self.onloadend_cbk = handler;
|
||||
}
|
||||
|
||||
|
||||
@@ -766,7 +766,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
||||
pub const Callback = struct {
|
||||
id: usize,
|
||||
scope: *Scope,
|
||||
_this: ?v8.Object = null,
|
||||
this: ?v8.Object = null,
|
||||
func: PersistentFunction,
|
||||
|
||||
// We use this when mapping a JS value to a Zig object. We can't
|
||||
@@ -781,8 +781,13 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
||||
exception: []const u8,
|
||||
};
|
||||
|
||||
pub fn setThis(self: *Callback, value: anytype) !void {
|
||||
self._this = try self.scope.valueToExistingObject(value);
|
||||
pub fn withThis(self: *const Callback, value: anytype) !Callback {
|
||||
return .{
|
||||
.id = self.id,
|
||||
.func = self.func,
|
||||
.scope = self.scope,
|
||||
.this = try self.scope.valueToExistingObject(value),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn call(self: *const Callback, args: anytype) !void {
|
||||
@@ -830,7 +835,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
||||
}
|
||||
|
||||
fn getThis(self: *const Callback) v8.Object {
|
||||
return self._this orelse self.scope.context.getGlobal();
|
||||
return self.this orelse self.scope.context.getGlobal();
|
||||
}
|
||||
|
||||
// debug/helper to print the source of the JS callback
|
||||
|
||||
Reference in New Issue
Block a user