discardPendingPage on Session.initiateRootNavigation

This commit is contained in:
Pierre Tachoire
2026-04-28 21:44:22 +02:00
parent 11172a341a
commit e33018f40e
4 changed files with 37 additions and 17 deletions

View File

@@ -413,7 +413,8 @@ pub fn deinit(self: *Frame) void {
self._script_manager.base.shutdown = true;
browser.http_client.abortFrame(self._frame_id);
// don't abort pending frames.
browser.http_client.abortFrame(self._frame_id, .{});
self._script_manager.deinit();
self._style_manager.deinit();
@@ -758,7 +759,7 @@ fn scheduleNavigationWithArena(originator: *Frame, arena: Allocator, request_url
.type = target._type,
});
session.browser.http_client.abortFrame(target._frame_id);
session.browser.http_client.abortFrame(target._frame_id, .{});
// Capture the originating frame's URL as the Referer for this
// navigation. The originator's frame may be torn down before navigate()

View File

@@ -304,19 +304,25 @@ pub fn getUserAgent(self: *const Client) [:0]const u8 {
return self.user_agent_override orelse self.network.config.http_headers.user_agent;
}
const AbortOpts = struct {
scope: enum { normal, full } = .normal,
};
pub fn abort(self: *Client) void {
self._abort(true, 0);
self._abort(true, 0, .{ .scope = .full });
}
pub fn abortFrame(self: *Client, frame_id: u32) void {
self._abort(false, frame_id);
// abortFrame with .normal doesn't abort protect_from_abort requests.
// .full abort all relqtive requests.
pub fn abortFrame(self: *Client, frame_id: u32, opts: AbortOpts) void {
self._abort(false, frame_id, opts);
}
// Written this way so that both abort and abortFrame can share the same code
// but abort can avoid the frame_id check at comptime.
fn _abort(self: *Client, comptime abort_all: bool, frame_id: u32) void {
abortConnections(self.in_use, abort_all, frame_id);
abortConnections(self.ready_queue, abort_all, frame_id);
fn _abort(self: *Client, comptime abort_all: bool, frame_id: u32, opts: AbortOpts) void {
abortConnections(self.in_use, abort_all, frame_id, opts);
abortConnections(self.ready_queue, abort_all, frame_id, opts);
{
var q = &self.queue;
@@ -327,9 +333,11 @@ fn _abort(self: *Client, comptime abort_all: bool, frame_id: u32) void {
const params = transfer.req.params;
if (comptime abort_all) {
transfer.kill();
} else if (params.frame_id == frame_id and !params.protect_from_abort) {
q.remove(node);
transfer.kill();
} else if (params.frame_id == frame_id) {
if (opts.scope == .full or !params.protect_from_abort) {
q.remove(node);
transfer.kill();
}
}
}
}
@@ -355,7 +363,7 @@ fn _abort(self: *Client, comptime abort_all: bool, frame_id: u32) void {
}
}
fn abortConnections(list: std.DoublyLinkedList, comptime abort_all: bool, frame_id: u32) void {
fn abortConnections(list: std.DoublyLinkedList, comptime abort_all: bool, frame_id: u32, opts: AbortOpts) void {
var n = list.first;
while (n) |node| {
n = node.next;
@@ -363,9 +371,12 @@ fn abortConnections(list: std.DoublyLinkedList, comptime abort_all: bool, frame_
switch (conn.transport) {
.http => |transfer| {
const params = transfer.req.params;
const matches = (comptime abort_all) or (params.frame_id == frame_id and !params.protect_from_abort);
if (matches) {
if (comptime abort_all) {
transfer.kill();
} else if (params.frame_id == frame_id) {
if (opts.scope == .full or !params.protect_from_abort) {
transfer.kill();
}
}
},
.websocket => |ws| {

View File

@@ -534,7 +534,9 @@ fn replaceRootImmediate(self: *Session, frame_id: u32, qn: *QueuedNavigation) !v
// trip — Runtime.evaluate, DOM.*, etc. continue to operate on the OLD page
// until commitPendingPage swaps the pointer when response headers arrive.
pub fn initiateRootNavigation(self: *Session, frame_id: u32, url: [:0]const u8, opts: Frame.NavigateOpts) !void {
lp.assert(self._pending_idx == null, "Session.initiateRootNavigation - pending already set", .{});
if (self._pending_idx != null) {
self.discardPendingPage();
}
// Pick the slot NOT occupied by the active page.
const slot = try self.findFreeSlot();
@@ -643,8 +645,13 @@ pub fn discardPendingPage(self: *Session) void {
log.debug(.browser, "discard pending page", .{});
}
const pending_page = &self._pages[idx].?;
// Force abort all inflight queries.
self.browser.http_client.abortFrame(pending_page.frame._frame_id, .{ .scope = .full });
self._pending_idx = null;
self._pages[idx].?.deinit();
pending_page.deinit();
self.freeSlot(idx);
}

View File

@@ -121,7 +121,8 @@ pub fn init(url: []const u8, exec: *Execution) !*Worker {
// Called from Frame.deinit when the frame is destroyed, so we don't need to
// remove from the frame's worker list.
pub fn deinit(self: *Worker) void {
self._frame._session.browser.http_client.abortFrame(self._frame_id);
// No pending frame for workers, so we can abort all frames.
self._frame._session.browser.http_client.abortFrame(self._frame_id, .{ .scope = .full });
if (self._http_response) |res| {
res.abort(error.Abort);
self._http_response = null;