8742cb25be
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
78 lines
4.4 KiB
Markdown
78 lines
4.4 KiB
Markdown
---
|
|
id: cdp_handle_dialog_go_browser
|
|
name: cdp_handle_dialog
|
|
kind: function
|
|
lang: go
|
|
domain: browser
|
|
purity: impure
|
|
version: 1.1.0
|
|
tested: true
|
|
tests: ["TestCdpHandleDialog_nilConn", "TestDialogLog"]
|
|
test_file_path: "functions/browser/cdp_handle_dialog_test.go"
|
|
description: "Instala un auto-handler que responde automaticamente a dialogos JS (alert/confirm/prompt/beforeunload) via Page.javascriptDialogOpening CDP hasta que se llame el cancel devuelto. Devuelve un *DialogLog con Count/LastType/LastMessage de lo auto-respondido. Un unico worker serializa las respuestas (no spawnea una goroutine por dialogo)."
|
|
tags: [cdp, browser, dialog, input, navegator]
|
|
signature: "func CdpHandleDialog(c *CDPConn, accept bool, promptText string) (func(), *DialogLog, error)"
|
|
uses_functions: []
|
|
uses_types: []
|
|
returns: []
|
|
returns_optional: false
|
|
error_type: error_go_core
|
|
imports: []
|
|
file_path: "functions/browser/cdp_handle_dialog.go"
|
|
example: |
|
|
// Aceptar automaticamente confirm() antes de navegar
|
|
cancel, _ := CdpHandleDialog(c, true, "")
|
|
defer cancel()
|
|
_ = CdpClick(c, "#delete-account-btn")
|
|
_ = CdpWaitIdle(c, 2000)
|
|
params:
|
|
- name: c
|
|
desc: "Conexion CDP activa obtenida con CdpConnect."
|
|
- name: accept
|
|
desc: "true para aceptar/OK el dialogo; false para rechazar/Cancel. Para alert() el valor no importa (siempre se cierra), para confirm() determina el valor de retorno, para prompt() determina si se devuelve el texto o null."
|
|
- name: promptText
|
|
desc: "Texto a inyectar en dialogos prompt(). Vacio string para no inyectar texto. Ignorado en alert() y confirm()."
|
|
output: "(cancel func(), *DialogLog, error): cancel des-registra el handler y detiene el worker (idempotente, seguro llamarlo varias veces); DialogLog acumula Count/LastType/LastMessage de lo auto-respondido (leer con Snapshot()); error si la conexion es nula o Page.enable falla."
|
|
---
|
|
|
|
## Ejemplo
|
|
|
|
```go
|
|
conn, _ := CdpConnect(9222)
|
|
_ = CdpNavigate(conn, "https://example.com/admin")
|
|
_ = CdpWaitLoad(conn, 3*time.Second)
|
|
|
|
// Instalar handler antes de la accion que dispara el dialogo
|
|
cancel, dlog, err := CdpHandleDialog(conn, true, "")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer cancel()
|
|
|
|
// Este boton dispara confirm("¿Seguro que quieres borrar?")
|
|
// El handler lo acepta automaticamente sin bloquear
|
|
_ = CdpClick(conn, "#btn-delete-all")
|
|
_ = CdpWaitIdle(conn, CdpWaitIdleOpts{})
|
|
|
|
// Saber qué se auto-respondió
|
|
count, lastType, lastMsg := dlog.Snapshot()
|
|
fmt.Printf("auto-respondidos: %d (último %s: %q)\n", count, lastType, lastMsg)
|
|
```
|
|
|
|
## Cuando usarla
|
|
|
|
Instalar antes de cualquier accion que pueda disparar `alert()`, `confirm()`, `prompt()` o `beforeunload` en la pagina. Sin este handler, el dialogo bloquea el tab del navegador indefinidamente y todas las llamadas CDP siguientes se quedan colgadas esperando. Imprescindible en scraping de paneles de administracion, flujos de borrado con confirmacion, y paginas con `beforeunload` que pregunta si quieres salir.
|
|
|
|
## Gotchas
|
|
|
|
- DEADLOCK GARANTIZADO si se llama `sendCDP` de forma sincrona dentro del handler de evento. El handler corre en la goroutine de lectura del WebSocket; `sendCDP` espera una respuesta que esa misma goroutine deberia leer. La implementacion encola el evento en un canal y lo responde desde UN worker aparte — no modificar este patron.
|
|
- **Un único worker, no goroutine por diálogo**: el handler antiguo hacía `go c.sendCDP(...)` por cada diálogo (spawn ilimitado). Ahora encola en un canal con buffer (64) que consume un worker. Si la página dispara una tormenta de diálogos que llena el buffer, los excedentes se descartan (no se responden) para no colgar la conexión — caso patológico, raro en la práctica.
|
|
- **Leer el log con `Snapshot()`**: `DialogLog` tiene mutex interno. En concurrencia, usa `dlog.Snapshot()` en vez de leer los campos públicos directamente (evita data race con el worker).
|
|
- El handler responde todos los diálogos con los mismos `accept` y `promptText` hasta que se llame `cancel()`.
|
|
- `cancel()` es idempotente (seguro llamarlo varias veces) y detiene el worker. No cierra diálogos ya abiertos; solo evita responder los futuros.
|
|
- Para `beforeunload`, `accept: true` permite la navegacion y `accept: false` la bloquea.
|
|
|
|
## Capability growth log
|
|
|
|
- v1.1.0 (2026-06-06) — devuelve `*DialogLog` (Count/LastType/LastMessage) para que el caller sepa qué se auto-respondió; reemplaza el spawn de una goroutine por diálogo por un worker único alimentado por canal con buffer; `cancel()` ahora idempotente vía sync.Once.
|