Skip to content

Commit 300428d

Browse files
authored
Merge pull request #840 from lightpanda-io/xhr_readystatechange
Add readystate change event to XHR
2 parents 1c27f82 + c331713 commit 300428d

File tree

6 files changed

+75
-13
lines changed

6 files changed

+75
-13
lines changed

src/browser/dom/event_target.zig

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ const Page = @import("../page.zig").Page;
2323
const EventHandler = @import("../events/event.zig").EventHandler;
2424

2525
const DOMException = @import("exceptions.zig").DOMException;
26-
const Nod = @import("node.zig");
26+
const nod = @import("node.zig");
2727

28-
// EventTarget interfaces
29-
pub const Union = Nod.Union;
28+
pub const Union = union(enum) {
29+
node: nod.Union,
30+
xhr: *@import("../xhr/xhr.zig").XMLHttpRequest,
31+
};
3032

3133
// EventTarget implementation
3234
pub const EventTarget = struct {
@@ -39,18 +41,22 @@ pub const EventTarget = struct {
3941
// The window is a common non-node target, but it's easy to handle as
4042
// its a singleton.
4143
if (@intFromPtr(et) == @intFromPtr(&page.window.base)) {
42-
return .{ .Window = &page.window };
44+
return .{ .node = .{ .Window = &page.window } };
4345
}
4446

4547
// AbortSignal is another non-node target. It has a distinct usage though
4648
// so we hijack the event internal type to identity if.
4749
switch (try parser.eventGetInternalType(e)) {
4850
.abort_signal => {
49-
return .{ .AbortSignal = @fieldParentPtr("proto", @as(*parser.EventTargetTBase, @ptrCast(et))) };
51+
return .{ .node = .{ .AbortSignal = @fieldParentPtr("proto", @as(*parser.EventTargetTBase, @ptrCast(et))) } };
52+
},
53+
.xhr_event => {
54+
const XMLHttpRequestEventTarget = @import("../xhr/event_target.zig").XMLHttpRequestEventTarget;
55+
const base: *XMLHttpRequestEventTarget = @fieldParentPtr("base", @as(*parser.EventTargetTBase, @ptrCast(et)));
56+
return .{ .xhr = @fieldParentPtr("proto", base) };
5057
},
5158
else => {
52-
// some of these probably need to be special-cased like abort_signal
53-
return Nod.Node.toInterface(@as(*parser.Node, @ptrCast(et)));
59+
return .{ .node = try nod.Node.toInterface(@as(*parser.Node, @ptrCast(et))) };
5460
},
5561
}
5662
}

src/browser/events/event.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub const Event = struct {
5454

5555
pub fn toInterface(evt: *parser.Event) !Union {
5656
return switch (try parser.eventGetInternalType(evt)) {
57-
.event, .abort_signal => .{ .Event = evt },
57+
.event, .abort_signal, .xhr_event => .{ .Event = evt },
5858
.custom_event => .{ .CustomEvent = @as(*CustomEvent, @ptrCast(evt)).* },
5959
.progress_event => .{ .ProgressEvent = @as(*ProgressEvent, @ptrCast(evt)).* },
6060
.mouse_event => .{ .MouseEvent = @as(*parser.MouseEvent, @ptrCast(evt)) },

src/browser/netsurf.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ pub const EventType = enum(u8) {
527527
mouse_event = 3,
528528
error_event = 4,
529529
abort_signal = 5,
530+
xhr_event = 6,
530531
};
531532

532533
pub const MutationEvent = c.dom_mutation_event;

src/browser/page.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,7 @@ const Script = struct {
10171017

10181018
const src: []const u8 = blk: {
10191019
const s = self.src orelse break :blk page.url.raw;
1020-
break :blk try URL.stitch(page.arena, s, page.url.raw, .{.alloc = .if_needed});
1020+
break :blk try URL.stitch(page.arena, s, page.url.raw, .{ .alloc = .if_needed });
10211021
};
10221022

10231023
// if self.src is null, then this is an inline script, and it should

src/browser/xhr/event_target.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub const XMLHttpRequestEventTarget = struct {
3939
onload_cbk: ?Function = null,
4040
ontimeout_cbk: ?Function = null,
4141
onloadend_cbk: ?Function = null,
42+
onreadystatechange_cbk: ?Function = null,
4243

4344
fn register(
4445
self: *XMLHttpRequestEventTarget,
@@ -86,6 +87,9 @@ pub const XMLHttpRequestEventTarget = struct {
8687
pub fn get_onloadend(self: *XMLHttpRequestEventTarget) ?Function {
8788
return self.onloadend_cbk;
8889
}
90+
pub fn get_onreadystatechange(self: *XMLHttpRequestEventTarget) ?Function {
91+
return self.onreadystatechange_cbk;
92+
}
8993

9094
pub fn set_onloadstart(self: *XMLHttpRequestEventTarget, listener: EventHandler.Listener, page: *Page) !void {
9195
if (self.onloadstart_cbk) |cbk| try self.unregister("loadstart", cbk.id);
@@ -111,4 +115,8 @@ pub const XMLHttpRequestEventTarget = struct {
111115
if (self.onloadend_cbk) |cbk| try self.unregister("loadend", cbk.id);
112116
self.onloadend_cbk = try self.register(page.arena, "loadend", listener);
113117
}
118+
pub fn set_onreadystatechange(self: *XMLHttpRequestEventTarget, listener: EventHandler.Listener, page: *Page) !void {
119+
if (self.onreadystatechange_cbk) |cbk| try self.unregister("readystatechange", cbk.id);
120+
self.onreadystatechange_cbk = try self.register(page.arena, "readystatechange", listener);
121+
}
114122
};

src/browser/xhr/xhr.zig

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,13 @@ pub const XMLHttpRequest = struct {
138138
done = 4,
139139
};
140140

141+
// class attributes
142+
pub const _UNSENT = @intFromEnum(State.unsent);
143+
pub const _OPENED = @intFromEnum(State.opened);
144+
pub const _HEADERS_RECEIVED = @intFromEnum(State.headers_received);
145+
pub const _LOADING = @intFromEnum(State.loading);
146+
pub const _DONE = @intFromEnum(State.done);
147+
141148
// https://xhr.spec.whatwg.org/#response-type
142149
const ResponseType = enum {
143150
Empty,
@@ -360,6 +367,8 @@ pub const XMLHttpRequest = struct {
360367
// We can we defer event destroy once the event is dispatched.
361368
defer parser.eventDestroy(evt);
362369

370+
try parser.eventSetInternalType(evt, .xhr_event);
371+
363372
try parser.eventInit(evt, typ, .{ .bubbles = true, .cancelable = true });
364373
_ = try parser.eventTargetDispatchEvent(@as(*parser.EventTarget, @ptrCast(self)), evt);
365374
}
@@ -579,11 +588,27 @@ pub const XMLHttpRequest = struct {
579588
}
580589

581590
fn onErr(self: *XMLHttpRequest, err: anyerror) void {
582-
self.state = .done;
583591
self.send_flag = false;
584-
self.dispatchEvt("readystatechange");
585-
self.dispatchProgressEvent("error", .{});
586-
self.dispatchProgressEvent("loadend", .{});
592+
593+
// capture the state before we change it
594+
const s = self.state;
595+
596+
const is_abort = err == DOMError.Abort;
597+
598+
if (is_abort) {
599+
self.state = .unsent;
600+
} else {
601+
self.state = .done;
602+
self.dispatchEvt("error");
603+
}
604+
605+
if (s != .done or s != .unsent) {
606+
self.dispatchEvt("readystatechange");
607+
if (is_abort) {
608+
self.dispatchProgressEvent("abort", .{});
609+
}
610+
self.dispatchProgressEvent("loadend", .{});
611+
}
587612

588613
const level: log.Level = if (err == DOMError.Abort) .debug else .err;
589614
log.log(.http, level, "error", .{
@@ -922,4 +947,26 @@ test "Browser.XHR.XMLHttpRequest" {
922947
// So the url has been retrieved.
923948
.{ "status", "200" },
924949
}, .{});
950+
951+
try runner.testCases(&.{
952+
.{ "const req6 = new XMLHttpRequest()", null },
953+
.{
954+
\\ var readyStates = [];
955+
\\ var currentTarget = null;
956+
\\ req6.onreadystatechange = (e) => {
957+
\\ currentTarget = e.currentTarget;
958+
\\ readyStates.push(req6.readyState);
959+
\\ }
960+
,
961+
null,
962+
},
963+
.{ "req6.open('GET', 'https://127.0.0.1:9581/xhr')", null },
964+
.{ "req6.send()", null },
965+
.{ "readyStates.length", "4" },
966+
.{ "readyStates[0] === XMLHttpRequest.OPENED", "true" },
967+
.{ "readyStates[1] === XMLHttpRequest.HEADERS_RECEIVED", "true" },
968+
.{ "readyStates[2] === XMLHttpRequest.LOADING", "true" },
969+
.{ "readyStates[3] === XMLHttpRequest.DONE", "true" },
970+
.{ "currentTarget == req6", "true" },
971+
}, .{});
925972
}

0 commit comments

Comments
 (0)