From 6b990f8f123f5f9a3c9ee887f01502db9e075e38 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Sat, 22 Nov 2025 12:33:29 +0800 Subject: [PATCH] CustomEvent and document.createEvent --- src/browser/js/bridge.zig | 1 + src/browser/tests/event/custom_event.html | 95 +++++++++++++++++++++++ src/browser/webapi/Document.zig | 21 +++++ src/browser/webapi/Event.zig | 1 + src/browser/webapi/event/CustomEvent.zig | 78 +++++++++++++++++++ 5 files changed, 196 insertions(+) create mode 100644 src/browser/tests/event/custom_event.html create mode 100644 src/browser/webapi/event/CustomEvent.zig diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 7acb8473..bc380d1a 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -544,6 +544,7 @@ pub const JsApis = flattenTypes(&.{ @import("../webapi/encoding/TextDecoder.zig"), @import("../webapi/encoding/TextEncoder.zig"), @import("../webapi/Event.zig"), + @import("../webapi/event/CustomEvent.zig"), @import("../webapi/event/ErrorEvent.zig"), @import("../webapi/event/ProgressEvent.zig"), @import("../webapi/EventTarget.zig"), diff --git a/src/browser/tests/event/custom_event.html b/src/browser/tests/event/custom_event.html new file mode 100644 index 00000000..97f114d8 --- /dev/null +++ b/src/browser/tests/event/custom_event.html @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index eaa9b6e0..e895bfd4 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -175,6 +175,26 @@ pub fn createTextNode(_: *const Document, data: []const u8, page: *Page) !*Node return page.createTextNode(data); } +pub fn createEvent(_: *const Document, event_type: []const u8, page: *Page) !*@import("Event.zig") { + const Event = @import("Event.zig"); + + if (std.ascii.eqlIgnoreCase(event_type, "event") or std.ascii.eqlIgnoreCase(event_type, "events") or std.ascii.eqlIgnoreCase(event_type, "htmlevents")) { + return Event.init("", null, page); + } + + if (std.ascii.eqlIgnoreCase(event_type, "customevent") or std.ascii.eqlIgnoreCase(event_type, "customevents")) { + const CustomEvent = @import("event/CustomEvent.zig"); + const custom_event = try CustomEvent.init("", null, page); + return custom_event.asEvent(); + } + + if (std.ascii.eqlIgnoreCase(event_type, "messageevent")) { + return error.NotSupported; + } + + return error.NotSupported; +} + pub fn createTreeWalker(_: *const Document, root: *Node, what_to_show: ?u32, filter: ?DOMTreeWalker.FilterOpts, page: *Page) !*DOMTreeWalker { const show = what_to_show orelse NodeFilter.SHOW_ALL; return DOMTreeWalker.init(root, show, filter, page); @@ -239,6 +259,7 @@ pub const JsApi = struct { pub const createDocumentFragment = bridge.function(Document.createDocumentFragment, .{}); pub const createComment = bridge.function(Document.createComment, .{}); pub const createTextNode = bridge.function(Document.createTextNode, .{}); + pub const createEvent = bridge.function(Document.createEvent, .{ .dom_exception = true }); pub const createTreeWalker = bridge.function(Document.createTreeWalker, .{}); pub const createNodeIterator = bridge.function(Document.createNodeIterator, .{}); pub const getElementById = bridge.function(Document.getElementById, .{}); diff --git a/src/browser/webapi/Event.zig b/src/browser/webapi/Event.zig index 9884ff85..70de6e07 100644 --- a/src/browser/webapi/Event.zig +++ b/src/browser/webapi/Event.zig @@ -48,6 +48,7 @@ pub const Type = union(enum) { generic, progress_event: *@import("event/ProgressEvent.zig"), error_event: *@import("event/ErrorEvent.zig"), + custom_event: *@import("event/CustomEvent.zig"), }; const Options = struct { diff --git a/src/browser/webapi/event/CustomEvent.zig b/src/browser/webapi/event/CustomEvent.zig new file mode 100644 index 00000000..1c36fc33 --- /dev/null +++ b/src/browser/webapi/event/CustomEvent.zig @@ -0,0 +1,78 @@ +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +const std = @import("std"); +const js = @import("../../js/js.zig"); + +const Page = @import("../../Page.zig"); +const Event = @import("../Event.zig"); +const Allocator = std.mem.Allocator; + +const CustomEvent = @This(); + +_proto: *Event, +_detail: ?js.Object = null, +_arena: Allocator, + +pub const InitOptions = struct { + detail: ?js.Object = null, + bubbles: bool = false, + cancelable: bool = false, +}; + +pub fn init(typ: []const u8, opts_: ?InitOptions, page: *Page) !*CustomEvent { + const arena = page.arena; + const opts = opts_ orelse InitOptions{}; + + const event = try page._factory.event(typ, CustomEvent{ + ._arena = arena, + ._proto = undefined, + ._detail = if (opts.detail) |detail| try detail.persist() else null, + }); + + event._proto._bubbles = opts.bubbles; + event._proto._cancelable = opts.cancelable; + + return event; +} + +pub fn asEvent(self: *CustomEvent) *Event { + return self._proto; +} + +pub fn getDetail(self: *const CustomEvent) ?js.Object { + return self._detail; +} + +pub const JsApi = struct { + pub const bridge = js.Bridge(CustomEvent); + + pub const Meta = struct { + pub const name = "CustomEvent"; + pub const prototype_chain = bridge.prototypeChain(); + pub var class_id: bridge.ClassId = undefined; + }; + + pub const constructor = bridge.constructor(CustomEvent.init, .{}); + pub const detail = bridge.accessor(CustomEvent.getDetail, null, .{}); +}; + +const testing = @import("../../../testing.zig"); +test "WebApi: CustomEvent" { + try testing.htmlRunner("event/custom_event.html", .{}); +}