Merge pull request #2085 from tmchow/feat/2082-handle-javascript-dialog

feat: emit Page.javascriptDialogOpening CDP events for JS dialogs
This commit is contained in:
Karl Seguin
2026-04-07 08:54:36 +08:00
committed by GitHub
4 changed files with 62 additions and 4 deletions

View File

@@ -83,6 +83,7 @@ const EventListeners = struct {
http_request_auth_required: List = .{},
http_response_data: List = .{},
http_response_header_done: List = .{},
javascript_dialog_opening: List = .{},
};
const Events = union(enum) {
@@ -102,6 +103,7 @@ const Events = union(enum) {
http_request_done: *const RequestDone,
http_response_data: *const ResponseData,
http_response_header_done: *const ResponseHeaderDone,
javascript_dialog_opening: *const JavascriptDialogOpening,
};
const EventType = std.meta.FieldEnum(Events);
@@ -185,6 +187,12 @@ pub const RequestFail = struct {
err: anyerror,
};
pub const JavascriptDialogOpening = struct {
url: [:0]const u8,
message: []const u8,
dialog_type: []const u8,
};
pub fn init(allocator: Allocator) !*Notification {
const notification = try allocator.create(Notification);
errdefer allocator.destroy(notification);

View File

@@ -903,15 +903,31 @@ pub const JsApi = struct {
pub const opener = bridge.property(null, .{ .template = false });
pub const alert = bridge.function(struct {
fn alert(_: *const Window, _: ?[]const u8) void {}
}.alert, .{ .noop = true });
fn alert(_: *const Window, message: ?[]const u8, page: *Page) void {
page._session.notification.dispatch(.javascript_dialog_opening, &.{
.url = page.url,
.message = message orelse "",
.dialog_type = "alert",
});
}
}.alert, .{});
pub const confirm = bridge.function(struct {
fn confirm(_: *const Window, _: ?[]const u8) bool {
fn confirm(_: *const Window, message: ?[]const u8, page: *Page) bool {
page._session.notification.dispatch(.javascript_dialog_opening, &.{
.url = page.url,
.message = message orelse "",
.dialog_type = "confirm",
});
return false;
}
}.confirm, .{});
pub const prompt = bridge.function(struct {
fn prompt(_: *const Window, _: ?[]const u8, _: ?[]const u8) ?[]const u8 {
fn prompt(_: *const Window, message: ?[]const u8, _: ?[]const u8, page: *Page) ?[]const u8 {
page._session.notification.dispatch(.javascript_dialog_opening, &.{
.url = page.url,
.message = message orelse "",
.dialog_type = "prompt",
});
return null;
}
}.prompt, .{});

View File

@@ -431,6 +431,7 @@ pub const BrowserContext = struct {
try notification.register(.page_frame_created, self, onPageFrameCreated);
try notification.register(.page_dom_content_loaded, self, onPageDOMContentLoaded);
try notification.register(.page_loaded, self, onPageLoaded);
try notification.register(.javascript_dialog_opening, self, onJavascriptDialogOpening);
}
pub fn deinit(self: *BrowserContext) void {
@@ -641,6 +642,11 @@ pub const BrowserContext = struct {
return @import("domains/page.zig").pageLoaded(self, msg);
}
pub fn onJavascriptDialogOpening(ctx: *anyopaque, msg: *const Notification.JavascriptDialogOpening) !void {
const self: *BrowserContext = @ptrCast(@alignCast(ctx));
return @import("domains/page.zig").javascriptDialogOpening(self, msg);
}
pub fn onHttpResponseHeadersDone(ctx: *anyopaque, msg: *const Notification.ResponseHeaderDone) !void {
const self: *BrowserContext = @ptrCast(@alignCast(ctx));
defer self.resetNotificationArena();

View File

@@ -48,6 +48,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
close,
captureScreenshot,
getLayoutMetrics,
handleJavaScriptDialog,
}, cmd.input.action) orelse return error.UnknownMethod;
switch (action) {
@@ -63,6 +64,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
.close => return close(cmd),
.captureScreenshot => return captureScreenshot(cmd),
.getLayoutMetrics => return getLayoutMetrics(cmd),
.handleJavaScriptDialog => return handleJavaScriptDialog(cmd),
}
}
@@ -642,6 +644,32 @@ fn sendPageLifecycle(bc: *CDP.BrowserContext, name: []const u8, timestamp: u64,
}, .{ .session_id = session_id });
}
// https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-handleJavaScriptDialog
fn handleJavaScriptDialog(cmd: *CDP.Command) !void {
// Dialogs auto-dismiss in headless mode. By the time the CDP client
// sends this command, the dialog has already returned and there is
// no pending dialog to accept or dismiss.
_ = try cmd.params(struct {
accept: bool,
promptText: ?[]const u8 = null,
});
return cmd.sendError(-32000, "No dialog is showing", .{});
}
// https://chromedevtools.github.io/devtools-protocol/tot/Page/#event-javascriptDialogOpening
pub fn javascriptDialogOpening(bc: anytype, event: *const Notification.JavascriptDialogOpening) !void {
const session_id = bc.session_id orelse return;
var cdp = bc.cdp;
try cdp.sendEvent("Page.javascriptDialogOpening", .{
.url = event.url,
.message = event.message,
.type = event.dialog_type,
.hasBrowserHandler = false,
.defaultPrompt = "",
}, .{ .session_id = session_id });
}
const LifecycleEvent = struct {
frameId: []const u8,
loaderId: ?[]const u8,