xhr: fix ProgressEvent implementation

This commit is contained in:
Pierre Tachoire
2024-02-09 11:35:22 +01:00
parent d24df5725c
commit 76df0a1ff7
3 changed files with 60 additions and 14 deletions

View File

@@ -13,6 +13,16 @@ const DOMException = @import("../dom/exceptions.zig").DOMException;
const EventTarget = @import("../dom/event_target.zig").EventTarget;
const EventTargetUnion = @import("../dom/event_target.zig").Union;
const xhr = @import("../xhr/xhr.zig");
// Event interfaces
pub const Interfaces = generate.Tuple(.{
Event,
xhr.ProgressEvent,
});
const Generated = generate.Union.compile(Interfaces);
pub const Union = Generated._union;
// https://dom.spec.whatwg.org/#event
pub const Event = struct {
pub const Self = parser.Event;
@@ -28,6 +38,13 @@ pub const Event = struct {
pub const _AT_TARGET = 2;
pub const _BUBBLING_PHASE = 3;
pub fn toInterface(evt: *parser.Event) !Union {
return switch (try parser.eventGetInternalType(evt)) {
.event => .{ .Event = evt },
.progress_event => .{ .ProgressEvent = @as(*xhr.ProgressEvent, @ptrCast(evt)).* },
};
}
pub fn constructor(eventType: []const u8, opts: ?EventInit) !*parser.Event {
const event = try parser.eventCreate();
try parser.eventInit(event, eventType, opts orelse EventInit{});
@@ -104,13 +121,6 @@ pub const Event = struct {
}
};
// Event interfaces
pub const Interfaces = generate.Tuple(.{
Event,
});
const Generated = generate.Union.compile(Interfaces);
pub const Union = Generated._union;
pub fn testExecFn(
_: std.mem.Allocator,
js_env: *jsruntime.Env,
@@ -200,7 +210,7 @@ pub fn testExecFn(
try checkCases(js_env, &legacy);
var remove = [_]Case{
.{ .src = "var nb = 0; function cbk(event) { nb ++; }", .ex = "undefined" },
.{ .src = "var nb = 0; var evt = null; function cbk(event) { nb ++; evt=event; }", .ex = "undefined" },
.{ .src = "document.addEventListener('count', cbk)", .ex = "undefined" },
.{ .src = "document.removeEventListener('count', cbk)", .ex = "undefined" },
.{ .src = "document.dispatchEvent(new Event('count'))", .ex = "true" },

View File

@@ -8,6 +8,7 @@ const c = @cImport({
});
const Callback = @import("jsruntime").Callback;
const EventToInterface = @import("events/event.zig").Event.toInterface;
// Vtable
// ------
@@ -449,6 +450,23 @@ pub fn eventPreventDefault(evt: *Event) !void {
try DOMErr(err);
}
pub fn eventGetInternalType(evt: *Event) !EventType {
var res: u32 = undefined;
const err = c._dom_event_get_internal_type(evt, &res);
try DOMErr(err);
return @enumFromInt(res);
}
pub fn eventSetInternalType(evt: *Event, internal_type: EventType) !void {
const err = c._dom_event_set_internal_type(evt, @intFromEnum(internal_type));
try DOMErr(err);
}
pub const EventType = enum(u8) {
event = 0,
progress_event = 1,
};
// EventHandler
fn event_handler_cbk(data: *anyopaque) *Callback {
const ptr: *align(@alignOf(*Callback)) anyopaque = @alignCast(data);
@@ -459,7 +477,14 @@ const event_handler = struct {
fn handle(event: ?*Event, data: ?*anyopaque) callconv(.C) void {
if (data) |d| {
const func = event_handler_cbk(d);
if (event) |evt| {
func.call(.{
EventToInterface(evt) catch unreachable,
}) catch unreachable;
} else {
func.call(.{event}) catch unreachable;
}
// NOTE: we can not call func.deinit here
// b/c the handler can be called several times
// either on this dispatch event or in anoter one
@@ -652,7 +677,12 @@ pub const EventTargetTBase = struct {
pub fn dispatch_event(et: [*c]c.dom_event_target, evt: ?*c.struct_dom_event, res: [*c]bool) callconv(.C) c.dom_exception {
const self = @as(*Self, @ptrCast(et));
return c._dom_event_target_dispatch(et, &self.eti, evt, c.DOM_BUBBLING_PHASE, res);
// Set the event target to the target dispatched.
const e = c._dom_event_set_target(evt, et);
if (e != c.DOM_NO_ERR) {
return e;
}
return c._dom_event_target_dispatch(et, &self.eti, evt, c.DOM_AT_TARGET, res);
}
pub fn remove_event_listener(et: [*c]c.dom_event_target, t: [*c]c.dom_string, l: ?*c.struct_dom_event_listener, capture: bool) callconv(.C) c.dom_exception {

View File

@@ -28,7 +28,6 @@ pub const Interfaces = generate.Tuple(.{
XMLHttpRequestEventTarget,
XMLHttpRequestUpload,
XMLHttpRequest,
ProgressEvent,
});
pub const XMLHttpRequestEventTarget = struct {
@@ -144,6 +143,7 @@ pub const ProgressEvent = struct {
const event = try parser.eventCreate();
defer parser.eventDestroy(event);
try parser.eventInit(event, eventType, .{});
try parser.eventSetInternalType(event, .progress_event);
const o = opts orelse EventInit{};
@@ -476,7 +476,6 @@ pub const XMLHttpRequest = struct {
self.dispatchEvt("readystatechange");
// dispatch a progress event load.
self.dispatchEvt("load");
self.dispatchProgressEvent("load", .{ .loaded = loaded, .total = total });
// dispatch a progress event loadend.
self.dispatchProgressEvent("loadend", .{ .loaded = loaded, .total = total });
@@ -585,7 +584,9 @@ pub fn testExecFn(
// Each case executed waits for all loop callaback calls.
// So the url has been retrieved.
.{ .src = "nb", .ex = "1" },
// .{ .src = "evt.__proto__.constructor.name", .ex = "ProgressEvent" },
.{ .src = "evt.type", .ex = "load" },
.{ .src = "evt.loaded > 0", .ex = "true" },
.{ .src = "evt instanceof ProgressEvent", .ex = "true" },
.{ .src = "req.status", .ex = "200" },
.{ .src = "req.statusText", .ex = "OK" },
.{ .src = "req.getResponseHeader('Content-Type')", .ex = "text/html; charset=UTF-8" },
@@ -597,7 +598,12 @@ pub fn testExecFn(
var progress_event = [_]Case{
.{ .src = "let pevt = new ProgressEvent('foo');", .ex = "undefined" },
.{ .src = "pevt.loaded", .ex = "0" },
.{ .src = "pevt.__proto__.constructor.name", .ex = "ProgressEvent" },
.{ .src = "pevt instanceof ProgressEvent", .ex = "true" },
.{ .src = "var nnb = 0; var eevt = null; function ccbk(event) { nnb ++; eevt = event; }", .ex = "undefined" },
.{ .src = "document.addEventListener('foo', ccbk)", .ex = "undefined" },
.{ .src = "document.dispatchEvent(pevt)", .ex = "true" },
.{ .src = "eevt.type", .ex = "foo" },
.{ .src = "eevt instanceof ProgressEvent", .ex = "true" },
};
try checkCases(js_env, &progress_event);
}