diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index 281328f25..e3b711b7f 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -484,56 +484,85 @@ pub fn createEvent(_: *const Document, event_type: []const u8, frame: *Frame) !* } const normalized = std.ascii.lowerString(&frame.buf, event_type); - if (std.mem.eql(u8, normalized, "event") or std.mem.eql(u8, normalized, "events") or std.mem.eql(u8, normalized, "htmlevents")) { - return Event.init("", null, frame._page); - } + const event: *Event = blk: { + if (std.mem.eql(u8, normalized, "event") or std.mem.eql(u8, normalized, "events") or std.mem.eql(u8, normalized, "htmlevents") or std.mem.eql(u8, normalized, "svgevents")) { + break :blk try Event.init("", null, frame._page); + } - if (std.mem.eql(u8, normalized, "customevent") or std.mem.eql(u8, normalized, "customevents")) { - const CustomEvent = @import("event/CustomEvent.zig"); - return (try CustomEvent.init("", null, frame._page)).asEvent(); - } + if (std.mem.eql(u8, normalized, "customevent") or std.mem.eql(u8, normalized, "customevents")) { + const CustomEvent = @import("event/CustomEvent.zig"); + break :blk (try CustomEvent.init("", null, frame._page)).asEvent(); + } - if (std.mem.eql(u8, normalized, "keyboardevent")) { - const KeyboardEvent = @import("event/KeyboardEvent.zig"); - return (try KeyboardEvent.init("", null, frame)).asEvent(); - } + if (std.mem.eql(u8, normalized, "keyboardevent")) { + const KeyboardEvent = @import("event/KeyboardEvent.zig"); + break :blk (try KeyboardEvent.init("", null, frame)).asEvent(); + } - if (std.mem.eql(u8, normalized, "inputevent")) { - const InputEvent = @import("event/InputEvent.zig"); - return (try InputEvent.init("", null, frame)).asEvent(); - } + if (std.mem.eql(u8, normalized, "inputevent")) { + const InputEvent = @import("event/InputEvent.zig"); + break :blk (try InputEvent.init("", null, frame)).asEvent(); + } - if (std.mem.eql(u8, normalized, "mouseevent") or std.mem.eql(u8, normalized, "mouseevents")) { - const MouseEvent = @import("event/MouseEvent.zig"); - return (try MouseEvent.init("", null, frame)).asEvent(); - } + if (std.mem.eql(u8, normalized, "mouseevent") or std.mem.eql(u8, normalized, "mouseevents")) { + const MouseEvent = @import("event/MouseEvent.zig"); + break :blk (try MouseEvent.init("", null, frame)).asEvent(); + } - if (std.mem.eql(u8, normalized, "messageevent")) { - const MessageEvent = @import("event/MessageEvent.zig"); - return (try MessageEvent.init("", null, frame._page)).asEvent(); - } + if (std.mem.eql(u8, normalized, "dragevent")) { + const DragEvent = @import("event/DragEvent.zig"); + break :blk (try DragEvent.init("", null, frame)).asEvent(); + } - if (std.mem.eql(u8, normalized, "uievent") or std.mem.eql(u8, normalized, "uievents")) { - const UIEvent = @import("event/UIEvent.zig"); - return (try UIEvent.init("", null, frame)).asEvent(); - } + if (std.mem.eql(u8, normalized, "messageevent")) { + const MessageEvent = @import("event/MessageEvent.zig"); + break :blk (try MessageEvent.init("", null, frame._page)).asEvent(); + } - if (std.mem.eql(u8, normalized, "focusevent") or std.mem.eql(u8, normalized, "focusevents")) { - const FocusEvent = @import("event/FocusEvent.zig"); - return (try FocusEvent.init("", null, frame)).asEvent(); - } + if (std.mem.eql(u8, normalized, "hashchangeevent")) { + const HashChangeEvent = @import("event/HashChangeEvent.zig"); + break :blk (try HashChangeEvent.init("", null, frame)).asEvent(); + } - if (std.mem.eql(u8, normalized, "textevent") or std.mem.eql(u8, normalized, "textevents")) { - const TextEvent = @import("event/TextEvent.zig"); - return (try TextEvent.init("", null, frame)).asEvent(); - } + if (std.mem.eql(u8, normalized, "uievent") or std.mem.eql(u8, normalized, "uievents")) { + const UIEvent = @import("event/UIEvent.zig"); + break :blk (try UIEvent.init("", null, frame)).asEvent(); + } - if (std.mem.eql(u8, normalized, "compositionevent")) { - const CompositionEvent = @import("event/CompositionEvent.zig"); - return (try CompositionEvent.init("", null, frame)).asEvent(); - } + if (std.mem.eql(u8, normalized, "focusevent") or std.mem.eql(u8, normalized, "focusevents")) { + const FocusEvent = @import("event/FocusEvent.zig"); + break :blk (try FocusEvent.init("", null, frame)).asEvent(); + } - return error.NotSupported; + if (std.mem.eql(u8, normalized, "textevent") or std.mem.eql(u8, normalized, "textevents")) { + const TextEvent = @import("event/TextEvent.zig"); + break :blk (try TextEvent.init("", null, frame)).asEvent(); + } + + if (std.mem.eql(u8, normalized, "compositionevent")) { + const CompositionEvent = @import("event/CompositionEvent.zig"); + break :blk (try CompositionEvent.init("", null, frame)).asEvent(); + } + + // Aliases the spec requires createEvent to support but whose + // interfaces aren't implemented yet: return a plain Event so the + // caller can at least initialize and dispatch it. + if (std.mem.eql(u8, normalized, "beforeunloadevent") or + std.mem.eql(u8, normalized, "devicemotionevent") or + std.mem.eql(u8, normalized, "deviceorientationevent") or + std.mem.eql(u8, normalized, "storageevent")) + { + log.info(.not_implemented, "createEvent interface", .{ .type = event_type }); + break :blk try Event.init("", null, frame._page); + } + + return error.NotSupported; + }; + + // createEvent returns an uninitialized event: dispatching it before one + // of the init*Event calls throws an InvalidStateError. + event._initialized = false; + return event; } pub fn createTreeWalker(_: *const Document, root: *Node, what_to_show: ?js.Value, filter: ?DOMTreeWalker.FilterOpts, frame: *Frame) !*DOMTreeWalker { diff --git a/src/browser/webapi/Event.zig b/src/browser/webapi/Event.zig index 53179e125..c592e2793 100644 --- a/src/browser/webapi/Event.zig +++ b/src/browser/webapi/Event.zig @@ -50,6 +50,10 @@ _needs_retargeting: bool = false, _is_trusted: bool = false, _in_passive_listener: bool = false, _listeners_did_throw: bool = false, // IndexedDB needs to abort on callback throw +// Per spec, events created via document.createEvent are not initialized +// until one of the init*Event methods runs; dispatching one throws an +// InvalidStateError. Events created any other way start initialized. +_initialized: bool = true, // 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. The timing @@ -137,6 +141,7 @@ pub fn initEvent( return; } + self._initialized = true; self._type_string = try String.init(self._arena, event_string, .{}); self._bubbles = bubbles orelse false; self._cancelable = cancelable orelse false; diff --git a/src/browser/webapi/EventTarget.zig b/src/browser/webapi/EventTarget.zig index 7fe689396..e8422f99c 100644 --- a/src/browser/webapi/EventTarget.zig +++ b/src/browser/webapi/EventTarget.zig @@ -69,6 +69,11 @@ pub fn dispatchEvent(self: *EventTarget, event: *Event, exec: *js.Execution) !bo if (event._event_phase != .none) { return error.InvalidStateError; } + // An event created by document.createEvent stays uninitialized until an + // init*Event call; dispatching it is an error. + if (!event._initialized) { + return error.InvalidStateError; + } event._is_trusted = false; switch (exec.js.global) { diff --git a/src/browser/webapi/event/CompositionEvent.zig b/src/browser/webapi/event/CompositionEvent.zig index fb3b4fd4d..93493d418 100644 --- a/src/browser/webapi/event/CompositionEvent.zig +++ b/src/browser/webapi/event/CompositionEvent.zig @@ -79,6 +79,7 @@ pub fn initCompositionEvent( } const arena = event._arena; + event._initialized = true; event._type_string = try String.init(arena, typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; diff --git a/src/browser/webapi/event/CustomEvent.zig b/src/browser/webapi/event/CustomEvent.zig index e1412a5d7..61f96e941 100644 --- a/src/browser/webapi/event/CustomEvent.zig +++ b/src/browser/webapi/event/CustomEvent.zig @@ -73,6 +73,7 @@ pub fn initCustomEvent( // This function can only be called after the constructor has called. // So we assume proto is initialized already by constructor. + self._proto._initialized = true; self._proto._type_string = try String.init(self._proto._arena, event_string, .{}); self._proto._bubbles = bubbles orelse false; self._proto._cancelable = cancelable orelse false; diff --git a/src/browser/webapi/event/KeyboardEvent.zig b/src/browser/webapi/event/KeyboardEvent.zig index 73ff74706..5c66cde3d 100644 --- a/src/browser/webapi/event/KeyboardEvent.zig +++ b/src/browser/webapi/event/KeyboardEvent.zig @@ -428,6 +428,7 @@ pub fn initKeyboardEvent( } const arena = event._arena; + event._initialized = true; event._type_string = try String.init(arena, typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; diff --git a/src/browser/webapi/event/MouseEvent.zig b/src/browser/webapi/event/MouseEvent.zig index eac879547..819d6fe9c 100644 --- a/src/browser/webapi/event/MouseEvent.zig +++ b/src/browser/webapi/event/MouseEvent.zig @@ -239,6 +239,7 @@ pub fn initMouseEvent( return; } + event._initialized = true; event._type_string = try String.init(event._arena, typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; diff --git a/src/browser/webapi/event/TextEvent.zig b/src/browser/webapi/event/TextEvent.zig index 8a5e238f8..f03cdd0dc 100644 --- a/src/browser/webapi/event/TextEvent.zig +++ b/src/browser/webapi/event/TextEvent.zig @@ -83,6 +83,7 @@ pub fn initTextEvent( } const arena = event._arena; + event._initialized = true; event._type_string = try String.init(arena, typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; diff --git a/src/browser/webapi/event/UIEvent.zig b/src/browser/webapi/event/UIEvent.zig index c5145e7f7..ded62c17a 100644 --- a/src/browser/webapi/event/UIEvent.zig +++ b/src/browser/webapi/event/UIEvent.zig @@ -146,6 +146,7 @@ pub fn initUIEvent( return; } + event._initialized = true; event._type_string = try String.init(event._arena, typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false;