Files
navegator/pkg/browser/extensions.go
T
Developer 6e31ad38de feat: gestión de extensiones de Chrome
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
2026-03-25 00:48:01 +01:00

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
}