diff --git a/src/browser/EventManager.zig b/src/browser/EventManager.zig index fb6d3674..b98c7f6a 100644 --- a/src/browser/EventManager.zig +++ b/src/browser/EventManager.zig @@ -136,12 +136,16 @@ pub fn dispatch(self: *EventManager, target: *EventTarget, event: *Event) !void event._dispatch_target = target; // Store original target for composedPath() var was_handled = false; - defer if (was_handled) { - var ls: js.Local.Scope = undefined; - self.page.js.localScope(&ls); - defer ls.deinit(); - ls.local.runMicrotasks(); - }; + defer { + if (was_handled) { + var ls: js.Local.Scope = undefined; + self.page.js.localScope(&ls); + defer ls.deinit(); + ls.local.runMicrotasks(); + } else { + event.deinit(false); + } + } switch (target._type) { .node => |node| try self.dispatchNode(node, event, &was_handled), @@ -181,26 +185,30 @@ pub fn dispatchWithFunction(self: *EventManager, target: *EventTarget, event: *E event._dispatch_target = target; // Store original target for composedPath() } - var was_dispatched = false; - defer if (was_dispatched) { - var ls: js.Local.Scope = undefined; - self.page.js.localScope(&ls); - defer ls.deinit(); - ls.local.runMicrotasks(); - }; - - if (function_) |func| { - event._current_target = target; - if (func.callWithThis(void, target, .{event})) { - was_dispatched = true; - } else |err| { - // a non-JS error - log.warn(.event, opts.context, .{ .err = err }); + var was_handled = false; + defer { + if (was_handled) { + var ls: js.Local.Scope = undefined; + self.page.js.localScope(&ls); + defer ls.deinit(); + ls.local.runMicrotasks(); + } else { + event.deinit(false); } } + if (function_) |func| { + const js_val = try func.local.zigValueToJs(event, .{}); + was_handled = true; + + event._current_target = target; + func.callWithThis(void, target, .{js_val}) catch |err| { + log.warn(.event, opts.context, .{ .err = err }); + }; + } + const list = self.lookup.get(@intFromPtr(target)) orelse return; - try self.dispatchAll(list, target, event, &was_dispatched); + try self.dispatchAll(list, target, event, &was_handled); } fn dispatchNode(self: *EventManager, target: *Node, event: *Event, was_handled: *bool) !void { @@ -364,7 +372,6 @@ fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_targe self.removeListener(list, listener); } - was_handled.* = true; event._current_target = current_target; // Compute adjusted target for shadow DOM retargeting (only if needed) @@ -377,8 +384,11 @@ fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_targe page.js.localScope(&ls); defer ls.deinit(); + const js_val = try ls.local.zigValueToJs(event, .{}); + was_handled.* = true; + switch (listener.function) { - .value => |value| try ls.toLocal(value).callWithThis(void, current_target, .{event}), + .value => |value| try ls.toLocal(value).callWithThis(void, current_target, .{js_val}), .string => |string| { const str = try page.call_arena.dupeZ(u8, string.str()); try ls.local.eval(str, null); @@ -386,7 +396,7 @@ fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_targe .object => |obj_global| { const obj = ls.toLocal(obj_global); if (try obj.getFunction("handleEvent")) |handleEvent| { - try handleEvent.callWithThis(void, obj, .{event}); + try handleEvent.callWithThis(void, obj, .{js_val}); } }, } diff --git a/src/browser/Factory.zig b/src/browser/Factory.zig index ba4e3ddd..8cb101ac 100644 --- a/src/browser/Factory.zig +++ b/src/browser/Factory.zig @@ -172,60 +172,56 @@ pub fn eventTarget(self: *Factory, child: anytype) !*@TypeOf(child) { return chain.get(1); } -fn eventInit(typ: []const u8, value: anytype, page: *Page) !Event { +fn eventInit(arena: Allocator, 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 .{ + ._page = page, + ._arena = arena, ._type = unionInit(Event.Type, value), - ._type_string = try String.init(page.arena, typ, .{}), + ._type_string = try String.init(arena, typ, .{}), ._time_stamp = time_stamp, }; } // this is a root object -pub fn event(self: *Factory, typ: []const u8, child: anytype) !*@TypeOf(child) { - const allocator = self._slab.allocator(); - +pub fn event(self: *Factory, arena: Allocator, typ: []const u8, child: anytype) !*@TypeOf(child) { const chain = try PrototypeChain( &.{ Event, @TypeOf(child) }, - ).allocate(allocator); + ).allocate(arena); // Special case: Event has a _type_string field, so we need manual setup const event_ptr = chain.get(0); - event_ptr.* = try eventInit(typ, chain.get(1), self._page); + event_ptr.* = try eventInit(arena, typ, chain.get(1), self._page); chain.setLeaf(1, child); return chain.get(1); } -pub fn uiEvent(self: *Factory, typ: []const u8, child: anytype) !*@TypeOf(child) { - const allocator = self._slab.allocator(); - +pub fn uiEvent(self: *Factory, arena: Allocator, typ: []const u8, child: anytype) !*@TypeOf(child) { const chain = try PrototypeChain( &.{ Event, UIEvent, @TypeOf(child) }, - ).allocate(allocator); + ).allocate(arena); // Special case: Event has a _type_string field, so we need manual setup const event_ptr = chain.get(0); - event_ptr.* = try eventInit(typ, chain.get(1), self._page); + event_ptr.* = try eventInit(arena, typ, chain.get(1), self._page); chain.setMiddle(1, UIEvent.Type); chain.setLeaf(2, child); return chain.get(2); } -pub fn mouseEvent(self: *Factory, typ: []const u8, mouse: MouseEvent, child: anytype) !*@TypeOf(child) { - const allocator = self._slab.allocator(); - +pub fn mouseEvent(self: *Factory, arena: Allocator, typ: []const u8, mouse: MouseEvent, child: anytype) !*@TypeOf(child) { const chain = try PrototypeChain( &.{ Event, UIEvent, MouseEvent, @TypeOf(child) }, - ).allocate(allocator); + ).allocate(arena); // Special case: Event has a _type_string field, so we need manual setup const event_ptr = chain.get(0); - event_ptr.* = try eventInit(typ, chain.get(1), self._page); + event_ptr.* = try eventInit(arena, typ, chain.get(1), self._page); chain.setMiddle(1, UIEvent.Type); // Set MouseEvent with all its fields diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 0cdd177f..830b29b1 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -333,6 +333,10 @@ fn reset(self: *Page, comptime initializing: bool) !void { self._customized_builtin_disconnected_callback_invoked = .{}; self._undefined_custom_elements = .{}; + if (comptime IS_DEBUG) { + self._arena_pool_leak_track = .{}; + } + try self.registerBackgroundTasks(); } diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index c47a71a7..9ac5c40e 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -197,22 +197,21 @@ pub fn mapZigInstanceToJs(self: *const Local, js_obj_handle: ?*const v8.Object, // dont' use js_obj.persist(), because we don't want to track this in // context.global_objects, we want to track it in context.identity_map. v8.v8__Global__New(isolate.handle, js_obj.handle, gop.value_ptr); - if (@hasDecl(JsApi.Meta, "finalizer")) { - if (comptime IS_DEBUG) { - // You can normally return a "*Node" and we'll correctly - // handle it as what it really is, e.g. an HTMLScriptElement. - // But for finalizers, we can't do that. I think this - // limitation will be OK - this auto-resolution is largely - // limited to Node -> HtmlElement, none of which has finalizers - std.debug.assert(resolved.class_id == JsApi.Meta.class_id); - } + if (@hasDecl(JsApi.Meta, "finalizer")) { + // It would be great if resolved knew the resolved type, but I + // can't figure out how to make that work, since it depends on + // the [runtime] `value. + // We need the resolved finalizer, which we have in resolved. + // The above if statement would be more clear as: + // if (resolved.finalizer) |finalizer| { + // 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, than the base + // should have a finalizer too. try ctx.finalizer_callbacks.put(ctx.arena, @intFromPtr(resolved.ptr), .init(value)); - if (@hasDecl(JsApi.Meta, "weak")) { - if (comptime IS_DEBUG) { - std.debug.assert(JsApi.Meta.weak == true); - } - v8.v8__Global__SetWeakFinalizer(gop.value_ptr, resolved.ptr, JsApi.Meta.finalizer.from_v8, v8.kParameter); + if (resolved.weak) { + v8.v8__Global__SetWeakFinalizer(gop.value_ptr, resolved.ptr, resolved.finalizer.?, v8.kParameter); } } return js_obj; @@ -1032,9 +1031,11 @@ fn jsUnsignedIntToZig(comptime T: type, max: comptime_int, maybe: u32) !T { // This function recursively walks the _type union field (if there is one) to // get the most specific class_id possible. const Resolved = struct { + weak: bool, ptr: *anyopaque, class_id: u16, prototype_chain: []const @import("TaggedOpaque.zig").PrototypeChainEntry, + finalizer: ?*const fn (handle: ?*const v8.WeakCallbackInfo) callconv(.c) void = null, }; pub fn resolveValue(value: anytype) Resolved { const T = bridge.Struct(@TypeOf(value)); @@ -1062,10 +1063,13 @@ pub fn resolveValue(value: anytype) Resolved { } fn resolveT(comptime T: type, value: *anyopaque) Resolved { + const Meta = T.JsApi.Meta; return .{ .ptr = value, - .class_id = T.JsApi.Meta.class_id, - .prototype_chain = &T.JsApi.Meta.prototype_chain, + .class_id = Meta.class_id, + .prototype_chain = &Meta.prototype_chain, + .weak = if (@hasDecl(Meta, "weak")) Meta.weak else false, + .finalizer = if (@hasDecl(Meta, "finalizer")) Meta.finalizer.from_v8 else null, }; } diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 9860d98f..080f129d 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -111,7 +111,8 @@ pub fn Builder(comptime T: type) type { // to be possible between finalization and context shutdown, // we need to be defensive). // There _ARE_ alternatives to this. But this is simple. - const ctx = self._page.js; + const page = findPageField(T, self); + const ctx = page.js; if (!ctx.identity_map.contains(@intFromPtr(ptr))) { return; } @@ -515,6 +516,17 @@ fn countFlattenedTypes(comptime Types: []const type) usize { return c; } +fn findPageField(comptime OriginalT: type, value: anytype) *Page { + const T = Struct(@TypeOf(value)); + if (@hasField(T, "_page")) { + return value._page; + } + if (@hasField(T, "_proto") == false) { + @compileError("Expected to find a _page: *Page field on " ++ @typeName(OriginalT) ++ " or it's _proto chain"); + } + return findPageField(OriginalT, value._proto); +} + // T => T // *T => T pub fn Struct(comptime T: type) type { diff --git a/src/browser/webapi/Event.zig b/src/browser/webapi/Event.zig index 8781760a..700e71ba 100644 --- a/src/browser/webapi/Event.zig +++ b/src/browser/webapi/Event.zig @@ -24,11 +24,14 @@ const EventTarget = @import("EventTarget.zig"); const Node = @import("Node.zig"); const String = @import("../../string.zig").String; +const Allocator = std.mem.Allocator; + pub const Event = @This(); pub const _prototype_root = true; _type: Type, - +_page: *Page, +_arena: Allocator, _bubbles: bool = false, _cancelable: bool = false, _composed: bool = false, @@ -79,23 +82,32 @@ pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*Event { } fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) !*Event { + const arena = try page.getArena(.{ .debug = "Event" }); + errdefer page.releaseArena(arena); + const opts = opts_ orelse Options{}; // Round to 2ms for privacy (browsers do this) const raw_timestamp = @import("../../datetime.zig").milliTimestamp(.monotonic); const time_stamp = (raw_timestamp / 2) * 2; - const event = try page._factory.create(Event{ + const self = try arena.create(Event); + self.* = .{ + ._page = page, + ._arena = arena, ._type = .generic, ._bubbles = opts.bubbles, ._time_stamp = time_stamp, ._cancelable = opts.cancelable, ._composed = opts.composed, - ._type_string = try String.init(page.arena, typ, .{}), - }); + ._isTrusted = trusted, + ._type_string = try String.init(arena, typ, .{}), + }; + return self; +} - event._isTrusted = trusted; - return event; +pub fn deinit(self: *Event, _: bool) void { + self._page.releaseArena(self._arena); } pub fn initEvent( @@ -103,13 +115,12 @@ pub fn initEvent( event_string: []const u8, bubbles: ?bool, cancelable: ?bool, - page: *Page, ) !void { if (self._event_phase != .none) { 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._cancelable = cancelable orelse false; self._stop_propagation = false; @@ -385,6 +396,8 @@ pub const JsApi = struct { pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/CompositionEvent.zig b/src/browser/webapi/event/CompositionEvent.zig index f77489dc..3f183847 100644 --- a/src/browser/webapi/event/CompositionEvent.zig +++ b/src/browser/webapi/event/CompositionEvent.zig @@ -33,17 +33,24 @@ const CompositionEventOptions = struct { const Options = Event.inheritOptions(CompositionEvent, CompositionEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CompositionEvent { + const arena = try page.getArena(.{ .debug = "CompositionEvent" }); + errdefer page.releaseArena(arena); + const opts = opts_ orelse Options{}; - const event = try page._factory.event(typ, CompositionEvent{ + const event = try page._factory.event(arena, typ, CompositionEvent{ ._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); return event; } +pub fn deinit(self: *CompositionEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *CompositionEvent) *Event { return self._proto; } @@ -59,6 +66,8 @@ pub const JsApi = struct { pub const name = "CompositionEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/CustomEvent.zig b/src/browser/webapi/event/CustomEvent.zig index be5820d0..071ccd1f 100644 --- a/src/browser/webapi/event/CustomEvent.zig +++ b/src/browser/webapi/event/CustomEvent.zig @@ -37,10 +37,13 @@ const CustomEventOptions = struct { const Options = Event.inheritOptions(CustomEvent, CustomEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CustomEvent { - const arena = page.arena; + const arena = try page.getArena(.{ .debug = "CustomEvent" }); + errdefer page.releaseArena(arena); + const opts = opts_ orelse Options{}; const event = try page._factory.event( + arena, typ, CustomEvent{ ._arena = arena, @@ -53,21 +56,23 @@ pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CustomEvent { return event; } +pub fn deinit(self: *CustomEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn initCustomEvent( self: *CustomEvent, event_string: []const u8, bubbles: ?bool, cancelable: ?bool, - detail_: ?js.Value.Global, - page: *Page, + detail: ?js.Value.Global, ) !void { // This function can only be called after the constructor has called. // 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._cancelable = cancelable orelse false; - // Detail is stored separately. - self._detail = detail_; + self._detail = detail; } pub fn asEvent(self: *CustomEvent) *Event { @@ -85,6 +90,8 @@ pub const JsApi = struct { pub const name = "CustomEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/ErrorEvent.zig b/src/browser/webapi/event/ErrorEvent.zig index 296def3c..f9373a16 100644 --- a/src/browser/webapi/event/ErrorEvent.zig +++ b/src/browser/webapi/event/ErrorEvent.zig @@ -31,7 +31,6 @@ _filename: []const u8 = "", _line_number: u32 = 0, _column_number: u32 = 0, _error: ?js.Value.Global = null, -_arena: Allocator, pub const ErrorEventOptions = struct { message: ?[]const u8 = null, @@ -52,13 +51,15 @@ pub fn initTrusted(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent { } fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) !*ErrorEvent { - const arena = page.arena; + const arena = try page.getArena(.{ .debug = "ErrorEvent" }); + errdefer page.releaseArena(arena); + const opts = opts_ orelse Options{}; const event = try page._factory.event( + arena, typ, ErrorEvent{ - ._arena = arena, ._proto = undefined, ._message = if (opts.message) |str| try arena.dupe(u8, str) else "", ._filename = if (opts.filename) |str| try arena.dupe(u8, str) else "", @@ -72,6 +73,10 @@ fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) return event; } +pub fn deinit(self: *ErrorEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *ErrorEvent) *Event { return self._proto; } @@ -103,6 +108,8 @@ pub const JsApi = struct { pub const name = "ErrorEvent"; pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; + pub const weak = true; + pub const finalizer = bridge.finalizer(ErrorEvent.deinit); }; // Start API diff --git a/src/browser/webapi/event/KeyboardEvent.zig b/src/browser/webapi/event/KeyboardEvent.zig index f175ddee..132eeead 100644 --- a/src/browser/webapi/event/KeyboardEvent.zig +++ b/src/browser/webapi/event/KeyboardEvent.zig @@ -189,15 +189,19 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent { } fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*KeyboardEvent { + const arena = try page.getArena(.{ .debug = "KeyboardEvent" }); + errdefer page.releaseArena(arena); + const opts = _opts orelse Options{}; const event = try page._factory.uiEvent( + arena, typ, KeyboardEvent{ ._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, - ._code = if (opts.code) |c| try page.dupeString(c) else "", + ._code = if (opts.code) |c| try arena.dupe(u8, c) else "", ._repeat = opts.repeat, ._is_composing = opts.isComposing, ._ctrl_key = opts.ctrlKey, @@ -211,6 +215,10 @@ fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) return event; } +pub fn deinit(self: *KeyboardEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *KeyboardEvent) *Event { return self._proto.asEvent(); } @@ -251,8 +259,8 @@ pub fn getShiftKey(self: *const KeyboardEvent) bool { return self._shift_key; } -pub fn getModifierState(self: *const KeyboardEvent, str: []const u8, page: *Page) !bool { - const key = try Key.fromString(page.arena, str); +pub fn getModifierState(self: *KeyboardEvent, str: []const u8) !bool { + const key = try Key.fromString(self.asEvent()._arena, str); switch (key) { .Alt, .AltGraph => return self._alt_key, @@ -274,6 +282,8 @@ pub const JsApi = struct { pub const name = "KeyboardEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/MessageEvent.zig b/src/browser/webapi/event/MessageEvent.zig index bdf89fbd..fbe04b29 100644 --- a/src/browser/webapi/event/MessageEvent.zig +++ b/src/browser/webapi/event/MessageEvent.zig @@ -46,14 +46,18 @@ pub fn initTrusted(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent } fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) !*MessageEvent { + const arena = try page.getArena(.{ .debug = "MessageEvent" }); + errdefer page.releaseArena(arena); + const opts = opts_ orelse Options{}; const event = try page._factory.event( + arena, typ, MessageEvent{ ._proto = undefined, ._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, }, ); @@ -62,6 +66,10 @@ fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) return event; } +pub fn deinit(self: *MessageEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *MessageEvent) *Event { return self._proto; } @@ -85,6 +93,8 @@ pub const JsApi = struct { pub const name = "MessageEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/MouseEvent.zig b/src/browser/webapi/event/MouseEvent.zig index 81e2d7a6..bf6db5b0 100644 --- a/src/browser/webapi/event/MouseEvent.zig +++ b/src/browser/webapi/event/MouseEvent.zig @@ -75,9 +75,13 @@ pub const Options = Event.inheritOptions( ); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*MouseEvent { + const arena = try page.getArena(.{ .debug = "MouseEvent" }); + errdefer page.releaseArena(arena); + const opts = _opts orelse Options{}; const event = try page._factory.uiEvent( + arena, typ, MouseEvent{ ._type = .generic, @@ -99,6 +103,10 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*MouseEvent { return event; } +pub fn deinit(self: *MouseEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *MouseEvent) *Event { return self._proto.asEvent(); } @@ -180,6 +188,8 @@ pub const JsApi = struct { pub const name = "MouseEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig b/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig index 0a12a8a3..fb3292dc 100644 --- a/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig +++ b/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig @@ -53,12 +53,16 @@ fn initWithTrusted( trusted: bool, page: *Page, ) !*NavigationCurrentEntryChangeEvent { + const arena = try page.getArena(.{ .debug = "NavigationCurrentEntryChangeEvent" }); + errdefer page.releaseArena(arena); + const navigation_type = if (opts.navigationType) |nav_type_str| std.meta.stringToEnum(NavigationType, nav_type_str) else null; const event = try page._factory.event( + arena, typ, NavigationCurrentEntryChangeEvent{ ._proto = undefined, @@ -71,6 +75,10 @@ fn initWithTrusted( return event; } +pub fn deinit(self: *NavigationCurrentEntryChangeEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *NavigationCurrentEntryChangeEvent) *Event { return self._proto; } @@ -90,6 +98,8 @@ pub const JsApi = struct { pub const name = "NavigationCurrentEntryChangeEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/PageTransitionEvent.zig b/src/browser/webapi/event/PageTransitionEvent.zig index 4a260481..740ed71a 100644 --- a/src/browser/webapi/event/PageTransitionEvent.zig +++ b/src/browser/webapi/event/PageTransitionEvent.zig @@ -41,9 +41,13 @@ pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransiti } fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*PageTransitionEvent { + const arena = try page.getArena(.{ .debug = "PageTransitionEvent" }); + errdefer page.releaseArena(arena); + const opts = _opts orelse Options{}; const event = try page._factory.event( + arena, typ, PageTransitionEvent{ ._proto = undefined, @@ -55,6 +59,10 @@ fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) return event; } +pub fn deinit(self: *PageTransitionEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *PageTransitionEvent) *Event { return self._proto; } @@ -70,6 +78,8 @@ pub const JsApi = struct { pub const name = "PageTransitionEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/PointerEvent.zig b/src/browser/webapi/event/PointerEvent.zig index fab647ba..a1c47c4c 100644 --- a/src/browser/webapi/event/PointerEvent.zig +++ b/src/browser/webapi/event/PointerEvent.zig @@ -81,9 +81,13 @@ const Options = Event.inheritOptions( ); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PointerEvent { + const arena = try page.getArena(.{ .debug = "PointerEvent" }); + errdefer page.releaseArena(arena); + const opts = _opts orelse Options{}; const event = try page._factory.mouseEvent( + arena, typ, MouseEvent{ ._type = .{ .pointer_event = undefined }, @@ -120,6 +124,10 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PointerEvent { return event; } +pub fn deinit(self: *PointerEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *PointerEvent) *Event { return self._proto.asEvent(); } @@ -179,6 +187,8 @@ pub const JsApi = struct { pub const name = "PointerEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/PopStateEvent.zig b/src/browser/webapi/event/PopStateEvent.zig index 78480402..acf10b67 100644 --- a/src/browser/webapi/event/PopStateEvent.zig +++ b/src/browser/webapi/event/PopStateEvent.zig @@ -41,9 +41,13 @@ pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEven } fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*PopStateEvent { + const arena = try page.getArena(.{ .debug = "CustomEvPopStateEventent" }); + errdefer page.releaseArena(arena); + const opts = _opts orelse Options{}; const event = try page._factory.event( + arena, typ, PopStateEvent{ ._proto = undefined, @@ -55,6 +59,10 @@ fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) return event; } +pub fn deinit(self: *PopStateEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *PopStateEvent) *Event { return self._proto; } @@ -76,6 +84,8 @@ pub const JsApi = struct { pub const name = "PopStateEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/ProgressEvent.zig b/src/browser/webapi/event/ProgressEvent.zig index 61fe49c9..a973c5ae 100644 --- a/src/browser/webapi/event/ProgressEvent.zig +++ b/src/browser/webapi/event/ProgressEvent.zig @@ -42,9 +42,13 @@ pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEven } fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*ProgressEvent { + const arena = try page.getArena(.{ .debug = "ProgressEvent" }); + errdefer page.releaseArena(arena); + const opts = _opts orelse Options{}; const event = try page._factory.event( + arena, typ, ProgressEvent{ ._proto = undefined, @@ -57,6 +61,10 @@ fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) return event; } +pub fn deinit(self: *ProgressEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn asEvent(self: *ProgressEvent) *Event { return self._proto; } @@ -81,6 +89,8 @@ pub const JsApi = struct { pub const name = "ProgressEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/browser/webapi/event/UIEvent.zig b/src/browser/webapi/event/UIEvent.zig index 8ad24858..21bd0d3c 100644 --- a/src/browser/webapi/event/UIEvent.zig +++ b/src/browser/webapi/event/UIEvent.zig @@ -45,9 +45,13 @@ pub const Options = Event.inheritOptions( ); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*UIEvent { + const arena = try page.getArena(.{ .debug = "UIEvent" }); + errdefer page.releaseArena(arena); + const opts = _opts orelse Options{}; const event = try page._factory.event( + arena, typ, UIEvent{ ._type = .generic, @@ -61,6 +65,10 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*UIEvent { return event; } +pub fn deinit(self: *UIEvent, shutdown: bool) void { + self._proto.deinit(shutdown); +} + pub fn as(self: *UIEvent, comptime T: type) *T { return self.is(T).?; } @@ -105,6 +113,8 @@ pub const JsApi = struct { pub const name = "UIEvent"; pub const prototype_chain = bridge.prototypeChain(); 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, .{}); diff --git a/src/cdp/Node.zig b/src/cdp/Node.zig index d11dd8bd..2997a438 100644 --- a/src/cdp/Node.zig +++ b/src/cdp/Node.zig @@ -356,7 +356,7 @@ test "cdp Node: Registry register" { } { - const dom_node = (try doc.querySelector(.wrap ("p"), page)).?.asNode(); + const dom_node = (try doc.querySelector(.wrap("p"), page)).?.asNode(); const node = try registry.register(dom_node); const n1b = registry.lookup_by_id.get(2).?; const n1c = registry.lookup_by_node.get(node.dom).?; @@ -400,18 +400,18 @@ test "cdp Node: search list" { defer page._session.removePage(); var doc = page.window._document; - const s1 = try search_list.create((try doc.querySelectorAll(.wrap ("a"), page))._nodes); + const s1 = try search_list.create((try doc.querySelectorAll(.wrap("a"), page))._nodes); try testing.expectEqual("1", s1.name); try testing.expectEqualSlices(u32, &.{ 1, 2 }, s1.node_ids); try testing.expectEqual(2, registry.lookup_by_id.count()); try testing.expectEqual(2, registry.lookup_by_node.count()); - const s2 = try search_list.create((try doc.querySelectorAll(.wrap ("#a1"), page))._nodes); + const s2 = try search_list.create((try doc.querySelectorAll(.wrap("#a1"), page))._nodes); try testing.expectEqual("2", s2.name); try testing.expectEqualSlices(u32, &.{1}, s2.node_ids); - const s3 = try search_list.create((try doc.querySelectorAll(.wrap ("#a2"), page))._nodes); + const s3 = try search_list.create((try doc.querySelectorAll(.wrap("#a2"), page))._nodes); try testing.expectEqual("3", s3.name); try testing.expectEqualSlices(u32, &.{2}, s3.node_ids);