mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-28 15:40:04 +00:00
Merge pull request #1963 from lightpanda-io/nested_navigation
Use double-queue to better support recursive navigation
This commit is contained in:
@@ -77,7 +77,15 @@ arena_pool: *ArenaPool,
|
|||||||
|
|
||||||
page: ?Page,
|
page: ?Page,
|
||||||
|
|
||||||
queued_navigation: std.ArrayList(*Page),
|
// Double buffer so that, as we process one list of queued navigations, new entries
|
||||||
|
// are added to the separate buffer. This ensures that we don't end up with
|
||||||
|
// endless navigation loops AND that we don't invalidate the list while iterating
|
||||||
|
// if a new entry gets appended
|
||||||
|
queued_navigation_1: std.ArrayList(*Page),
|
||||||
|
queued_navigation_2: std.ArrayList(*Page),
|
||||||
|
// pointer to either queued_navigation_1 or queued_navigation_2
|
||||||
|
queued_navigation: *std.ArrayList(*Page),
|
||||||
|
|
||||||
// Temporary buffer for about:blank navigations during processing.
|
// Temporary buffer for about:blank navigations during processing.
|
||||||
// We process async navigations first (safe from re-entrance), then sync
|
// We process async navigations first (safe from re-entrance), then sync
|
||||||
// about:blank navigations (which may add to queued_navigation).
|
// about:blank navigations (which may add to queued_navigation).
|
||||||
@@ -107,11 +115,14 @@ pub fn init(self: *Session, browser: *Browser, notification: *Notification) !voi
|
|||||||
.navigation = .{ ._proto = undefined },
|
.navigation = .{ ._proto = undefined },
|
||||||
.storage_shed = .{},
|
.storage_shed = .{},
|
||||||
.browser = browser,
|
.browser = browser,
|
||||||
.queued_navigation = .{},
|
.queued_navigation = undefined,
|
||||||
|
.queued_navigation_1 = .{},
|
||||||
|
.queued_navigation_2 = .{},
|
||||||
.queued_queued_navigation = .{},
|
.queued_queued_navigation = .{},
|
||||||
.notification = notification,
|
.notification = notification,
|
||||||
.cookie_jar = storage.Cookie.Jar.init(allocator),
|
.cookie_jar = storage.Cookie.Jar.init(allocator),
|
||||||
};
|
};
|
||||||
|
self.queued_navigation = &self.queued_navigation_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Session) void {
|
pub fn deinit(self: *Session) void {
|
||||||
@@ -284,7 +295,7 @@ pub fn runner(self: *Session, opts: Runner.Opts) !Runner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn scheduleNavigation(self: *Session, page: *Page) !void {
|
pub fn scheduleNavigation(self: *Session, page: *Page) !void {
|
||||||
const list = &self.queued_navigation;
|
const list = self.queued_navigation;
|
||||||
|
|
||||||
// Check if page is already queued
|
// Check if page is already queued
|
||||||
for (list.items) |existing| {
|
for (list.items) |existing| {
|
||||||
@@ -298,7 +309,12 @@ pub fn scheduleNavigation(self: *Session, page: *Page) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn processQueuedNavigation(self: *Session) !void {
|
pub fn processQueuedNavigation(self: *Session) !void {
|
||||||
const navigations = &self.queued_navigation;
|
const navigations = self.queued_navigation;
|
||||||
|
if (self.queued_navigation == &self.queued_navigation_1) {
|
||||||
|
self.queued_navigation = &self.queued_navigation_2;
|
||||||
|
} else {
|
||||||
|
self.queued_navigation = &self.queued_navigation_1;
|
||||||
|
}
|
||||||
|
|
||||||
if (self.page.?._queued_navigation != null) {
|
if (self.page.?._queued_navigation != null) {
|
||||||
// This is both an optimization and a simplification of sorts. If the
|
// This is both an optimization and a simplification of sorts. If the
|
||||||
@@ -314,7 +330,6 @@ pub fn processQueuedNavigation(self: *Session) !void {
|
|||||||
defer about_blank_queue.clearRetainingCapacity();
|
defer about_blank_queue.clearRetainingCapacity();
|
||||||
|
|
||||||
// First pass: process async navigations (non-about:blank)
|
// First pass: process async navigations (non-about:blank)
|
||||||
// These cannot cause re-entrant navigation scheduling
|
|
||||||
for (navigations.items) |page| {
|
for (navigations.items) |page| {
|
||||||
const qn = page._queued_navigation.?;
|
const qn = page._queued_navigation.?;
|
||||||
|
|
||||||
@@ -329,7 +344,6 @@ pub fn processQueuedNavigation(self: *Session) !void {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the queue after first pass
|
|
||||||
navigations.clearRetainingCapacity();
|
navigations.clearRetainingCapacity();
|
||||||
|
|
||||||
// Second pass: process synchronous navigations (about:blank)
|
// Second pass: process synchronous navigations (about:blank)
|
||||||
@@ -339,15 +353,17 @@ pub fn processQueuedNavigation(self: *Session) !void {
|
|||||||
try self.processFrameNavigation(page, qn);
|
try self.processFrameNavigation(page, qn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety: Remove any about:blank navigations that were queued during the
|
// Safety: Remove any about:blank navigations that were queued during
|
||||||
// second pass to prevent infinite loops
|
// processing to prevent infinite loops. New navigations have been queued
|
||||||
|
// in the other buffer.
|
||||||
|
const new_navigations = self.queued_navigation;
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < navigations.items.len) {
|
while (i < new_navigations.items.len) {
|
||||||
const page = navigations.items[i];
|
const page = new_navigations.items[i];
|
||||||
if (page._queued_navigation) |qn| {
|
if (page._queued_navigation) |qn| {
|
||||||
if (qn.is_about_blank) {
|
if (qn.is_about_blank) {
|
||||||
log.warn(.page, "recursive about blank", .{});
|
log.warn(.page, "recursive about blank", .{});
|
||||||
_ = navigations.swapRemove(i);
|
_ = self.queued_navigation.swapRemove(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,8 +118,6 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!--
|
|
||||||
This test is flaky. Temporarily disabled until Runner can handle more advanced cases.
|
|
||||||
<script id=link_click>
|
<script id=link_click>
|
||||||
testing.async(async (restore) => {
|
testing.async(async (restore) => {
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
@@ -139,9 +137,9 @@ This test is flaky. Temporarily disabled until Runner can handle more advanced c
|
|||||||
restore();
|
restore();
|
||||||
testing.expectEqual("<html><head></head><body>It was clicked!\n</body></html>", f6.contentDocument.documentElement.outerHTML);
|
testing.expectEqual("<html><head></head><body>It was clicked!\n</body></html>", f6.contentDocument.documentElement.outerHTML);
|
||||||
});
|
});
|
||||||
</script> -->
|
</script>
|
||||||
|
|
||||||
<script id=count>
|
<script id=about_blank_nav>
|
||||||
{
|
{
|
||||||
let i = document.createElement('iframe');
|
let i = document.createElement('iframe');
|
||||||
document.documentElement.appendChild(i);
|
document.documentElement.appendChild(i);
|
||||||
|
|||||||
Reference in New Issue
Block a user