More aggressive popup cleanup, more efficient load tick

Follow up to https://github.com/lightpanda-io/browser/pull/2335 which hooks
into the tick's GC hint to close any closed window (rather than having to wait
until page unload).

Also, use schedule task for dispatchLoad which removes an check in every tick.
This commit is contained in:
Karl Seguin
2026-05-01 09:06:47 +08:00
parent 9a9e79ebc1
commit 2b95b1e743
6 changed files with 26 additions and 11 deletions

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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)) {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {