diff --git a/src/browser/Frame.zig b/src/browser/Frame.zig index 5edc027f..c26c6a76 100644 --- a/src/browser/Frame.zig +++ b/src/browser/Frame.zig @@ -1596,7 +1596,20 @@ pub fn checkIntersections(self: *Frame) !void { } } -pub fn dispatchLoad(self: *Frame) !void { +pub fn queueLoad(self: *Frame, html: *Element.Html) !void { + try self._to_load.append(self.arena, html); + if (self._to_load.items.len == 1) { + try self.js.scheduler.add(self, struct { + fn cleanup(ctx: *anyopaque) !?u32 { + const f: *Frame = @ptrCast(@alignCast(ctx)); + try f.dispatchLoad(); + return null; + } + }.cleanup, 0, .{ .name = "frame.dispatchLoad" }); + } +} + +fn dispatchLoad(self: *Frame) !void { const has_dom_load_listener = self._event_manager.has_dom_load_listener; // Swap buffers - new additions during dispatch go to the other buffer diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 927d2f05..1848692d 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -121,10 +121,7 @@ pub fn init(self: *Page, session: *Session, frame_id: u32) !void { // Tear down the Page and its root Frame. Equivalent to the old // Session.removePage + Session.resetFrameResources. pub fn deinit(self: *Page, abort_http: bool) void { - for (self.queued_close.items) |popup| { - popup.deinit(abort_http); - } - self.queued_close = .empty; + self.cleanupClosedPopups(); for (self.popups.items) |popup| { popup.deinit(abort_http); @@ -179,6 +176,13 @@ pub fn deinit(self: *Page, abort_http: bool) void { session.arena_pool.release(self.frame_arena); } +pub fn cleanupClosedPopups(self: *Page) void { + for (self.queued_close.items) |popup| { + popup.deinit(true); + } + self.queued_close = .empty; +} + pub fn getArena(self: *Page, size_or_bucket: anytype, debug: []const u8) !Allocator { return self.session.getArena(size_or_bucket, debug); } diff --git a/src/browser/Runner.zig b/src/browser/Runner.zig index 2489382e..c1f0ce89 100644 --- a/src/browser/Runner.zig +++ b/src/browser/Runner.zig @@ -84,6 +84,7 @@ fn _wait(self: *Runner, comptime is_cdp: bool, opts: WaitOpts) !CDPWaitResult { while (true) { if (gc_hint_timer.read() >= gc_hint_period_ns) { gc_hint_timer.reset(); + self.frame._page.cleanupClosedPopups(); self.session.browser.env.memoryPressureNotification(.moderate); } @@ -181,9 +182,6 @@ fn _tick(self: *Runner, comptime is_cdp: bool, opts: TickOpts) !CDPTickResult { // it AFTER. try browser.runMacrotasks(); - // Each call to this runs scheduled load events. - try frame.dispatchLoad(); - const http_active = http_client.http_active; const total_network_activity = http_active + http_client.interception_layer.intercepted; if (frame._notified_network_almost_idle.check(total_network_activity <= 2)) { diff --git a/src/browser/webapi/element/html/Image.zig b/src/browser/webapi/element/html/Image.zig index ab60c943..dab4f4d5 100644 --- a/src/browser/webapi/element/html/Image.zig +++ b/src/browser/webapi/element/html/Image.zig @@ -128,7 +128,7 @@ pub fn imageAddedCallback(self: *Image, frame: *Frame) !void { const src = element.getAttributeSafe(comptime .wrap("src")) orelse return; if (src.len == 0) return; - try frame._to_load.append(frame.arena, self._proto); + try frame.queueLoad(self._proto); } pub const JsApi = struct { diff --git a/src/browser/webapi/element/html/Link.zig b/src/browser/webapi/element/html/Link.zig index a1a14874..8f561355 100644 --- a/src/browser/webapi/element/html/Link.zig +++ b/src/browser/webapi/element/html/Link.zig @@ -106,7 +106,7 @@ pub fn linkAddedCallback(self: *Link, frame: *Frame) !void { return; } - try frame._to_load.append(frame.arena, self._proto); + try frame.queueLoad(self._proto); } pub const JsApi = struct { diff --git a/src/browser/webapi/element/html/Style.zig b/src/browser/webapi/element/html/Style.zig index 95e0bc0d..a7b21b64 100644 --- a/src/browser/webapi/element/html/Style.zig +++ b/src/browser/webapi/element/html/Style.zig @@ -113,7 +113,7 @@ pub fn styleAddedCallback(self: *Style, frame: *Frame) !void { return; } - try frame._to_load.append(frame.arena, self._proto); + try frame.queueLoad(self._proto); } pub const JsApi = struct {