package browser import ( "fmt" "os" ) // CdpSetFileInput sube archivos a un identificado por el // selector CSS. Resuelve el nodo via DOM.getDocument + DOM.querySelector y luego // asigna los archivos con DOM.setFileInputFiles. Util para automatizar formularios // de subida sin simular el dialogo nativo de seleccion de archivos. // // Cada path de paths se valida con os.Stat ANTES de enviar el comando: si alguno // no existe (o no es accesible) se devuelve error inmediato sin tocar el DOM. Los // paths deben ser absolutos y accesibles por el proceso de Chrome (ver Gotchas en // el .md): Chrome lee los archivos desde su propio contexto, no desde el de este // programa. func CdpSetFileInput(c *CDPConn, selector string, paths []string) error { if c == nil { return fmt.Errorf("cdp set file input: conexion nula") } if selector == "" { return fmt.Errorf("cdp set file input: selector vacio") } if len(paths) == 0 { return fmt.Errorf("cdp set file input: lista de paths vacia") } // Validar que cada path exista en disco antes de mandar nada a Chrome. for _, p := range paths { if p == "" { return fmt.Errorf("cdp set file input: path vacio en la lista") } if _, err := os.Stat(p); err != nil { if os.IsNotExist(err) { return fmt.Errorf("cdp set file input: el archivo no existe: %q", p) } return fmt.Errorf("cdp set file input: no se puede acceder al archivo %q: %w", p, err) } } // Obtener el nodo raiz del documento. docRes, err := c.sendCDP("DOM.getDocument", map[string]any{"depth": 0}) if err != nil { return fmt.Errorf("cdp set file input: DOM.getDocument: %w", err) } root, ok := docRes["root"].(map[string]any) if !ok { return fmt.Errorf("cdp set file input: respuesta de DOM.getDocument sin root") } rootNodeID, ok := root["nodeId"].(float64) if !ok { return fmt.Errorf("cdp set file input: DOM.getDocument sin nodeId raiz") } // Resolver el input por selector. qsRes, err := c.sendCDP("DOM.querySelector", map[string]any{ "nodeId": int(rootNodeID), "selector": selector, }) if err != nil { return fmt.Errorf("cdp set file input: DOM.querySelector %q: %w", selector, err) } nodeIDVal, ok := qsRes["nodeId"].(float64) if !ok || int(nodeIDVal) == 0 { return fmt.Errorf("cdp set file input: el selector %q no coincide con ningun elemento", selector) } // Asignar los archivos al input. files := make([]any, len(paths)) for i, p := range paths { files[i] = p } if _, err := c.sendCDP("DOM.setFileInputFiles", map[string]any{ "files": files, "nodeId": int(nodeIDVal), }); err != nil { return fmt.Errorf("cdp set file input: DOM.setFileInputFiles en %q: %w", selector, err) } return nil }