@@ -39,6 +39,8 @@ pub const Interfaces = .{
3939};
4040
4141pub const NavigationType = enum {
42+ pub const ENUM_JS_USE_TAG = true ;
43+
4244 push ,
4345 replace ,
4446 traverse ,
@@ -56,7 +58,8 @@ pub const prototype = *EventTarget;
5658base : parser.EventTargetTBase = parser.EventTargetTBase { .internal_target_type = .plain },
5759
5860index : usize = 0 ,
59- entries : std .ArrayListUnmanaged (NavigationHistoryEntry ) = .empty ,
61+ // Need to be stable pointers, because Events can reference entries.
62+ entries : std .ArrayListUnmanaged (* NavigationHistoryEntry ) = .empty ,
6063next_entry_id : usize = 0 ,
6164
6265// https://developer.mozilla.org/en-US/docs/Web/API/NavigationHistoryEntry
@@ -75,7 +78,7 @@ const NavigationHistoryEntry = struct {
7578
7679 pub fn get_index (self : * const NavigationHistoryEntry , page : * Page ) i32 {
7780 const navigation = page .session .navigation ;
78- for (navigation .entries .items , 0.. ) | * entry , i | {
81+ for (navigation .entries .items , 0.. ) | entry , i | {
7982 if (std .mem .eql (u8 , entry .id , self .id )) {
8083 return @intCast (i );
8184 }
@@ -151,11 +154,11 @@ pub fn get_canGoForward(self: *const Navigation) bool {
151154}
152155
153156pub fn currentEntry (self : * Navigation ) * NavigationHistoryEntry {
154- return & self .entries .items [self .index ];
157+ return self .entries .items [self .index ];
155158}
156159
157- pub fn get_currentEntry (self : * const Navigation ) NavigationHistoryEntry {
158- return self .entries . items [ self . index ] ;
160+ pub fn get_currentEntry (self : * Navigation ) * NavigationHistoryEntry {
161+ return self .currentEntry () ;
159162}
160163
161164pub fn get_transition (_ : * const Navigation ) ? NavigationTransition {
@@ -180,7 +183,7 @@ pub fn _back(self: *Navigation, page: *Page) !NavigationReturn {
180183 return self .navigate (next_entry .url , .{ .traverse = new_index }, page );
181184}
182185
183- pub fn _entries (self : * const Navigation ) []NavigationHistoryEntry {
186+ pub fn _entries (self : * const Navigation ) []* NavigationHistoryEntry {
184187 return self .entries .items ;
185188}
186189
@@ -222,7 +225,7 @@ pub fn processNavigation(self: *Navigation, page: *Page) !void {
222225
223226/// Pushes an entry into the Navigation stack WITHOUT actually navigating to it.
224227/// For that, use `navigate`.
225- pub fn pushEntry (self : * Navigation , _url : ? []const u8 , state : ? []const u8 , page : * Page ) ! NavigationHistoryEntry {
228+ pub fn pushEntry (self : * Navigation , _url : ? []const u8 , state : ? []const u8 , page : * Page ) ! * NavigationHistoryEntry {
226229 const arena = page .session .arena ;
227230
228231 const url = if (_url ) | u | try arena .dupe (u8 , u ) else null ;
@@ -231,21 +234,31 @@ pub fn pushEntry(self: *Navigation, _url: ?[]const u8, state: ?[]const u8, page:
231234 if (self .entries .items .len > self .index + 1 ) {
232235 self .entries .shrinkRetainingCapacity (self .index + 1 );
233236 }
234- self .index = self .entries .items .len ;
237+
238+ const index = self .entries .items .len ;
235239
236240 const id = self .next_entry_id ;
237241 self .next_entry_id += 1 ;
238242
239243 const id_str = try std .fmt .allocPrint (arena , "{d}" , .{id });
240244
241- const entry = NavigationHistoryEntry {
245+ const entry = try arena .create (NavigationHistoryEntry );
246+ entry .* = NavigationHistoryEntry {
242247 .id = id_str ,
243248 .key = id_str ,
244249 .url = url ,
245250 .state = state ,
246251 };
247252
253+ // we don't always have a current entry...
254+ const previous = if (self .entries .items .len > 0 ) self .currentEntry () else null ;
248255 try self .entries .append (arena , entry );
256+ if (previous ) | prev | {
257+ NavigationCurrentEntryChangeEvent .dispatch (prev , .push , page );
258+ }
259+
260+ self .index = index ;
261+
249262 return entry ;
250263}
251264
@@ -337,7 +350,9 @@ pub fn _reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !Navigatio
337350 const opts = _opts orelse ReloadOptions {};
338351 const entry = self .currentEntry ();
339352 if (opts .state ) | state | {
353+ const previous = entry ;
340354 entry .state = state .toJson (arena ) catch return error .DataClone ;
355+ NavigationCurrentEntryChangeEvent .dispatch (previous , .reload , page );
341356 }
342357
343358 return self .navigate (entry .url , .reload , page );
@@ -365,9 +380,80 @@ pub const UpdateCurrentEntryOptions = struct {
365380
366381pub fn _updateCurrentEntry (self : * Navigation , options : UpdateCurrentEntryOptions , page : * Page ) ! void {
367382 const arena = page .session .arena ;
383+
384+ const previous = self .currentEntry ();
368385 self .currentEntry ().state = options .state .toJson (arena ) catch return error .DataClone ;
386+ NavigationCurrentEntryChangeEvent .dispatch (previous , null , page );
369387}
370388
389+ const Event = @import ("../events/event.zig" ).Event ;
390+
391+ pub const NavigationCurrentEntryChangeEvent = struct {
392+ pub const prototype = * Event ;
393+ pub const union_make_copy = true ;
394+
395+ pub const EventInit = struct {
396+ from : * NavigationHistoryEntry ,
397+ navigation_type : ? NavigationType = null ,
398+ };
399+
400+ proto : parser.Event ,
401+ from : * NavigationHistoryEntry ,
402+ navigation_type : ? NavigationType ,
403+
404+ pub fn constructor (event_type : []const u8 , opts : EventInit ) ! NavigationCurrentEntryChangeEvent {
405+ const event = try parser .eventCreate ();
406+ defer parser .eventDestroy (event );
407+ try parser .eventInit (event , event_type , .{});
408+ parser .eventSetInternalType (event , .navigation_current_entry_change_event );
409+
410+ return .{
411+ .proto = event .* ,
412+ .from = opts .from ,
413+ .navigation_type = opts .navigation_type ,
414+ };
415+ }
416+
417+ pub fn get_from (self : * NavigationCurrentEntryChangeEvent ) * NavigationHistoryEntry {
418+ return self .from ;
419+ }
420+
421+ pub fn get_navigationType (self : * const NavigationCurrentEntryChangeEvent ) ? NavigationType {
422+ return self .navigation_type ;
423+ }
424+
425+ pub fn dispatch (from : * NavigationHistoryEntry , typ : ? NavigationType , page : * Page ) void {
426+ log .debug (.script_event , "dispatch event" , .{
427+ .type = "currententrychange" ,
428+ .source = "navigation" ,
429+ });
430+
431+ var evt = NavigationCurrentEntryChangeEvent .constructor (
432+ "currententrychange" ,
433+ .{ .from = from , .navigation_type = typ },
434+ ) catch | err | {
435+ log .err (.app , "event constructor error" , .{
436+ .err = err ,
437+ .type = "currententrychange" ,
438+ .source = "navigation" ,
439+ });
440+
441+ return ;
442+ };
443+
444+ _ = parser .eventTargetDispatchEvent (
445+ @as (* parser .EventTarget , @ptrCast (& page .session .navigation )),
446+ & evt .proto ,
447+ ) catch | err | {
448+ log .err (.app , "dispatch event error" , .{
449+ .err = err ,
450+ .type = "currententrychange" ,
451+ .source = "navigation" ,
452+ });
453+ };
454+ }
455+ };
456+
371457const testing = @import ("../../testing.zig" );
372458test "Browser: Navigation" {
373459 try testing .htmlRunner ("html/navigation.html" );
0 commit comments