xhr: dispatch generic events

This commit is contained in:
Pierre Tachoire
2024-02-06 17:42:28 +01:00
parent 8a61f0f454
commit f791891314

View File

@@ -13,6 +13,13 @@ const Loop = jsruntime.Loop;
const YieldImpl = Loop.Yield(XMLHttpRequest); const YieldImpl = Loop.Yield(XMLHttpRequest);
const Client = @import("../async/Client.zig"); const Client = @import("../async/Client.zig");
const parser = @import("../netsurf.zig");
const c = @cImport({
@cInclude("events/event_target.h");
});
const log = std.log.scoped(.xhr);
// XHR interfaces // XHR interfaces
// https://xhr.spec.whatwg.org/#interface-xmlhttprequest // https://xhr.spec.whatwg.org/#interface-xmlhttprequest
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = generate.Tuple(.{
@@ -25,48 +32,42 @@ pub const XMLHttpRequestEventTarget = struct {
pub const prototype = *EventTarget; pub const prototype = *EventTarget;
pub const mem_guarantied = true; pub const mem_guarantied = true;
onloadstart_cbk: ?Callback = null, // Extend libdom event target for pure zig struct.
onprogress_cbk: ?Callback = null, base: parser.EventTargetTBase = parser.EventTargetTBase{},
onabort_cbk: ?Callback = null,
onload_cbk: ?Callback = null,
ontimeout_cbk: ?Callback = null,
onloadend_cbk: ?Callback = null,
pub fn constructor() !XMLHttpRequestEventTarget { fn register(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, typ: []const u8, cbk: Callback) !void {
return .{}; try parser.eventTargetAddEventListener(@as(*parser.EventTarget, @ptrCast(self)), alloc, typ, cbk, false);
} }
pub fn set_onloadstart(self: *XMLHttpRequestEventTarget, handler: Callback) void { pub fn set_onloadstart(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, handler: Callback) !void {
self.onloadstart_cbk = handler; try self.register(alloc, "loadstart", handler);
} }
pub fn set_onprogress(self: *XMLHttpRequestEventTarget, handler: Callback) void { pub fn set_onprogress(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, handler: Callback) !void {
self.onprogress_cbk = handler; try self.register(alloc, "progress", handler);
} }
pub fn set_onabort(self: *XMLHttpRequestEventTarget, handler: Callback) void { pub fn set_onabort(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, handler: Callback) !void {
self.onabort_cbk = handler; try self.register(alloc, "abort", handler);
} }
// TODO remove-me, test func du to an issue w/ the setter. // TODO remove-me, test func du to an issue w/ the setter.
// see https://lightpanda.slack.com/archives/C05TRU6RBM1/p1706708213838989 // see https://lightpanda.slack.com/archives/C05TRU6RBM1/p1706708213838989
pub fn _setOnload(self: *XMLHttpRequestEventTarget, handler: Callback) void { pub fn _setOnload(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, handler: Callback) !void {
self.set_onload(handler); try self.set_onload(alloc, handler);
} }
pub fn set_onload(self: *XMLHttpRequestEventTarget, handler: Callback) void {
self.onload_cbk = handler; pub fn set_onload(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, handler: Callback) !void {
try self.register(alloc, "load", handler);
} }
pub fn set_ontimeout(self: *XMLHttpRequestEventTarget, handler: Callback) void { pub fn set_ontimeout(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, handler: Callback) !void {
self.ontimeout_cbk = handler; try self.register(alloc, "timeout", handler);
} }
pub fn set_onloadend(self: *XMLHttpRequestEventTarget, handler: Callback) void { pub fn set_onloadend(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, handler: Callback) !void {
self.onloadend_cbk = handler; try self.register(alloc, "loadend", handler);
} }
pub fn deinit(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator) void { pub fn deinit(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator) void {
if (self.onloadstart_cbk) |cbk| cbk.deinit(alloc); parser.eventTargetRemoveAllEventListeners(@as(*parser.EventTarget, @ptrCast(self)), alloc) catch |e| {
if (self.onprogress_cbk) |cbk| cbk.deinit(alloc); log.err("remove all listeners: {any}", .{e});
if (self.onabort_cbk) |cbk| cbk.deinit(alloc); };
if (self.onload_cbk) |cbk| cbk.deinit(alloc);
if (self.ontimeout_cbk) |cbk| cbk.deinit(alloc);
if (self.onloadend_cbk) |cbk| cbk.deinit(alloc);
} }
}; };
@@ -74,7 +75,7 @@ pub const XMLHttpRequestUpload = struct {
pub const prototype = *XMLHttpRequestEventTarget; pub const prototype = *XMLHttpRequestEventTarget;
pub const mem_guarantied = true; pub const mem_guarantied = true;
proto: XMLHttpRequestEventTarget, proto: XMLHttpRequestEventTarget = XMLHttpRequestEventTarget{},
}; };
pub const XMLHttpRequest = struct { pub const XMLHttpRequest = struct {
@@ -99,7 +100,7 @@ pub const XMLHttpRequest = struct {
const PrivState = enum { new, open, send, finish, wait, done }; const PrivState = enum { new, open, send, finish, wait, done };
proto: XMLHttpRequestEventTarget, proto: XMLHttpRequestEventTarget = XMLHttpRequestEventTarget{},
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
cli: Client, cli: Client,
impl: YieldImpl, impl: YieldImpl,
@@ -128,7 +129,6 @@ pub const XMLHttpRequest = struct {
pub fn constructor(alloc: std.mem.Allocator, loop: *Loop) !XMLHttpRequest { pub fn constructor(alloc: std.mem.Allocator, loop: *Loop) !XMLHttpRequest {
return .{ return .{
.alloc = alloc, .alloc = alloc,
.proto = try XMLHttpRequestEventTarget.constructor(),
.headers = .{ .allocator = alloc, .owned = true }, .headers = .{ .allocator = alloc, .owned = true },
.response_headers = .{ .allocator = alloc, .owned = true }, .response_headers = .{ .allocator = alloc, .owned = true },
.impl = YieldImpl.init(loop), .impl = YieldImpl.init(loop),
@@ -218,6 +218,22 @@ pub const XMLHttpRequest = struct {
r.deinit(); r.deinit();
self.req = null; self.req = null;
} }
self.dispatchEvt("readystatechange");
}
// dispatch request event.
// errors are logged only.
fn dispatchEvt(self: *XMLHttpRequest, typ: []const u8) void {
const evt = parser.eventCreate() catch |e| {
return log.err("dispatch event create: {any}", .{e});
};
parser.eventInit(evt, typ, .{ .bubbles = true, .cancelable = true }) catch |e| {
return log.err("dispatch event init: {any}", .{e});
};
_ = parser.eventTargetDispatchEvent(@as(*parser.EventTarget, @ptrCast(self)), evt) catch |e| {
return log.err("dispatch event: {any}", .{e});
};
} }
const methods = [_]struct { const methods = [_]struct {
@@ -300,13 +316,14 @@ pub const XMLHttpRequest = struct {
self.response_headers = self.req.?.response.headers.clone(self.response_headers.allocator) catch |e| return self.onerr(e); self.response_headers = self.req.?.response.headers.clone(self.response_headers.allocator) catch |e| return self.onerr(e);
self.state = HEADERS_RECEIVED; self.state = HEADERS_RECEIVED;
self.dispatchEvt("readystatechange");
self.response_status = @intFromEnum(self.req.?.response.status); self.response_status = @intFromEnum(self.req.?.response.status);
self.state = LOADING;
var buf: std.ArrayListUnmanaged(u8) = .{}; var buf: std.ArrayListUnmanaged(u8) = .{};
// TODO dispatch a progress event loadstart.
const reader = self.req.?.reader(); const reader = self.req.?.reader();
var buffer: [1024]u8 = undefined; var buffer: [1024]u8 = undefined;
var ln = buffer.len; var ln = buffer.len;
@@ -319,10 +336,25 @@ pub const XMLHttpRequest = struct {
buf.deinit(self.alloc); buf.deinit(self.alloc);
return self.onerr(e); return self.onerr(e);
}; };
// TODO dispatch only if 50ms have passed.
self.state = LOADING;
self.dispatchEvt("readystatechange");
// TODO dispatch a progress event progress.
self.dispatchEvt("progress");
} }
self.response_bytes = buf.items; self.response_bytes = buf.items;
self.send_flag = false;
self.state = DONE; self.state = DONE;
self.dispatchEvt("readystatechange");
// TODO dispatch a progress event load.
self.dispatchEvt("load");
// TODO dispatch a progress event loadend.
self.dispatchEvt("loadend");
}, },
.done => { .done => {
if (self.req) |*r| { if (self.req) |*r| {
@@ -330,14 +362,6 @@ pub const XMLHttpRequest = struct {
self.req = null; self.req = null;
} }
// TODO use events instead
if (self.proto.onload_cbk) |cbk| {
// TODO pass an EventProgress
cbk.call(null) catch |e| {
std.debug.print("--- CALLBACK ERROR: {any}\n", .{e});
}; // TODO handle error
}
// finalize fetch process. // finalize fetch process.
return; return;
}, },
@@ -347,13 +371,17 @@ pub const XMLHttpRequest = struct {
} }
fn onerr(self: *XMLHttpRequest, err: anyerror) void { fn onerr(self: *XMLHttpRequest, err: anyerror) void {
self.err = err;
self.state = DONE;
self.priv_state = .done; self.priv_state = .done;
if (self.req) |*r| { if (self.req) |*r| {
r.deinit(); r.deinit();
self.req = null; self.req = null;
} }
self.err = err;
self.state = DONE;
self.send_flag = false;
self.dispatchEvt("readystatechange");
self.dispatchEvt("error");
} }
pub fn get_responseText(self: *XMLHttpRequest) ![]const u8 { pub fn get_responseText(self: *XMLHttpRequest) ![]const u8 {