diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 18e37cec..00dbc9c2 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -313,10 +313,13 @@ pub const Page = struct { // TODO wait for deferred scripts - // TODO dispatch DOMContentLoaded before the transition to "complete", + // dispatch DOMContentLoaded before the transition to "complete", // at the point where all subresources apart from async script elements // have loaded. // https://html.spec.whatwg.org/#reporting-document-loading-status + const evt = try parser.eventCreate(); + try parser.eventInit(evt, "DOMContentLoaded", .{ .bubbles = true, .cancelable = true }); + _ = try parser.eventTargetDispatchEvent(parser.toEventTarget(parser.DocumentHTML, html_doc), evt); // eval async scripts. for (sasync.items) |e| { @@ -326,6 +329,11 @@ pub const Page = struct { // TODO wait for async scripts // TODO set document.readyState to complete + + // dispatch window.load event + const loadevt = try parser.eventCreate(); + try parser.eventInit(loadevt, "load", .{}); + _ = try parser.eventTargetDispatchEvent(parser.toEventTarget(Window, &self.session.window), loadevt); } // evalScript evaluates the src in priority. diff --git a/src/html/window.zig b/src/html/window.zig index 5c844a49..b61c11f0 100644 --- a/src/html/window.zig +++ b/src/html/window.zig @@ -10,6 +10,9 @@ pub const Window = struct { pub const prototype = *EventTarget; pub const mem_guarantied = true; + // Extend libdom event target for pure zig struct. + base: parser.EventTargetTBase = parser.EventTargetTBase{}, + document: ?*parser.Document = null, target: []const u8, @@ -42,6 +45,4 @@ pub const Window = struct { pub fn get_name(self: *Window) []const u8 { return self.target; } - - // TODO we need to re-implement EventTarget interface. }; diff --git a/src/netsurf.zig b/src/netsurf.zig index 7b40363c..035912d7 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -3,6 +3,7 @@ const std = @import("std"); const c = @cImport({ @cInclude("dom/dom.h"); @cInclude("dom/bindings/hubbub/parser.h"); + @cInclude("events/event_target.h"); }); const Callback = @import("jsruntime").Callback; @@ -476,6 +477,11 @@ fn eventTargetVtable(et: *EventTarget) c.dom_event_target_vtable { return getVtable(c.dom_event_target_vtable, EventTarget, et); } +pub inline fn toEventTarget(comptime T: type, v: *T) *EventTarget { + const et_aligned: *align(@alignOf(EventTarget)) T = @alignCast(v); + return @as(*EventTarget, @ptrCast(et_aligned)); +} + pub fn eventTargetHasListener( et: *EventTarget, typ: []const u8, @@ -622,6 +628,46 @@ pub fn eventTargetDispatchEvent(et: *EventTarget, event: *Event) !bool { return res; } +// EventTargetBase is used to implement EventTarget for pure zig struct. +pub const EventTargetTBase = struct { + const Self = @This(); + + vtable: ?*const c.struct_dom_event_target_vtable = &c.struct_dom_event_target_vtable{ + .dispatch_event = dispatch_event, + .remove_event_listener = remove_event_listener, + .add_event_listener = add_event_listener, + .iter_event_listener = iter_event_listener, + }, + eti: c.dom_event_target_internal = c.dom_event_target_internal{ .listeners = null }, + + pub fn add_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 { + const self = @as(*Self, @ptrCast(et)); + return c._dom_event_target_add_event_listener(&self.eti, t, l, capture); + } + + 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); + } + + 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 { + const self = @as(*Self, @ptrCast(et)); + return c._dom_event_target_remove_event_listener(&self.eti, t, l, capture); + } + + pub fn iter_event_listener( + et: [*c]c.dom_event_target, + t: [*c]c.dom_string, + capture: bool, + cur: [*c]c.struct_listener_entry, + next: [*c][*c]c.struct_listener_entry, + l: [*c]?*c.struct_dom_event_listener, + ) callconv(.C) c.dom_exception { + const self = @as(*Self, @ptrCast(et)); + return c._dom_event_target_iter_event_listener(self.eti, t, capture, cur, next, l); + } +}; + // NodeType pub const NodeType = enum(u4) { diff --git a/src/run_tests.zig b/src/run_tests.zig index 22adffe9..ae5a247e 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -6,6 +6,7 @@ const generate = @import("generate.zig"); const parser = @import("netsurf.zig"); const apiweb = @import("apiweb.zig"); +const Window = @import("html/window.zig").Window; const documentTestExecFn = @import("dom/document.zig").testExecFn; const HTMLDocumentTestExecFn = @import("html/document.zig").testExecFn; @@ -135,3 +136,13 @@ test "run browser tests" { try dump.HTMLFileTestFn(out); } + +test "Window is a libdom event target" { + var window = Window.create(null); + + const event = try parser.eventCreate(); + try parser.eventInit(event, "foo", .{}); + + const et = @as(*parser.EventTarget, @ptrCast(&window)); + _ = try parser.eventTargetDispatchEvent(et, event); +} diff --git a/src/wpt/run.zig b/src/wpt/run.zig index ba8c474f..ae7e18e2 100644 --- a/src/wpt/run.zig +++ b/src/wpt/run.zig @@ -40,12 +40,19 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const try js_env.start(alloc); defer js_env.stop(); - // add document object - try js_env.addObject(html_doc, "document"); + // display console logs + defer { + var res = evalJS(js_env, alloc, "console.join('\\n');", "console") catch unreachable; + defer res.deinit(alloc); + if (res.result.len > 0) { + std.debug.print("-- CONSOLE LOG\n{s}\n--\n", .{res.result}); + } + } - // alias global as self and window + // setup global env vars. try js_env.attachObject(try js_env.getGlobal(), "self", null); try js_env.attachObject(try js_env.getGlobal(), "window", null); + try js_env.addObject(html_doc, "document"); // thanks to the arena, we don't need to deinit res. var res: jsruntime.JSResult = undefined; @@ -114,12 +121,6 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const return res; } - // display console logs - res = try evalJS(js_env, alloc, "console.join(', ');", "console"); - if (res.result.len > 0) { - std.debug.print("-- CONSOLE LOG\n{s}\n--\n", .{res.result}); - } - // Check the final test status. res = try evalJS(js_env, alloc, "report.status;", "teststatus"); if (!res.success) { @@ -148,7 +149,7 @@ pub fn find(allocator: std.mem.Allocator, comptime path: []const u8, list: *std. if (entry.kind != .file) { continue; } - if (!std.mem.endsWith(u8, entry.basename, ".html")) { + if (!std.mem.endsWith(u8, entry.basename, ".html") and !std.mem.endsWith(u8, entry.basename, ".htm")) { continue; }