- add Chrome/Chromium Manifest V3 build with PAC proxy routing - keep Firefox Manifest V2 and Android compatibility - add Chromium API adapter and service worker - add Chrome build script and Makefile target - update README and add AGENT.md - document installation, signing, permissions, themes, and platform limitations
103 lines
4.0 KiB
JavaScript
103 lines
4.0 KiB
JavaScript
// 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: ['<all_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));
|