optional listener, or object listener

This commit is contained in:
Karl Seguin
2025-11-02 00:17:45 +08:00
parent 21d008c6c2
commit de71b97b1f
2 changed files with 87 additions and 6 deletions

View File

@@ -285,3 +285,65 @@
testing.expectEqual('window-capture', non_bubble_calls[0]);
testing.expectEqual('child', non_bubble_calls[1]);
</script>
<script id=nullCallback>
// Test that null callback is handled gracefully
let nullTestPassed = false;
window.addEventListener('testnull', null);
window.removeEventListener('testnull', null);
nullTestPassed = true;
testing.expectEqual(true, nullTestPassed);
</script>
<script id=objectCallback>
// Test object with handleEvent method
let handleEventCalls = 0;
const handler = {
handleEvent: function(e) {
handleEventCalls += 1;
testing.expectEqual('customhandler', e.type);
}
};
window.addEventListener('customhandler', handler);
window.dispatchEvent(new Event('customhandler'));
testing.expectEqual(1, handleEventCalls);
window.dispatchEvent(new Event('customhandler'));
testing.expectEqual(2, handleEventCalls);
// Remove using the same object
window.removeEventListener('customhandler', handler);
window.dispatchEvent(new Event('customhandler'));
testing.expectEqual(2, handleEventCalls); // Should not increment
</script>
<script id=objectWithoutHandleEvent>
// Test object without handleEvent (should be ignored)
const badHandler = { foo: 'bar' };
let badHandlerTestPassed = false;
window.addEventListener('testbad', badHandler);
// Event should not be triggered since there's no valid handler
window.dispatchEvent(new Event('testbad'));
badHandlerTestPassed = true;
testing.expectEqual(true, badHandlerTestPassed);
</script>
<script id=passiveDetection>
// Test passive event listener detection pattern (common in polyfills)
let passiveSupported = false;
try {
const opts = {};
Object.defineProperty(opts, 'passive', {
get: function() {
passiveSupported = true;
}
});
window.addEventListener('testpassive', opts, opts);
window.removeEventListener('testpassive', opts, opts);
} catch (e) {
passiveSupported = false;
}
testing.expectEqual(true, passiveSupported);
</script>

View File

@@ -23,11 +23,23 @@ pub fn dispatchEvent(self: *EventTarget, event: *Event, page: *Page) !bool {
return !event._cancelable or !event._prevent_default;
}
const addEventListenerOptions = union(enum) {
const AddEventListenerOptions = union(enum) {
capture: bool,
options: RegisterOptions,
};
pub fn addEventListener(self: *EventTarget, typ: []const u8, callback: js.Function, opts_: ?addEventListenerOptions, page: *Page) !void {
pub const EventListenerCallback = union(enum) {
function: js.Function,
object: js.Object,
};
pub fn addEventListener(self: *EventTarget, typ: []const u8, callback_: ?EventListenerCallback, opts_: ?AddEventListenerOptions, page: *Page) !void {
const callback = callback_ orelse return;
const actual_callback = switch (callback) {
.function => |func| func,
.object => |obj| (try obj.getFunction("handleEvent")) orelse return,
};
const options = blk: {
const o = opts_ orelse break :blk RegisterOptions{};
break :blk switch (o) {
@@ -35,10 +47,10 @@ pub fn addEventListener(self: *EventTarget, typ: []const u8, callback: js.Functi
.capture => |capture| RegisterOptions{ .capture = capture },
};
};
return page._event_manager.register(self, typ, callback, options);
return page._event_manager.register(self, typ, actual_callback, options);
}
const removeEventListenerOptions = union(enum) {
const RemoveEventListenerOptions = union(enum) {
capture: bool,
options: Options,
@@ -46,7 +58,14 @@ const removeEventListenerOptions = union(enum) {
useCapture: bool = false,
};
};
pub fn removeEventListener(self: *EventTarget, typ: []const u8, callback: js.Function, opts_: ?removeEventListenerOptions, page: *Page) !void {
pub fn removeEventListener(self: *EventTarget, typ: []const u8, callback_: ?EventListenerCallback, opts_: ?RemoveEventListenerOptions, page: *Page) !void {
const callback = callback_ orelse return;
const actual_callback = switch (callback) {
.function => |func| func,
.object => |obj| (try obj.getFunction("handleEvent")) orelse return,
};
const use_capture = blk: {
const o = opts_ orelse break :blk false;
break :blk switch (o) {
@@ -54,7 +73,7 @@ pub fn removeEventListener(self: *EventTarget, typ: []const u8, callback: js.Fun
.options => |opts| opts.useCapture,
};
};
return page._event_manager.remove(self, typ, callback, use_capture);
return page._event_manager.remove(self, typ, actual_callback, use_capture);
}
pub fn format(self: *EventTarget, writer: *std.Io.Writer) !void {