mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-31 01:36:15 -04:00
webapi: uninitialized-event dispatch throws; more createEvent aliases
Fixes the 20 failing tests in WPT /dom/events/EventTarget- dispatchEvent.html (5/25 -> 25/25), all of the form "If the event's initialized flag is not set, an InvalidStateError must be thrown". Per the DOM spec, an event created via document.createEvent has its initialized flag unset until one of the legacy init*Event methods runs, and dispatchEvent must throw an InvalidStateError for it. Lightpanda dispatched such events happily. Event now carries an _initialized flag: true by default (constructor-created and internal events), cleared by document.createEvent, and set by initEvent, initCustomEvent, initUIEvent, initMouseEvent, initKeyboardEvent, initCompositionEvent and initTextEvent. EventTarget.dispatchEvent rejects uninitialized events. document.createEvent also gains the spec-required aliases it was missing: DragEvent, HashChangeEvent and SVGEvents, plus BeforeUnloadEvent, DeviceMotionEvent, DeviceOrientationEvent and StorageEvent mapped to a plain Event (logged as not_implemented) until those interfaces exist. Coverage: /dom/events/EventTarget-dispatchEvent.html 5/25 -> 25/25. No regressions across /dom/events. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Karl Seguin
parent
a0418aed5c
commit
ad9e1d8dc6
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user