@@ -267,7 +267,7 @@ pub const Page = struct {
267267 // load polyfills
268268 try polyfill .load (self .arena , self .scope );
269269
270- // _ = try session.browser.app.loop.timeout(1 * std.time.ns_per_ms, &self.microtask_node);
270+ _ = try session .browser .app .loop .timeout (1 * std .time .ns_per_ms , & self .microtask_node );
271271 }
272272
273273 fn microtaskCallback (node : * Loop.CallbackNode , repeat_delay : * ? u63 ) void {
@@ -705,12 +705,35 @@ pub const Page = struct {
705705 .a = > {
706706 const element : * parser.Element = @ptrCast (node );
707707 const href = (try parser .elementGetAttribute (element , "href" )) orelse return ;
708- return self .session .pageNavigate (href );
708+
709+ // We cannot navigate immediately as navigating will delete the DOM tree, which holds this event's node.
710+ // As such we schedule the function to be called as soon as possible.
711+ // NOTE Using the page.arena assumes that the scheduling loop does use this object after invoking the callback
712+ // If that changes we may want to consider storing DelayedNavigation in the session instead.
713+ const arena = self .arena ;
714+ const navi = try arena .create (DelayedNavigation );
715+ navi .* = .{
716+ .session = self .session ,
717+ .href = try arena .dupe (u8 , href ),
718+ };
719+ _ = try self .state .loop .timeout (0 , & navi .navigate_node );
709720 },
710721 else = > {},
711722 }
712723 }
713724
725+ const DelayedNavigation = struct {
726+ navigate_node : Loop.CallbackNode = .{ .func = DelayedNavigation .delay_navigate },
727+ session : * Session ,
728+ href : []const u8 ,
729+
730+ fn delay_navigate (node : * Loop.CallbackNode , repeat_delay : * ? u63 ) void {
731+ _ = repeat_delay ;
732+ const self : * DelayedNavigation = @fieldParentPtr ("navigate_node" , node );
733+ self .session .pageNavigate (self .href ) catch unreachable ;
734+ }
735+ };
736+
714737 const Script = struct {
715738 element : * parser.Element ,
716739 kind : Kind ,
0 commit comments