package browser import ( "fmt" "time" ) // CdpWaitLoad espera a que la página actual termine de cargar completamente. // Bloquea hasta recibir el evento CDP Page.loadEventFired (sin polling): suscribe // el evento via OnEvent y espera en un canal con timeout. Antes de esperar hace un // fast path comprobando document.readyState — si la página ya está "complete", // retorna de inmediato sin armar el handler. // Retorna error si el timeout se agota o si no logra habilitar el dominio Page. func CdpWaitLoad(c *CDPConn, timeout time.Duration) error { if c == nil { return fmt.Errorf("cdp wait load: conexion nula") } if timeout <= 0 { timeout = 30 * time.Second } // Fast path: si el documento ya terminó de cargar, no esperamos eventos. if rs, err := CdpEvaluate(c, "document.readyState"); err == nil && rs == "complete" { return nil } // Habilitar Page (idempotente, cacheado) y suscribir el evento de carga. if err := c.ensurePage(); err != nil { return fmt.Errorf("cdp wait load: Page.enable: %w", err) } loaded := make(chan struct{}, 1) cancel := c.OnEvent("Page.loadEventFired", func(_ string, _ map[string]any) { select { case loaded <- struct{}{}: default: } }) defer cancel() // Re-chequear readyState tras suscribir: si la carga terminó entre el fast // path y el registro del handler, ya no llegaría el evento (carrera) — lo // captamos aquí en vez de colgarnos hasta el timeout. if rs, err := CdpEvaluate(c, "document.readyState"); err == nil && rs == "complete" { return nil } select { case <-loaded: return nil case <-time.After(timeout): return fmt.Errorf("cdp wait load: pagina no cargo despues de %s", timeout) } }