CDP: implement Page.reload

Add `Page.reload` to the CDP Page domain dispatch. Reuses the existing
`page.navigate()` path with `NavigationKind.reload`, matching what
`Location.reload` already does for the JS `location.reload()` API.

Accepts the standard CDP params (`ignoreCache`, `scriptToEvaluateOnLoad`)
per the Chrome DevTools Protocol spec.

The current page URL is copied to the stack before `replacePage()` to
avoid a use-after-free when the old page's arena is freed.

This unblocks CDP clients (Puppeteer, capybara-lightpanda, etc.) that
call `Page.reload` and currently get `UnknownMethod`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Navid EMAD
2026-03-25 14:39:21 +01:00
parent 0324d5c232
commit 93485c1ef3

View File

@@ -39,6 +39,7 @@ pub fn processMessage(cmd: anytype) !void {
addScriptToEvaluateOnNewDocument,
createIsolatedWorld,
navigate,
reload,
stopLoading,
close,
captureScreenshot,
@@ -52,6 +53,7 @@ pub fn processMessage(cmd: anytype) !void {
.addScriptToEvaluateOnNewDocument => return addScriptToEvaluateOnNewDocument(cmd),
.createIsolatedWorld => return createIsolatedWorld(cmd),
.navigate => return navigate(cmd),
.reload => return doReload(cmd),
.stopLoading => return cmd.sendResult(null, .{}),
.close => return close(cmd),
.captureScreenshot => return captureScreenshot(cmd),
@@ -252,6 +254,40 @@ fn navigate(cmd: anytype) !void {
});
}
fn doReload(cmd: anytype) !void {
const params = try cmd.params(struct {
ignoreCache: ?bool = null,
scriptToEvaluateOnLoad: ?[]const u8 = null,
});
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
if (bc.session_id == null) {
return error.SessionIdNotLoaded;
}
const session = bc.session;
var page = session.currentPage() orelse return error.PageNotLoaded;
// Copy URL to stack before replacePage() frees the old page's arena.
var url_buf: [8192:0]u8 = undefined;
const len = @min(page.url.len, url_buf.len);
@memcpy(url_buf[0..len], page.url[0..len]);
url_buf[len] = 0;
const reload_url: [:0]const u8 = url_buf[0..len :0];
if (page._load_state != .waiting) {
page = try session.replacePage();
}
try page.navigate(reload_url, .{
.reason = .address_bar,
.cdp_id = cmd.input.id,
.kind = .reload,
.force = if (params) |p| p.ignoreCache orelse false else false,
});
}
pub fn pageNavigate(bc: anytype, event: *const Notification.PageNavigate) !void {
// detachTarget could be called, in which case, we still have a page doing
// things, but no session.
@@ -784,3 +820,27 @@ test "cdp.page: getLayoutMetrics" {
},
}, .{ .id = 12 });
}
test "cdp.page: reload" {
var ctx = testing.context();
defer ctx.deinit();
{
// reload without browser context — should error
try ctx.processMessage(.{ .id = 30, .method = "Page.reload" });
try ctx.expectSentError(-31998, "BrowserContextNotLoaded", .{ .id = 30 });
}
_ = try ctx.loadBrowserContext(.{ .id = "BID-9", .url = "hi.html", .target_id = "FID-000000000X".* });
{
// reload with no params — should not error (navigation is async,
// so no result is sent synchronously; we just verify no error)
try ctx.processMessage(.{ .id = 31, .method = "Page.reload" });
}
{
// reload with ignoreCache param
try ctx.processMessage(.{ .id = 32, .method = "Page.reload", .params = .{ .ignoreCache = true } });
}
}