diff --git a/src/browser/Page.zig b/src/browser/Page.zig index e769f9a5..8e86c47a 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -65,6 +65,9 @@ const timestamp = @import("../datetime.zig").timestamp; const milliTimestamp = @import("../datetime.zig").milliTimestamp; const WebApiURL = @import("webapi/URL.zig"); +const global_event_handlers = @import("webapi/global_event_handlers.zig"); +const GlobalEventHandlersLookup = global_event_handlers.Lookup; +const GlobalEventHandler = global_event_handlers.Handler; var default_url = WebApiURL{ ._raw = "about:blank" }; pub var default_location: Location = Location{ ._url = &default_url }; @@ -104,16 +107,22 @@ _element_assigned_slots: Element.AssignedSlotLookup = .{}, /// Lazily-created inline event listeners (or listeners provided as attributes). /// Avoids bloating all elements with extra function fields for rare usage. /// -/// Use this when a listener provided like these: +/// Use this when a listener provided like this: +/// +/// ```js +/// img.onload = () => { ... }; +/// ``` +/// +/// Its also used as cache for such cases after lazy evaluation: /// /// ```html /// /// ``` /// /// ```js -/// img.onload = () => { ... }; +/// img.setAttribute("onload", "(() => { ... })()"); /// ``` -_element_attr_listeners: Element.AttrListenerLookup = .{}, +_element_attr_listeners: GlobalEventHandlersLookup = .{}, _script_manager: ScriptManager, @@ -1209,7 +1218,7 @@ pub fn getElementByIdFromNode(self: *Page, node: *Node, id: []const u8) ?*Elemen pub fn setAttrListener( self: *Page, element: *Element, - listener_type: Element.KnownListener, + listener_type: GlobalEventHandler, listener_callback: JS.Function.Global, ) !void { if (comptime IS_DEBUG) { @@ -1219,7 +1228,7 @@ pub fn setAttrListener( }); } - const key = element.calcAttrListenerKey(listener_type); + const key = global_event_handlers.calculateKey(element.asEventTarget(), listener_type); const gop = try self._element_attr_listeners.getOrPut(self.arena, key); gop.value_ptr.* = listener_callback; } @@ -1228,9 +1237,10 @@ pub fn setAttrListener( pub fn getAttrListener( self: *const Page, element: *Element, - listener_type: Element.KnownListener, + listener_type: GlobalEventHandler, ) ?JS.Function.Global { - return self._element_attr_listeners.get(element.calcAttrListenerKey(listener_type)); + const key = global_event_handlers.calculateKey(element.asEventTarget(), listener_type); + return self._element_attr_listeners.get(key); } pub fn registerPerformanceObserver(self: *Page, observer: *PerformanceObserver) !void { @@ -2272,236 +2282,6 @@ fn populateElementAttributes(self: *Page, element: *Element, list: anytype) !voi } var attributes = try element.createAttributeList(self); while (list.next()) |attr| { - // Event handlers can be provided like attributes; here we check if there's such. - const name = attr.name.local; - lp.assert(name.len != 0, "populateElementAttributes: 0-length attr name", .{ .attr = attr }); - // Idea here is to make this check as cheap as possible. - const has_on_prefix = @as(u16, @bitCast([2]u8{ name.ptr[0], name.ptr[1 % name.len] })) == asUint("on"); - // We may have found an event handler. - if (has_on_prefix) { - // Must be usable as function. - const func = self.js.stringToPersistedFunction(attr.value.slice()) catch continue; - - // Longest known listener kind is 32 bytes long. - const remaining: u6 = @truncate(name.len -| 2); - const unsafe = name.ptr + 2; - const Vec16x8 = @Vector(16, u8); - const Vec32x8 = @Vector(32, u8); - - switch (remaining) { - 3 => if (@as(u24, @bitCast(unsafe[0..3].*)) == asUint("cut")) { - try self.setAttrListener(element, .cut, func); - }, - 4 => switch (@as(u32, @bitCast(unsafe[0..4].*))) { - asUint("blur") => try self.setAttrListener(element, .blur, func), - asUint("copy") => try self.setAttrListener(element, .copy, func), - asUint("drag") => try self.setAttrListener(element, .drag, func), - asUint("drop") => try self.setAttrListener(element, .drop, func), - asUint("load") => try self.setAttrListener(element, .load, func), - asUint("play") => try self.setAttrListener(element, .play, func), - else => {}, - }, - 5 => switch (@as(u40, @bitCast(unsafe[0..5].*))) { - asUint("abort") => try self.setAttrListener(element, .abort, func), - asUint("click") => try self.setAttrListener(element, .click, func), - asUint("close") => try self.setAttrListener(element, .close, func), - asUint("ended") => try self.setAttrListener(element, .ended, func), - asUint("error") => try self.setAttrListener(element, .@"error", func), - asUint("focus") => try self.setAttrListener(element, .focus, func), - asUint("input") => try self.setAttrListener(element, .input, func), - asUint("keyup") => try self.setAttrListener(element, .keyup, func), - asUint("paste") => try self.setAttrListener(element, .paste, func), - asUint("pause") => try self.setAttrListener(element, .pause, func), - asUint("reset") => try self.setAttrListener(element, .reset, func), - asUint("wheel") => try self.setAttrListener(element, .wheel, func), - else => {}, - }, - 6 => switch (@as(u48, @bitCast(unsafe[0..6].*))) { - asUint("cancel") => try self.setAttrListener(element, .cancel, func), - asUint("change") => try self.setAttrListener(element, .change, func), - asUint("resize") => try self.setAttrListener(element, .resize, func), - asUint("scroll") => try self.setAttrListener(element, .scroll, func), - asUint("seeked") => try self.setAttrListener(element, .seeked, func), - asUint("select") => try self.setAttrListener(element, .select, func), - asUint("submit") => try self.setAttrListener(element, .submit, func), - asUint("toggle") => try self.setAttrListener(element, .toggle, func), - else => {}, - }, - 7 => switch (@as(u56, @bitCast(unsafe[0..7].*))) { - asUint("canplay") => try self.setAttrListener(element, .canplay, func), - asUint("command") => try self.setAttrListener(element, .command, func), - asUint("dragend") => try self.setAttrListener(element, .dragend, func), - asUint("emptied") => try self.setAttrListener(element, .emptied, func), - asUint("invalid") => try self.setAttrListener(element, .invalid, func), - asUint("keydown") => try self.setAttrListener(element, .keydown, func), - asUint("mouseup") => try self.setAttrListener(element, .mouseup, func), - asUint("playing") => try self.setAttrListener(element, .playing, func), - asUint("seeking") => try self.setAttrListener(element, .seeking, func), - asUint("stalled") => try self.setAttrListener(element, .stalled, func), - asUint("suspend") => try self.setAttrListener(element, .@"suspend", func), - asUint("waiting") => try self.setAttrListener(element, .waiting, func), - else => {}, - }, - 8 => switch (@as(u64, @bitCast(unsafe[0..8].*))) { - asUint("auxclick") => try self.setAttrListener(element, .auxclick, func), - asUint("dblclick") => try self.setAttrListener(element, .dblclick, func), - asUint("dragexit") => try self.setAttrListener(element, .dragexit, func), - asUint("dragover") => try self.setAttrListener(element, .dragover, func), - asUint("formdata") => try self.setAttrListener(element, .formdata, func), - asUint("keypress") => try self.setAttrListener(element, .keypress, func), - asUint("mouseout") => try self.setAttrListener(element, .mouseout, func), - asUint("progress") => try self.setAttrListener(element, .progress, func), - else => {}, - }, - // Won't fit to 64-bit integer; we do 2 checks. - 9 => switch (@as(u64, @bitCast(unsafe[0..8].*))) { - asUint("cuechang") => if (unsafe[8] == 'e') try self.setAttrListener(element, .cuechange, func), - asUint("dragente") => if (unsafe[8] == 'r') try self.setAttrListener(element, .dragenter, func), - asUint("dragleav") => if (unsafe[8] == 'e') try self.setAttrListener(element, .dragleave, func), - asUint("dragstar") => if (unsafe[8] == 't') try self.setAttrListener(element, .dragstart, func), - asUint("loadstar") => if (unsafe[8] == 't') try self.setAttrListener(element, .loadstart, func), - asUint("mousedow") => if (unsafe[8] == 'n') try self.setAttrListener(element, .mousedown, func), - asUint("mousemov") => if (unsafe[8] == 'e') try self.setAttrListener(element, .mousemove, func), - asUint("mouseove") => if (unsafe[8] == 'r') try self.setAttrListener(element, .mouseover, func), - asUint("pointeru") => if (unsafe[8] == 'p') try self.setAttrListener(element, .pointerup, func), - asUint("scrollen") => if (unsafe[8] == 'd') try self.setAttrListener(element, .scrollend, func), - else => {}, - }, - 10 => switch (@as(u64, @bitCast(unsafe[0..8].*))) { - asUint("loadedda") => if (asUint("ta") == @as(u16, @bitCast(unsafe[8..10].*))) - try self.setAttrListener(element, .loadeddata, func), - asUint("pointero") => if (asUint("ut") == @as(u16, @bitCast(unsafe[8..10].*))) - try self.setAttrListener(element, .pointerout, func), - asUint("ratechan") => if (asUint("ge") == @as(u16, @bitCast(unsafe[8..10].*))) - try self.setAttrListener(element, .ratechange, func), - asUint("slotchan") => if (asUint("ge") == @as(u16, @bitCast(unsafe[8..10].*))) - try self.setAttrListener(element, .slotchange, func), - asUint("timeupda") => if (asUint("te") == @as(u16, @bitCast(unsafe[8..10].*))) - try self.setAttrListener(element, .timeupdate, func), - else => {}, - }, - 11 => switch (@as(u64, @bitCast(unsafe[0..8].*))) { - asUint("beforein") => if (asUint("put") == @as(u24, @bitCast(unsafe[8..11].*))) - try self.setAttrListener(element, .beforeinput, func), - asUint("beforema") => if (asUint("tch") == @as(u24, @bitCast(unsafe[8..11].*))) - try self.setAttrListener(element, .beforematch, func), - asUint("contextl") => if (asUint("ost") == @as(u24, @bitCast(unsafe[8..11].*))) - try self.setAttrListener(element, .contextlost, func), - asUint("contextm") => if (asUint("enu") == @as(u24, @bitCast(unsafe[8..11].*))) - try self.setAttrListener(element, .contextmenu, func), - asUint("pointerd") => if (asUint("own") == @as(u24, @bitCast(unsafe[8..11].*))) - try self.setAttrListener(element, .pointerdown, func), - asUint("pointerm") => if (asUint("ove") == @as(u24, @bitCast(unsafe[8..11].*))) - try self.setAttrListener(element, .pointermove, func), - asUint("pointero") => if (asUint("ver") == @as(u24, @bitCast(unsafe[8..11].*))) - try self.setAttrListener(element, .pointerover, func), - asUint("selectst") => if (asUint("art") == @as(u24, @bitCast(unsafe[8..11].*))) - try self.setAttrListener(element, .selectstart, func), - else => {}, - }, - 12 => switch (@as(u64, @bitCast(unsafe[0..8].*))) { - asUint("animatio") => if (asUint("nend") == @as(u32, @bitCast(unsafe[8..12].*))) - try self.setAttrListener(element, .animationend, func), - asUint("beforeto") => if (asUint("ggle") == @as(u32, @bitCast(unsafe[8..12].*))) - try self.setAttrListener(element, .beforetoggle, func), - asUint("pointere") => if (asUint("nter") == @as(u32, @bitCast(unsafe[8..12].*))) - try self.setAttrListener(element, .pointerenter, func), - asUint("pointerl") => if (asUint("eave") == @as(u32, @bitCast(unsafe[8..12].*))) - try self.setAttrListener(element, .pointerleave, func), - asUint("volumech") => if (asUint("ange") == @as(u32, @bitCast(unsafe[8..12].*))) - try self.setAttrListener(element, .volumechange, func), - else => {}, - }, - 13 => switch (@as(u64, @bitCast(unsafe[0..8].*))) { - asUint("pointerc") => if (asUint("ancel") == @as(u40, @bitCast(unsafe[8..13].*))) - try self.setAttrListener(element, .pointercancel, func), - asUint("transiti") => switch (@as(u40, @bitCast(unsafe[8..13].*))) { - asUint("onend") => try self.setAttrListener(element, .transitionend, func), - asUint("onrun") => try self.setAttrListener(element, .transitionrun, func), - else => {}, - }, - else => {}, - }, - 14 => switch (@as(u64, @bitCast(unsafe[0..8].*))) { - asUint("animatio") => if (asUint("nstart") == @as(u48, @bitCast(unsafe[8..14].*))) - try self.setAttrListener(element, .animationstart, func), - asUint("canplayt") => if (asUint("hrough") == @as(u48, @bitCast(unsafe[8..14].*))) - try self.setAttrListener(element, .canplaythrough, func), - asUint("duration") => if (asUint("change") == @as(u48, @bitCast(unsafe[8..14].*))) - try self.setAttrListener(element, .durationchange, func), - asUint("loadedme") => if (asUint("tadata") == @as(u48, @bitCast(unsafe[8..14].*))) - try self.setAttrListener(element, .loadedmetadata, func), - else => {}, - }, - 15 => switch (@as(u64, @bitCast(unsafe[0..8].*))) { - asUint("animatio") => if (asUint("ncancel") == @as(u56, @bitCast(unsafe[8..15].*))) - try self.setAttrListener(element, .animationcancel, func), - asUint("contextr") => if (asUint("estored") == @as(u56, @bitCast(unsafe[8..15].*))) - try self.setAttrListener(element, .contextrestored, func), - asUint("fullscre") => if (asUint("enerror") == @as(u56, @bitCast(unsafe[8..15].*))) - try self.setAttrListener(element, .fullscreenerror, func), - asUint("selectio") => if (asUint("nchange") == @as(u56, @bitCast(unsafe[8..15].*))) - try self.setAttrListener(element, .selectionchange, func), - asUint("transiti") => if (asUint("onstart") == @as(u56, @bitCast(unsafe[8..15].*))) - try self.setAttrListener(element, .transitionstart, func), - else => {}, - }, - // Can't switch on vector types. - 16 => { - const as_vector: Vec16x8 = unsafe[0..16].*; - - if (@reduce(.And, as_vector == @as(Vec16x8, "fullscreenchange".*))) { - try self.setAttrListener(element, .fullscreenchange, func); - } else if (@reduce(.And, as_vector == @as(Vec16x8, "pointerrawupdate".*))) { - try self.setAttrListener(element, .pointerrawupdate, func); - } else if (@reduce(.And, as_vector == @as(Vec16x8, "transitioncancel".*))) { - try self.setAttrListener(element, .transitioncancel, func); - } - }, - 17 => { - const as_vector: Vec16x8 = unsafe[0..16].*; - - const dirty = @reduce(.And, as_vector == @as(Vec16x8, "gotpointercaptur".*)) and - unsafe[16] == 'e'; - if (dirty) { - try self.setAttrListener(element, .gotpointercapture, func); - } - }, - 18 => { - const as_vector: Vec16x8 = unsafe[0..16].*; - - const is_animationiteration = @reduce(.And, as_vector == @as(Vec16x8, "animationiterati".*)) and - asUint("on") == @as(u16, @bitCast(unsafe[16..18].*)); - if (is_animationiteration) { - try self.setAttrListener(element, .animationiteration, func); - } else { - const is_lostpointercapture = @reduce(.And, as_vector == @as(Vec16x8, "lostpointercaptu".*)) and - asUint("re") == @as(u16, @bitCast(unsafe[16..18].*)); - if (is_lostpointercapture) { - try self.setAttrListener(element, .lostpointercapture, func); - } - } - }, - 23 => { - const as_vector: Vec16x8 = unsafe[0..16].*; - - const dirty = @reduce(.And, as_vector == @as(Vec16x8, "securitypolicyvi".*)) and - asUint("olation") == @as(u56, @bitCast(unsafe[16..23].*)); - if (dirty) { - try self.setAttrListener(element, .securitypolicyviolation, func); - } - }, - 32 => { - const as_vector: Vec32x8 = unsafe[0..32].*; - - if (@reduce(.And, as_vector == @as(Vec32x8, "contentvisibilityautostatechange".*))) { - try self.setAttrListener(element, .contentvisibilityautostatechange, func); - } - }, - else => {}, - } - } - try attributes.putNew(attr.name.local.slice(), attr.value.slice(), self); } } @@ -3364,7 +3144,7 @@ pub fn submitForm(self: *Page, submitter_: ?*Element, form_: ?*Element.Html.Form if (submit_opts.fire_event) { const submit_event = try Event.initTrusted("submit", .{ .bubbles = true, .cancelable = true }, self); - const onsubmit_handler = form.asHtmlElement().getOnSubmit(self); + const onsubmit_handler = try form.asHtmlElement().getOnSubmit(self); var ls: JS.Local.Scope = undefined; self.js.localScope(&ls); diff --git a/src/browser/webapi/Element.zig b/src/browser/webapi/Element.zig index d0e779a9..4a12f390 100644 --- a/src/browser/webapi/Element.zig +++ b/src/browser/webapi/Element.zig @@ -49,129 +49,6 @@ pub const RelListLookup = std.AutoHashMapUnmanaged(*Element, *collections.DOMTok pub const ShadowRootLookup = std.AutoHashMapUnmanaged(*Element, *ShadowRoot); pub const AssignedSlotLookup = std.AutoHashMapUnmanaged(*Element, *Html.Slot); -/// Better to discriminate it since not directly a pointer int. -/// -/// See `calcAttrListenerKey` to obtain one. -const AttrListenerKey = u64; -/// Use `getAttrListenerKey` to create a key. -pub const AttrListenerLookup = std.AutoHashMapUnmanaged(AttrListenerKey, js.Function.Global); - -/// Enum of known event listeners; increasing the size of it (u7) -/// can cause `AttrListenerKey` to behave incorrectly. -pub const KnownListener = enum(u7) { - abort, - animationcancel, - animationend, - animationiteration, - animationstart, - auxclick, - beforeinput, - beforematch, - beforetoggle, - blur, - cancel, - canplay, - canplaythrough, - change, - click, - close, - command, - contentvisibilityautostatechange, - contextlost, - contextmenu, - contextrestored, - copy, - cuechange, - cut, - dblclick, - drag, - dragend, - dragenter, - dragexit, - dragleave, - dragover, - dragstart, - drop, - durationchange, - emptied, - ended, - @"error", - focus, - formdata, - fullscreenchange, - fullscreenerror, - gotpointercapture, - input, - invalid, - keydown, - keypress, - keyup, - load, - loadeddata, - loadedmetadata, - loadstart, - lostpointercapture, - mousedown, - mousemove, - mouseout, - mouseover, - mouseup, - paste, - pause, - play, - playing, - pointercancel, - pointerdown, - pointerenter, - pointerleave, - pointermove, - pointerout, - pointerover, - pointerrawupdate, - pointerup, - progress, - ratechange, - reset, - resize, - scroll, - scrollend, - securitypolicyviolation, - seeked, - seeking, - select, - selectionchange, - selectstart, - slotchange, - stalled, - submit, - @"suspend", - timeupdate, - toggle, - transitioncancel, - transitionend, - transitionrun, - transitionstart, - volumechange, - waiting, - wheel, -}; - -/// Calculates a lookup key (`AttrListenerKey`) to use with `AttrListenerLookup` for an element. -/// NEVER use generated key to retrieve a pointer back. Portability is not guaranteed. -pub fn calcAttrListenerKey(self: *Element, event_type: KnownListener) AttrListenerKey { - // We can use `Element` for the key too; `EventTarget` is strict about - // its size and alignment, though. - const target = self.asEventTarget(); - // Check if we have 3 bits available from alignment of 8. - lp.assert(@alignOf(@TypeOf(target)) == 8, "createLookupKey: incorrect alignment", .{ - .event_target_alignment = @alignOf(@TypeOf(target)), - }); - - const ptr = @intFromPtr(target) >> 3; - lp.assert(ptr < (1 << 57), "createLookupKey: pointer overflow", .{ .ptr = ptr }); - return ptr | (@as(u64, @intFromEnum(event_type)) << 57); -} - pub const Namespace = enum(u8) { html, svg, diff --git a/src/browser/webapi/element/Html.zig b/src/browser/webapi/element/Html.zig index 3844275b..cc20622a 100644 --- a/src/browser/webapi/element/Html.zig +++ b/src/browser/webapi/element/Html.zig @@ -19,6 +19,11 @@ const std = @import("std"); const js = @import("../../js/js.zig"); const reflect = @import("../../reflect.zig"); +const log = @import("../../../log.zig"); + +const global_event_handlers = @import("../global_event_handlers.zig"); +const GlobalEventHandlersLookup = global_event_handlers.Lookup; +const GlobalEventHandler = global_event_handlers.Handler; const Page = @import("../../Page.zig"); const Node = @import("../Node.zig"); @@ -332,764 +337,793 @@ pub fn click(self: *HtmlElement, page: *Page) !void { try page._event_manager.dispatch(self.asEventTarget(), event.asEvent()); } -pub fn setOnAbort(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .abort, callback); +fn getAttributeFunction( + self: *HtmlElement, + listener_type: GlobalEventHandler, + page: *Page, +) !?js.Function.Global { + const element = self.asElement(); + if (page.getAttrListener(element, listener_type)) |cached| { + return cached; + } + + const attr = element.getAttributeSafe(.wrap(@tagName(listener_type))) orelse return null; + const callback = page.js.stringToPersistedFunction(attr) catch |err| switch (err) { + error.OutOfMemory => return err, + else => { + // Not a valid expression; log this to find out if its something we should be supporting. + log.warn(.js, "Html.getAttributeFunction", .{ + .expression = attr, + .err = err, + }); + + return null; + }, + }; + + try page.setAttrListener(element, listener_type, callback); + + return callback; } -pub fn getOnAbort(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .abort); +pub fn setOnAbort(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { + return page.setAttrListener(self.asElement(), .onabort, callback); +} + +pub fn getOnAbort(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onabort, page); } pub fn setOnAnimationCancel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .animationcancel, callback); + return page.setAttrListener(self.asElement(), .onanimationcancel, callback); } -pub fn getOnAnimationCancel(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .animationcancel); +pub fn getOnAnimationCancel(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onanimationcancel, page); } pub fn setOnAnimationEnd(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .animationend, callback); + return page.setAttrListener(self.asElement(), .onanimationend, callback); } -pub fn getOnAnimationEnd(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .animationend); +pub fn getOnAnimationEnd(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onanimationend, page); } pub fn setOnAnimationIteration(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .animationiteration, callback); + return page.setAttrListener(self.asElement(), .onanimationiteration, callback); } -pub fn getOnAnimationIteration(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .animationiteration); +pub fn getOnAnimationIteration(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onanimationiteration, page); } pub fn setOnAnimationStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .animationstart, callback); + return page.setAttrListener(self.asElement(), .onanimationstart, callback); } -pub fn getOnAnimationStart(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .animationstart); +pub fn getOnAnimationStart(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onanimationstart, page); } pub fn setOnAuxClick(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .auxclick, callback); + return page.setAttrListener(self.asElement(), .onauxclick, callback); } -pub fn getOnAuxClick(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .auxclick); +pub fn getOnAuxClick(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onauxclick, page); } pub fn setOnBeforeInput(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .beforeinput, callback); + return page.setAttrListener(self.asElement(), .onbeforeinput, callback); } -pub fn getOnBeforeInput(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .beforeinput); +pub fn getOnBeforeInput(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onbeforeinput, page); } pub fn setOnBeforeMatch(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .beforematch, callback); + return page.setAttrListener(self.asElement(), .onbeforematch, callback); } -pub fn getOnBeforeMatch(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .beforematch); +pub fn getOnBeforeMatch(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onbeforematch, page); } pub fn setOnBeforeToggle(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .beforetoggle, callback); + return page.setAttrListener(self.asElement(), .onbeforetoggle, callback); } -pub fn getOnBeforeToggle(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .beforetoggle); +pub fn getOnBeforeToggle(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onbeforetoggle, page); } pub fn setOnBlur(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .blur, callback); + return page.setAttrListener(self.asElement(), .onblur, callback); } -pub fn getOnBlur(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .blur); +pub fn getOnBlur(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onblur, page); } pub fn setOnCancel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .cancel, callback); + return page.setAttrListener(self.asElement(), .oncancel, callback); } -pub fn getOnCancel(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .cancel); +pub fn getOnCancel(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncancel, page); } pub fn setOnCanPlay(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .canplay, callback); + return page.setAttrListener(self.asElement(), .oncanplay, callback); } -pub fn getOnCanPlay(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .canplay); +pub fn getOnCanPlay(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncanplay, page); } pub fn setOnCanPlayThrough(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .canplaythrough, callback); + return page.setAttrListener(self.asElement(), .oncanplaythrough, callback); } -pub fn getOnCanPlayThrough(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .canplaythrough); +pub fn getOnCanPlayThrough(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncanplaythrough, page); } pub fn setOnChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .change, callback); + return page.setAttrListener(self.asElement(), .onchange, callback); } -pub fn getOnChange(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .change); +pub fn getOnChange(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onchange, page); } pub fn setOnClick(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .click, callback); + return page.setAttrListener(self.asElement(), .onclick, callback); } -pub fn getOnClick(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .click); +pub fn getOnClick(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onclick, page); } pub fn setOnClose(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .close, callback); + return page.setAttrListener(self.asElement(), .onclose, callback); } -pub fn getOnClose(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .close); +pub fn getOnClose(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onclose, page); } pub fn setOnCommand(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .command, callback); + return page.setAttrListener(self.asElement(), .oncommand, callback); } -pub fn getOnCommand(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .command); +pub fn getOnCommand(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncommand, page); } pub fn setOnContentVisibilityAutoStateChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .contentvisibilityautostatechange, callback); + return page.setAttrListener(self.asElement(), .oncontentvisibilityautostatechange, callback); } -pub fn getOnContentVisibilityAutoStateChange(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .contentvisibilityautostatechange); +pub fn getOnContentVisibilityAutoStateChange(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncontentvisibilityautostatechange, page); } pub fn setOnContextLost(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .contextlost, callback); + return page.setAttrListener(self.asElement(), .oncontextlost, callback); } -pub fn getOnContextLost(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .contextlost); +pub fn getOnContextLost(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncontextlost, page); } pub fn setOnContextMenu(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .contextmenu, callback); + return page.setAttrListener(self.asElement(), .oncontextmenu, callback); } -pub fn getOnContextMenu(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .contextmenu); +pub fn getOnContextMenu(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncontextmenu, page); } pub fn setOnContextRestored(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .contextrestored, callback); + return page.setAttrListener(self.asElement(), .oncontextrestored, callback); } -pub fn getOnContextRestored(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .contextrestored); +pub fn getOnContextRestored(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncontextrestored, page); } pub fn setOnCopy(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .copy, callback); + return page.setAttrListener(self.asElement(), .oncopy, callback); } -pub fn getOnCopy(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .copy); +pub fn getOnCopy(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncopy, page); } pub fn setOnCueChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .cuechange, callback); + return page.setAttrListener(self.asElement(), .oncuechange, callback); } -pub fn getOnCueChange(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .cuechange); +pub fn getOnCueChange(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncuechange, page); } pub fn setOnCut(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .cut, callback); + return page.setAttrListener(self.asElement(), .oncut, callback); } -pub fn getOnCut(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .cut); +pub fn getOnCut(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oncut, page); } pub fn setOnDblClick(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .dblclick, callback); + return page.setAttrListener(self.asElement(), .ondblclick, callback); } -pub fn getOnDblClick(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .dblclick); +pub fn getOnDblClick(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondblclick, page); } pub fn setOnDrag(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .drag, callback); + return page.setAttrListener(self.asElement(), .ondrag, callback); } -pub fn getOnDrag(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .drag); +pub fn getOnDrag(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondrag, page); } pub fn setOnDragEnd(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .dragend, callback); + return page.setAttrListener(self.asElement(), .ondragend, callback); } -pub fn getOnDragEnd(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .dragend); +pub fn getOnDragEnd(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondragend, page); } pub fn setOnDragEnter(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .dragenter, callback); + return page.setAttrListener(self.asElement(), .ondragenter, callback); } -pub fn getOnDragEnter(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .dragenter); +pub fn getOnDragEnter(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondragenter, page); } pub fn setOnDragExit(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .dragexit, callback); + return page.setAttrListener(self.asElement(), .ondragexit, callback); } -pub fn getOnDragExit(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .dragexit); +pub fn getOnDragExit(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondragexit, page); } pub fn setOnDragLeave(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .dragleave, callback); + return page.setAttrListener(self.asElement(), .ondragleave, callback); } -pub fn getOnDragLeave(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .dragleave); +pub fn getOnDragLeave(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondragleave, page); } pub fn setOnDragOver(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .dragover, callback); + return page.setAttrListener(self.asElement(), .ondragover, callback); } -pub fn getOnDragOver(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .dragover); +pub fn getOnDragOver(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondragover, page); } pub fn setOnDragStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .dragstart, callback); + return page.setAttrListener(self.asElement(), .ondragstart, callback); } -pub fn getOnDragStart(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .dragstart); +pub fn getOnDragStart(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondragstart, page); } pub fn setOnDrop(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .drop, callback); + return page.setAttrListener(self.asElement(), .ondrop, callback); } -pub fn getOnDrop(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .drop); +pub fn getOnDrop(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondrop, page); } pub fn setOnDurationChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .durationchange, callback); + return page.setAttrListener(self.asElement(), .ondurationchange, callback); } -pub fn getOnDurationChange(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .durationchange); +pub fn getOnDurationChange(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ondurationchange, page); } pub fn setOnEmptied(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .emptied, callback); + return page.setAttrListener(self.asElement(), .onemptied, callback); } -pub fn getOnEmptied(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .emptied); +pub fn getOnEmptied(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onemptied, page); } pub fn setOnEnded(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .ended, callback); + return page.setAttrListener(self.asElement(), .onended, callback); } -pub fn getOnEnded(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .ended); +pub fn getOnEnded(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onended, page); } pub fn setOnError(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .@"error", callback); + return page.setAttrListener(self.asElement(), .onerror, callback); } -pub fn getOnError(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .@"error"); +pub fn getOnError(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onerror, page); } pub fn setOnFocus(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .focus, callback); + return page.setAttrListener(self.asElement(), .onfocus, callback); } -pub fn getOnFocus(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .focus); +pub fn getOnFocus(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onfocus, page); } pub fn setOnFormData(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .formdata, callback); + return page.setAttrListener(self.asElement(), .onformdata, callback); } -pub fn getOnFormData(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .formdata); +pub fn getOnFormData(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onformdata, page); } pub fn setOnFullscreenChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .fullscreenchange, callback); + return page.setAttrListener(self.asElement(), .onfullscreenchange, callback); } -pub fn getOnFullscreenChange(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .fullscreenchange); +pub fn getOnFullscreenChange(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onfullscreenchange, page); } pub fn setOnFullscreenError(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .fullscreenerror, callback); + return page.setAttrListener(self.asElement(), .onfullscreenerror, callback); } -pub fn getOnFullscreenError(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .fullscreenerror); +pub fn getOnFullscreenError(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onfullscreenerror, page); } pub fn setOnGotPointerCapture(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .gotpointercapture, callback); + return page.setAttrListener(self.asElement(), .ongotpointercapture, callback); } -pub fn getOnGotPointerCapture(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .gotpointercapture); +pub fn getOnGotPointerCapture(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ongotpointercapture, page); } pub fn setOnInput(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .input, callback); + return page.setAttrListener(self.asElement(), .oninput, callback); } -pub fn getOnInput(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .input); +pub fn getOnInput(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oninput, page); } pub fn setOnInvalid(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .invalid, callback); + return page.setAttrListener(self.asElement(), .oninvalid, callback); } -pub fn getOnInvalid(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .invalid); +pub fn getOnInvalid(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.oninvalid, page); } pub fn setOnKeyDown(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .keydown, callback); + return page.setAttrListener(self.asElement(), .onkeydown, callback); } -pub fn getOnKeyDown(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .keydown); +pub fn getOnKeyDown(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onkeydown, page); } pub fn setOnKeyPress(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .keypress, callback); + return page.setAttrListener(self.asElement(), .onkeypress, callback); } -pub fn getOnKeyPress(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .keypress); +pub fn getOnKeyPress(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onkeypress, page); } pub fn setOnKeyUp(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .keyup, callback); + return page.setAttrListener(self.asElement(), .onkeyup, callback); } -pub fn getOnKeyUp(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .keyup); +pub fn getOnKeyUp(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onkeyup, page); } pub fn setOnLoad(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .load, callback); + return page.setAttrListener(self.asElement(), .onload, callback); } -pub fn getOnLoad(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .load); +pub fn getOnLoad(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onload, page); } pub fn setOnLoadedData(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .loadeddata, callback); + return page.setAttrListener(self.asElement(), .onloadeddata, callback); } -pub fn getOnLoadedData(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .loadeddata); +pub fn getOnLoadedData(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onloadeddata, page); } pub fn setOnLoadedMetadata(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .loadedmetadata, callback); + return page.setAttrListener(self.asElement(), .onloadedmetadata, callback); } -pub fn getOnLoadedMetadata(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .loadedmetadata); +pub fn getOnLoadedMetadata(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onloadedmetadata, page); } pub fn setOnLoadStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .loadstart, callback); + return page.setAttrListener(self.asElement(), .onloadstart, callback); } -pub fn getOnLoadStart(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .loadstart); +pub fn getOnLoadStart(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onloadstart, page); } pub fn setOnLostPointerCapture(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .lostpointercapture, callback); + return page.setAttrListener(self.asElement(), .onlostpointercapture, callback); } -pub fn getOnLostPointerCapture(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .lostpointercapture); +pub fn getOnLostPointerCapture(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onlostpointercapture, page); } pub fn setOnMouseDown(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .mousedown, callback); + return page.setAttrListener(self.asElement(), .onmousedown, callback); } -pub fn getOnMouseDown(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .mousedown); +pub fn getOnMouseDown(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onmousedown, page); } pub fn setOnMouseMove(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .mousemove, callback); + return page.setAttrListener(self.asElement(), .onmousemove, callback); } -pub fn getOnMouseMove(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .mousemove); +pub fn getOnMouseMove(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onmousemove, page); } pub fn setOnMouseOut(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .mouseout, callback); + return page.setAttrListener(self.asElement(), .onmouseout, callback); } -pub fn getOnMouseOut(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .mouseout); +pub fn getOnMouseOut(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onmouseout, page); } pub fn setOnMouseOver(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .mouseover, callback); + return page.setAttrListener(self.asElement(), .onmouseover, callback); } -pub fn getOnMouseOver(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .mouseover); +pub fn getOnMouseOver(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onmouseover, page); } pub fn setOnMouseUp(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .mouseup, callback); + return page.setAttrListener(self.asElement(), .onmouseup, callback); } -pub fn getOnMouseUp(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .mouseup); +pub fn getOnMouseUp(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onmouseup, page); } pub fn setOnPaste(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .paste, callback); + return page.setAttrListener(self.asElement(), .onpaste, callback); } -pub fn getOnPaste(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .paste); +pub fn getOnPaste(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpaste, page); } pub fn setOnPause(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pause, callback); + return page.setAttrListener(self.asElement(), .onpause, callback); } -pub fn getOnPause(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pause); +pub fn getOnPause(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpause, page); } pub fn setOnPlay(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .play, callback); + return page.setAttrListener(self.asElement(), .onplay, callback); } -pub fn getOnPlay(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .play); +pub fn getOnPlay(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onplay, page); } pub fn setOnPlaying(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .playing, callback); + return page.setAttrListener(self.asElement(), .onplaying, callback); } -pub fn getOnPlaying(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .playing); +pub fn getOnPlaying(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onplaying, page); } pub fn setOnPointerCancel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pointercancel, callback); + return page.setAttrListener(self.asElement(), .onpointercancel, callback); } -pub fn getOnPointerCancel(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pointercancel); +pub fn getOnPointerCancel(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpointercancel, page); } pub fn setOnPointerDown(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pointerdown, callback); + return page.setAttrListener(self.asElement(), .onpointerdown, callback); } -pub fn getOnPointerDown(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pointerdown); +pub fn getOnPointerDown(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpointerdown, page); } pub fn setOnPointerEnter(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pointerenter, callback); + return page.setAttrListener(self.asElement(), .onpointerenter, callback); } -pub fn getOnPointerEnter(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pointerenter); +pub fn getOnPointerEnter(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpointerenter, page); } pub fn setOnPointerLeave(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pointerleave, callback); + return page.setAttrListener(self.asElement(), .onpointerleave, callback); } -pub fn getOnPointerLeave(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pointerleave); +pub fn getOnPointerLeave(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpointerleave, page); } pub fn setOnPointerMove(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pointermove, callback); + return page.setAttrListener(self.asElement(), .onpointermove, callback); } -pub fn getOnPointerMove(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pointermove); +pub fn getOnPointerMove(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpointermove, page); } pub fn setOnPointerOut(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pointerout, callback); + return page.setAttrListener(self.asElement(), .onpointerout, callback); } -pub fn getOnPointerOut(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pointerout); +pub fn getOnPointerOut(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpointerout, page); } pub fn setOnPointerOver(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pointerover, callback); + return page.setAttrListener(self.asElement(), .onpointerover, callback); } -pub fn getOnPointerOver(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pointerover); +pub fn getOnPointerOver(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpointerover, page); } pub fn setOnPointerRawUpdate(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pointerrawupdate, callback); + return page.setAttrListener(self.asElement(), .onpointerrawupdate, callback); } -pub fn getOnPointerRawUpdate(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pointerrawupdate); +pub fn getOnPointerRawUpdate(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpointerrawupdate, page); } pub fn setOnPointerUp(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .pointerup, callback); + return page.setAttrListener(self.asElement(), .onpointerup, callback); } -pub fn getOnPointerUp(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .pointerup); +pub fn getOnPointerUp(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onpointerup, page); } pub fn setOnProgress(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .progress, callback); + return page.setAttrListener(self.asElement(), .onprogress, callback); } -pub fn getOnProgress(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .progress); +pub fn getOnProgress(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onprogress, page); } pub fn setOnRateChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .ratechange, callback); + return page.setAttrListener(self.asElement(), .onratechange, callback); } -pub fn getOnRateChange(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .ratechange); +pub fn getOnRateChange(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onratechange, page); } pub fn setOnReset(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .reset, callback); + return page.setAttrListener(self.asElement(), .onreset, callback); } -pub fn getOnReset(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .reset); +pub fn getOnReset(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onreset, page); } pub fn setOnResize(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .resize, callback); + return page.setAttrListener(self.asElement(), .onresize, callback); } -pub fn getOnResize(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .resize); +pub fn getOnResize(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onresize, page); } pub fn setOnScroll(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .scroll, callback); + return page.setAttrListener(self.asElement(), .onscroll, callback); } -pub fn getOnScroll(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .scroll); +pub fn getOnScroll(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onscroll, page); } pub fn setOnScrollEnd(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .scrollend, callback); + return page.setAttrListener(self.asElement(), .onscrollend, callback); } -pub fn getOnScrollEnd(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .scrollend); +pub fn getOnScrollEnd(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onscrollend, page); } pub fn setOnSecurityPolicyViolation(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .securitypolicyviolation, callback); + return page.setAttrListener(self.asElement(), .onsecuritypolicyviolation, callback); } -pub fn getOnSecurityPolicyViolation(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .securitypolicyviolation); +pub fn getOnSecurityPolicyViolation(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onsecuritypolicyviolation, page); } pub fn setOnSeeked(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .seeked, callback); + return page.setAttrListener(self.asElement(), .onseeked, callback); } -pub fn getOnSeeked(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .seeked); +pub fn getOnSeeked(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onseeked, page); } pub fn setOnSeeking(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .seeking, callback); + return page.setAttrListener(self.asElement(), .onseeking, callback); } -pub fn getOnSeeking(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .seeking); +pub fn getOnSeeking(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onseeking, page); } pub fn setOnSelect(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .select, callback); + return page.setAttrListener(self.asElement(), .onselect, callback); } -pub fn getOnSelect(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .select); +pub fn getOnSelect(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onselect, page); } pub fn setOnSelectionChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .selectionchange, callback); + return page.setAttrListener(self.asElement(), .onselectionchange, callback); } -pub fn getOnSelectionChange(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .selectionchange); +pub fn getOnSelectionChange(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onselectionchange, page); } pub fn setOnSelectStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .selectstart, callback); + return page.setAttrListener(self.asElement(), .onselectstart, callback); } -pub fn getOnSelectStart(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .selectstart); +pub fn getOnSelectStart(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onselectstart, page); } pub fn setOnSlotChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .slotchange, callback); + return page.setAttrListener(self.asElement(), .onslotchange, callback); } -pub fn getOnSlotChange(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .slotchange); +pub fn getOnSlotChange(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onslotchange, page); } pub fn setOnStalled(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .stalled, callback); + return page.setAttrListener(self.asElement(), .onstalled, callback); } -pub fn getOnStalled(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .stalled); +pub fn getOnStalled(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onstalled, page); } pub fn setOnSubmit(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .submit, callback); + return page.setAttrListener(self.asElement(), .onsubmit, callback); } -pub fn getOnSubmit(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .submit); +pub fn getOnSubmit(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onsubmit, page); } pub fn setOnSuspend(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .@"suspend", callback); + return page.setAttrListener(self.asElement(), .onsuspend, callback); } -pub fn getOnSuspend(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .@"suspend"); +pub fn getOnSuspend(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onsuspend, page); } pub fn setOnTimeUpdate(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .timeupdate, callback); + return page.setAttrListener(self.asElement(), .ontimeupdate, callback); } -pub fn getOnTimeUpdate(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .timeupdate); +pub fn getOnTimeUpdate(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ontimeupdate, page); } pub fn setOnToggle(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .toggle, callback); + return page.setAttrListener(self.asElement(), .ontoggle, callback); } -pub fn getOnToggle(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .toggle); +pub fn getOnToggle(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ontoggle, page); } pub fn setOnTransitionCancel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .transitioncancel, callback); + return page.setAttrListener(self.asElement(), .ontransitioncancel, callback); } -pub fn getOnTransitionCancel(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .transitioncancel); +pub fn getOnTransitionCancel(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ontransitioncancel, page); } pub fn setOnTransitionEnd(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .transitionend, callback); + return page.setAttrListener(self.asElement(), .ontransitionend, callback); } -pub fn getOnTransitionEnd(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .transitionend); +pub fn getOnTransitionEnd(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ontransitionend, page); } pub fn setOnTransitionRun(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .transitionrun, callback); + return page.setAttrListener(self.asElement(), .ontransitionrun, callback); } -pub fn getOnTransitionRun(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .transitionrun); +pub fn getOnTransitionRun(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ontransitionrun, page); } pub fn setOnTransitionStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .transitionstart, callback); + return page.setAttrListener(self.asElement(), .ontransitionstart, callback); } -pub fn getOnTransitionStart(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .transitionstart); +pub fn getOnTransitionStart(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.ontransitionstart, page); } pub fn setOnVolumeChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .volumechange, callback); + return page.setAttrListener(self.asElement(), .onvolumechange, callback); } -pub fn getOnVolumeChange(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .volumechange); +pub fn getOnVolumeChange(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onvolumechange, page); } pub fn setOnWaiting(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .waiting, callback); + return page.setAttrListener(self.asElement(), .onwaiting, callback); } -pub fn getOnWaiting(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .waiting); +pub fn getOnWaiting(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onwaiting, page); } pub fn setOnWheel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void { - return page.setAttrListener(self.asElement(), .wheel, callback); + return page.setAttrListener(self.asElement(), .onwheel, callback); } -pub fn getOnWheel(self: *HtmlElement, page: *Page) ?js.Function.Global { - return page.getAttrListener(self.asElement(), .wheel); +pub fn getOnWheel(self: *HtmlElement, page: *Page) !?js.Function.Global { + return self.getAttributeFunction(.onwheel, page); } pub const JsApi = struct { diff --git a/src/browser/webapi/global_event_handlers.zig b/src/browser/webapi/global_event_handlers.zig new file mode 100644 index 00000000..251fa8c6 --- /dev/null +++ b/src/browser/webapi/global_event_handlers.zig @@ -0,0 +1,145 @@ +// Copyright (C) 2023-2026 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +const std = @import("std"); +const lp = @import("lightpanda"); + +const js = @import("../js/js.zig"); + +const EventTarget = @import("EventTarget.zig"); + +/// Better to discriminate it since not directly a pointer int. +/// +/// See `calculateKey` to obtain one. +const Key = u64; + +/// Use `calculateKey` to create a key. +pub const Lookup = std.AutoHashMapUnmanaged(Key, js.Function.Global); + +/// Enum of known event listeners; increasing the size of it (u7) +/// can cause `Key` to behave incorrectly. +pub const Handler = enum(u7) { + onabort, + onanimationcancel, + onanimationend, + onanimationiteration, + onanimationstart, + onauxclick, + onbeforeinput, + onbeforematch, + onbeforetoggle, + onblur, + oncancel, + oncanplay, + oncanplaythrough, + onchange, + onclick, + onclose, + oncommand, + oncontentvisibilityautostatechange, + oncontextlost, + oncontextmenu, + oncontextrestored, + oncopy, + oncuechange, + oncut, + ondblclick, + ondrag, + ondragend, + ondragenter, + ondragexit, + ondragleave, + ondragover, + ondragstart, + ondrop, + ondurationchange, + onemptied, + onended, + onerror, + onfocus, + onformdata, + onfullscreenchange, + onfullscreenerror, + ongotpointercapture, + oninput, + oninvalid, + onkeydown, + onkeypress, + onkeyup, + onload, + onloadeddata, + onloadedmetadata, + onloadstart, + onlostpointercapture, + onmousedown, + onmousemove, + onmouseout, + onmouseover, + onmouseup, + onpaste, + onpause, + onplay, + onplaying, + onpointercancel, + onpointerdown, + onpointerenter, + onpointerleave, + onpointermove, + onpointerout, + onpointerover, + onpointerrawupdate, + onpointerup, + onprogress, + onratechange, + onreset, + onresize, + onscroll, + onscrollend, + onsecuritypolicyviolation, + onseeked, + onseeking, + onselect, + onselectionchange, + onselectstart, + onslotchange, + onstalled, + onsubmit, + onsuspend, + ontimeupdate, + ontoggle, + ontransitioncancel, + ontransitionend, + ontransitionrun, + ontransitionstart, + onvolumechange, + onwaiting, + onwheel, +}; + +/// Calculates a lookup key to use with lookup for event target. +/// NEVER use generated key to retrieve a pointer back. Portability is not guaranteed. +pub fn calculateKey(event_target: *EventTarget, handler_type: Handler) Key { + // Check if we have 3 bits available from alignment of 8. + lp.assert(@alignOf(EventTarget) == 8, "calculateKey: incorrect alignment", .{ + .event_target_alignment = @alignOf(EventTarget), + }); + + const ptr = @intFromPtr(event_target) >> 3; + lp.assert(ptr < (1 << 57), "calculateKey: pointer overflow", .{ .ptr = ptr }); + return ptr | (@as(Key, @intFromEnum(handler_type)) << 57); +}