sneedium/extension-manager.js

194 lines
No EOL
5.8 KiB
JavaScript

/**
* Extension Manager Module
*
* Handles Chrome extension management for Sneedium browser
* - Loading extensions from disk
* - Installing extensions from CRX files
* - Removing extensions
* - Creating extensions management UI
*/
const path = require('path');
const fs = require('fs');
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const { installCrxExtension } = require('./crx-extractor');
// Define extension directory path
const homePath = app.getPath('home');
const extensionsDir = path.join(homePath, 'SneedExtensions');
// Ensure extensions directory exists
if (!fs.existsSync(extensionsDir)) {
fs.mkdirSync(extensionsDir, { recursive: true });
}
/**
* Initialize extension manager and set up IPC handlers
* @returns {Object} Extension manager functions
*/
function initExtensionManager() {
// Set up IPC handlers for extension management
setupIpcHandlers();
return {
getExtensionsDir: () => extensionsDir,
createExtensionsWindow,
loadExtensions
};
}
/**
* Set up IPC handlers for extension management
*/
function setupIpcHandlers() {
// Get list of installed extensions
ipcMain.handle('get-extensions-list', getExtensionsList);
// Install extension from CRX file
ipcMain.handle('install-extension', handleInstallExtension);
// Remove installed extension
ipcMain.handle('remove-extension', handleRemoveExtension);
// Open extension management window
ipcMain.on('open-extensions-page', () => createExtensionsWindow());
}
/**
* Get list of installed extensions with their metadata
* @returns {Array} List of extension objects with metadata
*/
async function getExtensionsList() {
try {
const extensionFolders = fs.readdirSync(extensionsDir, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => {
const extPath = path.join(extensionsDir, dirent.name);
let manifest = {};
try {
const manifestPath = path.join(extPath, 'manifest.json');
if (fs.existsSync(manifestPath)) {
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
}
} catch (e) {
console.error(`Error reading manifest for extension ${dirent.name}:`, e);
}
return {
id: dirent.name,
name: manifest.name || dirent.name,
version: manifest.version || 'unknown',
description: manifest.description || '',
path: extPath
};
});
return extensionFolders;
} catch (error) {
console.error('Error getting extensions list:', error);
return [];
}
}
/**
* Handle extension installation request
* @param {Object} event - IPC event
* @param {string} crxPath - Path to CRX file or null to prompt for file
* @returns {Object} Result of installation
*/
async function handleInstallExtension(event, crxPath) {
try {
// If no path provided, open dialog to select CRX file
if (!crxPath) {
const result = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Chrome Extension', extensions: ['crx'] }]
});
if (result.canceled || result.filePaths.length === 0) {
return { success: false, message: 'No file selected' };
}
crxPath = result.filePaths[0];
}
// Use the CRX extractor to install the extension
const installResult = await installCrxExtension(crxPath, extensionsDir);
return installResult;
} catch (error) {
console.error('Error installing extension:', error);
return { success: false, message: error.message };
}
}
/**
* Handle extension removal request
* @param {Object} event - IPC event
* @param {string} extensionId - ID of extension to remove
* @returns {Object} Result of removal
*/
async function handleRemoveExtension(event, extensionId) {
try {
const extensionPath = path.join(extensionsDir, extensionId);
if (fs.existsSync(extensionPath)) {
fs.rmSync(extensionPath, { recursive: true, force: true });
return { success: true };
} else {
return { success: false, message: 'Extension not found' };
}
} catch (error) {
console.error('Error removing extension:', error);
return { success: false, message: error.message };
}
}
/**
* Create extension management window
*/
function createExtensionsWindow() {
const extensionsWindow = new BrowserWindow({
width: 800,
height: 600,
title: 'Extension Manager',
webPreferences: {
preload: path.join(__dirname, 'extensions-preload.js'),
nodeIntegration: false,
contextIsolation: true
}
});
extensionsWindow.loadFile('extensions.html');
extensionsWindow.setMenuBarVisibility(false);
}
/**
* Load all extensions from the extensions directory
* @param {Object} extensionsInstance - ElectronChromeExtensions instance
*/
function loadExtensions(extensionsInstance) {
if (!extensionsInstance) return;
try {
// Get list of extension directories
const extensionFolders = fs.readdirSync(extensionsDir, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => path.join(extensionsDir, dirent.name));
// Load each extension
for (const extensionPath of extensionFolders) {
try {
if (fs.existsSync(path.join(extensionPath, 'manifest.json'))) {
const loadResult = extensionsInstance.loadExtension(extensionPath);
console.log(`Loaded extension from ${extensionPath}`, loadResult);
} else {
console.warn(`Missing manifest.json in extension directory: ${extensionPath}`);
}
} catch (extError) {
console.error(`Failed to load extension at ${extensionPath}:`, extError);
}
}
} catch (error) {
console.error('Error loading extensions:', error);
}
}
module.exports = initExtensionManager;