From 9050f2586bb1af37ff51c738f4e4867984f5c0a0 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 17 Jan 2024 18:17:40 +0100 Subject: [PATCH 1/9] events: add EventTargetBase EventTargetBase implements event target vtable for pure zig struct --- src/html/window.zig | 8 ++++++-- src/netsurf.zig | 28 ++++++++++++++++++++++++++++ src/run_tests.zig | 11 +++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/html/window.zig b/src/html/window.zig index 5c844a49..cba2337a 100644 --- a/src/html/window.zig +++ b/src/html/window.zig @@ -1,6 +1,9 @@ const std = @import("std"); const parser = @import("../netsurf.zig"); +const c = @cImport({ + @cInclude("events/event_target.h"); +}); const EventTarget = @import("../dom/event_target.zig").EventTarget; @@ -10,6 +13,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 +48,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..a950d1a7 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; @@ -622,6 +623,33 @@ 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, + }, + 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_add_event_listener(&self.eti, t, l, capture); + } +}; + // 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); +} From 5aef5a21ac6486542d6d217a690f99cecb2dd84a Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 5 Feb 2024 16:40:35 +0100 Subject: [PATCH 2/9] browser: dispatch DOMContentLoaded event --- src/browser/browser.zig | 5 ++++- src/netsurf.zig | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 18e37cec..5c0f4535 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| { diff --git a/src/netsurf.zig b/src/netsurf.zig index a950d1a7..96097daa 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -477,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 node_aligned: *align(@alignOf(NodeExternal)) T = @alignCast(v); + return @as(*EventTarget, @ptrCast(node_aligned)); +} + pub fn eventTargetHasListener( et: *EventTarget, typ: []const u8, From 25d4aedb1dcab0351f9d0ecd2e28d709cc97cc00 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 12 Feb 2024 09:38:19 +0100 Subject: [PATCH 3/9] events: add mssing iter_event_listener in EventTargetTBase --- src/netsurf.zig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/netsurf.zig b/src/netsurf.zig index 96097daa..d66f93fb 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -636,6 +636,7 @@ pub const EventTargetTBase = struct { .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 }, @@ -653,6 +654,18 @@ pub const EventTargetTBase = struct { const self = @as(*Self, @ptrCast(et)); return c._dom_event_target_add_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 From 951bf4b501905ed76144bce02bcf0865ddf3d951 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 12 Feb 2024 09:38:58 +0100 Subject: [PATCH 4/9] events: fix remove_event_listener typo --- src/netsurf.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/netsurf.zig b/src/netsurf.zig index d66f93fb..4b693a7c 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -652,7 +652,7 @@ pub const EventTargetTBase = struct { 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_add_event_listener(&self.eti, t, l, capture); + return c._dom_event_target_remove_event_listener(&self.eti, t, l, capture); } pub fn iter_event_listener( From 5619ed020c4d1b451fa91da981111bc5ec605a48 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 12 Feb 2024 12:12:13 +0100 Subject: [PATCH 5/9] browser: dispatch window.load event --- src/browser/browser.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 5c0f4535..00dbc9c2 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -329,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. From e4b4719208b6cacb36f0771443ec9957bc5ecb9b Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 12 Feb 2024 12:12:42 +0100 Subject: [PATCH 6/9] wpt: defer console log display --- src/wpt/run.zig | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/wpt/run.zig b/src/wpt/run.zig index ba8c474f..6dd23562 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) { From fe31d81e29b0c011f17beb00ce76f5bc56a9f492 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 12 Feb 2024 12:12:54 +0100 Subject: [PATCH 7/9] wpt: allow .htm tests cases --- src/wpt/run.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wpt/run.zig b/src/wpt/run.zig index 6dd23562..ae7e18e2 100644 --- a/src/wpt/run.zig +++ b/src/wpt/run.zig @@ -149,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; } From 25a971c440ab73e66f76e07bcbec1a24a4d4434b Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Thu, 15 Feb 2024 14:06:49 +0100 Subject: [PATCH 8/9] window: remove useless import --- src/html/window.zig | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/html/window.zig b/src/html/window.zig index cba2337a..b61c11f0 100644 --- a/src/html/window.zig +++ b/src/html/window.zig @@ -1,9 +1,6 @@ const std = @import("std"); const parser = @import("../netsurf.zig"); -const c = @cImport({ - @cInclude("events/event_target.h"); -}); const EventTarget = @import("../dom/event_target.zig").EventTarget; From ae28d9d6a4082ee6c802ff25b93bc49e0b7109ed Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Thu, 15 Feb 2024 14:28:14 +0100 Subject: [PATCH 9/9] netsurf: use the right type for EventTarget cast --- src/netsurf.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/netsurf.zig b/src/netsurf.zig index 4b693a7c..035912d7 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -478,8 +478,8 @@ fn eventTargetVtable(et: *EventTarget) c.dom_event_target_vtable { } pub inline fn toEventTarget(comptime T: type, v: *T) *EventTarget { - const node_aligned: *align(@alignOf(NodeExternal)) T = @alignCast(v); - return @as(*EventTarget, @ptrCast(node_aligned)); + const et_aligned: *align(@alignOf(EventTarget)) T = @alignCast(v); + return @as(*EventTarget, @ptrCast(et_aligned)); } pub fn eventTargetHasListener(