mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-24 05:33:16 +00:00
Merge pull request #1479 from lightpanda-io/event_finalizers_and_arenas
Add Finalizers to events
This commit is contained in:
@@ -172,60 +172,42 @@ pub fn eventTarget(self: *Factory, child: anytype) !*@TypeOf(child) {
|
|||||||
return chain.get(1);
|
return chain.get(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eventInit(typ: []const u8, value: anytype, page: *Page) !Event {
|
|
||||||
// Round to 2ms for privacy (browsers do this)
|
|
||||||
const raw_timestamp = @import("../datetime.zig").milliTimestamp(.monotonic);
|
|
||||||
const time_stamp = (raw_timestamp / 2) * 2;
|
|
||||||
|
|
||||||
return .{
|
|
||||||
._type = unionInit(Event.Type, value),
|
|
||||||
._type_string = try String.init(page.arena, typ, .{}),
|
|
||||||
._time_stamp = time_stamp,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a root object
|
// this is a root object
|
||||||
pub fn event(self: *Factory, typ: []const u8, child: anytype) !*@TypeOf(child) {
|
pub fn event(self: *Factory, arena: Allocator, typ: String, child: anytype) !*@TypeOf(child) {
|
||||||
const allocator = self._slab.allocator();
|
|
||||||
|
|
||||||
const chain = try PrototypeChain(
|
const chain = try PrototypeChain(
|
||||||
&.{ Event, @TypeOf(child) },
|
&.{ Event, @TypeOf(child) },
|
||||||
).allocate(allocator);
|
).allocate(arena);
|
||||||
|
|
||||||
// Special case: Event has a _type_string field, so we need manual setup
|
// Special case: Event has a _type_string field, so we need manual setup
|
||||||
const event_ptr = chain.get(0);
|
const event_ptr = chain.get(0);
|
||||||
event_ptr.* = try eventInit(typ, chain.get(1), self._page);
|
event_ptr.* = try self.eventInit(arena, typ, chain.get(1));
|
||||||
chain.setLeaf(1, child);
|
chain.setLeaf(1, child);
|
||||||
|
|
||||||
return chain.get(1);
|
return chain.get(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uiEvent(self: *Factory, typ: []const u8, child: anytype) !*@TypeOf(child) {
|
pub fn uiEvent(self: *Factory, arena: Allocator, typ: String, child: anytype) !*@TypeOf(child) {
|
||||||
const allocator = self._slab.allocator();
|
|
||||||
|
|
||||||
const chain = try PrototypeChain(
|
const chain = try PrototypeChain(
|
||||||
&.{ Event, UIEvent, @TypeOf(child) },
|
&.{ Event, UIEvent, @TypeOf(child) },
|
||||||
).allocate(allocator);
|
).allocate(arena);
|
||||||
|
|
||||||
// Special case: Event has a _type_string field, so we need manual setup
|
// Special case: Event has a _type_string field, so we need manual setup
|
||||||
const event_ptr = chain.get(0);
|
const event_ptr = chain.get(0);
|
||||||
event_ptr.* = try eventInit(typ, chain.get(1), self._page);
|
event_ptr.* = try self.eventInit(arena, typ, chain.get(1));
|
||||||
chain.setMiddle(1, UIEvent.Type);
|
chain.setMiddle(1, UIEvent.Type);
|
||||||
chain.setLeaf(2, child);
|
chain.setLeaf(2, child);
|
||||||
|
|
||||||
return chain.get(2);
|
return chain.get(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouseEvent(self: *Factory, typ: []const u8, mouse: MouseEvent, child: anytype) !*@TypeOf(child) {
|
pub fn mouseEvent(self: *Factory, arena: Allocator, typ: String, mouse: MouseEvent, child: anytype) !*@TypeOf(child) {
|
||||||
const allocator = self._slab.allocator();
|
|
||||||
|
|
||||||
const chain = try PrototypeChain(
|
const chain = try PrototypeChain(
|
||||||
&.{ Event, UIEvent, MouseEvent, @TypeOf(child) },
|
&.{ Event, UIEvent, MouseEvent, @TypeOf(child) },
|
||||||
).allocate(allocator);
|
).allocate(arena);
|
||||||
|
|
||||||
// Special case: Event has a _type_string field, so we need manual setup
|
// Special case: Event has a _type_string field, so we need manual setup
|
||||||
const event_ptr = chain.get(0);
|
const event_ptr = chain.get(0);
|
||||||
event_ptr.* = try eventInit(typ, chain.get(1), self._page);
|
event_ptr.* = try self.eventInit(arena, typ, chain.get(1));
|
||||||
chain.setMiddle(1, UIEvent.Type);
|
chain.setMiddle(1, UIEvent.Type);
|
||||||
|
|
||||||
// Set MouseEvent with all its fields
|
// Set MouseEvent with all its fields
|
||||||
@@ -239,6 +221,20 @@ pub fn mouseEvent(self: *Factory, typ: []const u8, mouse: MouseEvent, child: any
|
|||||||
return chain.get(3);
|
return chain.get(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eventInit(self: *const Factory, arena: Allocator, typ: String, value: anytype) !Event {
|
||||||
|
// Round to 2ms for privacy (browsers do this)
|
||||||
|
const raw_timestamp = @import("../datetime.zig").milliTimestamp(.monotonic);
|
||||||
|
const time_stamp = (raw_timestamp / 2) * 2;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
._arena = arena,
|
||||||
|
._page = self._page,
|
||||||
|
._type = unionInit(Event.Type, value),
|
||||||
|
._type_string = typ,
|
||||||
|
._time_stamp = time_stamp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn blob(self: *Factory, child: anytype) !*@TypeOf(child) {
|
pub fn blob(self: *Factory, child: anytype) !*@TypeOf(child) {
|
||||||
const allocator = self._slab.allocator();
|
const allocator = self._slab.allocator();
|
||||||
|
|
||||||
|
|||||||
@@ -653,7 +653,8 @@ pub fn documentIsLoaded(self: *Page) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn _documentIsLoaded(self: *Page) !void {
|
pub fn _documentIsLoaded(self: *Page) !void {
|
||||||
const event = try Event.initTrusted("DOMContentLoaded", .{ .bubbles = true }, self);
|
const event = try Event.initTrusted(.wrap("DOMContentLoaded"), .{ .bubbles = true }, self);
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
try self._event_manager.dispatch(
|
try self._event_manager.dispatch(
|
||||||
self.document.asEventTarget(),
|
self.document.asEventTarget(),
|
||||||
event,
|
event,
|
||||||
@@ -704,7 +705,9 @@ fn _documentIsComplete(self: *Page) !void {
|
|||||||
|
|
||||||
// Dispatch `_to_load` events before window.load.
|
// Dispatch `_to_load` events before window.load.
|
||||||
for (self._to_load.items) |element| {
|
for (self._to_load.items) |element| {
|
||||||
const event = try Event.initTrusted("load", .{}, self);
|
const event = try Event.initTrusted(comptime .wrap("load"), .{}, self);
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
// Dispatch inline event.
|
// Dispatch inline event.
|
||||||
blk: {
|
blk: {
|
||||||
const html_element = element.is(HtmlElement) orelse break :blk;
|
const html_element = element.is(HtmlElement) orelse break :blk;
|
||||||
@@ -723,7 +726,8 @@ fn _documentIsComplete(self: *Page) !void {
|
|||||||
self._to_load.clearAndFree(self.arena);
|
self._to_load.clearAndFree(self.arena);
|
||||||
|
|
||||||
// Dispatch window.load event.
|
// Dispatch window.load event.
|
||||||
const event = try Event.initTrusted("load", .{}, self);
|
const event = try Event.initTrusted(comptime .wrap("load"), .{}, self);
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
// This event is weird, it's dispatched directly on the window, but
|
// This event is weird, it's dispatched directly on the window, but
|
||||||
// with the document as the target.
|
// with the document as the target.
|
||||||
event._target = self.document.asEventTarget();
|
event._target = self.document.asEventTarget();
|
||||||
@@ -734,10 +738,11 @@ fn _documentIsComplete(self: *Page) !void {
|
|||||||
.{ .inject_target = false, .context = "page load" },
|
.{ .inject_target = false, .context = "page load" },
|
||||||
);
|
);
|
||||||
|
|
||||||
const pageshow_event = try PageTransitionEvent.initTrusted("pageshow", .{}, self);
|
const pageshow_event = (try PageTransitionEvent.initTrusted(comptime .wrap("pageshow"), .{}, self)).asEvent();
|
||||||
|
defer if (!pageshow_event._v8_handoff) pageshow_event.deinit(false);
|
||||||
try self._event_manager.dispatchWithFunction(
|
try self._event_manager.dispatchWithFunction(
|
||||||
self.window.asEventTarget(),
|
self.window.asEventTarget(),
|
||||||
pageshow_event.asEvent(),
|
pageshow_event,
|
||||||
ls.toLocal(self.window._on_pageshow),
|
ls.toLocal(self.window._on_pageshow),
|
||||||
.{ .context = "page show" },
|
.{ .context = "page show" },
|
||||||
);
|
);
|
||||||
@@ -1443,10 +1448,12 @@ pub fn deliverSlotchangeEvents(self: *Page) void {
|
|||||||
self._slots_pending_slotchange.clearRetainingCapacity();
|
self._slots_pending_slotchange.clearRetainingCapacity();
|
||||||
|
|
||||||
for (slots) |slot| {
|
for (slots) |slot| {
|
||||||
const event = Event.initTrusted("slotchange", .{ .bubbles = true }, self) catch |err| {
|
const event = Event.initTrusted(comptime .wrap("slotchange"), .{ .bubbles = true }, self) catch |err| {
|
||||||
log.err(.page, "deliverSlotchange.init", .{ .err = err });
|
log.err(.page, "deliverSlotchange.init", .{ .err = err });
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
const target = slot.asNode().asEventTarget();
|
const target = slot.asNode().asEventTarget();
|
||||||
_ = target.dispatchEvent(event, self) catch |err| {
|
_ = target.dispatchEvent(event, self) catch |err| {
|
||||||
log.err(.page, "deliverSlotchange.dispatch", .{ .err = err });
|
log.err(.page, "deliverSlotchange.dispatch", .{ .err = err });
|
||||||
@@ -3032,14 +3039,16 @@ pub fn triggerMouseClick(self: *Page, x: f64, y: f64) !void {
|
|||||||
.y = y,
|
.y = y,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const event = try @import("webapi/event/MouseEvent.zig").init("click", .{
|
const event = (try @import("webapi/event/MouseEvent.zig").init("click", .{
|
||||||
.bubbles = true,
|
.bubbles = true,
|
||||||
.cancelable = true,
|
.cancelable = true,
|
||||||
.composed = true,
|
.composed = true,
|
||||||
.clientX = x,
|
.clientX = x,
|
||||||
.clientY = y,
|
.clientY = y,
|
||||||
}, self);
|
}, self)).asEvent();
|
||||||
try self._event_manager.dispatch(target.asEventTarget(), event.asEvent());
|
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
try self._event_manager.dispatch(target.asEventTarget(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback when the "click" event reaches the pages.
|
// callback when the "click" event reaches the pages.
|
||||||
@@ -3091,6 +3100,9 @@ pub fn handleClick(self: *Page, target: *Node) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn triggerKeyboard(self: *Page, keyboard_event: *KeyboardEvent) !void {
|
pub fn triggerKeyboard(self: *Page, keyboard_event: *KeyboardEvent) !void {
|
||||||
|
const event = keyboard_event.asEvent();
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
const element = self.window._document._active_element orelse return;
|
const element = self.window._document._active_element orelse return;
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
log.debug(.page, "page keydown", .{
|
log.debug(.page, "page keydown", .{
|
||||||
@@ -3099,7 +3111,7 @@ pub fn triggerKeyboard(self: *Page, keyboard_event: *KeyboardEvent) !void {
|
|||||||
.key = keyboard_event._key,
|
.key = keyboard_event._key,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try self._event_manager.dispatch(element.asEventTarget(), keyboard_event.asEvent());
|
try self._event_manager.dispatch(element.asEventTarget(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handleKeydown(self: *Page, target: *Node, event: *Event) !void {
|
pub fn handleKeydown(self: *Page, target: *Node, event: *Event) !void {
|
||||||
@@ -3161,7 +3173,9 @@ pub fn submitForm(self: *Page, submitter_: ?*Element, form_: ?*Element.Html.Form
|
|||||||
const form_element = form.asElement();
|
const form_element = form.asElement();
|
||||||
|
|
||||||
if (submit_opts.fire_event) {
|
if (submit_opts.fire_event) {
|
||||||
const submit_event = try Event.initTrusted("submit", .{ .bubbles = true, .cancelable = true }, self);
|
const submit_event = try Event.initTrusted(comptime .wrap("submit"), .{ .bubbles = true, .cancelable = true }, self);
|
||||||
|
defer if (!submit_event._v8_handoff) submit_event.deinit(false);
|
||||||
|
|
||||||
const onsubmit_handler = try form.asHtmlElement().getOnSubmit(self);
|
const onsubmit_handler = try form.asHtmlElement().getOnSubmit(self);
|
||||||
|
|
||||||
var ls: JS.Local.Scope = undefined;
|
var ls: JS.Local.Scope = undefined;
|
||||||
|
|||||||
@@ -869,7 +869,7 @@ pub const Script = struct {
|
|||||||
const cb = cb_ orelse return;
|
const cb = cb_ orelse return;
|
||||||
|
|
||||||
const Event = @import("webapi/Event.zig");
|
const Event = @import("webapi/Event.zig");
|
||||||
const event = Event.initTrusted(typ, .{}, page) catch |err| {
|
const event = Event.initTrusted(comptime .wrap(typ), .{}, page) catch |err| {
|
||||||
log.warn(.js, "script internal callback", .{
|
log.warn(.js, "script internal callback", .{
|
||||||
.url = self.url,
|
.url = self.url,
|
||||||
.type = typ,
|
.type = typ,
|
||||||
@@ -877,6 +877,7 @@ pub const Script = struct {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
var caught: js.TryCatch.Caught = undefined;
|
var caught: js.TryCatch.Caught = undefined;
|
||||||
cb.tryCall(void, .{event}, &caught) catch {
|
cb.tryCall(void, .{event}, &caught) catch {
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ identity_map: std.AutoHashMapUnmanaged(usize, v8.Global) = .empty,
|
|||||||
// Any type that is stored in the identity_map which has a finalizer declared
|
// Any type that is stored in the identity_map which has a finalizer declared
|
||||||
// will have its finalizer stored here. This is only used when shutting down
|
// will have its finalizer stored here. This is only used when shutting down
|
||||||
// if v8 hasn't called the finalizer directly itself.
|
// if v8 hasn't called the finalizer directly itself.
|
||||||
finalizer_callbacks: std.AutoHashMapUnmanaged(usize, FinalizerCallback) = .empty,
|
finalizer_callbacks: std.AutoHashMapUnmanaged(usize, *FinalizerCallback) = .empty,
|
||||||
|
finalizer_callback_pool: std.heap.MemoryPool(FinalizerCallback),
|
||||||
|
|
||||||
// Some web APIs have to manage opaque values. Ideally, they use an
|
// Some web APIs have to manage opaque values. Ideally, they use an
|
||||||
// js.Object, but the js.Object has no lifetime guarantee beyond the
|
// js.Object, but the js.Object has no lifetime guarantee beyond the
|
||||||
@@ -196,8 +197,9 @@ pub fn deinit(self: *Context) void {
|
|||||||
{
|
{
|
||||||
var it = self.finalizer_callbacks.valueIterator();
|
var it = self.finalizer_callbacks.valueIterator();
|
||||||
while (it.next()) |finalizer| {
|
while (it.next()) |finalizer| {
|
||||||
finalizer.deinit();
|
finalizer.*.deinit();
|
||||||
}
|
}
|
||||||
|
self.finalizer_callback_pool.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (self.global_values.items) |*global| {
|
for (self.global_values.items) |*global| {
|
||||||
@@ -246,37 +248,37 @@ pub fn deinit(self: *Context) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn weakRef(self: *Context, obj: anytype) void {
|
pub fn weakRef(self: *Context, obj: anytype) void {
|
||||||
const global = self.identity_map.getPtr(@intFromPtr(obj)) orelse {
|
const fc = self.finalizer_callbacks.get(@intFromPtr(obj)) orelse {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
// should not be possible
|
// should not be possible
|
||||||
std.debug.assert(false);
|
std.debug.assert(false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
v8.v8__Global__SetWeakFinalizer(global, obj, bridge.Struct(@TypeOf(obj)).JsApi.Meta.finalizer.from_v8, v8.kParameter);
|
v8.v8__Global__SetWeakFinalizer(&fc.global, fc, bridge.Struct(@TypeOf(obj)).JsApi.Meta.finalizer.from_v8, v8.kParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn safeWeakRef(self: *Context, obj: anytype) void {
|
pub fn safeWeakRef(self: *Context, obj: anytype) void {
|
||||||
const global = self.identity_map.getPtr(@intFromPtr(obj)) orelse {
|
const fc = self.finalizer_callbacks.get(@intFromPtr(obj)) orelse {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
// should not be possible
|
// should not be possible
|
||||||
std.debug.assert(false);
|
std.debug.assert(false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
v8.v8__Global__ClearWeak(global);
|
v8.v8__Global__ClearWeak(&fc.global);
|
||||||
v8.v8__Global__SetWeakFinalizer(global, obj, bridge.Struct(@TypeOf(obj)).JsApi.Meta.finalizer.from_v8, v8.kParameter);
|
v8.v8__Global__SetWeakFinalizer(&fc.global, fc, bridge.Struct(@TypeOf(obj)).JsApi.Meta.finalizer.from_v8, v8.kParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strongRef(self: *Context, obj: anytype) void {
|
pub fn strongRef(self: *Context, obj: anytype) void {
|
||||||
const global = self.identity_map.getPtr(@intFromPtr(obj)) orelse {
|
const fc = self.finalizer_callbacks.get(@intFromPtr(obj)) orelse {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
// should not be possible
|
// should not be possible
|
||||||
std.debug.assert(false);
|
std.debug.assert(false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
v8.v8__Global__ClearWeak(global);
|
v8.v8__Global__ClearWeak(&fc.global);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release(self: *Context, item: anytype) void {
|
pub fn release(self: *Context, item: anytype) void {
|
||||||
@@ -294,12 +296,14 @@ pub fn release(self: *Context, item: anytype) void {
|
|||||||
|
|
||||||
// The item has been fianalized, remove it for the finalizer callback so that
|
// The item has been fianalized, remove it for the finalizer callback so that
|
||||||
// we don't try to call it again on shutdown.
|
// we don't try to call it again on shutdown.
|
||||||
_ = self.finalizer_callbacks.fetchRemove(@intFromPtr(item)) orelse {
|
const fc = self.finalizer_callbacks.fetchRemove(@intFromPtr(item)) orelse {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
// should not be possible
|
// should not be possible
|
||||||
std.debug.assert(false);
|
std.debug.assert(false);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
self.finalizer_callback_pool.destroy(fc.value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1004,29 +1008,31 @@ pub fn queueMicrotaskFunc(self: *Context, cb: js.Function) void {
|
|||||||
self.isolate.enqueueMicrotaskFunc(cb);
|
self.isolate.enqueueMicrotaskFunc(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn createFinalizerCallback(self: *Context, global: v8.Global, ptr: *anyopaque, finalizerFn: *const fn (ptr: *anyopaque) void) !*FinalizerCallback {
|
||||||
|
const fc = try self.finalizer_callback_pool.create();
|
||||||
|
fc.* = .{
|
||||||
|
.ctx = self,
|
||||||
|
.ptr = ptr,
|
||||||
|
.global = global,
|
||||||
|
.finalizerFn = finalizerFn,
|
||||||
|
};
|
||||||
|
return fc;
|
||||||
|
}
|
||||||
|
|
||||||
// == Misc ==
|
// == Misc ==
|
||||||
// A type that has a finalizer can have its finalizer called one of two ways.
|
// A type that has a finalizer can have its finalizer called one of two ways.
|
||||||
// The first is from V8 via the WeakCallback we give to weakRef. But that isn't
|
// The first is from V8 via the WeakCallback we give to weakRef. But that isn't
|
||||||
// guaranteed to fire, so we track this in ctx._finalizers and call them on
|
// guaranteed to fire, so we track this in ctx._finalizers and call them on
|
||||||
// context shutdown.
|
// context shutdown.
|
||||||
const FinalizerCallback = struct {
|
pub const FinalizerCallback = struct {
|
||||||
|
ctx: *Context,
|
||||||
ptr: *anyopaque,
|
ptr: *anyopaque,
|
||||||
|
global: v8.Global,
|
||||||
finalizerFn: *const fn (ptr: *anyopaque) void,
|
finalizerFn: *const fn (ptr: *anyopaque) void,
|
||||||
|
|
||||||
pub fn init(ptr: anytype) FinalizerCallback {
|
pub fn deinit(self: *FinalizerCallback) void {
|
||||||
const T = bridge.Struct(@TypeOf(ptr));
|
|
||||||
return .{
|
|
||||||
.ptr = ptr,
|
|
||||||
.finalizerFn = struct {
|
|
||||||
pub fn wrap(self: *anyopaque) void {
|
|
||||||
T.JsApi.Meta.finalizer.from_zig(self);
|
|
||||||
}
|
|
||||||
}.wrap,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: FinalizerCallback) void {
|
|
||||||
self.finalizerFn(self.ptr);
|
self.finalizerFn(self.ptr);
|
||||||
|
self.ctx.finalizer_callback_pool.destroy(self);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ pub fn createContext(self: *Env, page: *Page, enter: bool) !*Context {
|
|||||||
.call_arena = page.call_arena,
|
.call_arena = page.call_arena,
|
||||||
.script_manager = &page._script_manager,
|
.script_manager = &page._script_manager,
|
||||||
.scheduler = .init(context_arena),
|
.scheduler = .init(context_arena),
|
||||||
|
.finalizer_callback_pool = std.heap.MemoryPool(Context.FinalizerCallback).init(self.app.allocator),
|
||||||
};
|
};
|
||||||
try context.identity_map.putNoClobber(context_arena, @intFromPtr(page.window), global_global);
|
try context.identity_map.putNoClobber(context_arena, @intFromPtr(page.window), global_global);
|
||||||
|
|
||||||
|
|||||||
@@ -198,21 +198,28 @@ pub fn mapZigInstanceToJs(self: *const Local, js_obj_handle: ?*const v8.Object,
|
|||||||
// context.global_objects, we want to track it in context.identity_map.
|
// context.global_objects, we want to track it in context.identity_map.
|
||||||
v8.v8__Global__New(isolate.handle, js_obj.handle, gop.value_ptr);
|
v8.v8__Global__New(isolate.handle, js_obj.handle, gop.value_ptr);
|
||||||
if (@hasDecl(JsApi.Meta, "finalizer")) {
|
if (@hasDecl(JsApi.Meta, "finalizer")) {
|
||||||
if (comptime IS_DEBUG) {
|
// It would be great if resolved knew the resolved type, but I
|
||||||
// You can normally return a "*Node" and we'll correctly
|
// can't figure out how to make that work, since it depends on
|
||||||
// handle it as what it really is, e.g. an HTMLScriptElement.
|
// the [runtime] `value`.
|
||||||
// But for finalizers, we can't do that. I think this
|
// We need the resolved finalizer, which we have in resolved.
|
||||||
// limitation will be OK - this auto-resolution is largely
|
// The above if statement would be more clear as:
|
||||||
// limited to Node -> HtmlElement, none of which has finalizers
|
// if (resolved.finalizer_from_v8) |finalizer| {
|
||||||
std.debug.assert(resolved.class_id == JsApi.Meta.class_id);
|
// But that's a runtime check.
|
||||||
|
// Instead, we check if the base has finalizer. The assumption
|
||||||
|
// here is that if a resolve type has a finalizer, then the base
|
||||||
|
// should have a finalizer too.
|
||||||
|
const fc = try ctx.createFinalizerCallback(gop.value_ptr.*, resolved.ptr, resolved.finalizer_from_zig.?);
|
||||||
|
{
|
||||||
|
errdefer fc.deinit();
|
||||||
|
try ctx.finalizer_callbacks.put(ctx.arena, @intFromPtr(resolved.ptr), fc);
|
||||||
}
|
}
|
||||||
|
|
||||||
try ctx.finalizer_callbacks.put(ctx.arena, @intFromPtr(resolved.ptr), .init(value));
|
conditionallyFlagHandoff(value);
|
||||||
if (@hasDecl(JsApi.Meta, "weak")) {
|
if (@hasDecl(JsApi.Meta, "weak")) {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
std.debug.assert(JsApi.Meta.weak == true);
|
std.debug.assert(JsApi.Meta.weak == true);
|
||||||
}
|
}
|
||||||
v8.v8__Global__SetWeakFinalizer(gop.value_ptr, resolved.ptr, JsApi.Meta.finalizer.from_v8, v8.kParameter);
|
v8.v8__Global__SetWeakFinalizer(gop.value_ptr, fc, resolved.finalizer_from_v8, v8.kParameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return js_obj;
|
return js_obj;
|
||||||
@@ -1026,9 +1033,12 @@ fn jsUnsignedIntToZig(comptime T: type, max: comptime_int, maybe: u32) !T {
|
|||||||
// This function recursively walks the _type union field (if there is one) to
|
// This function recursively walks the _type union field (if there is one) to
|
||||||
// get the most specific class_id possible.
|
// get the most specific class_id possible.
|
||||||
const Resolved = struct {
|
const Resolved = struct {
|
||||||
|
weak: bool,
|
||||||
ptr: *anyopaque,
|
ptr: *anyopaque,
|
||||||
class_id: u16,
|
class_id: u16,
|
||||||
prototype_chain: []const @import("TaggedOpaque.zig").PrototypeChainEntry,
|
prototype_chain: []const @import("TaggedOpaque.zig").PrototypeChainEntry,
|
||||||
|
finalizer_from_v8: ?*const fn (handle: ?*const v8.WeakCallbackInfo) callconv(.c) void = null,
|
||||||
|
finalizer_from_zig: ?*const fn (ptr: *anyopaque) void = null,
|
||||||
};
|
};
|
||||||
pub fn resolveValue(value: anytype) Resolved {
|
pub fn resolveValue(value: anytype) Resolved {
|
||||||
const T = bridge.Struct(@TypeOf(value));
|
const T = bridge.Struct(@TypeOf(value));
|
||||||
@@ -1056,13 +1066,28 @@ pub fn resolveValue(value: anytype) Resolved {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resolveT(comptime T: type, value: *anyopaque) Resolved {
|
fn resolveT(comptime T: type, value: *anyopaque) Resolved {
|
||||||
|
const Meta = T.JsApi.Meta;
|
||||||
return .{
|
return .{
|
||||||
.ptr = value,
|
.ptr = value,
|
||||||
.class_id = T.JsApi.Meta.class_id,
|
.class_id = Meta.class_id,
|
||||||
.prototype_chain = &T.JsApi.Meta.prototype_chain,
|
.prototype_chain = &Meta.prototype_chain,
|
||||||
|
.weak = if (@hasDecl(Meta, "weak")) Meta.weak else false,
|
||||||
|
.finalizer_from_v8 = if (@hasDecl(Meta, "finalizer")) Meta.finalizer.from_v8 else null,
|
||||||
|
.finalizer_from_zig = if (@hasDecl(Meta, "finalizer")) Meta.finalizer.from_zig else null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn conditionallyFlagHandoff(value: anytype) void {
|
||||||
|
const T = bridge.Struct(@TypeOf(value));
|
||||||
|
if (@hasField(T, "_v8_handoff")) {
|
||||||
|
value._v8_handoff = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (@hasField(T, "_proto")) {
|
||||||
|
conditionallyFlagHandoff(value._proto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stackTrace(self: *const Local) !?[]const u8 {
|
pub fn stackTrace(self: *const Local) !?[]const u8 {
|
||||||
const isolate = self.isolate;
|
const isolate = self.isolate;
|
||||||
const separator = log.separator();
|
const separator = log.separator();
|
||||||
|
|||||||
@@ -115,20 +115,18 @@ pub fn Builder(comptime T: type) type {
|
|||||||
.from_v8 = struct {
|
.from_v8 = struct {
|
||||||
fn wrap(handle: ?*const v8.WeakCallbackInfo) callconv(.c) void {
|
fn wrap(handle: ?*const v8.WeakCallbackInfo) callconv(.c) void {
|
||||||
const ptr = v8.v8__WeakCallbackInfo__GetParameter(handle.?).?;
|
const ptr = v8.v8__WeakCallbackInfo__GetParameter(handle.?).?;
|
||||||
const self: *T = @ptrCast(@alignCast(ptr));
|
const fc: *Context.FinalizerCallback = @ptrCast(@alignCast(ptr));
|
||||||
// This is simply a requirement of any type that Finalizes:
|
|
||||||
// It must have a _page: *Page field. We need it because
|
const ctx = fc.ctx;
|
||||||
// we need to check the item has already been cleared
|
const value_ptr = fc.ptr;
|
||||||
// (There are all types of weird timing issues that seem
|
if (ctx.finalizer_callbacks.contains(@intFromPtr(value_ptr))) {
|
||||||
// to be possible between finalization and context shutdown,
|
func(@ptrCast(@alignCast(value_ptr)), false);
|
||||||
// we need to be defensive).
|
ctx.release(value_ptr);
|
||||||
// There _ARE_ alternatives to this. But this is simple.
|
} else {
|
||||||
const ctx = self._page.js;
|
// A bit weird, but v8 _requires_ that we release it
|
||||||
if (!ctx.identity_map.contains(@intFromPtr(ptr))) {
|
// If we don't. We'll 100% crash.
|
||||||
return;
|
v8.v8__Global__Reset(&fc.global);
|
||||||
}
|
}
|
||||||
func(self, false);
|
|
||||||
ctx.release(ptr);
|
|
||||||
}
|
}
|
||||||
}.wrap,
|
}.wrap,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ pub fn abort(self: *AbortSignal, reason_: ?Reason, local: *const js.Local, page:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch abort event
|
// Dispatch abort event
|
||||||
const event = try Event.initTrusted("abort", .{}, page);
|
const event = try Event.initTrusted(comptime .wrap("abort"), .{}, page);
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
try page._event_manager.dispatchWithFunction(
|
try page._event_manager.dispatchWithFunction(
|
||||||
self.asEventTarget(),
|
self.asEventTarget(),
|
||||||
event,
|
event,
|
||||||
|
|||||||
@@ -767,7 +767,8 @@ pub fn focus(self: *Element, page: *Page) !void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const blur_event = try Event.initTrusted("blur", null, page);
|
const blur_event = try Event.initTrusted(comptime .wrap("blur"), null, page);
|
||||||
|
defer if (!blur_event._v8_handoff) blur_event.deinit(false);
|
||||||
try page._event_manager.dispatch(old.asEventTarget(), blur_event);
|
try page._event_manager.dispatch(old.asEventTarget(), blur_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -775,7 +776,8 @@ pub fn focus(self: *Element, page: *Page) !void {
|
|||||||
page.document._active_element = self;
|
page.document._active_element = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
const focus_event = try Event.initTrusted("focus", null, page);
|
const focus_event = try Event.initTrusted(comptime .wrap("focus"), null, page);
|
||||||
|
defer if (!focus_event._v8_handoff) focus_event.deinit(false);
|
||||||
try page._event_manager.dispatch(self.asEventTarget(), focus_event);
|
try page._event_manager.dispatch(self.asEventTarget(), focus_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,7 +787,8 @@ pub fn blur(self: *Element, page: *Page) !void {
|
|||||||
page.document._active_element = null;
|
page.document._active_element = null;
|
||||||
|
|
||||||
const Event = @import("Event.zig");
|
const Event = @import("Event.zig");
|
||||||
const blur_event = try Event.initTrusted("blur", null, page);
|
const blur_event = try Event.initTrusted(comptime .wrap("blur"), null, page);
|
||||||
|
defer if (!blur_event._v8_handoff) blur_event.deinit(false);
|
||||||
try page._event_manager.dispatch(self.asEventTarget(), blur_event);
|
try page._event_manager.dispatch(self.asEventTarget(), blur_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,14 @@ const EventTarget = @import("EventTarget.zig");
|
|||||||
const Node = @import("Node.zig");
|
const Node = @import("Node.zig");
|
||||||
const String = @import("../../string.zig").String;
|
const String = @import("../../string.zig").String;
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub const Event = @This();
|
pub const Event = @This();
|
||||||
|
|
||||||
pub const _prototype_root = true;
|
pub const _prototype_root = true;
|
||||||
_type: Type,
|
_type: Type,
|
||||||
|
_page: *Page,
|
||||||
|
_arena: Allocator,
|
||||||
_bubbles: bool = false,
|
_bubbles: bool = false,
|
||||||
_cancelable: bool = false,
|
_cancelable: bool = false,
|
||||||
_composed: bool = false,
|
_composed: bool = false,
|
||||||
@@ -44,6 +47,12 @@ _time_stamp: u64,
|
|||||||
_needs_retargeting: bool = false,
|
_needs_retargeting: bool = false,
|
||||||
_isTrusted: bool = false,
|
_isTrusted: bool = false,
|
||||||
|
|
||||||
|
// There's a period of time between creating an event and handing it off to v8
|
||||||
|
// where things can fail. If it does fail, we need to deinit the event. This flag
|
||||||
|
// when true, tells us the event is registered in the js.Contxt and thus, at
|
||||||
|
// the very least, will be finalized on context shutdown.
|
||||||
|
_v8_handoff: bool = false,
|
||||||
|
|
||||||
pub const EventPhase = enum(u8) {
|
pub const EventPhase = enum(u8) {
|
||||||
none = 0,
|
none = 0,
|
||||||
capturing_phase = 1,
|
capturing_phase = 1,
|
||||||
@@ -70,31 +79,38 @@ pub const Options = struct {
|
|||||||
composed: bool = false,
|
composed: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn initTrusted(typ: []const u8, opts_: ?Options, page: *Page) !*Event {
|
|
||||||
return initWithTrusted(typ, opts_, true, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*Event {
|
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*Event {
|
||||||
return initWithTrusted(typ, opts_, false, page);
|
const arena = try page.getArena(.{ .debug = "Event" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const str = try String.init(arena, typ, .{});
|
||||||
|
return initWithTrusted(arena, str, opts_, false, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) !*Event {
|
pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*Event {
|
||||||
|
const arena = try page.getArena(.{ .debug = "Event.trusted" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
return initWithTrusted(arena, typ, opts_, true, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initWithTrusted(arena: Allocator, typ: String, opts_: ?Options, trusted: bool, page: *Page) !*Event {
|
||||||
const opts = opts_ orelse Options{};
|
const opts = opts_ orelse Options{};
|
||||||
|
|
||||||
// Round to 2ms for privacy (browsers do this)
|
// Round to 2ms for privacy (browsers do this)
|
||||||
const raw_timestamp = @import("../../datetime.zig").milliTimestamp(.monotonic);
|
const raw_timestamp = @import("../../datetime.zig").milliTimestamp(.monotonic);
|
||||||
const time_stamp = (raw_timestamp / 2) * 2;
|
const time_stamp = (raw_timestamp / 2) * 2;
|
||||||
|
|
||||||
const event = try page._factory.create(Event{
|
const event = try arena.create(Event);
|
||||||
|
event.* = .{
|
||||||
|
._page = page,
|
||||||
|
._arena = arena,
|
||||||
._type = .generic,
|
._type = .generic,
|
||||||
._bubbles = opts.bubbles,
|
._bubbles = opts.bubbles,
|
||||||
._time_stamp = time_stamp,
|
._time_stamp = time_stamp,
|
||||||
._cancelable = opts.cancelable,
|
._cancelable = opts.cancelable,
|
||||||
._composed = opts.composed,
|
._composed = opts.composed,
|
||||||
._type_string = try String.init(page.arena, typ, .{}),
|
._type_string = typ,
|
||||||
});
|
._isTrusted = trusted,
|
||||||
|
};
|
||||||
event._isTrusted = trusted;
|
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,18 +119,22 @@ pub fn initEvent(
|
|||||||
event_string: []const u8,
|
event_string: []const u8,
|
||||||
bubbles: ?bool,
|
bubbles: ?bool,
|
||||||
cancelable: ?bool,
|
cancelable: ?bool,
|
||||||
page: *Page,
|
|
||||||
) !void {
|
) !void {
|
||||||
if (self._event_phase != .none) {
|
if (self._event_phase != .none) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self._type_string = try String.init(page.arena, event_string, .{});
|
self._type_string = try String.init(self._arena, event_string, .{});
|
||||||
self._bubbles = bubbles orelse false;
|
self._bubbles = bubbles orelse false;
|
||||||
self._cancelable = cancelable orelse false;
|
self._cancelable = cancelable orelse false;
|
||||||
self._stop_propagation = false;
|
self._stop_propagation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Event, shutdown: bool) void {
|
||||||
|
_ = shutdown;
|
||||||
|
self._page.releaseArena(self._arena);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as(self: *Event, comptime T: type) *T {
|
pub fn as(self: *Event, comptime T: type) *T {
|
||||||
return self.is(T).?;
|
return self.is(T).?;
|
||||||
}
|
}
|
||||||
@@ -385,6 +405,8 @@ pub const JsApi = struct {
|
|||||||
|
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(Event.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(Event.init, .{});
|
pub const constructor = bridge.constructor(Event.init, .{});
|
||||||
|
|||||||
@@ -79,11 +79,12 @@ fn goInner(delta: i32, page: *Page) !void {
|
|||||||
|
|
||||||
if (entry._url) |url| {
|
if (entry._url) |url| {
|
||||||
if (try page.isSameOrigin(url)) {
|
if (try page.isSameOrigin(url)) {
|
||||||
const event = try PopStateEvent.initTrusted("popstate", .{ .state = entry._state.value }, page);
|
const event = (try PopStateEvent.initTrusted(comptime .wrap("popstate"), .{ .state = entry._state.value }, page)).asEvent();
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
try page._event_manager.dispatchWithFunction(
|
try page._event_manager.dispatchWithFunction(
|
||||||
page.window.asEventTarget(),
|
page.window.asEventTarget(),
|
||||||
event.asEvent(),
|
event,
|
||||||
page.js.toLocal(page.window._on_popstate),
|
page.js.toLocal(page.window._on_popstate),
|
||||||
.{ .context = "Pop State" },
|
.{ .context = "Pop State" },
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -122,14 +122,15 @@ const PostMessageCallback = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = MessageEvent.initTrusted("message", .{
|
const event = (MessageEvent.initTrusted(comptime .wrap("message"), .{
|
||||||
.data = self.message,
|
.data = self.message,
|
||||||
.origin = "",
|
.origin = "",
|
||||||
.source = null,
|
.source = null,
|
||||||
}, page) catch |err| {
|
}, page) catch |err| {
|
||||||
log.err(.dom, "MessagePort.postMessage", .{ .err = err });
|
log.err(.dom, "MessagePort.postMessage", .{ .err = err });
|
||||||
return null;
|
return null;
|
||||||
};
|
}).asEvent();
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
var ls: js.Local.Scope = undefined;
|
var ls: js.Local.Scope = undefined;
|
||||||
page.js.localScope(&ls);
|
page.js.localScope(&ls);
|
||||||
@@ -137,7 +138,7 @@ const PostMessageCallback = struct {
|
|||||||
|
|
||||||
page._event_manager.dispatchWithFunction(
|
page._event_manager.dispatchWithFunction(
|
||||||
self.port.asEventTarget(),
|
self.port.asEventTarget(),
|
||||||
event.asEvent(),
|
event,
|
||||||
ls.toLocal(self.port._on_message),
|
ls.toLocal(self.port._on_message),
|
||||||
.{ .context = "MessagePort message" },
|
.{ .context = "MessagePort message" },
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ pub fn cancelIdleCallback(self: *Window, id: u32) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reportError(self: *Window, err: js.Value, page: *Page) !void {
|
pub fn reportError(self: *Window, err: js.Value, page: *Page) !void {
|
||||||
const error_event = try ErrorEvent.initTrusted("error", .{
|
const error_event = try ErrorEvent.initTrusted(comptime .wrap("error"), .{
|
||||||
.@"error" = try err.persist(),
|
.@"error" = try err.persist(),
|
||||||
.message = err.toStringSlice() catch "Unknown error",
|
.message = err.toStringSlice() catch "Unknown error",
|
||||||
.bubbles = false,
|
.bubbles = false,
|
||||||
@@ -285,6 +285,7 @@ pub fn reportError(self: *Window, err: js.Value, page: *Page) !void {
|
|||||||
}, page);
|
}, page);
|
||||||
|
|
||||||
const event = error_event.asEvent();
|
const event = error_event.asEvent();
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
// Invoke window.onerror callback if set (per WHATWG spec, this is called
|
// Invoke window.onerror callback if set (per WHATWG spec, this is called
|
||||||
// with 5 arguments: message, source, lineno, colno, error)
|
// with 5 arguments: message, source, lineno, colno, error)
|
||||||
@@ -443,7 +444,8 @@ pub fn scrollTo(self: *Window, opts: ScrollToOpts, y: ?i32, page: *Page) !void {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = try Event.initTrusted("scroll", .{ .bubbles = true }, p);
|
const event = try Event.initTrusted(comptime .wrap("scroll"), .{ .bubbles = true }, p);
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
||||||
|
|
||||||
pos.state = .end;
|
pos.state = .end;
|
||||||
@@ -470,7 +472,8 @@ pub fn scrollTo(self: *Window, opts: ScrollToOpts, y: ?i32, page: *Page) !void {
|
|||||||
.end => {},
|
.end => {},
|
||||||
.done => return null,
|
.done => return null,
|
||||||
}
|
}
|
||||||
const event = try Event.initTrusted("scrollend", .{ .bubbles = true }, p);
|
const event = try Event.initTrusted(comptime .wrap("scrollend"), .{ .bubbles = true }, p);
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
||||||
|
|
||||||
pos.state = .done;
|
pos.state = .done;
|
||||||
@@ -640,15 +643,14 @@ const PostMessageCallback = struct {
|
|||||||
const page = self.page;
|
const page = self.page;
|
||||||
const window = page.window;
|
const window = page.window;
|
||||||
|
|
||||||
const message_event = try MessageEvent.initTrusted("message", .{
|
const event = (try MessageEvent.initTrusted(comptime .wrap("message"), .{
|
||||||
.data = self.message,
|
.data = self.message,
|
||||||
.origin = self.origin,
|
.origin = self.origin,
|
||||||
.source = window,
|
.source = window,
|
||||||
.bubbles = false,
|
.bubbles = false,
|
||||||
.cancelable = false,
|
.cancelable = false,
|
||||||
}, page);
|
}, page)).asEvent();
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
const event = message_event.asEvent();
|
|
||||||
try page._event_manager.dispatch(window.asEventTarget(), event);
|
try page._event_manager.dispatch(window.asEventTarget(), event);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -15,11 +15,13 @@
|
|||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
const std = @import("std");
|
||||||
|
const String = @import("../../..//string.zig").String;
|
||||||
|
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
|
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
const Event = @import("../Event.zig");
|
const Event = @import("../Event.zig");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const CompositionEvent = @This();
|
const CompositionEvent = @This();
|
||||||
|
|
||||||
@@ -33,17 +35,28 @@ const CompositionEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(CompositionEvent, CompositionEventOptions);
|
const Options = Event.inheritOptions(CompositionEvent, CompositionEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CompositionEvent {
|
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CompositionEvent {
|
||||||
const opts = opts_ orelse Options{};
|
const arena = try page.getArena(.{ .debug = "CompositionEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
|
||||||
const event = try page._factory.event(typ, CompositionEvent{
|
const opts = opts_ orelse Options{};
|
||||||
|
const event = try page._factory.event(
|
||||||
|
arena,
|
||||||
|
type_string,
|
||||||
|
CompositionEvent{
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
._data = if (opts.data) |str| try page.dupeString(str) else "",
|
._data = if (opts.data) |str| try arena.dupe(u8, str) else "",
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
Event.populatePrototypes(event, opts, false);
|
Event.populatePrototypes(event, opts, false);
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *CompositionEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *CompositionEvent) *Event {
|
pub fn asEvent(self: *CompositionEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -59,6 +72,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "CompositionEvent";
|
pub const name = "CompositionEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(CompositionEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(CompositionEvent.init, .{});
|
pub const constructor = bridge.constructor(CompositionEvent.init, .{});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||||
//
|
//
|
||||||
// Francis Bouvier <francis@lightpanda.io>
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
// Pierre Tachoire <pierre@lightpanda.io>
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const js = @import("../../js/js.zig");
|
|
||||||
const String = @import("../../..//string.zig").String;
|
const String = @import("../../..//string.zig").String;
|
||||||
|
|
||||||
|
const js = @import("../../js/js.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
const Event = @import("../Event.zig");
|
const Event = @import("../Event.zig");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
@@ -37,11 +37,14 @@ const CustomEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(CustomEvent, CustomEventOptions);
|
const Options = Event.inheritOptions(CustomEvent, CustomEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CustomEvent {
|
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CustomEvent {
|
||||||
const arena = page.arena;
|
const arena = try page.getArena(.{ .debug = "CustomEvent" });
|
||||||
const opts = opts_ orelse Options{};
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
|
||||||
|
const opts = opts_ orelse Options{};
|
||||||
const event = try page._factory.event(
|
const event = try page._factory.event(
|
||||||
typ,
|
arena,
|
||||||
|
type_string,
|
||||||
CustomEvent{
|
CustomEvent{
|
||||||
._arena = arena,
|
._arena = arena,
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
@@ -59,17 +62,20 @@ pub fn initCustomEvent(
|
|||||||
bubbles: ?bool,
|
bubbles: ?bool,
|
||||||
cancelable: ?bool,
|
cancelable: ?bool,
|
||||||
detail_: ?js.Value.Global,
|
detail_: ?js.Value.Global,
|
||||||
page: *Page,
|
|
||||||
) !void {
|
) !void {
|
||||||
// This function can only be called after the constructor has called.
|
// This function can only be called after the constructor has called.
|
||||||
// So we assume proto is initialized already by constructor.
|
// So we assume proto is initialized already by constructor.
|
||||||
self._proto._type_string = try String.init(page.arena, event_string, .{});
|
self._proto._type_string = try String.init(self._proto._arena, event_string, .{});
|
||||||
self._proto._bubbles = bubbles orelse false;
|
self._proto._bubbles = bubbles orelse false;
|
||||||
self._proto._cancelable = cancelable orelse false;
|
self._proto._cancelable = cancelable orelse false;
|
||||||
// Detail is stored separately.
|
// Detail is stored separately.
|
||||||
self._detail = detail_;
|
self._detail = detail_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *CustomEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *CustomEvent) *Event {
|
pub fn asEvent(self: *CustomEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -85,6 +91,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "CustomEvent";
|
pub const name = "CustomEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(CustomEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(CustomEvent.init, .{});
|
pub const constructor = bridge.constructor(CustomEvent.init, .{});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||||
//
|
//
|
||||||
// Francis Bouvier <francis@lightpanda.io>
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
// Pierre Tachoire <pierre@lightpanda.io>
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
@@ -17,9 +17,11 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const js = @import("../../js/js.zig");
|
const String = @import("../../../string.zig").String;
|
||||||
|
|
||||||
|
const js = @import("../../js/js.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
|
|
||||||
const Event = @import("../Event.zig");
|
const Event = @import("../Event.zig");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
@@ -44,18 +46,23 @@ pub const ErrorEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
|
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent {
|
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent {
|
||||||
return initWithTrusted(typ, opts_, false, page);
|
const arena = try page.getArena(.{ .debug = "ErrorEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
return initWithTrusted(arena, type_string, opts_, false, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initTrusted(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent {
|
pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*ErrorEvent {
|
||||||
return initWithTrusted(typ, opts_, true, page);
|
const arena = try page.getArena(.{ .debug = "ErrorEvent.trusted" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
return initWithTrusted(arena, typ, opts_, true, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) !*ErrorEvent {
|
fn initWithTrusted(arena: Allocator, typ: String, opts_: ?Options, trusted: bool, page: *Page) !*ErrorEvent {
|
||||||
const arena = page.arena;
|
|
||||||
const opts = opts_ orelse Options{};
|
const opts = opts_ orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.event(
|
const event = try page._factory.event(
|
||||||
|
arena,
|
||||||
typ,
|
typ,
|
||||||
ErrorEvent{
|
ErrorEvent{
|
||||||
._arena = arena,
|
._arena = arena,
|
||||||
@@ -72,6 +79,10 @@ fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page)
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *ErrorEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *ErrorEvent) *Event {
|
pub fn asEvent(self: *ErrorEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -103,6 +114,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "ErrorEvent";
|
pub const name = "ErrorEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(ErrorEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start API
|
// Start API
|
||||||
|
|||||||
@@ -17,10 +17,14 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const String = @import("../../../string.zig").String;
|
||||||
|
|
||||||
|
const js = @import("../../js/js.zig");
|
||||||
|
const Page = @import("../../Page.zig");
|
||||||
|
|
||||||
const Event = @import("../Event.zig");
|
const Event = @import("../Event.zig");
|
||||||
const UIEvent = @import("UIEvent.zig");
|
const UIEvent = @import("UIEvent.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Allocator = std.mem.Allocator;
|
||||||
const js = @import("../../js/js.zig");
|
|
||||||
|
|
||||||
const KeyboardEvent = @This();
|
const KeyboardEvent = @This();
|
||||||
|
|
||||||
@@ -180,24 +184,30 @@ const Options = Event.inheritOptions(
|
|||||||
KeyboardEventOptions,
|
KeyboardEventOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
||||||
return initWithTrusted(typ, _opts, true, page);
|
const arena = try page.getArena(.{ .debug = "KeyboardEvent.trusted" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
return initWithTrusted(arena, typ, _opts, true, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
||||||
return initWithTrusted(typ, _opts, false, page);
|
const arena = try page.getArena(.{ .debug = "KeyboardEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*KeyboardEvent {
|
fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, page: *Page) !*KeyboardEvent {
|
||||||
const opts = _opts orelse Options{};
|
const opts = _opts orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.uiEvent(
|
const event = try page._factory.uiEvent(
|
||||||
|
arena,
|
||||||
typ,
|
typ,
|
||||||
KeyboardEvent{
|
KeyboardEvent{
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
._key = try Key.fromString(page.arena, opts.key),
|
._key = try Key.fromString(arena, opts.key),
|
||||||
._location = std.meta.intToEnum(Location, opts.location) catch return error.TypeError,
|
._location = std.meta.intToEnum(Location, opts.location) catch return error.TypeError,
|
||||||
._code = if (opts.code) |c| try page.dupeString(c) else "",
|
._code = if (opts.code) |c| try arena.dupe(u8, c) else "",
|
||||||
._repeat = opts.repeat,
|
._repeat = opts.repeat,
|
||||||
._is_composing = opts.isComposing,
|
._is_composing = opts.isComposing,
|
||||||
._ctrl_key = opts.ctrlKey,
|
._ctrl_key = opts.ctrlKey,
|
||||||
@@ -211,6 +221,10 @@ fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page)
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *KeyboardEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *KeyboardEvent) *Event {
|
pub fn asEvent(self: *KeyboardEvent) *Event {
|
||||||
return self._proto.asEvent();
|
return self._proto.asEvent();
|
||||||
}
|
}
|
||||||
@@ -251,8 +265,8 @@ pub fn getShiftKey(self: *const KeyboardEvent) bool {
|
|||||||
return self._shift_key;
|
return self._shift_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getModifierState(self: *const KeyboardEvent, str: []const u8, page: *Page) !bool {
|
pub fn getModifierState(self: *const KeyboardEvent, str: []const u8) !bool {
|
||||||
const key = try Key.fromString(page.arena, str);
|
const key = try Key.fromString(self._proto._proto._arena, str);
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
.Alt, .AltGraph => return self._alt_key,
|
.Alt, .AltGraph => return self._alt_key,
|
||||||
@@ -274,6 +288,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "KeyboardEvent";
|
pub const name = "KeyboardEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(KeyboardEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(KeyboardEvent.init, .{});
|
pub const constructor = bridge.constructor(KeyboardEvent.init, .{});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||||
//
|
//
|
||||||
// Francis Bouvier <francis@lightpanda.io>
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
// Pierre Tachoire <pierre@lightpanda.io>
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
@@ -16,11 +16,15 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const String = @import("../../../string.zig").String;
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
|
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
const Event = @import("../Event.zig");
|
const Event = @import("../Event.zig");
|
||||||
const Window = @import("../Window.zig");
|
const Window = @import("../Window.zig");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const MessageEvent = @This();
|
const MessageEvent = @This();
|
||||||
|
|
||||||
@@ -38,22 +42,28 @@ const MessageEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(MessageEvent, MessageEventOptions);
|
const Options = Event.inheritOptions(MessageEvent, MessageEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent {
|
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent {
|
||||||
return initWithTrusted(typ, opts_, false, page);
|
const arena = try page.getArena(.{ .debug = "MessageEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
return initWithTrusted(arena, type_string, opts_, false, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initTrusted(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent {
|
pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*MessageEvent {
|
||||||
return initWithTrusted(typ, opts_, true, page);
|
const arena = try page.getArena(.{ .debug = "MessageEvent.trusted" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
return initWithTrusted(arena, typ, opts_, true, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) !*MessageEvent {
|
fn initWithTrusted(arena: Allocator, typ: String, opts_: ?Options, trusted: bool, page: *Page) !*MessageEvent {
|
||||||
const opts = opts_ orelse Options{};
|
const opts = opts_ orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.event(
|
const event = try page._factory.event(
|
||||||
|
arena,
|
||||||
typ,
|
typ,
|
||||||
MessageEvent{
|
MessageEvent{
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
._data = opts.data,
|
._data = opts.data,
|
||||||
._origin = if (opts.origin) |str| try page.arena.dupe(u8, str) else "",
|
._origin = if (opts.origin) |str| try arena.dupe(u8, str) else "",
|
||||||
._source = opts.source,
|
._source = opts.source,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -62,6 +72,10 @@ fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page)
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *MessageEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *MessageEvent) *Event {
|
pub fn asEvent(self: *MessageEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -85,6 +99,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "MessageEvent";
|
pub const name = "MessageEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(MessageEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(MessageEvent.init, .{});
|
pub const constructor = bridge.constructor(MessageEvent.init, .{});
|
||||||
|
|||||||
@@ -17,11 +17,14 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Event = @import("../Event.zig");
|
const String = @import("../../../string.zig").String;
|
||||||
const UIEvent = @import("UIEvent.zig");
|
|
||||||
const EventTarget = @import("../EventTarget.zig");
|
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
|
|
||||||
|
const Event = @import("../Event.zig");
|
||||||
|
const EventTarget = @import("../EventTarget.zig");
|
||||||
|
|
||||||
|
const UIEvent = @import("UIEvent.zig");
|
||||||
const PointerEvent = @import("PointerEvent.zig");
|
const PointerEvent = @import("PointerEvent.zig");
|
||||||
|
|
||||||
const MouseEvent = @This();
|
const MouseEvent = @This();
|
||||||
@@ -75,10 +78,15 @@ pub const Options = Event.inheritOptions(
|
|||||||
);
|
);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*MouseEvent {
|
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*MouseEvent {
|
||||||
|
const arena = try page.getArena(.{ .debug = "MouseEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
|
||||||
const opts = _opts orelse Options{};
|
const opts = _opts orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.uiEvent(
|
const event = try page._factory.uiEvent(
|
||||||
typ,
|
arena,
|
||||||
|
type_string,
|
||||||
MouseEvent{
|
MouseEvent{
|
||||||
._type = .generic,
|
._type = .generic,
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
@@ -99,6 +107,10 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*MouseEvent {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *MouseEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *MouseEvent) *Event {
|
pub fn asEvent(self: *MouseEvent) *Event {
|
||||||
return self._proto.asEvent();
|
return self._proto.asEvent();
|
||||||
}
|
}
|
||||||
@@ -172,6 +184,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "MouseEvent";
|
pub const name = "MouseEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(MouseEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(MouseEvent.init, .{});
|
pub const constructor = bridge.constructor(MouseEvent.init, .{});
|
||||||
|
|||||||
@@ -17,11 +17,15 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Event = @import("../Event.zig");
|
const String = @import("../../../string.zig").String;
|
||||||
|
|
||||||
|
const js = @import("../../js/js.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
|
|
||||||
|
const Event = @import("../Event.zig");
|
||||||
const NavigationHistoryEntry = @import("../navigation/NavigationHistoryEntry.zig");
|
const NavigationHistoryEntry = @import("../navigation/NavigationHistoryEntry.zig");
|
||||||
const NavigationType = @import("../navigation/root.zig").NavigationType;
|
const NavigationType = @import("../navigation/root.zig").NavigationType;
|
||||||
const js = @import("../../js/js.zig");
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const NavigationCurrentEntryChangeEvent = @This();
|
const NavigationCurrentEntryChangeEvent = @This();
|
||||||
|
|
||||||
@@ -40,15 +44,21 @@ const Options = Event.inheritOptions(
|
|||||||
);
|
);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent {
|
pub fn init(typ: []const u8, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent {
|
||||||
return initWithTrusted(typ, opts, false, page);
|
const arena = try page.getArena(.{ .debug = "NavigationCurrentEntryChangeEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
return initWithTrusted(arena, type_string, opts, false, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initTrusted(typ: []const u8, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent {
|
pub fn initTrusted(typ: String, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent {
|
||||||
return initWithTrusted(typ, opts, true, page);
|
const arena = try page.getArena(.{ .debug = "NavigationCurrentEntryChangeEvent.trusted" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
return initWithTrusted(arena, typ, opts, true, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initWithTrusted(
|
fn initWithTrusted(
|
||||||
typ: []const u8,
|
arena: Allocator,
|
||||||
|
typ: String,
|
||||||
opts: Options,
|
opts: Options,
|
||||||
trusted: bool,
|
trusted: bool,
|
||||||
page: *Page,
|
page: *Page,
|
||||||
@@ -59,6 +69,7 @@ fn initWithTrusted(
|
|||||||
null;
|
null;
|
||||||
|
|
||||||
const event = try page._factory.event(
|
const event = try page._factory.event(
|
||||||
|
arena,
|
||||||
typ,
|
typ,
|
||||||
NavigationCurrentEntryChangeEvent{
|
NavigationCurrentEntryChangeEvent{
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
@@ -71,6 +82,10 @@ fn initWithTrusted(
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *NavigationCurrentEntryChangeEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *NavigationCurrentEntryChangeEvent) *Event {
|
pub fn asEvent(self: *NavigationCurrentEntryChangeEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -90,6 +105,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "NavigationCurrentEntryChangeEvent";
|
pub const name = "NavigationCurrentEntryChangeEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(NavigationCurrentEntryChangeEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(NavigationCurrentEntryChangeEvent.init, .{});
|
pub const constructor = bridge.constructor(NavigationCurrentEntryChangeEvent.init, .{});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||||
//
|
//
|
||||||
// Francis Bouvier <francis@lightpanda.io>
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
// Pierre Tachoire <pierre@lightpanda.io>
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
@@ -16,9 +16,13 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const Event = @import("../Event.zig");
|
const std = @import("std");
|
||||||
|
const String = @import("../../../string.zig").String;
|
||||||
|
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
|
const Event = @import("../Event.zig");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent
|
// https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent
|
||||||
const PageTransitionEvent = @This();
|
const PageTransitionEvent = @This();
|
||||||
@@ -33,17 +37,23 @@ const PageTransitionEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(PageTransitionEvent, PageTransitionEventOptions);
|
const Options = Event.inheritOptions(PageTransitionEvent, PageTransitionEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent {
|
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent {
|
||||||
return initWithTrusted(typ, _opts, false, page);
|
const arena = try page.getArena(.{ .debug = "PageTransitionEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent {
|
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*PageTransitionEvent {
|
||||||
return initWithTrusted(typ, _opts, true, page);
|
const arena = try page.getArena(.{ .debug = "PageTransitionEvent.trusted" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
return initWithTrusted(arena, typ, _opts, true, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*PageTransitionEvent {
|
fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, page: *Page) !*PageTransitionEvent {
|
||||||
const opts = _opts orelse Options{};
|
const opts = _opts orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.event(
|
const event = try page._factory.event(
|
||||||
|
arena,
|
||||||
typ,
|
typ,
|
||||||
PageTransitionEvent{
|
PageTransitionEvent{
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
@@ -55,6 +65,10 @@ fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page)
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *PageTransitionEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *PageTransitionEvent) *Event {
|
pub fn asEvent(self: *PageTransitionEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -70,6 +84,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "PageTransitionEvent";
|
pub const name = "PageTransitionEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(PageTransitionEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(PageTransitionEvent.init, .{});
|
pub const constructor = bridge.constructor(PageTransitionEvent.init, .{});
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const String = @import("../../../string.zig").String;
|
||||||
|
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
const Event = @import("../Event.zig");
|
const Event = @import("../Event.zig");
|
||||||
@@ -81,10 +83,14 @@ const Options = Event.inheritOptions(
|
|||||||
);
|
);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PointerEvent {
|
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PointerEvent {
|
||||||
const opts = _opts orelse Options{};
|
const arena = try page.getArena(.{ .debug = "UIEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
|
||||||
|
const opts = _opts orelse Options{};
|
||||||
const event = try page._factory.mouseEvent(
|
const event = try page._factory.mouseEvent(
|
||||||
typ,
|
arena,
|
||||||
|
type_string,
|
||||||
MouseEvent{
|
MouseEvent{
|
||||||
._type = .{ .pointer_event = undefined },
|
._type = .{ .pointer_event = undefined },
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
@@ -120,6 +126,10 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PointerEvent {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *PointerEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *PointerEvent) *Event {
|
pub fn asEvent(self: *PointerEvent) *Event {
|
||||||
return self._proto.asEvent();
|
return self._proto.asEvent();
|
||||||
}
|
}
|
||||||
@@ -179,6 +189,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "PointerEvent";
|
pub const name = "PointerEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(PointerEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(PointerEvent.init, .{});
|
pub const constructor = bridge.constructor(PointerEvent.init, .{});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||||
//
|
//
|
||||||
// Francis Bouvier <francis@lightpanda.io>
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
// Pierre Tachoire <pierre@lightpanda.io>
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
@@ -16,10 +16,15 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const Event = @import("../Event.zig");
|
const std = @import("std");
|
||||||
|
const String = @import("../../../string.zig").String;
|
||||||
|
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
|
|
||||||
|
const Event = @import("../Event.zig");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent
|
// https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent
|
||||||
const PopStateEvent = @This();
|
const PopStateEvent = @This();
|
||||||
|
|
||||||
@@ -33,17 +38,23 @@ const PopStateEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(PopStateEvent, PopStateEventOptions);
|
const Options = Event.inheritOptions(PopStateEvent, PopStateEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent {
|
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent {
|
||||||
return initWithTrusted(typ, _opts, false, page);
|
const arena = try page.getArena(.{ .debug = "PopStateEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent {
|
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*PopStateEvent {
|
||||||
return initWithTrusted(typ, _opts, true, page);
|
const arena = try page.getArena(.{ .debug = "PopStateEvent.trusted" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
return initWithTrusted(arena, typ, _opts, true, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*PopStateEvent {
|
fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, page: *Page) !*PopStateEvent {
|
||||||
const opts = _opts orelse Options{};
|
const opts = _opts orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.event(
|
const event = try page._factory.event(
|
||||||
|
arena,
|
||||||
typ,
|
typ,
|
||||||
PopStateEvent{
|
PopStateEvent{
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
@@ -55,6 +66,10 @@ fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page)
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *PopStateEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *PopStateEvent) *Event {
|
pub fn asEvent(self: *PopStateEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -76,6 +91,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "PopStateEvent";
|
pub const name = "PopStateEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(PopStateEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(PopStateEvent.init, .{});
|
pub const constructor = bridge.constructor(PopStateEvent.init, .{});
|
||||||
|
|||||||
@@ -16,8 +16,12 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const String = @import("../../../string.zig").String;
|
||||||
|
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
const Event = @import("../Event.zig");
|
const Event = @import("../Event.zig");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const ProgressEvent = @This();
|
const ProgressEvent = @This();
|
||||||
_proto: *Event,
|
_proto: *Event,
|
||||||
@@ -34,17 +38,23 @@ const ProgressEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(ProgressEvent, ProgressEventOptions);
|
const Options = Event.inheritOptions(ProgressEvent, ProgressEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent {
|
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent {
|
||||||
return initWithTrusted(typ, _opts, false, page);
|
const arena = try page.getArena(.{ .debug = "ProgressEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent {
|
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*ProgressEvent {
|
||||||
return initWithTrusted(typ, _opts, true, page);
|
const arena = try page.getArena(.{ .debug = "ProgressEvent.trusted" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
return initWithTrusted(arena, typ, _opts, true, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*ProgressEvent {
|
fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, page: *Page) !*ProgressEvent {
|
||||||
const opts = _opts orelse Options{};
|
const opts = _opts orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.event(
|
const event = try page._factory.event(
|
||||||
|
arena,
|
||||||
typ,
|
typ,
|
||||||
ProgressEvent{
|
ProgressEvent{
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
@@ -57,6 +67,10 @@ fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page)
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *ProgressEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asEvent(self: *ProgressEvent) *Event {
|
pub fn asEvent(self: *ProgressEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -81,6 +95,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "ProgressEvent";
|
pub const name = "ProgressEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(ProgressEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(ProgressEvent.init, .{});
|
pub const constructor = bridge.constructor(ProgressEvent.init, .{});
|
||||||
|
|||||||
@@ -16,11 +16,13 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const Event = @import("../Event.zig");
|
const String = @import("../../../string.zig").String;
|
||||||
const Window = @import("../Window.zig");
|
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
|
|
||||||
|
const Event = @import("../Event.zig");
|
||||||
|
const Window = @import("../Window.zig");
|
||||||
|
|
||||||
const UIEvent = @This();
|
const UIEvent = @This();
|
||||||
|
|
||||||
_type: Type,
|
_type: Type,
|
||||||
@@ -45,10 +47,14 @@ pub const Options = Event.inheritOptions(
|
|||||||
);
|
);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*UIEvent {
|
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*UIEvent {
|
||||||
const opts = _opts orelse Options{};
|
const arena = try page.getArena(.{ .debug = "UIEvent" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
const type_string = try String.init(arena, typ, .{});
|
||||||
|
|
||||||
|
const opts = _opts orelse Options{};
|
||||||
const event = try page._factory.event(
|
const event = try page._factory.event(
|
||||||
typ,
|
arena,
|
||||||
|
type_string,
|
||||||
UIEvent{
|
UIEvent{
|
||||||
._type = .generic,
|
._type = .generic,
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
@@ -61,6 +67,10 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*UIEvent {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *UIEvent, shutdown: bool) void {
|
||||||
|
self._proto.deinit(shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as(self: *UIEvent, comptime T: type) *T {
|
pub fn as(self: *UIEvent, comptime T: type) *T {
|
||||||
return self.is(T).?;
|
return self.is(T).?;
|
||||||
}
|
}
|
||||||
@@ -105,6 +115,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "UIEvent";
|
pub const name = "UIEvent";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(UIEvent.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(UIEvent.init, .{});
|
pub const constructor = bridge.constructor(UIEvent.init, .{});
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ pub fn pushEntry(
|
|||||||
if (previous) |prev| {
|
if (previous) |prev| {
|
||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
.wrap("currententrychange"),
|
||||||
.{ .from = prev, .navigationType = @tagName(.push) },
|
.{ .from = prev, .navigationType = @tagName(.push) },
|
||||||
page,
|
page,
|
||||||
);
|
);
|
||||||
@@ -238,7 +238,7 @@ pub fn replaceEntry(
|
|||||||
|
|
||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
.wrap("currententrychange"),
|
||||||
.{ .from = previous, .navigationType = @tagName(.replace) },
|
.{ .from = previous, .navigationType = @tagName(.replace) },
|
||||||
page,
|
page,
|
||||||
);
|
);
|
||||||
@@ -330,7 +330,7 @@ pub fn navigateInner(
|
|||||||
|
|
||||||
// If we haven't navigated off, let us fire off an a currententrychange.
|
// If we haven't navigated off, let us fire off an a currententrychange.
|
||||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
.wrap("currententrychange"),
|
||||||
.{ .from = previous, .navigationType = @tagName(kind) },
|
.{ .from = previous, .navigationType = @tagName(kind) },
|
||||||
page,
|
page,
|
||||||
);
|
);
|
||||||
@@ -372,7 +372,7 @@ pub fn reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !Navigation
|
|||||||
entry._state = .{ .source = .navigation, .value = state.toJson(arena) catch return error.DataClone };
|
entry._state = .{ .source = .navigation, .value = state.toJson(arena) catch return error.DataClone };
|
||||||
|
|
||||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
.wrap("currententrychange"),
|
||||||
.{ .from = previous, .navigationType = @tagName(.reload) },
|
.{ .from = previous, .navigationType = @tagName(.reload) },
|
||||||
page,
|
page,
|
||||||
);
|
);
|
||||||
@@ -414,7 +414,7 @@ pub fn updateCurrentEntry(self: *Navigation, options: UpdateCurrentEntryOptions,
|
|||||||
};
|
};
|
||||||
|
|
||||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
.wrap("currententrychange"),
|
||||||
.{ .from = previous, .navigationType = null },
|
.{ .from = previous, .navigationType = null },
|
||||||
page,
|
page,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ pub fn dispatch(self: *NavigationEventTarget, event_type: DispatchType, page: *P
|
|||||||
.currententrychange => |cec| .{ cec.asEvent(), "_on_currententrychange" },
|
.currententrychange => |cec| .{ cec.asEvent(), "_on_currententrychange" },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
if (page.js.local == null) {
|
if (page.js.local == null) {
|
||||||
|
|||||||
@@ -515,7 +515,9 @@ fn stateChanged(self: *XMLHttpRequest, state: ReadyState, local: *const js.Local
|
|||||||
|
|
||||||
self._ready_state = state;
|
self._ready_state = state;
|
||||||
|
|
||||||
const event = try Event.initTrusted("readystatechange", .{}, page);
|
const event = try Event.initTrusted(.wrap("readystatechange"), .{}, page);
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
try page._event_manager.dispatchWithFunction(
|
try page._event_manager.dispatchWithFunction(
|
||||||
self.asEventTarget(),
|
self.asEventTarget(),
|
||||||
event,
|
event,
|
||||||
|
|||||||
@@ -57,15 +57,16 @@ pub fn dispatch(self: *XMLHttpRequestEventTarget, comptime event_type: DispatchT
|
|||||||
};
|
};
|
||||||
|
|
||||||
const progress = progress_ orelse Progress{};
|
const progress = progress_ orelse Progress{};
|
||||||
const event = try ProgressEvent.initTrusted(
|
const event = (try ProgressEvent.initTrusted(
|
||||||
typ,
|
comptime .wrap(typ),
|
||||||
.{ .total = progress.total, .loaded = progress.loaded },
|
.{ .total = progress.total, .loaded = progress.loaded },
|
||||||
page,
|
page,
|
||||||
);
|
)).asEvent();
|
||||||
|
defer if (!event._v8_handoff) event.deinit(false);
|
||||||
|
|
||||||
return page._event_manager.dispatchWithFunction(
|
return page._event_manager.dispatchWithFunction(
|
||||||
self.asEventTarget(),
|
self.asEventTarget(),
|
||||||
event.asEvent(),
|
event,
|
||||||
local.toLocal(@field(self, field)),
|
local.toLocal(@field(self, field)),
|
||||||
.{ .context = "XHR " ++ typ },
|
.{ .context = "XHR " ++ typ },
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ fn dispatchKeyEvent(cmd: anytype) !void {
|
|||||||
const page = bc.session.currentPage() orelse return;
|
const page = bc.session.currentPage() orelse return;
|
||||||
|
|
||||||
const KeyboardEvent = @import("../../browser/webapi/event/KeyboardEvent.zig");
|
const KeyboardEvent = @import("../../browser/webapi/event/KeyboardEvent.zig");
|
||||||
const keyboard_event = try KeyboardEvent.initTrusted("keydown", .{
|
const keyboard_event = try KeyboardEvent.initTrusted(comptime .wrap("keydown"), .{
|
||||||
.key = params.key,
|
.key = params.key,
|
||||||
.code = params.code,
|
.code = params.code,
|
||||||
.altKey = params.modifiers & 1 == 1,
|
.altKey = params.modifiers & 1 == 1,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ pub fn main() !void {
|
|||||||
// allocator
|
// allocator
|
||||||
// - in Debug mode we use the General Purpose Allocator to detect memory leaks
|
// - in Debug mode we use the General Purpose Allocator to detect memory leaks
|
||||||
// - in Release mode we use the c allocator
|
// - in Release mode we use the c allocator
|
||||||
var gpa_instance: std.heap.DebugAllocator(.{}) = .init;
|
var gpa_instance: std.heap.DebugAllocator(.{ .stack_trace_frames = 10 }) = .init;
|
||||||
const gpa = if (builtin.mode == .Debug) gpa_instance.allocator() else std.heap.c_allocator;
|
const gpa = if (builtin.mode == .Debug) gpa_instance.allocator() else std.heap.c_allocator;
|
||||||
|
|
||||||
defer if (builtin.mode == .Debug) {
|
defer if (builtin.mode == .Debug) {
|
||||||
|
|||||||
Reference in New Issue
Block a user