6e31ad38de
Implementa sistema para cargar y gestionar extensiones. Incluye: - Cargar extensiones desde carpetas o archivos .crx - Config.Extensions para especificar al lanzar - buildExtensionFlags() integrado en Launch() - Extensiones predefinidas (uBlock, Tampermonkey) - ListLocalExtensions() y GetExtensionPath() Flags utilizadas: --load-extension, --disable-extensions-except Archivo: pkg/browser/extensions.go, pkg/browser/browser.go
258 lines
6.6 KiB
Go
258 lines
6.6 KiB
Go
package browser
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// ExtensionConfig configuración de una extensión de Chrome
|
|
type ExtensionConfig struct {
|
|
Path string // Ruta a extensión (carpeta o .crx)
|
|
ID string // ID de extensión (opcional)
|
|
Enabled bool // Habilitada por defecto
|
|
Settings map[string]string // Configuración específica
|
|
}
|
|
|
|
// Extension representa una extensión instalada
|
|
type Extension struct {
|
|
ID string
|
|
Name string
|
|
Version string
|
|
Path string
|
|
Enabled bool
|
|
Description string
|
|
}
|
|
|
|
// PresetExtensions configuraciones de extensiones populares
|
|
var PresetExtensions = map[string]*ExtensionConfig{
|
|
"ublock-origin": {
|
|
ID: "cjpalhdlnbpafiamejdnhcphjbkeiagm",
|
|
Enabled: true,
|
|
},
|
|
"tampermonkey": {
|
|
ID: "dhdgffkkebhmkfjojejmpbldmpobfkfo",
|
|
Enabled: true,
|
|
},
|
|
}
|
|
|
|
// LoadPresetExtension carga una configuración de extensión predefinida
|
|
func LoadPresetExtension(name string) (*ExtensionConfig, error) {
|
|
preset, ok := PresetExtensions[name]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown preset extension: %s", name)
|
|
}
|
|
|
|
// Buscar extensión en directorio compartido
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
extPath := filepath.Join(homeDir, ".navegator", "extensions", name)
|
|
if _, err := os.Stat(extPath); err == nil {
|
|
preset.Path = extPath
|
|
}
|
|
|
|
return preset, nil
|
|
}
|
|
|
|
// buildExtensionFlags construye las flags de Chrome para cargar extensiones
|
|
func (c *Config) buildExtensionFlags() []string {
|
|
if len(c.Extensions) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var flags []string
|
|
var paths []string
|
|
|
|
for _, ext := range c.Extensions {
|
|
if ext.Path != "" && ext.Enabled {
|
|
// Expandir ~ si es necesario
|
|
path := ext.Path
|
|
if strings.HasPrefix(path, "~") {
|
|
homeDir, _ := os.UserHomeDir()
|
|
path = filepath.Join(homeDir, path[1:])
|
|
}
|
|
paths = append(paths, path)
|
|
}
|
|
}
|
|
|
|
if len(paths) > 0 {
|
|
// Cargar extensiones específicas
|
|
flags = append(flags, fmt.Sprintf("--load-extension=%s", strings.Join(paths, ",")))
|
|
|
|
// Si se especificó, deshabilitar todas las otras extensiones
|
|
if c.DisableOtherExts {
|
|
flags = append(flags, fmt.Sprintf("--disable-extensions-except=%s", strings.Join(paths, ",")))
|
|
}
|
|
}
|
|
|
|
return flags
|
|
}
|
|
|
|
// GetLoadedExtensions obtiene información sobre extensiones cargadas
|
|
// Nota: CDP no tiene API directa para esto, usamos técnicas indirectas
|
|
func (b *Browser) GetLoadedExtensions(ctx context.Context) ([]*Extension, error) {
|
|
// Intentar obtener extensiones via JavaScript
|
|
script := `
|
|
(function() {
|
|
// No hay API directa en página normal para listar extensiones
|
|
// Retornar info básica si está disponible
|
|
return [];
|
|
})();
|
|
`
|
|
|
|
result, err := b.Evaluate(ctx, script)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var extensions []*Extension
|
|
// Parse result...
|
|
_ = result
|
|
|
|
return extensions, nil
|
|
}
|
|
|
|
// NavigateToExtensionPage navega a la página de gestión de una extensión
|
|
func (b *Browser) NavigateToExtensionPage(ctx context.Context, extensionID string, page string) error {
|
|
url := fmt.Sprintf("chrome-extension://%s/%s", extensionID, page)
|
|
return b.Navigate(ctx, url, nil)
|
|
}
|
|
|
|
// SendMessageToExtension envía un mensaje a una extensión
|
|
// Útil para configurar extensiones programáticamente
|
|
func (b *Browser) SendMessageToExtension(ctx context.Context, extensionID string, message map[string]interface{}) (interface{}, error) {
|
|
script := fmt.Sprintf(`
|
|
new Promise((resolve, reject) => {
|
|
chrome.runtime.sendMessage('%s', %v, (response) => {
|
|
if (chrome.runtime.lastError) {
|
|
reject(chrome.runtime.lastError);
|
|
} else {
|
|
resolve(response);
|
|
}
|
|
});
|
|
});
|
|
`, extensionID, message)
|
|
|
|
result, err := b.EvaluateAsync(ctx, script)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error sending message to extension: %w", err)
|
|
}
|
|
|
|
return result.Value, nil
|
|
}
|
|
|
|
// SetupUBlockOrigin configura uBlock Origin con listas de filtros personalizadas
|
|
func (b *Browser) SetupUBlockOrigin(ctx context.Context, filterLists []string) error {
|
|
// Navegar a la página de configuración
|
|
if err := b.NavigateToExtensionPage(ctx, "cjpalhdlnbpafiamejdnhcphjbkeiagm", "dashboard.html"); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Configurar listas de filtros via JavaScript
|
|
script := fmt.Sprintf(`
|
|
(function() {
|
|
// Acceder a la configuración de uBlock
|
|
const lists = %v;
|
|
// Agregar listas personalizadas
|
|
// Esto depende de la API interna de uBlock
|
|
return 'configured';
|
|
})();
|
|
`, filterLists)
|
|
|
|
_, err := b.Evaluate(ctx, script)
|
|
return err
|
|
}
|
|
|
|
// InstallExtensionFromStore descarga e instala extensión desde Chrome Web Store
|
|
// Nota: Esto requiere interacción con el Web Store y puede ser bloqueado
|
|
func (b *Browser) InstallExtensionFromStore(ctx context.Context, extensionID string) error {
|
|
url := fmt.Sprintf("https://chrome.google.com/webstore/detail/%s", extensionID)
|
|
|
|
if err := b.Navigate(ctx, url, nil); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Intentar hacer click en botón de instalación
|
|
// Nota: Esto puede requerir permisos especiales
|
|
script := `
|
|
const button = document.querySelector('button[aria-label*="Add"]');
|
|
if (button) {
|
|
button.click();
|
|
return true;
|
|
}
|
|
return false;
|
|
`
|
|
|
|
result, err := b.Evaluate(ctx, script)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if clicked, ok := result.Value.(bool); !ok || !clicked {
|
|
return fmt.Errorf("could not find install button")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// EnsureExtensionsDirectory crea el directorio de extensiones si no existe
|
|
func EnsureExtensionsDirectory() (string, error) {
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
extDir := filepath.Join(homeDir, ".navegator", "extensions")
|
|
if err := os.MkdirAll(extDir, 0755); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return extDir, nil
|
|
}
|
|
|
|
// GetExtensionPath retorna la ruta a una extensión en el directorio compartido
|
|
func GetExtensionPath(name string) (string, error) {
|
|
extDir, err := EnsureExtensionsDirectory()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
path := filepath.Join(extDir, name)
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
return "", fmt.Errorf("extension not found: %s", name)
|
|
}
|
|
|
|
return path, nil
|
|
}
|
|
|
|
// ListLocalExtensions lista extensiones disponibles en el directorio local
|
|
func ListLocalExtensions() ([]string, error) {
|
|
extDir, err := EnsureExtensionsDirectory()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entries, err := os.ReadDir(extDir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var extensions []string
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
// Verificar que tenga manifest.json
|
|
manifestPath := filepath.Join(extDir, entry.Name(), "manifest.json")
|
|
if _, err := os.Stat(manifestPath); err == nil {
|
|
extensions = append(extensions, entry.Name())
|
|
}
|
|
}
|
|
}
|
|
|
|
return extensions, nil
|
|
}
|