diff --git a/src/browser/events/event.zig b/src/browser/events/event.zig index 0fa31921..ba6b367d 100644 --- a/src/browser/events/event.zig +++ b/src/browser/events/event.zig @@ -17,6 +17,7 @@ // along with this program. If not, see . const std = @import("std"); +const js = @import("../js/js.zig"); const Allocator = std.mem.Allocator; const log = @import("../../log.zig"); @@ -231,8 +232,6 @@ pub const EventHandler = struct { node: parser.EventNode, listener: *parser.EventListener, - const js = @import("../js/js.zig"); - pub const Listener = union(enum) { function: js.Function, object: js.Object, @@ -404,6 +403,40 @@ const SignalCallback = struct { } }; +pub fn DirectEventHandler( + comptime TargetT: type, + target: *TargetT, + event_type: []const u8, + maybe_listener: ?EventHandler.Listener, + cb: *?js.Function, + page_arena: std.mem.Allocator, +) !void { + const event_target = parser.toEventTarget(TargetT, target); + + // Check if we have a listener set. + if (cb.*) |callback| { + const listener = try parser.eventTargetHasListener(event_target, event_type, false, callback.id); + std.debug.assert(listener != null); + try parser.eventTargetRemoveEventListener(event_target, event_type, listener.?, false); + } + + if (maybe_listener) |listener| { + switch (listener) { + // If an object is given as listener, do nothing. + .object => {}, + .function => |callback| { + _ = try EventHandler.register(page_arena, event_target, event_type, listener, null) orelse unreachable; + cb.* = callback; + + return; + }, + } + } + + // Just unset the listener. + cb.* = null; +} + const testing = @import("../../testing.zig"); test "Browser: Event" { try testing.htmlRunner("events/event.html"); diff --git a/src/browser/html/History.zig b/src/browser/html/History.zig index e7b6b866..4412e887 100644 --- a/src/browser/html/History.zig +++ b/src/browser/html/History.zig @@ -177,5 +177,6 @@ pub const PopStateEvent = struct { const testing = @import("../../testing.zig"); test "Browser: HTML.History" { - try testing.htmlRunner("html/history.html"); + try testing.htmlRunner("html/history/history.html"); + try testing.htmlRunner("html/history/history2.html"); } diff --git a/src/browser/html/Navigation.zig b/src/browser/html/Navigation.zig index e16d01ff..defa50c2 100644 --- a/src/browser/html/Navigation.zig +++ b/src/browser/html/Navigation.zig @@ -23,6 +23,7 @@ const URL = @import("../../url.zig").URL; const js = @import("../js/js.zig"); const Page = @import("../page.zig").Page; +const DirectEventHandler = @import("../events/event.zig").DirectEventHandler; const EventTarget = @import("../dom/event_target.zig").EventTarget; const EventHandler = @import("../events/event.zig").EventHandler; @@ -62,6 +63,8 @@ index: usize = 0, entries: std.ArrayListUnmanaged(*NavigationHistoryEntry) = .empty, next_entry_id: usize = 0, +oncurrententrychange_callback: ?js.Function = null, + // https://developer.mozilla.org/en-US/docs/Web/API/NavigationHistoryEntry const NavigationHistoryEntry = struct { pub const prototype = *EventTarget; @@ -199,6 +202,16 @@ pub fn _forward(self: *Navigation, page: *Page) !NavigationReturn { return self.navigate(next_entry.url, .{ .traverse = new_index }, page); } +/// Returns `oncurrententrychange_callback`. +pub fn get_oncurrententrychange(self: *const Navigation) ?js.Function { + return self.oncurrententrychange_callback; +} + +/// Sets `oncurrententrychange_callback`. +pub fn set_oncurrententrychange(self: *Navigation, maybe_listener: ?EventHandler.Listener, page: *Page) !void { + try DirectEventHandler(Navigation, self, "currententrychange", maybe_listener, &self.oncurrententrychange_callback, page.arena); +} + // This is for after true navigation processing, where we need to ensure that our entries are up to date. // This is only really safe to run in the `pageDoneCallback` where we can guarantee that the URL and NavigationKind are correct. pub fn processNavigation(self: *Navigation, page: *Page) !void { @@ -456,5 +469,5 @@ pub const NavigationCurrentEntryChangeEvent = struct { const testing = @import("../../testing.zig"); test "Browser: Navigation" { - try testing.htmlRunner("html/navigation.html"); + try testing.htmlRunner("html/navigation/navigation.html"); } diff --git a/src/browser/html/window.zig b/src/browser/html/window.zig index 3947c526..e89ab203 100644 --- a/src/browser/html/window.zig +++ b/src/browser/html/window.zig @@ -44,6 +44,8 @@ const fetchFn = @import("../fetch/fetch.zig").fetch; const storage = @import("../storage/storage.zig"); const ErrorEvent = @import("error_event.zig").ErrorEvent; +const DirectEventHandler = @import("../events/event.zig").DirectEventHandler; + // https://dom.spec.whatwg.org/#interface-window-extensions // https://html.spec.whatwg.org/multipage/nav-history-apis.html#window pub const Window = struct { @@ -70,6 +72,7 @@ pub const Window = struct { scroll_x: u32 = 0, scroll_y: u32 = 0, onload_callback: ?js.Function = null, + onpopstate_callback: ?js.Function = null, pub fn create(target: ?[]const u8, navigator: ?Navigator) !Window { var fbs = std.io.fixedBufferStream(""); @@ -111,31 +114,17 @@ pub const Window = struct { /// Sets `onload_callback`. pub fn set_onload(self: *Window, maybe_listener: ?EventHandler.Listener, page: *Page) !void { - const event_target = parser.toEventTarget(Window, self); - const event_type = "load"; + try DirectEventHandler(Window, self, "load", maybe_listener, &self.onload_callback, page.arena); + } - // Check if we have a listener set. - if (self.onload_callback) |callback| { - const listener = try parser.eventTargetHasListener(event_target, event_type, false, callback.id); - std.debug.assert(listener != null); - try parser.eventTargetRemoveEventListener(event_target, event_type, listener.?, false); - } + /// Returns `onpopstate_callback`. + pub fn get_onpopstate(self: *const Window) ?js.Function { + return self.onpopstate_callback; + } - if (maybe_listener) |listener| { - switch (listener) { - // If an object is given as listener, do nothing. - .object => {}, - .function => |callback| { - _ = try EventHandler.register(page.arena, event_target, event_type, listener, null) orelse unreachable; - self.onload_callback = callback; - - return; - }, - } - } - - // Just unset the listener. - self.onload_callback = null; + /// Sets `onpopstate_callback`. + pub fn set_onpopstate(self: *Window, maybe_listener: ?EventHandler.Listener, page: *Page) !void { + try DirectEventHandler(Window, self, "popstate", maybe_listener, &self.onpopstate_callback, page.arena); } pub fn get_location(self: *Window) *Location { diff --git a/src/tests/html/history.html b/src/tests/html/history/history.html similarity index 91% rename from src/tests/html/history.html rename to src/tests/html/history/history.html index 60b54b52..fbb7dd95 100644 --- a/src/tests/html/history.html +++ b/src/tests/html/history/history.html @@ -1,5 +1,5 @@ - + + + diff --git a/src/tests/html/history2.html b/src/tests/html/history/history_after_nav.html similarity index 75% rename from src/tests/html/history2.html rename to src/tests/html/history/history_after_nav.html index 735c71e9..d9e4e66d 100644 --- a/src/tests/html/history2.html +++ b/src/tests/html/history/history_after_nav.html @@ -1,5 +1,5 @@ - + + diff --git a/src/tests/html/navigation2.html b/src/tests/html/navigation/navigation2.html similarity index 85% rename from src/tests/html/navigation2.html rename to src/tests/html/navigation/navigation2.html index 3b8ad282..b16fa917 100644 --- a/src/tests/html/navigation2.html +++ b/src/tests/html/navigation/navigation2.html @@ -1,5 +1,5 @@ - +