How to Handle JavaScript Dialogs in ego-lite: Detection and Resolution Patterns
Ego-lite tracks browser-side JavaScript dialogs through CDP Page events and exposes them via pendingDialog() and pageInfo(), allowing you to accept, dismiss, or respond to prompts using the cdp() helper.
The citrolabs/ego-lite repository provides a lightweight browser automation framework that intercepts JavaScript dialogs (alert, confirm, prompt, and beforeunload) through Chrome DevTools Protocol (CDP) integration. Understanding how to handle JavaScript dialogs in ego-lite is essential for building automation scripts that don't hang on unexpected modal interactions. The framework stores dialog state in a per-session map and exposes helper functions that let you check for pending dialogs before performing actions like navigation or screenshots.
How Dialog Detection Works Internally
Ego-lite monitors dialog events at the browser runtime level through two CDP Page domain events: javascriptDialogOpening and javascriptDialogClosed. When the browser runtime receives an opening event, it stores the dialog description in an internal pendingDialogs map keyed by session ID.
The CDP Event Infrastructure
The runtime subscribes to dialog events when the session initializes. If the target browser does not support Page.enable, the runtime silently skips dialog tracking (see src/browser-runtime.ts lines 13-15), ensuring your scripts degrade gracefully rather than throwing initialization errors.
The pendingDialog() Implementation
The core detection mechanism lives in src/browser-runtime.ts lines 198-200. The pendingDialog() function returns a copy of the stored dialog object for the current session:
export function pendingDialog(sessionId = state.sessionId) {
if (sessionId && pendingDialogs.has(sessionId)) {
return { ...pendingDialogs.get(sessionId) };
}
return null;
}
This function is the primary interface used throughout the codebase to check whether a modal dialog is blocking page execution.
Checking for Active Dialogs in Your Scripts
High-level helpers automatically check for pending dialogs before executing operations that would otherwise fail or hang.
Using pageInfo() for Dialog Detection
In src/driver/nav.ts lines 84-90, the pageInfo() function short-circuits its normal execution when it detects a dialog:
export async function pageInfo() {
if (isBrowserRuntime()) {
await ensureSession();
const dialog = pendingDialog();
if (dialog) {
return { dialog };
}
}
// ... normal page-info extraction
}
When a dialog is present, pageInfo() returns an object containing only { dialog } with properties including type, message, and defaultPrompt. This allows your automation logic to branch before attempting interactions that would be blocked.
Handling Dialogs During Screenshots
The screenshot() function in src/driver/observe.ts lines 115-125 checks for pending dialogs to avoid "page is blocked by a dialog" errors:
if (!pendingDialog()) {
// ... normal screenshot logic
} else {
return screenshot({ ...options, path, raw: true });
}
When a dialog is detected, the function falls back to a raw screenshot that captures the entire screen buffer rather than attempting to evaluate the page layout, which would fail while a modal dialog blocks execution.
Responding to Dialogs via CDP Commands
Once detected, you must explicitly resolve the dialog by sending the Page.handleJavaScriptDialog CDP command through the cdp() helper exported from src/cdp-eval.ts.
Accepting and Dismissing Dialogs
Use the following patterns to resolve dialogs based on their type:
import { cdp, pendingDialog } from "ego-browser";
/** Accept the current dialog (default for alerts and confirm). */
async function acceptDialog() {
const dlg = pendingDialog();
if (!dlg) throw new Error("No dialog to accept");
await cdp("Page.handleJavaScriptDialog", { accept: true });
}
/** Dismiss the current dialog (useful for confirm/prompt). */
async function dismissDialog() {
const dlg = pendingDialog();
if (!dlg) throw new Error("No dialog to dismiss");
await cdp("Page.handleJavaScriptDialog", {
accept: false,
});
}
Handling Prompt Inputs
For prompt dialogs, supply the user response via the promptText parameter:
async function answerPrompt(responseText: string) {
const dlg = pendingDialog();
if (dlg?.type === "prompt") {
await cdp("Page.handleJavaScriptDialog", {
accept: true,
promptText: responseText,
});
}
}
Note that promptText is only honored when the dialog type is prompt; for alert or confirm dialogs, this parameter is ignored by the browser.
Complete Workflow Implementation
Follow this sequence to robustly handle JavaScript dialogs in ego-lite automation scripts:
- Detect the dialog using
const info = await pageInfo();and checkif (info.dialog). - Inspect the dialog properties:
info.dialog.type,info.dialog.message, andinfo.dialog.defaultPrompt. - Decide whether to accept or dismiss, and prepare prompt text if needed.
- Execute the appropriate
cdp("Page.handleJavaScriptDialog", ...)call. - Resume normal operations such as
click()orscreenshot()once the dialog is resolved.
Practical Example: Safe Navigation with Dialog Handling
import { pageInfo, pendingDialog, cdp, goto } from "ego-browser";
async function safeNavigate(url: string) {
await goto(url);
const info = await pageInfo();
if (info.dialog) {
console.log(`Dialog shown: ${info.dialog.message}`);
await cdp("Page.handleJavaScriptDialog", { accept: true });
// Re-query the page after dismissing the dialog
const refreshed = await pageInfo();
console.log(`Now at ${refreshed.url}`);
}
}
Practical Example: Conditional Prompt Response
async function answerPrompt(expected: string) {
const dlg = pendingDialog();
if (dlg?.type === "prompt") {
await cdp("Page.handleJavaScriptDialog", {
accept: true,
promptText: expected,
});
}
}
Summary
- Ego-lite tracks JavaScript dialogs through CDP
javascriptDialogOpeningevents stored in a per-sessionpendingDialogsmap insrc/browser-runtime.ts. - The
pendingDialog()function exposes active dialog state, whilepageInfo()insrc/driver/nav.tsreturns a{ dialog }envelope when dialogs block the page. - Screenshots automatically fall back to raw mode when dialogs are detected, preventing "page blocked" errors in
src/driver/observe.ts. - Resolve dialogs explicitly using
cdp("Page.handleJavaScriptDialog", { accept: boolean, promptText?: string })fromsrc/cdp-eval.ts. - Dialog detection degrades gracefully if the browser runtime does not support
Page.enable.
Frequently Asked Questions
What types of JavaScript dialogs can ego-lite detect?
Ego-lite handles all standard browser dialogs: alert, confirm, prompt, and beforeunload. These are tracked through CDP Page events and exposed via the same pendingDialog() interface regardless of dialog type.
How do I check if a dialog is currently open without triggering an error?
Call pageInfo() and inspect the return value. If a dialog is active, the function returns { dialog } containing type, message, and defaultPrompt properties instead of the usual page metrics. Alternatively, call pendingDialog() directly to get the dialog object or null if no dialog is present.
Why does screenshot() behave differently when a dialog is open?
When screenshot() in src/driver/observe.ts detects a pending dialog via pendingDialog(), it switches to raw screenshot mode to avoid layout evaluation that would fail while the page is blocked. This ensures you can still capture the screen state even when modal dialogs prevent normal DOM access.
Can ego-lite handle dialogs in browsers that don't support CDP Page events?
Yes. According to src/browser-runtime.ts lines 13-15, if Page.enable is not supported by the target runtime, dialog tracking is silently skipped. Your scripts will continue to work, though you won't be able to detect or handle dialogs programmatically in those environments.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →