Skip to content

Handle native browser dialogs (beforeunload, alert, confirm, prompt) #13

Description

@maxsv0

Problem

When SideButton's browser automation sends a Page.navigate CDP command to a page with a beforeunload handler (e.g., LinkedIn messaging with unsaved draft text), the browser shows a native "Leave site?" dialog. This dialog blocks ALL subsequent CDP commands indefinitely, causing every tool call to time out.

Screenshot of the blocking dialog:

The dialog shows "Leave site? Changes you made may not be saved." with Cancel/Leave buttons. While this dialog is open, snapshot, screenshot, evaluate, click, and all other browser tools return timeout errors.

Root Cause

The Chrome extension's debugger integration is missing two things:

  1. Page.enable is never called after chrome.debugger.attach() — without this, CDP never emits Page.javascriptDialogOpening events
  2. No chrome.debugger.onEvent listener exists for dialog events — even if Page domain were enabled, there's no handler to auto-dismiss dialogs

How Other Tools Handle This

Tool Approach
Playwright Auto-dismisses all dialogs if no page.on('dialog') listener registered. Exposes Dialog class with accept()/dismiss() for explicit handling
Puppeteer Requires pre-registered page.on('dialog') listener. Dialog blocks if no handler
Browser Use Migrated from Playwright to raw CDP specifically because of dialog edge cases. Uses "watchdog" background services that auto-handle CDP events
OpenClaw Uses "arming call" pattern — dialog --accept command before the triggering action
Chrome DevTools Protocol Provides Page.javascriptDialogOpening event + Page.handleJavaScriptDialog method

Proposed Solution

1. Enable Page domain events after debugger attach

In attachDebugger(), after chrome.debugger.attach():

await chrome.debugger.sendCommand({ tabId }, "Page.enable");

2. Add dialog event listener

Register a chrome.debugger.onEvent listener for Page.javascriptDialogOpening:

chrome.debugger.onEvent.addListener((source, method, params) => {
  if (method === "Page.javascriptDialogOpening" && source.tabId === connectedTabId) {
    // Auto-handle based on dialog type
    const accept = params.type === "beforeunload" || params.type === "alert";
    chrome.debugger.sendCommand(
      { tabId: source.tabId },
      "Page.handleJavaScriptDialog",
      { accept }
    ).catch(() => {}); // Ignore "no dialog showing" race
  }
});

3. Default handling policy

Dialog Type Default Action Rationale
beforeunload Accept (Leave) Automation intentionally triggered the navigation
alert Accept (OK) Informational only, no decision needed
confirm Accept (OK) Safe default for automation; can be made configurable
prompt Accept (empty) Safe default; future: expose to LLM for input

4. Future: Expose dialog events to MCP layer (optional)

For advanced use cases, expose dialog type/message to the workflow engine so agents can make informed decisions about confirm/prompt dialogs.

Files to Change

  • extension/background.jsattachDebugger() function + new chrome.debugger.onEvent listener

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions