Skip to content

Commit 2b6cf95

Browse files
committed
Add document.readyState
To support this, add the ability to embedded data into libdom nodes, so that we can extend libdom without having to alter it.
1 parent a99d193 commit 2b6cf95

File tree

5 files changed

+67
-10
lines changed

5 files changed

+67
-10
lines changed

src/browser/env.zig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,16 @@ pub const SessionState = struct {
6262
// shorter-lived than the arena above, which
6363
// exists for the entire rendering of the page
6464
call_arena: std.mem.Allocator = undefined,
65+
66+
pub fn getNodeWrapper(self: *SessionState, comptime T: type, node: *parser.Node) !*T {
67+
if (parser.nodeGetEmbedderData(node)) |wrap| {
68+
return @alignCast(@ptrCast(wrap));
69+
}
70+
71+
const wrap = try self.arena.create(T);
72+
wrap.* = T{};
73+
74+
parser.nodeSetEmbedderData(node, wrap);
75+
return wrap;
76+
}
6577
};

src/browser/html/document.zig

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ pub const HTMLDocument = struct {
3636
pub const prototype = *Document;
3737
pub const subtype = .node;
3838

39+
ready_state: ReadyState = .loading,
40+
41+
const ReadyState = enum {
42+
loading,
43+
interactive,
44+
complete,
45+
};
46+
3947
// JS funcs
4048
// --------
4149

@@ -176,6 +184,11 @@ pub const HTMLDocument = struct {
176184
return state.window;
177185
}
178186

187+
pub fn get_readyState(node: *parser.DocumentHTML, state: *SessionState) ![]const u8 {
188+
const self = try state.getNodeWrapper(HTMLDocument, @ptrCast(node));
189+
return @tagName(self.ready_state);
190+
}
191+
179192
// noop legacy functions
180193
// https://html.spec.whatwg.org/#Document-partial
181194
pub fn _clear(_: *parser.DocumentHTML) void {}
@@ -212,6 +225,22 @@ pub const HTMLDocument = struct {
212225
pub fn set_bgColor(_: *parser.DocumentHTML, _: []const u8) []const u8 {
213226
return "";
214227
}
228+
229+
pub fn documentIsLoaded(html_doc: *parser.DocumentHTML, state: *SessionState) !void {
230+
const self = try state.getNodeWrapper(HTMLDocument, @ptrCast(html_doc));
231+
self.ready_state = .interactive;
232+
233+
const evt = try parser.eventCreate();
234+
defer parser.eventDestroy(evt);
235+
236+
try parser.eventInit(evt, "DOMContentLoaded", .{ .bubbles = true, .cancelable = true });
237+
_ = try parser.eventTargetDispatchEvent(parser.toEventTarget(parser.DocumentHTML, html_doc), evt);
238+
}
239+
240+
pub fn documentIsComplete(html_doc: *parser.DocumentHTML, state: *SessionState) !void {
241+
const self = try state.getNodeWrapper(HTMLDocument, @ptrCast(html_doc));
242+
self.ready_state = .complete;
243+
}
215244
};
216245

217246
// Tests
@@ -276,4 +305,18 @@ test "Browser.HTML.Document" {
276305
try runner.testCases(&.{
277306
.{ "document.defaultView.document == document", "true" },
278307
}, .{});
308+
309+
try runner.testCases(&.{
310+
.{ "document.readyState", "loading" },
311+
}, .{});
312+
313+
try HTMLDocument.documentIsLoaded(runner.state.document.?, &runner.state);
314+
try runner.testCases(&.{
315+
.{ "document.readyState", "interactive" },
316+
}, .{});
317+
318+
try HTMLDocument.documentIsComplete(runner.state.document.?, &runner.state);
319+
try runner.testCases(&.{
320+
.{ "document.readyState", "complete" },
321+
}, .{});
279322
}

src/browser/netsurf.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,14 @@ pub fn nodeGetPrefix(node: *Node) !?[]const u8 {
12741274
return strToData(s.?);
12751275
}
12761276

1277+
pub fn nodeGetEmbedderData(node: *Node) ?*anyopaque {
1278+
return c._dom_node_get_embedder_data(node);
1279+
}
1280+
1281+
pub fn nodeSetEmbedderData(node: *Node, data: *anyopaque) void {
1282+
c._dom_node_set_embedder_data(node, data);
1283+
}
1284+
12771285
// nodeToElement is an helper to convert a node to an element.
12781286
pub inline fn nodeToElement(node: *Node) *Element {
12791287
return @as(*Element, @ptrCast(node));

src/browser/page.zig

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const Window = @import("html/window.zig").Window;
3131
const Walker = @import("dom/walker.zig").WalkerDepthFirst;
3232
const Env = @import("env.zig").Env;
3333
const Loop = @import("../runtime/loop.zig").Loop;
34+
const HTMLDocument = @import("html/document.zig").HTMLDocument;
3435

3536
const URL = @import("../url.zig").URL;
3637

@@ -351,16 +352,11 @@ pub const Page = struct {
351352
self.evalScript(&s) catch |err| log.warn("evaljs: {any}", .{err});
352353
try parser.documentHTMLSetCurrentScript(html_doc, null);
353354
}
354-
355355
// dispatch DOMContentLoaded before the transition to "complete",
356356
// at the point where all subresources apart from async script elements
357357
// have loaded.
358358
// https://html.spec.whatwg.org/#reporting-document-loading-status
359-
const evt = try parser.eventCreate();
360-
defer parser.eventDestroy(evt);
361-
362-
try parser.eventInit(evt, "DOMContentLoaded", .{ .bubbles = true, .cancelable = true });
363-
_ = try parser.eventTargetDispatchEvent(parser.toEventTarget(parser.DocumentHTML, html_doc), evt);
359+
try HTMLDocument.documentIsLoaded(html_doc, &self.state);
364360

365361
// eval async scripts.
366362
for (async_scripts.items) |s| {
@@ -369,9 +365,7 @@ pub const Page = struct {
369365
try parser.documentHTMLSetCurrentScript(html_doc, null);
370366
}
371367

372-
// TODO wait for async scripts
373-
374-
// TODO set document.readyState to complete
368+
try HTMLDocument.documentIsComplete(html_doc, &self.state);
375369

376370
// dispatch window.load event
377371
const loadevt = try parser.eventCreate();

0 commit comments

Comments
 (0)