Improve WPT uievents/ tests

1. Add a bunch of initXYZEvent functions, and fix the overload of initTextEvent
2. Add a number of missing (mostly legacy) accessors, e.g. uievent.getWhich()
3. CompositionEvent inherits from UIEvent, not Event
4. Give every accessor get/set a name. E.g the "name" of script.src is
  "get script" and "set script".
This commit is contained in:
Karl Seguin
2026-04-27 15:57:11 +08:00
parent a578f4d6ad
commit 9441e4fec8
14 changed files with 425 additions and 34 deletions

View File

@@ -276,9 +276,11 @@ fn createSnapshotContext(
const name = JsApi.Meta.name;
const v8_class_name = v8.v8__String__NewFromUtf8(isolate, name.ptr, v8.kNormal, @intCast(name.len));
var maybe_result: v8.MaybeBool = undefined;
var properties: v8.PropertyAttribute = v8.None;
if (@hasDecl(JsApi.Meta, "enumerable") and JsApi.Meta.enumerable == false) {
properties |= v8.DontEnum;
// Web IDL: interface objects on the global are non-enumerable
// by default. Opt back in via JsApi.Meta.enumerable = true.
var properties: v8.PropertyAttribute = v8.DontEnum;
if (@hasDecl(JsApi.Meta, "enumerable") and JsApi.Meta.enumerable == true) {
properties = v8.None;
}
v8.v8__Object__DefineOwnProperty(global_obj, context, v8_class_name, func, properties, &maybe_result);
}
@@ -578,6 +580,8 @@ pub fn generateConstructor(comptime JsApi: type, isolate: *v8.Isolate) *const v8
const name_str = if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi);
const class_name = v8.v8__String__NewFromUtf8(isolate, name_str.ptr, v8.kNormal, @intCast(name_str.len));
v8.v8__FunctionTemplate__SetClassName(template, class_name);
// Web IDL: interface object's `prototype` property is non-writable/non-configurable.
v8.v8__FunctionTemplate__ReadOnlyPrototype(template);
return template;
}
@@ -615,13 +619,21 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *const v8.F
.callback = value.getter,
.signature = getter_signature,
}).?;
const setter_callback = if (value.setter) |setter|
v8.v8__FunctionTemplate__New__Config(isolate, &.{
// WebIDL: getter function's .name should be "get X"
const getter_name_str = "get " ++ name;
const getter_name_v8 = v8.v8__String__NewFromUtf8(isolate, getter_name_str.ptr, v8.kNormal, @intCast(getter_name_str.len));
v8.v8__FunctionTemplate__SetClassName(getter_callback, getter_name_v8);
const setter_callback = if (value.setter) |setter| blk: {
const cb = v8.v8__FunctionTemplate__New__Config(isolate, &.{
.callback = setter,
.signature = getter_signature,
}).?
else
null;
}).?;
const setter_name_str = "set " ++ name;
const setter_name_v8 = v8.v8__String__NewFromUtf8(isolate, setter_name_str.ptr, v8.kNormal, @intCast(setter_name_str.len));
v8.v8__FunctionTemplate__SetClassName(cb, setter_name_v8);
break :blk cb;
} else null;
var attribute: v8.PropertyAttribute = 0;
if (value.setter == null) {
@@ -655,6 +667,7 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *const v8.F
.signature = func_signature,
}).?;
const js_name = v8.v8__String__NewFromUtf8(isolate, name.ptr, v8.kNormal, @intCast(name.len));
v8.v8__FunctionTemplate__SetClassName(function_template, js_name);
if (value.static) {
v8.v8__Template__Set(@ptrCast(template), js_name, @ptrCast(function_template), v8.None);
} else {

View File

@@ -25,5 +25,15 @@
testing.expectEqual(true, dom_load);
testing.expectEqual(true, attribute_load);
});
// Web IDL requires attribute getters/setters to be named "get X" / "set X"
// and methods to use their identifier. Sentinel for the binding-level naming
// fix in Snapshot.zig — Script.src exercises both accessor paths.
const desc = Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, 'src');
testing.expectEqual('get src', desc.get.name);
testing.expectEqual('set src', desc.set.name);
const append = Object.getOwnPropertyDescriptor(Element.prototype, 'append');
testing.expectEqual('append', append.value.name);
}
</script>

View File

@@ -5,10 +5,13 @@
{
let event = new CompositionEvent("test", {});
testing.expectEqual(true, event instanceof CompositionEvent);
testing.expectEqual(true, event instanceof UIEvent);
testing.expectEqual(true, event instanceof Event);
testing.expectEqual("test", event.type);
testing.expectEqual("", event.data);
testing.expectEqual(0, event.detail);
testing.expectEqual(window, event.view);
}
</script>
@@ -34,3 +37,36 @@
}
</script>
<script id=createEvent>
{
let evt = document.createEvent('CompositionEvent');
testing.expectEqual(true, evt instanceof CompositionEvent);
testing.expectEqual(true, evt instanceof UIEvent);
testing.expectEqual(true, evt instanceof Event);
testing.expectEqual('', evt.data);
}
</script>
<script id=initCompositionEvent>
{
let evt = document.createEvent('CompositionEvent');
evt.initCompositionEvent('compositionstart', true, false, window, 'hello');
testing.expectEqual('compositionstart', evt.type);
testing.expectEqual(true, evt.bubbles);
testing.expectEqual(false, evt.cancelable);
testing.expectEqual(window, evt.view);
testing.expectEqual('hello', evt.data);
}
</script>
<script id=initCompositionEventOptional>
{
let evt = document.createEvent('CompositionEvent');
evt.initCompositionEvent('compositionend');
testing.expectEqual('compositionend', evt.type);
testing.expectEqual(false, evt.bubbles);
testing.expectEqual(false, evt.cancelable);
testing.expectEqual('', evt.data);
}
</script>

View File

@@ -116,3 +116,54 @@
testing.expectEqual(true, called);
}
</script>
<script id=domKeyLocationConstants>
{
testing.expectEqual(0, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
testing.expectEqual(1, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
testing.expectEqual(2, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
testing.expectEqual(3, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
let evt = new KeyboardEvent('keydown', {key: 'a'});
testing.expectEqual(0, evt.DOM_KEY_LOCATION_STANDARD);
testing.expectEqual(1, evt.DOM_KEY_LOCATION_LEFT);
testing.expectEqual(2, evt.DOM_KEY_LOCATION_RIGHT);
testing.expectEqual(3, evt.DOM_KEY_LOCATION_NUMPAD);
}
</script>
<script id=charCodeKeyCode>
{
let evt = new KeyboardEvent('keypress', {key: 'a'});
testing.expectEqual(0, evt.charCode);
testing.expectEqual(0, evt.keyCode);
}
</script>
<script id=initKeyboardEvent>
{
let evt = new KeyboardEvent('keydown', {key: 'a'});
evt.initKeyboardEvent('keyup', true, true, window, 'q', 1, true, false, true, false);
testing.expectEqual('keyup', evt.type);
testing.expectEqual(true, evt.bubbles);
testing.expectEqual(true, evt.cancelable);
testing.expectEqual('q', evt.key);
testing.expectEqual(1, evt.location);
testing.expectEqual(true, evt.ctrlKey);
testing.expectEqual(false, evt.altKey);
testing.expectEqual(true, evt.shiftKey);
testing.expectEqual(false, evt.metaKey);
}
</script>
<script id=initKeyboardEventOptional>
{
let evt = new KeyboardEvent('keydown', {key: 'x'});
evt.initKeyboardEvent('foo');
testing.expectEqual('foo', evt.type);
testing.expectEqual(false, evt.bubbles);
testing.expectEqual(false, evt.cancelable);
testing.expectEqual('', evt.key);
testing.expectEqual(0, evt.location);
}
</script>

View File

@@ -52,3 +52,67 @@
document.dispatchEvent(new MouseEvent('mousetest'));
testing.expectEqual(false, mouseIsTrusted);
</script>
<script id=which>
{
testing.expectEqual(1, new MouseEvent('click').which);
testing.expectEqual(2, new MouseEvent('click', {button: 1}).which);
testing.expectEqual(3, new MouseEvent('click', {button: 2}).which);
}
</script>
<script id=layerXY>
{
let evt = new MouseEvent('click', {clientX: 11, clientY: 22});
testing.expectEqual(11, evt.layerX);
testing.expectEqual(22, evt.layerY);
}
</script>
<script id=getModifierState>
{
let evt = new MouseEvent('click', {altKey: true, ctrlKey: true, shiftKey: true, metaKey: true});
testing.expectEqual(true, evt.getModifierState('Alt'));
testing.expectEqual(true, evt.getModifierState('AltGraph'));
testing.expectEqual(true, evt.getModifierState('Control'));
testing.expectEqual(true, evt.getModifierState('Shift'));
testing.expectEqual(true, evt.getModifierState('Meta'));
testing.expectEqual(true, evt.getModifierState('Accel'));
testing.expectEqual(false, evt.getModifierState('NumLock'));
let none = new MouseEvent('click');
testing.expectEqual(false, none.getModifierState('Shift'));
}
</script>
<script id=initMouseEvent>
{
let evt = document.createEvent('MouseEvent');
evt.initMouseEvent('click', true, true, window, 1, 10, 20, 30, 40, true, false, true, false, 2, null);
testing.expectEqual('click', evt.type);
testing.expectEqual(true, evt.bubbles);
testing.expectEqual(true, evt.cancelable);
testing.expectEqual(1, evt.detail);
testing.expectEqual(10, evt.screenX);
testing.expectEqual(20, evt.screenY);
testing.expectEqual(30, evt.clientX);
testing.expectEqual(40, evt.clientY);
testing.expectEqual(true, evt.ctrlKey);
testing.expectEqual(false, evt.altKey);
testing.expectEqual(true, evt.shiftKey);
testing.expectEqual(false, evt.metaKey);
testing.expectEqual(2, evt.button);
}
</script>
<script id=initMouseEventOptional>
{
let evt = document.createEvent('MouseEvent');
evt.initMouseEvent('mousedown');
testing.expectEqual('mousedown', evt.type);
testing.expectEqual(false, evt.bubbles);
testing.expectEqual(false, evt.cancelable);
testing.expectEqual(0, evt.clientX);
testing.expectEqual(0, evt.button);
}
</script>

View File

@@ -1,17 +1,32 @@
<!DOCTYPE html>
<script src="../testing.js"></script>
<script id=createEvent>
let evt = document.createEvent('TextEvent');
testing.expectEqual(true, evt instanceof TextEvent);
testing.expectEqual(true, evt instanceof UIEvent);
testing.expectEqual('', evt.data);
{
let evt = document.createEvent('TextEvent');
testing.expectEqual(true, evt instanceof TextEvent);
testing.expectEqual(true, evt instanceof UIEvent);
testing.expectEqual('', evt.data);
}
</script>
<script id=initTextEvent>
let textEvent = document.createEvent('TextEvent');
textEvent.initTextEvent('textInput', true, false, window, 'test data');
testing.expectEqual('textInput', textEvent.type);
testing.expectEqual('test data', textEvent.data);
testing.expectEqual(true, textEvent.bubbles);
testing.expectEqual(false, textEvent.cancelable);
{
let textEvent = document.createEvent('TextEvent');
textEvent.initTextEvent('textInput', true, false, window, 'test data');
testing.expectEqual('textInput', textEvent.type);
testing.expectEqual('test data', textEvent.data);
testing.expectEqual(true, textEvent.bubbles);
testing.expectEqual(false, textEvent.cancelable);
}
</script>
<script id=initTextEventOptional>
{
let evt = document.createEvent('TextEvent');
evt.initTextEvent('foo');
testing.expectEqual('foo', evt.type);
testing.expectEqual('', evt.data);
testing.expectEqual(false, evt.bubbles);
testing.expectEqual(false, evt.cancelable);
}
</script>

View File

@@ -52,3 +52,33 @@
const evt7 = new UIEvent('custom', { bubbles: true });
testing.expectEqual(0, evt7.detail);
</script>
<script id=which>
{
const evt = new UIEvent('focus');
testing.expectEqual(0, evt.which);
}
</script>
<script id=initUIEvent>
{
const evt = document.createEvent('UIEvent');
evt.initUIEvent('foo', true, true, window, 7);
testing.expectEqual('foo', evt.type);
testing.expectEqual(true, evt.bubbles);
testing.expectEqual(true, evt.cancelable);
testing.expectEqual(window, evt.view);
testing.expectEqual(7, evt.detail);
}
</script>
<script id=initUIEventOptional>
{
const evt = document.createEvent('UIEvent');
evt.initUIEvent('bar');
testing.expectEqual('bar', evt.type);
testing.expectEqual(false, evt.bubbles);
testing.expectEqual(false, evt.cancelable);
testing.expectEqual(0, evt.detail);
}
</script>

View File

@@ -396,6 +396,11 @@ pub fn createEvent(_: *const Document, event_type: []const u8, frame: *Frame) !*
return (try TextEvent.init("", null, frame)).asEvent();
}
if (std.mem.eql(u8, normalized, "compositionevent")) {
const CompositionEvent = @import("event/CompositionEvent.zig");
return (try CompositionEvent.init("", null, frame)).asEvent();
}
return error.NotSupported;
}

View File

@@ -71,7 +71,6 @@ pub const Type = union(enum) {
custom_event: *@import("event/CustomEvent.zig"),
message_event: *@import("event/MessageEvent.zig"),
progress_event: *@import("event/ProgressEvent.zig"),
composition_event: *@import("event/CompositionEvent.zig"),
navigation_current_entry_change_event: *@import("event/NavigationCurrentEntryChangeEvent.zig"),
page_transition_event: *@import("event/PageTransitionEvent.zig"),
pop_state_event: *@import("event/PopStateEvent.zig"),
@@ -163,7 +162,6 @@ pub fn is(self: *Event, comptime T: type) ?*T {
.custom_event => |e| return if (T == @import("event/CustomEvent.zig")) e else null,
.message_event => |e| return if (T == @import("event/MessageEvent.zig")) e else null,
.progress_event => |e| return if (T == @import("event/ProgressEvent.zig")) e else null,
.composition_event => |e| return if (T == @import("event/CompositionEvent.zig")) e else null,
.navigation_current_entry_change_event => |e| return if (T == @import("event/NavigationCurrentEntryChangeEvent.zig")) e else null,
.page_transition_event => |e| return if (T == @import("event/PageTransitionEvent.zig")) e else null,
.pop_state_event => |e| return if (T == @import("event/PopStateEvent.zig")) e else null,

View File

@@ -22,12 +22,14 @@ const js = @import("../../js/js.zig");
const Frame = @import("../../Frame.zig");
const Event = @import("../Event.zig");
const UIEvent = @import("UIEvent.zig");
const Window = @import("../Window.zig");
const String = lp.String;
const CompositionEvent = @This();
_proto: *Event,
_proto: *UIEvent,
_data: []const u8 = "",
const CompositionEventOptions = struct {
@@ -42,7 +44,7 @@ pub fn init(typ: []const u8, opts_: ?Options, frame: *Frame) !*CompositionEvent
const type_string = try String.init(arena, typ, .{});
const opts = opts_ orelse Options{};
const event = try frame._factory.event(
const event = try frame._factory.uiEvent(
arena,
type_string,
CompositionEvent{
@@ -56,13 +58,35 @@ pub fn init(typ: []const u8, opts_: ?Options, frame: *Frame) !*CompositionEvent
}
pub fn asEvent(self: *CompositionEvent) *Event {
return self._proto;
return self._proto.asEvent();
}
pub fn getData(self: *const CompositionEvent) []const u8 {
return self._data;
}
pub fn initCompositionEvent(
self: *CompositionEvent,
typ: []const u8,
bubbles: ?bool,
cancelable: ?bool,
view: ?*Window,
data: ?[]const u8,
) !void {
const ui = self._proto;
const event = ui._proto;
if (event._event_phase != .none) {
return;
}
const arena = event._arena;
event._type_string = try String.init(arena, typ, .{});
event._bubbles = bubbles orelse false;
event._cancelable = cancelable orelse false;
ui._view = view;
self._data = if (data) |d| try arena.dupe(u8, d) else "";
}
pub const JsApi = struct {
pub const bridge = js.Bridge(CompositionEvent);
@@ -74,6 +98,7 @@ pub const JsApi = struct {
pub const constructor = bridge.constructor(CompositionEvent.init, .{});
pub const data = bridge.accessor(CompositionEvent.getData, null, .{});
pub const initCompositionEvent = bridge.function(CompositionEvent.initCompositionEvent, .{});
};
const testing = @import("../../../testing.zig");

View File

@@ -270,6 +270,49 @@ pub fn getShiftKey(self: *const KeyboardEvent) bool {
return self._shift_key;
}
// Deprecated: tracked as 0 since we don't synthesise legacy character codes.
pub fn getCharCode(self: *const KeyboardEvent) u32 {
_ = self;
return 0;
}
pub fn getKeyCode(self: *const KeyboardEvent) u32 {
_ = self;
return 0;
}
pub fn initKeyboardEvent(
self: *KeyboardEvent,
typ: []const u8,
bubbles: ?bool,
cancelable: ?bool,
view: ?*@import("../Window.zig"),
key: ?[]const u8,
location: ?u32,
ctrl_key: ?bool,
alt_key: ?bool,
shift_key: ?bool,
meta_key: ?bool,
) !void {
const ui = self._proto;
const event = ui._proto;
if (event._event_phase != .none) {
return;
}
const arena = event._arena;
event._type_string = try String.init(arena, typ, .{});
event._bubbles = bubbles orelse false;
event._cancelable = cancelable orelse false;
ui._view = view;
self._key = try Key.fromString(arena, key orelse "");
self._location = std.meta.intToEnum(Location, location orelse 0) catch return error.TypeError;
self._ctrl_key = ctrl_key orelse false;
self._alt_key = alt_key orelse false;
self._shift_key = shift_key orelse false;
self._meta_key = meta_key orelse false;
}
pub fn getModifierState(self: *const KeyboardEvent, str: []const u8) !bool {
const key = try Key.fromString(self._proto._proto._arena, str);
@@ -309,7 +352,15 @@ pub const JsApi = struct {
pub const metaKey = bridge.accessor(KeyboardEvent.getMetaKey, null, .{});
pub const repeat = bridge.accessor(KeyboardEvent.getRepeat, null, .{});
pub const shiftKey = bridge.accessor(KeyboardEvent.getShiftKey, null, .{});
pub const charCode = bridge.accessor(KeyboardEvent.getCharCode, null, .{});
pub const keyCode = bridge.accessor(KeyboardEvent.getKeyCode, null, .{});
pub const getModifierState = bridge.function(KeyboardEvent.getModifierState, .{});
pub const initKeyboardEvent = bridge.function(KeyboardEvent.initKeyboardEvent, .{});
pub const DOM_KEY_LOCATION_STANDARD = bridge.property(@intFromEnum(Location.DOM_KEY_LOCATION_STANDARD), .{ .template = true });
pub const DOM_KEY_LOCATION_LEFT = bridge.property(@intFromEnum(Location.DOM_KEY_LOCATION_LEFT), .{ .template = true });
pub const DOM_KEY_LOCATION_RIGHT = bridge.property(@intFromEnum(Location.DOM_KEY_LOCATION_RIGHT), .{ .template = true });
pub const DOM_KEY_LOCATION_NUMPAD = bridge.property(@intFromEnum(Location.DOM_KEY_LOCATION_NUMPAD), .{ .template = true });
};
const testing = @import("../../../testing.zig");

View File

@@ -193,6 +193,65 @@ pub fn getShiftKey(self: *const MouseEvent) bool {
return self._shift_key;
}
// Deprecated: tracks the same value as offsetX/clientX in the absence of layout.
pub fn getLayerX(self: *const MouseEvent) f64 {
return self._client_x;
}
pub fn getLayerY(self: *const MouseEvent) f64 {
return self._client_y;
}
pub fn getModifierState(self: *const MouseEvent, key: []const u8) bool {
if (std.mem.eql(u8, key, "Alt") or std.mem.eql(u8, key, "AltGraph")) return self._alt_key;
if (std.mem.eql(u8, key, "Control")) return self._ctrl_key;
if (std.mem.eql(u8, key, "Shift")) return self._shift_key;
if (std.mem.eql(u8, key, "Meta")) return self._meta_key;
if (std.mem.eql(u8, key, "Accel")) return self._ctrl_key or self._meta_key;
return false;
}
pub fn initMouseEvent(
self: *MouseEvent,
typ: []const u8,
bubbles: ?bool,
cancelable: ?bool,
view: ?*@import("../Window.zig"),
detail: ?i32,
screen_x: ?i32,
screen_y: ?i32,
client_x: ?i32,
client_y: ?i32,
ctrl_key: ?bool,
alt_key: ?bool,
shift_key: ?bool,
meta_key: ?bool,
button: ?i16,
related_target: ?*EventTarget,
) !void {
const ui = self._proto;
const event = ui._proto;
if (event._event_phase != .none) {
return;
}
event._type_string = try String.init(event._arena, typ, .{});
event._bubbles = bubbles orelse false;
event._cancelable = cancelable orelse false;
ui._view = view;
ui._detail = if (detail) |d| @intCast(@max(d, 0)) else 0;
self._screen_x = @floatFromInt(screen_x orelse 0);
self._screen_y = @floatFromInt(screen_y orelse 0);
self._client_x = @floatFromInt(client_x orelse 0);
self._client_y = @floatFromInt(client_y orelse 0);
self._ctrl_key = ctrl_key orelse false;
self._alt_key = alt_key orelse false;
self._shift_key = shift_key orelse false;
self._meta_key = meta_key orelse false;
self._button = std.meta.intToEnum(MouseButton, button orelse 0) catch return error.TypeError;
self._related_target = related_target;
}
pub const JsApi = struct {
pub const bridge = js.Bridge(MouseEvent);
@@ -218,8 +277,12 @@ pub const JsApi = struct {
pub const screenX = bridge.accessor(getScreenX, null, .{});
pub const screenY = bridge.accessor(getScreenY, null, .{});
pub const shiftKey = bridge.accessor(getShiftKey, null, .{});
pub const layerX = bridge.accessor(getLayerX, null, .{});
pub const layerY = bridge.accessor(getLayerY, null, .{});
pub const x = bridge.accessor(getClientX, null, .{});
pub const y = bridge.accessor(getClientY, null, .{});
pub const getModifierState = bridge.function(MouseEvent.getModifierState, .{});
pub const initMouseEvent = bridge.function(MouseEvent.initMouseEvent, .{});
};
const testing = @import("../../../testing.zig");

View File

@@ -72,24 +72,23 @@ pub fn getData(self: *const TextEvent) []const u8 {
pub fn initTextEvent(
self: *TextEvent,
typ: []const u8,
bubbles: bool,
cancelable: bool,
bubbles: ?bool,
cancelable: ?bool,
view: ?*@import("../Window.zig"),
data: []const u8,
data: ?[]const u8,
) !void {
_ = view; // view parameter is ignored in modern implementations
const event = self._proto._proto;
const ui = self._proto;
const event = ui._proto;
if (event._event_phase != .none) {
// Only allow initialization if event hasn't been dispatched
return;
}
const arena = event._arena;
event._type_string = try String.init(arena, typ, .{});
event._bubbles = bubbles;
event._cancelable = cancelable;
self._data = try arena.dupe(u8, data);
event._bubbles = bubbles orelse false;
event._cancelable = cancelable orelse false;
ui._view = view;
self._data = if (data) |d| try arena.dupe(u8, d) else "";
}
pub const JsApi = struct {

View File

@@ -40,6 +40,7 @@ pub const Type = union(enum) {
focus_event: *@import("FocusEvent.zig"),
text_event: *@import("TextEvent.zig"),
input_event: *@import("InputEvent.zig"),
composition_event: *@import("CompositionEvent.zig"),
};
pub const UIEventOptions = struct {
@@ -88,6 +89,7 @@ pub fn is(self: *UIEvent, comptime T: type) ?*T {
.focus_event => |e| return if (T == @import("FocusEvent.zig")) e else null,
.text_event => |e| return if (T == @import("TextEvent.zig")) e else null,
.input_event => |e| return if (T == @import("InputEvent.zig")) e else null,
.composition_event => |e| return if (T == @import("CompositionEvent.zig")) e else null,
}
return null;
}
@@ -111,7 +113,34 @@ pub fn getView(self: *UIEvent, frame: *Frame) *Window {
return self._view orelse frame.window;
}
// deprecated `initUIEvent()` not implemented
// Legacy: see https://w3c.github.io/uievents/#dom-uievent-which
pub fn getWhich(self: *const UIEvent) u32 {
return switch (self._type) {
.mouse_event => |me| @as(u32, @intCast(me.getButton())) + 1,
.keyboard_event => 0,
else => 0,
};
}
pub fn initUIEvent(
self: *UIEvent,
typ: []const u8,
bubbles: ?bool,
cancelable: ?bool,
view: ?*Window,
detail: ?i32,
) !void {
const event = self._proto;
if (event._event_phase != .none) {
return;
}
event._type_string = try String.init(event._arena, typ, .{});
event._bubbles = bubbles orelse false;
event._cancelable = cancelable orelse false;
self._view = view;
self._detail = if (detail) |d| @intCast(@max(d, 0)) else 0;
}
pub const JsApi = struct {
pub const bridge = js.Bridge(UIEvent);
@@ -125,6 +154,8 @@ pub const JsApi = struct {
pub const constructor = bridge.constructor(UIEvent.init, .{});
pub const detail = bridge.accessor(UIEvent.getDetail, null, .{});
pub const view = bridge.accessor(UIEvent.getView, null, .{});
pub const which = bridge.accessor(UIEvent.getWhich, null, .{});
pub const initUIEvent = bridge.function(UIEvent.initUIEvent, .{});
};
const testing = @import("../../../testing.zig");