mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-08-01 10:16:25 -04:00
Merge pull request #2950 from lightpanda-io/dom-pr-09-event-types-and-realms
webapi: new event interfaces, HTML-namespace naming, realm-correct exceptions
This commit is contained in:
@@ -500,6 +500,65 @@ fn nameToString(local: *const Local, comptime T: type, name: *const v8.Name) !T
|
||||
return try js.String.toSlice(.{ .local = local, .handle = handle });
|
||||
}
|
||||
|
||||
// Per Web IDL, exceptions belong to the operation's relevant realm — the
|
||||
// receiver's — which differs from the calling realm for cross-realm calls
|
||||
// (v8 API callbacks run in the caller's context, and our JS wrappers are
|
||||
// shared across the page's contexts). For DOM nodes, the relevant realm is
|
||||
// the node document's frame; otherwise fall back to the calling realm.
|
||||
fn errorLocal(comptime T: type, local: *const Local, info: anytype) Local {
|
||||
if (@TypeOf(info) != FunctionCallbackInfo) {
|
||||
return local.*;
|
||||
}
|
||||
|
||||
const frame = switch (local.ctx.global) {
|
||||
.frame => |f| f,
|
||||
.worker => return local.*,
|
||||
};
|
||||
|
||||
const Node = @import("../webapi/Node.zig");
|
||||
const Document = @import("../webapi/Document.zig");
|
||||
|
||||
const is_node_type = comptime blk: {
|
||||
if (@typeInfo(T) != .@"struct" or !@hasDecl(T, "JsApi")) break :blk false;
|
||||
break :blk @import("bridge.zig").inheritsOrIs(T.JsApi, Node.JsApi);
|
||||
};
|
||||
if (comptime !is_node_type) {
|
||||
return local.*;
|
||||
}
|
||||
|
||||
const instance = TaggedOpaque.fromJS(*T, info.getThis()) catch return local.*;
|
||||
const node = protoNode(T, instance);
|
||||
|
||||
const doc: *Document = node.ownerDocument(frame) orelse switch (node._type) {
|
||||
.document => |d| d,
|
||||
else => return local.*,
|
||||
};
|
||||
const doc_frame = doc._frame orelse return local.*;
|
||||
if (doc_frame == frame) {
|
||||
return local.*;
|
||||
}
|
||||
|
||||
const ctx = doc_frame.js;
|
||||
const local_v8_context: *const v8.Context = @ptrCast(v8.v8__Global__Get(&ctx.handle, ctx.isolate.handle) orelse return local.*);
|
||||
return .{
|
||||
.ctx = ctx,
|
||||
.handle = local_v8_context,
|
||||
.call_arena = ctx.call_arena,
|
||||
.isolate = ctx.isolate,
|
||||
};
|
||||
}
|
||||
|
||||
// Upcast a Node-descendant instance to *Node by walking the _proto chain.
|
||||
// Not every node type defines an asNode() helper (e.g. Comment, Text), but
|
||||
// inheritsOrIs guarantees Node is in the chain
|
||||
fn protoNode(comptime T: type, instance: *T) *@import("../webapi/Node.zig") {
|
||||
if (T == @import("../webapi/Node.zig")) {
|
||||
return instance;
|
||||
}
|
||||
const Proto = @typeInfo(std.meta.fieldInfo(T, ._proto).type).pointer.child;
|
||||
return protoNode(Proto, instance._proto);
|
||||
}
|
||||
|
||||
fn handleError(comptime T: type, comptime F: type, local: *const Local, err: anyerror, info: anytype) void {
|
||||
const isolate = local.isolate;
|
||||
|
||||
@@ -513,18 +572,34 @@ fn handleError(comptime T: type, comptime F: type, local: *const Local, err: any
|
||||
}
|
||||
}
|
||||
|
||||
const js_err: *const v8.Value = switch (err) {
|
||||
// early exit
|
||||
switch (err) {
|
||||
error.TryCatchRethrow => return,
|
||||
// A JS exception is already pending in the isolate (e.g. a value's
|
||||
// toString threw during argument conversion); throwing anything here
|
||||
// would replace the original exception the script expects to see.
|
||||
error.JsException => return,
|
||||
error.InvalidArgument => isolate.createTypeError("invalid argument"),
|
||||
error.TypeError => isolate.createTypeError(""),
|
||||
error.RangeError => isolate.createRangeError(""),
|
||||
error.OutOfMemory => isolate.createError("out of memory"),
|
||||
error.IllegalConstructor => isolate.createError("Illegal Constructor"),
|
||||
else => domExceptionToJs(local, err) orelse isolate.createError(@errorName(err)),
|
||||
else => {},
|
||||
}
|
||||
|
||||
const err_local = errorLocal(T, local, info);
|
||||
|
||||
const js_err: *const v8.Value = blk: {
|
||||
// Error constructors use the isolate's current context: enter the
|
||||
// receiver's realm so the exception gets its prototypes.
|
||||
const entered = err_local.ctx != local.ctx;
|
||||
if (entered) v8.v8__Context__Enter(err_local.handle);
|
||||
defer if (entered) v8.v8__Context__Exit(err_local.handle);
|
||||
|
||||
break :blk switch (err) {
|
||||
error.InvalidArgument => isolate.createTypeError("invalid argument"),
|
||||
error.TypeError => isolate.createTypeError(""),
|
||||
error.RangeError => isolate.createRangeError(""),
|
||||
error.OutOfMemory => isolate.createError("out of memory"),
|
||||
error.IllegalConstructor => isolate.createError("Illegal Constructor"),
|
||||
error.TryCatchRethrow, error.JsException => unreachable, // early exited a few lines up
|
||||
else => domExceptionToJs(&err_local, err) orelse isolate.createError(@errorName(err)),
|
||||
};
|
||||
};
|
||||
|
||||
const js_exception = isolate.throwException(js_err);
|
||||
|
||||
@@ -737,7 +737,7 @@ fn inheritsFromHtmlElement(comptime JsApi: type) bool {
|
||||
if (JsApi.bridge.type == HtmlElement) {
|
||||
return false;
|
||||
}
|
||||
return inheritsOrIs(JsApi, HtmlElement.JsApi);
|
||||
return bridge.inheritsOrIs(JsApi, HtmlElement.JsApi);
|
||||
}
|
||||
|
||||
const Unforgeable = struct {
|
||||
@@ -874,7 +874,7 @@ fn attachClass(comptime JsApi: type, comptime flatten: bool, isolate: *v8.Isolat
|
||||
|
||||
if (comptime flatten == false) {
|
||||
inline for (unforgeables) |u| {
|
||||
if (comptime inheritsOrIs(JsApi, u.Owner)) {
|
||||
if (comptime bridge.inheritsOrIs(JsApi, u.Owner)) {
|
||||
// unforgeables attributes aren't only applied directly on the
|
||||
// instance, they're also applied directly on every child instance
|
||||
attachAccessorProperty(u.name, u.accessor, isolate, template, signature, instance);
|
||||
@@ -949,23 +949,6 @@ const unforgeables: []const Unforgeable = blk: {
|
||||
break :blk list;
|
||||
};
|
||||
|
||||
// Whether Child is Ancestor or inherits from it, following the _proto chain.
|
||||
fn inheritsOrIs(comptime Child: type, comptime Ancestor: type) bool {
|
||||
comptime {
|
||||
const target = Ancestor.bridge.type;
|
||||
var T = Child.bridge.type;
|
||||
while (true) {
|
||||
if (T == target) {
|
||||
return true;
|
||||
}
|
||||
if (!@hasField(T, "_proto")) {
|
||||
return false;
|
||||
}
|
||||
T = @typeInfo(std.meta.fieldInfo(T, ._proto).type).pointer.child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attach JsApi members to a template (public for reuse). This is called on all
|
||||
// types. But, for globals (window, WGS) it's called twice. The first time, it's
|
||||
// called like any other interface. The 2nd time, it's called with flatten == true
|
||||
|
||||
@@ -106,7 +106,14 @@ pub fn fromJS(comptime R: type, js_obj_handle: *const v8.Object) !R {
|
||||
@compileError("unknown Zig type: " ++ @typeName(R));
|
||||
}
|
||||
|
||||
const tao_ptr = v8.v8__Object__GetAlignedPointerFromInternalField(js_obj_handle, 0).?;
|
||||
const tao_ptr = v8.v8__Object__GetAlignedPointerFromInternalField(js_obj_handle, 0) orelse return error.InvalidArgument;
|
||||
// A wrapped object always embeds an aligned TaggedOpaque pointer. An
|
||||
// unaligned value is a leftover v8 tagged value: the object was created
|
||||
// from our template but never mapped to a Zig instance (e.g. a custom
|
||||
// element whose constructor threw).
|
||||
if (@intFromPtr(tao_ptr) % @alignOf(TaggedOpaque) != 0) {
|
||||
return error.InvalidArgument;
|
||||
}
|
||||
const tao: *TaggedOpaque = @ptrCast(@alignCast(tao_ptr));
|
||||
const expected_type_index = bridge.JsApiLookup.getId(JsApi);
|
||||
|
||||
|
||||
@@ -1091,6 +1091,11 @@ pub const PageJsApis = flattenTypes(&.{
|
||||
@import("../webapi/event/PageTransitionEvent.zig"),
|
||||
@import("../webapi/event/PopStateEvent.zig"),
|
||||
@import("../webapi/event/HashChangeEvent.zig"),
|
||||
@import("../webapi/event/BeforeUnloadEvent.zig"),
|
||||
@import("../webapi/event/StorageEvent.zig"),
|
||||
@import("../webapi/event/DeviceMotionEvent.zig"),
|
||||
@import("../webapi/event/DeviceOrientationEvent.zig"),
|
||||
@import("../webapi/event/TouchEvent.zig"),
|
||||
@import("../webapi/event/UIEvent.zig"),
|
||||
@import("../webapi/event/MouseEvent.zig"),
|
||||
@import("../webapi/event/PointerEvent.zig"),
|
||||
@@ -1264,3 +1269,20 @@ pub const JsApis = blk: {
|
||||
}
|
||||
break :blk base ++ [_]type{@import("../webapi/WebDriver.zig").JsApi};
|
||||
};
|
||||
|
||||
// Whether Child is Ancestor or inherits from it, following the _proto chain.
|
||||
pub fn inheritsOrIs(comptime Child: type, comptime Ancestor: type) bool {
|
||||
comptime {
|
||||
const target = Ancestor.bridge.type;
|
||||
var T = Child.bridge.type;
|
||||
while (true) {
|
||||
if (T == target) {
|
||||
return true;
|
||||
}
|
||||
if (!@hasField(T, "_proto")) {
|
||||
return false;
|
||||
}
|
||||
T = @typeInfo(std.meta.fieldInfo(T, ._proto).type).pointer.child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,10 @@
|
||||
const event2 = document.createEvent('CUSTOMEVENT');
|
||||
testing.expectEqual('', event2.type);
|
||||
|
||||
const event3 = document.createEvent('CustomEvents');
|
||||
testing.expectEqual('', event3.type);
|
||||
// per spec, the pluralized legacy alias is not supported
|
||||
let threw = false;
|
||||
try { document.createEvent('CustomEvents'); } catch (e) { threw = e.name === 'NotSupportedError'; }
|
||||
testing.expectEqual(true, threw);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -495,7 +495,7 @@ pub fn createEvent(_: *const Document, event_type: []const u8, frame: *Frame) !*
|
||||
break :blk try Event.init("", null, frame._page);
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, normalized, "customevent") or std.mem.eql(u8, normalized, "customevents")) {
|
||||
if (std.mem.eql(u8, normalized, "customevent")) {
|
||||
const CustomEvent = @import("event/CustomEvent.zig");
|
||||
break :blk (try CustomEvent.init("", null, frame._page)).asEvent();
|
||||
}
|
||||
@@ -535,12 +535,12 @@ pub fn createEvent(_: *const Document, event_type: []const u8, frame: *Frame) !*
|
||||
break :blk (try UIEvent.init("", null, frame)).asEvent();
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, normalized, "focusevent") or std.mem.eql(u8, normalized, "focusevents")) {
|
||||
if (std.mem.eql(u8, normalized, "focusevent")) {
|
||||
const FocusEvent = @import("event/FocusEvent.zig");
|
||||
break :blk (try FocusEvent.init("", null, frame)).asEvent();
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, normalized, "textevent") or std.mem.eql(u8, normalized, "textevents")) {
|
||||
if (std.mem.eql(u8, normalized, "textevent")) {
|
||||
const TextEvent = @import("event/TextEvent.zig");
|
||||
break :blk (try TextEvent.init("", null, frame)).asEvent();
|
||||
}
|
||||
@@ -550,16 +550,29 @@ pub fn createEvent(_: *const Document, event_type: []const u8, frame: *Frame) !*
|
||||
break :blk (try CompositionEvent.init("", null, frame)).asEvent();
|
||||
}
|
||||
|
||||
// Aliases the spec requires createEvent to support but whose
|
||||
// interfaces aren't implemented yet: return a plain Event so the
|
||||
// caller can at least initialize and dispatch it.
|
||||
if (std.mem.eql(u8, normalized, "beforeunloadevent") or
|
||||
std.mem.eql(u8, normalized, "devicemotionevent") or
|
||||
std.mem.eql(u8, normalized, "deviceorientationevent") or
|
||||
std.mem.eql(u8, normalized, "storageevent"))
|
||||
{
|
||||
log.info(.not_implemented, "createEvent interface", .{ .type = event_type });
|
||||
break :blk try Event.init("", null, frame._page);
|
||||
if (std.mem.eql(u8, normalized, "beforeunloadevent")) {
|
||||
const BeforeUnloadEvent = @import("event/BeforeUnloadEvent.zig");
|
||||
break :blk (try BeforeUnloadEvent.init("", null, frame)).asEvent();
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, normalized, "devicemotionevent")) {
|
||||
const DeviceMotionEvent = @import("event/DeviceMotionEvent.zig");
|
||||
break :blk (try DeviceMotionEvent.init("", null, frame)).asEvent();
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, normalized, "deviceorientationevent")) {
|
||||
const DeviceOrientationEvent = @import("event/DeviceOrientationEvent.zig");
|
||||
break :blk (try DeviceOrientationEvent.init("", null, frame)).asEvent();
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, normalized, "storageevent")) {
|
||||
const StorageEvent = @import("event/StorageEvent.zig");
|
||||
break :blk (try StorageEvent.init("", null, frame)).asEvent();
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, normalized, "touchevent")) {
|
||||
const TouchEvent = @import("event/TouchEvent.zig");
|
||||
break :blk (try TouchEvent.init("", null, frame)).asEvent();
|
||||
}
|
||||
|
||||
return error.NotSupported;
|
||||
@@ -1409,6 +1422,10 @@ pub const JsApi = struct {
|
||||
|
||||
pub const onselectionchange = bridge.accessor(Document.getOnSelectionChange, Document.setOnSelectionChange, .{});
|
||||
pub const onclick = bridge.accessor(Document.getOnClick, Document.setOnClick, .{});
|
||||
pub const ontouchstart = bridge.accessor(handlerAccessor(.ontouchstart).get, handlerAccessor(.ontouchstart).set, .{});
|
||||
pub const ontouchend = bridge.accessor(handlerAccessor(.ontouchend).get, handlerAccessor(.ontouchend).set, .{});
|
||||
pub const ontouchmove = bridge.accessor(handlerAccessor(.ontouchmove).get, handlerAccessor(.ontouchmove).set, .{});
|
||||
pub const ontouchcancel = bridge.accessor(handlerAccessor(.ontouchcancel).get, handlerAccessor(.ontouchcancel).set, .{});
|
||||
pub const URL = bridge.accessor(Document.getURL, null, .{});
|
||||
pub const location = bridge.accessor(Document.getLocation, Document.setLocation, .{});
|
||||
pub const documentURI = bridge.accessor(Document.getURL, null, .{});
|
||||
@@ -1495,6 +1512,26 @@ pub const JsApi = struct {
|
||||
return doc_frame.charset;
|
||||
}
|
||||
pub const referrer = bridge.property("", .{ .template = false });
|
||||
|
||||
// Generates a getter/setter pair backed by the frame's attribute-listener
|
||||
// map, like onclick above, for other document event handler properties.
|
||||
fn handlerAccessor(comptime handler: @import("global_event_handlers.zig").Handler) type {
|
||||
return struct {
|
||||
pub fn get(self: *Document, frame: *Frame) ?js.Function.Global {
|
||||
const owner = self._frame orelse frame;
|
||||
return owner._event_target_attr_listeners.get(.{ .target = self.asEventTarget(), .handler = handler });
|
||||
}
|
||||
|
||||
pub fn set(self: *Document, setter: ?Window.FunctionSetter, frame: *Frame) !void {
|
||||
const owner = self._frame orelse frame;
|
||||
if (Window.getFunctionFromSetter(setter)) |cb| {
|
||||
try owner._event_target_attr_listeners.put(owner.arena, .{ .target = self.asEventTarget(), .handler = handler }, cb);
|
||||
} else {
|
||||
_ = owner._event_target_attr_listeners.remove(.{ .target = self.asEventTarget(), .handler = handler });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
|
||||
@@ -84,6 +84,10 @@ pub const Type = union(enum) {
|
||||
page_transition_event: *@import("event/PageTransitionEvent.zig"),
|
||||
pop_state_event: *@import("event/PopStateEvent.zig"),
|
||||
hash_change_event: *@import("event/HashChangeEvent.zig"),
|
||||
before_unload_event: *@import("event/BeforeUnloadEvent.zig"),
|
||||
storage_event: *@import("event/StorageEvent.zig"),
|
||||
device_motion_event: *@import("event/DeviceMotionEvent.zig"),
|
||||
device_orientation_event: *@import("event/DeviceOrientationEvent.zig"),
|
||||
ui_event: *@import("event/UIEvent.zig"),
|
||||
promise_rejection_event: *@import("event/PromiseRejectionEvent.zig"),
|
||||
submit_event: *@import("event/SubmitEvent.zig"),
|
||||
@@ -193,6 +197,10 @@ pub fn is(self: *Event, comptime T: type) ?*T {
|
||||
.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,
|
||||
.hash_change_event => |e| return if (T == @import("event/HashChangeEvent.zig")) e else null,
|
||||
.before_unload_event => |e| return if (T == @import("event/BeforeUnloadEvent.zig")) e else null,
|
||||
.storage_event => |e| return if (T == @import("event/StorageEvent.zig")) e else null,
|
||||
.device_motion_event => |e| return if (T == @import("event/DeviceMotionEvent.zig")) e else null,
|
||||
.device_orientation_event => |e| return if (T == @import("event/DeviceOrientationEvent.zig")) e else null,
|
||||
.promise_rejection_event => |e| return if (T == @import("event/PromiseRejectionEvent.zig")) e else null,
|
||||
.submit_event => |e| return if (T == @import("event/SubmitEvent.zig")) e else null,
|
||||
.form_data_event => |e| return if (T == @import("event/FormDataEvent.zig")) e else null,
|
||||
|
||||
@@ -194,6 +194,11 @@ pub fn NodeLive(comptime mode: Mode) type {
|
||||
// (like length or getAtIndex)
|
||||
var tw = self._tw.clone();
|
||||
while (self.nextTw(&tw)) |element| {
|
||||
// Per spec, only HTML-namespace elements are exposed via
|
||||
// their name attribute (ids expose any element).
|
||||
if (element._namespace != .html) {
|
||||
continue;
|
||||
}
|
||||
const element_name = element.getAttributeSafe(comptime .wrap("name")) orelse continue;
|
||||
if (std.mem.eql(u8, element_name, name)) {
|
||||
return element;
|
||||
|
||||
@@ -1227,6 +1227,38 @@ pub fn getOnToggle(self: *HtmlElement, frame: *Frame) !?js.Function.Global {
|
||||
return self.getAttributeFunction(.ontoggle, frame);
|
||||
}
|
||||
|
||||
pub fn setOnTouchCancel(self: *HtmlElement, callback: ?js.Function.Global, frame: *Frame) !void {
|
||||
return self.setAttributeListener(.ontouchcancel, callback, frame);
|
||||
}
|
||||
|
||||
pub fn getOnTouchCancel(self: *HtmlElement, frame: *Frame) !?js.Function.Global {
|
||||
return self.getAttributeFunction(.ontouchcancel, frame);
|
||||
}
|
||||
|
||||
pub fn setOnTouchEnd(self: *HtmlElement, callback: ?js.Function.Global, frame: *Frame) !void {
|
||||
return self.setAttributeListener(.ontouchend, callback, frame);
|
||||
}
|
||||
|
||||
pub fn getOnTouchEnd(self: *HtmlElement, frame: *Frame) !?js.Function.Global {
|
||||
return self.getAttributeFunction(.ontouchend, frame);
|
||||
}
|
||||
|
||||
pub fn setOnTouchMove(self: *HtmlElement, callback: ?js.Function.Global, frame: *Frame) !void {
|
||||
return self.setAttributeListener(.ontouchmove, callback, frame);
|
||||
}
|
||||
|
||||
pub fn getOnTouchMove(self: *HtmlElement, frame: *Frame) !?js.Function.Global {
|
||||
return self.getAttributeFunction(.ontouchmove, frame);
|
||||
}
|
||||
|
||||
pub fn setOnTouchStart(self: *HtmlElement, callback: ?js.Function.Global, frame: *Frame) !void {
|
||||
return self.setAttributeListener(.ontouchstart, callback, frame);
|
||||
}
|
||||
|
||||
pub fn getOnTouchStart(self: *HtmlElement, frame: *Frame) !?js.Function.Global {
|
||||
return self.getAttributeFunction(.ontouchstart, frame);
|
||||
}
|
||||
|
||||
pub fn setOnTransitionCancel(self: *HtmlElement, callback: ?js.Function.Global, frame: *Frame) !void {
|
||||
return self.setAttributeListener(.ontransitioncancel, callback, frame);
|
||||
}
|
||||
@@ -1773,6 +1805,10 @@ pub const JsApi = struct {
|
||||
pub const onsuspend = bridge.accessor(HtmlElement.getOnSuspend, HtmlElement.setOnSuspend, .{});
|
||||
pub const ontimeupdate = bridge.accessor(HtmlElement.getOnTimeUpdate, HtmlElement.setOnTimeUpdate, .{});
|
||||
pub const ontoggle = bridge.accessor(HtmlElement.getOnToggle, HtmlElement.setOnToggle, .{});
|
||||
pub const ontouchcancel = bridge.accessor(HtmlElement.getOnTouchCancel, HtmlElement.setOnTouchCancel, .{});
|
||||
pub const ontouchend = bridge.accessor(HtmlElement.getOnTouchEnd, HtmlElement.setOnTouchEnd, .{});
|
||||
pub const ontouchmove = bridge.accessor(HtmlElement.getOnTouchMove, HtmlElement.setOnTouchMove, .{});
|
||||
pub const ontouchstart = bridge.accessor(HtmlElement.getOnTouchStart, HtmlElement.setOnTouchStart, .{});
|
||||
pub const ontransitioncancel = bridge.accessor(HtmlElement.getOnTransitionCancel, HtmlElement.setOnTransitionCancel, .{});
|
||||
pub const ontransitionend = bridge.accessor(HtmlElement.getOnTransitionEnd, HtmlElement.setOnTransitionEnd, .{});
|
||||
pub const ontransitionrun = bridge.accessor(HtmlElement.getOnTransitionRun, HtmlElement.setOnTransitionRun, .{});
|
||||
|
||||
89
src/browser/webapi/event/BeforeUnloadEvent.zig
Normal file
89
src/browser/webapi/event/BeforeUnloadEvent.zig
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Frame = @import("../../Frame.zig");
|
||||
|
||||
const Event = @import("../Event.zig");
|
||||
|
||||
const String = lp.String;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#the-beforeunloadevent-interface
|
||||
const BeforeUnloadEvent = @This();
|
||||
|
||||
_proto: *Event,
|
||||
_return_value: []const u8 = "",
|
||||
|
||||
const Options = Event.inheritOptions(BeforeUnloadEvent, struct {});
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*BeforeUnloadEvent {
|
||||
const arena = try frame.getArena(.tiny, "BeforeUnloadEvent");
|
||||
errdefer frame.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, frame);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*BeforeUnloadEvent {
|
||||
const arena = try frame.getArena(.tiny, "BeforeUnloadEvent.trusted");
|
||||
errdefer frame.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, frame);
|
||||
}
|
||||
|
||||
fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*BeforeUnloadEvent {
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try frame._factory.event(
|
||||
arena,
|
||||
typ,
|
||||
BeforeUnloadEvent{
|
||||
._proto = undefined,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *BeforeUnloadEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getReturnValue(self: *const BeforeUnloadEvent) []const u8 {
|
||||
return self._return_value;
|
||||
}
|
||||
|
||||
pub fn setReturnValue(self: *BeforeUnloadEvent, value: []const u8, frame: *Frame) !void {
|
||||
self._return_value = try frame.dupeString(value);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(BeforeUnloadEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "BeforeUnloadEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
// Per spec BeforeUnloadEvent has no constructor.
|
||||
pub const returnValue = bridge.accessor(BeforeUnloadEvent.getReturnValue, BeforeUnloadEvent.setReturnValue, .{});
|
||||
};
|
||||
96
src/browser/webapi/event/DeviceMotionEvent.zig
Normal file
96
src/browser/webapi/event/DeviceMotionEvent.zig
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Frame = @import("../../Frame.zig");
|
||||
|
||||
const Event = @import("../Event.zig");
|
||||
|
||||
const String = lp.String;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// https://w3c.github.io/deviceorientation/#devicemotionevent
|
||||
const DeviceMotionEvent = @This();
|
||||
|
||||
_proto: *Event,
|
||||
_interval: f64 = 0,
|
||||
|
||||
const DeviceMotionEventOptions = struct {
|
||||
interval: f64 = 0,
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(DeviceMotionEvent, DeviceMotionEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*DeviceMotionEvent {
|
||||
const arena = try frame.getArena(.tiny, "DeviceMotionEvent");
|
||||
errdefer frame.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
const opts = _opts orelse Options{};
|
||||
const event = try frame._factory.event(
|
||||
arena,
|
||||
type_string,
|
||||
DeviceMotionEvent{
|
||||
._proto = undefined,
|
||||
._interval = opts.interval,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *DeviceMotionEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
// There is no motion sensor: the acceleration and rotation members are null.
|
||||
pub fn getAcceleration(_: *const DeviceMotionEvent) ?bool {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getAccelerationIncludingGravity(_: *const DeviceMotionEvent) ?bool {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getRotationRate(_: *const DeviceMotionEvent) ?bool {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getInterval(self: *const DeviceMotionEvent) f64 {
|
||||
return self._interval;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(DeviceMotionEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "DeviceMotionEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(DeviceMotionEvent.init, .{});
|
||||
pub const acceleration = bridge.accessor(DeviceMotionEvent.getAcceleration, null, .{});
|
||||
pub const accelerationIncludingGravity = bridge.accessor(DeviceMotionEvent.getAccelerationIncludingGravity, null, .{});
|
||||
pub const rotationRate = bridge.accessor(DeviceMotionEvent.getRotationRate, null, .{});
|
||||
pub const interval = bridge.accessor(DeviceMotionEvent.getInterval, null, .{});
|
||||
};
|
||||
104
src/browser/webapi/event/DeviceOrientationEvent.zig
Normal file
104
src/browser/webapi/event/DeviceOrientationEvent.zig
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Frame = @import("../../Frame.zig");
|
||||
|
||||
const Event = @import("../Event.zig");
|
||||
|
||||
const String = lp.String;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// https://w3c.github.io/deviceorientation/#deviceorientationevent
|
||||
const DeviceOrientationEvent = @This();
|
||||
|
||||
_proto: *Event,
|
||||
_alpha: ?f64 = null,
|
||||
_beta: ?f64 = null,
|
||||
_gamma: ?f64 = null,
|
||||
_absolute: bool = false,
|
||||
|
||||
const DeviceOrientationEventOptions = struct {
|
||||
alpha: ?f64 = null,
|
||||
beta: ?f64 = null,
|
||||
gamma: ?f64 = null,
|
||||
absolute: bool = false,
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(DeviceOrientationEvent, DeviceOrientationEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*DeviceOrientationEvent {
|
||||
const arena = try frame.getArena(.tiny, "DeviceOrientationEvent");
|
||||
errdefer frame.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
const opts = _opts orelse Options{};
|
||||
const event = try frame._factory.event(
|
||||
arena,
|
||||
type_string,
|
||||
DeviceOrientationEvent{
|
||||
._proto = undefined,
|
||||
._alpha = opts.alpha,
|
||||
._beta = opts.beta,
|
||||
._gamma = opts.gamma,
|
||||
._absolute = opts.absolute,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *DeviceOrientationEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getAlpha(self: *const DeviceOrientationEvent) ?f64 {
|
||||
return self._alpha;
|
||||
}
|
||||
|
||||
pub fn getBeta(self: *const DeviceOrientationEvent) ?f64 {
|
||||
return self._beta;
|
||||
}
|
||||
|
||||
pub fn getGamma(self: *const DeviceOrientationEvent) ?f64 {
|
||||
return self._gamma;
|
||||
}
|
||||
|
||||
pub fn getAbsolute(self: *const DeviceOrientationEvent) bool {
|
||||
return self._absolute;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(DeviceOrientationEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "DeviceOrientationEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(DeviceOrientationEvent.init, .{});
|
||||
pub const alpha = bridge.accessor(DeviceOrientationEvent.getAlpha, null, .{});
|
||||
pub const beta = bridge.accessor(DeviceOrientationEvent.getBeta, null, .{});
|
||||
pub const gamma = bridge.accessor(DeviceOrientationEvent.getGamma, null, .{});
|
||||
pub const absolute = bridge.accessor(DeviceOrientationEvent.getAbsolute, null, .{});
|
||||
};
|
||||
147
src/browser/webapi/event/StorageEvent.zig
Normal file
147
src/browser/webapi/event/StorageEvent.zig
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Frame = @import("../../Frame.zig");
|
||||
|
||||
const Event = @import("../Event.zig");
|
||||
|
||||
const String = lp.String;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webstorage.html#the-storageevent-interface
|
||||
const StorageEvent = @This();
|
||||
|
||||
_proto: *Event,
|
||||
_key: ?[]const u8 = null,
|
||||
_old_value: ?[]const u8 = null,
|
||||
_new_value: ?[]const u8 = null,
|
||||
_url: []const u8 = "",
|
||||
|
||||
const StorageEventOptions = struct {
|
||||
key: ?[]const u8 = null,
|
||||
oldValue: ?[]const u8 = null,
|
||||
newValue: ?[]const u8 = null,
|
||||
url: []const u8 = "",
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(StorageEvent, StorageEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*StorageEvent {
|
||||
const arena = try frame.getArena(.tiny, "StorageEvent");
|
||||
errdefer frame.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, frame);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*StorageEvent {
|
||||
const arena = try frame.getArena(.tiny, "StorageEvent.trusted");
|
||||
errdefer frame.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, frame);
|
||||
}
|
||||
|
||||
fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*StorageEvent {
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try frame._factory.event(
|
||||
arena,
|
||||
typ,
|
||||
StorageEvent{
|
||||
._proto = undefined,
|
||||
._key = if (opts.key) |k| try arena.dupe(u8, k) else null,
|
||||
._old_value = if (opts.oldValue) |v| try arena.dupe(u8, v) else null,
|
||||
._new_value = if (opts.newValue) |v| try arena.dupe(u8, v) else null,
|
||||
._url = try arena.dupe(u8, opts.url),
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *StorageEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getKey(self: *const StorageEvent) ?[]const u8 {
|
||||
return self._key;
|
||||
}
|
||||
|
||||
pub fn getOldValue(self: *const StorageEvent) ?[]const u8 {
|
||||
return self._old_value;
|
||||
}
|
||||
|
||||
pub fn getNewValue(self: *const StorageEvent) ?[]const u8 {
|
||||
return self._new_value;
|
||||
}
|
||||
|
||||
pub fn getUrl(self: *const StorageEvent) []const u8 {
|
||||
return self._url;
|
||||
}
|
||||
|
||||
// The initiating Storage object is not tracked; always null.
|
||||
pub fn getStorageArea(_: *const StorageEvent) ?bool {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn initStorageEvent(
|
||||
self: *StorageEvent,
|
||||
typ: []const u8,
|
||||
bubbles: ?bool,
|
||||
cancelable: ?bool,
|
||||
key: ?[]const u8,
|
||||
old_value: ?[]const u8,
|
||||
new_value: ?[]const u8,
|
||||
url: ?[]const u8,
|
||||
) !void {
|
||||
const event = self._proto;
|
||||
if (event._event_phase != .none) {
|
||||
return;
|
||||
}
|
||||
|
||||
const arena = event._arena;
|
||||
event._initialized = true;
|
||||
event._type_string = try String.init(arena, typ, .{});
|
||||
event._bubbles = bubbles orelse false;
|
||||
event._cancelable = cancelable orelse false;
|
||||
self._key = if (key) |k| try arena.dupe(u8, k) else null;
|
||||
self._old_value = if (old_value) |v| try arena.dupe(u8, v) else null;
|
||||
self._new_value = if (new_value) |v| try arena.dupe(u8, v) else null;
|
||||
self._url = if (url) |u| try arena.dupe(u8, u) else "";
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(StorageEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "StorageEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(StorageEvent.init, .{});
|
||||
pub const key = bridge.accessor(StorageEvent.getKey, null, .{});
|
||||
pub const oldValue = bridge.accessor(StorageEvent.getOldValue, null, .{});
|
||||
pub const newValue = bridge.accessor(StorageEvent.getNewValue, null, .{});
|
||||
pub const url = bridge.accessor(StorageEvent.getUrl, null, .{});
|
||||
pub const storageArea = bridge.accessor(StorageEvent.getStorageArea, null, .{});
|
||||
pub const initStorageEvent = bridge.function(StorageEvent.initStorageEvent, .{});
|
||||
};
|
||||
124
src/browser/webapi/event/TouchEvent.zig
Normal file
124
src/browser/webapi/event/TouchEvent.zig
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Frame = @import("../../Frame.zig");
|
||||
|
||||
const Event = @import("../Event.zig");
|
||||
const UIEvent = @import("UIEvent.zig");
|
||||
|
||||
const String = lp.String;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// https://w3c.github.io/touch-events/#touchevent-interface
|
||||
// There is no touch input source: the touch lists are always empty.
|
||||
const TouchEvent = @This();
|
||||
|
||||
_proto: *UIEvent,
|
||||
_alt_key: bool = false,
|
||||
_meta_key: bool = false,
|
||||
_ctrl_key: bool = false,
|
||||
_shift_key: bool = false,
|
||||
|
||||
pub const TouchEventOptions = struct {
|
||||
altKey: bool = false,
|
||||
metaKey: bool = false,
|
||||
ctrlKey: bool = false,
|
||||
shiftKey: bool = false,
|
||||
};
|
||||
|
||||
pub const Options = Event.inheritOptions(
|
||||
TouchEvent,
|
||||
TouchEventOptions,
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*TouchEvent {
|
||||
const arena = try frame.getArena(.tiny, "TouchEvent");
|
||||
errdefer frame.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
const opts = _opts orelse Options{};
|
||||
const event = try frame._factory.uiEvent(
|
||||
arena,
|
||||
type_string,
|
||||
TouchEvent{
|
||||
._proto = undefined,
|
||||
._alt_key = opts.altKey,
|
||||
._meta_key = opts.metaKey,
|
||||
._ctrl_key = opts.ctrlKey,
|
||||
._shift_key = opts.shiftKey,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *TouchEvent) *Event {
|
||||
return self._proto.asEvent();
|
||||
}
|
||||
|
||||
pub fn getTouches(_: *const TouchEvent) []const bool {
|
||||
return &.{};
|
||||
}
|
||||
|
||||
pub fn getTargetTouches(_: *const TouchEvent) []const bool {
|
||||
return &.{};
|
||||
}
|
||||
|
||||
pub fn getChangedTouches(_: *const TouchEvent) []const bool {
|
||||
return &.{};
|
||||
}
|
||||
|
||||
pub fn getAltKey(self: *const TouchEvent) bool {
|
||||
return self._alt_key;
|
||||
}
|
||||
|
||||
pub fn getMetaKey(self: *const TouchEvent) bool {
|
||||
return self._meta_key;
|
||||
}
|
||||
|
||||
pub fn getCtrlKey(self: *const TouchEvent) bool {
|
||||
return self._ctrl_key;
|
||||
}
|
||||
|
||||
pub fn getShiftKey(self: *const TouchEvent) bool {
|
||||
return self._shift_key;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(TouchEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "TouchEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(TouchEvent.init, .{});
|
||||
pub const touches = bridge.accessor(TouchEvent.getTouches, null, .{});
|
||||
pub const targetTouches = bridge.accessor(TouchEvent.getTargetTouches, null, .{});
|
||||
pub const changedTouches = bridge.accessor(TouchEvent.getChangedTouches, null, .{});
|
||||
pub const altKey = bridge.accessor(TouchEvent.getAltKey, null, .{});
|
||||
pub const metaKey = bridge.accessor(TouchEvent.getMetaKey, null, .{});
|
||||
pub const ctrlKey = bridge.accessor(TouchEvent.getCtrlKey, null, .{});
|
||||
pub const shiftKey = bridge.accessor(TouchEvent.getShiftKey, null, .{});
|
||||
};
|
||||
@@ -41,6 +41,7 @@ pub const Type = union(enum) {
|
||||
text_event: *@import("TextEvent.zig"),
|
||||
input_event: *@import("InputEvent.zig"),
|
||||
composition_event: *@import("CompositionEvent.zig"),
|
||||
touch_event: *@import("TouchEvent.zig"),
|
||||
};
|
||||
|
||||
pub const UIEventOptions = struct {
|
||||
@@ -90,6 +91,7 @@ pub fn is(self: *UIEvent, comptime T: type) ?*T {
|
||||
.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,
|
||||
.touch_event => |e| return if (T == @import("TouchEvent.zig")) e else null,
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -156,6 +156,10 @@ pub const Handler = enum(u7) {
|
||||
onsuspend,
|
||||
ontimeupdate,
|
||||
ontoggle,
|
||||
ontouchcancel,
|
||||
ontouchend,
|
||||
ontouchmove,
|
||||
ontouchstart,
|
||||
ontransitioncancel,
|
||||
ontransitionend,
|
||||
ontransitionrun,
|
||||
|
||||
Reference in New Issue
Block a user