// Chromium implementation. Chrome's proxy API uses a PAC script rather than // Firefox's per-request proxy.onRequest listener. let config = configManager.getConfig(); async function init() { await configManager.load(); config = configManager.getConfig(); await applyProxyConfig(); configManager.addListener(() => { config = configManager.getConfig(); applyProxyConfig().catch(error => console.error('Failed to apply proxy config:', error)); }); } function escapePacString(value) { return JSON.stringify(String(value)); } function buildPacScript() { const rules = {}; Object.entries(config.domains || {}).forEach(([domain, entry]) => { if (!entry || !entry.enabled || !entry.host || !entry.port) return; const scheme = entry.type === 'socks' ? 'SOCKS5' : 'PROXY'; rules[domain] = `${scheme} ${entry.host}:${parseInt(entry.port, 10)}`; }); return `function FindProxyForURL(url, host) { var rules = ${JSON.stringify(rules)}; return Object.prototype.hasOwnProperty.call(rules, host) ? rules[host] : "DIRECT"; }`; } async function applyProxyConfig() { const hasRules = Object.values(config.domains || {}).some(entry => entry && entry.enabled && entry.host && entry.port ); return chrome.proxy.settings.set({ value: hasRules ? { mode: 'pac_script', pacScript: { data: buildPacScript(), mandatory: true } } : { mode: 'direct' }, scope: 'regular' }); } function getDomain(url) { try { return new URL(url).hostname.replace(/^www\./, ''); } catch (_) { return null; } } function sendConfigResponse(sendResponse, action) { if (action === 'getConfig') sendResponse({ config: configManager.getConfig() }); else if (action === 'getProfiles') sendResponse({ profiles: configManager.getProfiles() }); else return false; return true; } chrome.webRequest.onAuthRequired.addListener( (details, sendResponse) => { if (!details.isProxy) return sendResponse({}); const entry = config.domains && config.domains[getDomain(details.url)]; if (!entry || !entry.enabled || !entry.username || !entry.password) return sendResponse({}); sendResponse({ authCredentials: { username: entry.username, password: entry.password } }); }, { urls: [''] }, ['blocking'] ); chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { const direct = sendConfigResponse(sendResponse, message.action); if (direct) return true; const asyncActions = { updateDomain: () => configManager.updateDomain(message.domain, message.domainConfig), removeDomain: () => configManager.removeDomain(message.domain), updateProfile: () => configManager.updateProfile(message.profileName, message.profileConfig), removeProfile: () => configManager.removeProfile(message.profileName), updateSettings: () => configManager.updateSettings(message.settings || {}), reloadConfig: () => configManager.reload(), clearAllDomains: () => { configManager.config.domains = {}; return configManager.save(); }, clearAllProfiles: () => { configManager.config.profiles = {}; return configManager.save(); }, clearEverything: () => { configManager.config.domains = {}; configManager.config.profiles = {}; return configManager.save(); } }; if (message.action === 'getProfile') { sendResponse({ profile: configManager.getProfileConfig(message.profileName) }); return false; } if (message.action === 'toggleProxy') { const entry = configManager.getDomainConfig(message.domain); if (!entry) { sendResponse({ success: false, error: 'Domain not configured' }); return false; } entry.enabled = message.enabled; asyncActions.updateDomain = () => configManager.updateDomain(message.domain, entry); } const operation = asyncActions[message.action]; if (!operation) return false; operation().then(() => sendResponse({ success: true })).catch(error => sendResponse({ success: false, error: error.message }) ); return true; }); init().catch(error => console.error('Failed to initialize Chrome proxy support:', error));