feat: add cross-browser builds and refresh project documentation
- 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
This commit is contained in:
1 parent
11508e6740
commit
d0c5fc5679
12 files changed
+256
-14
No files matched your search
@@ -0,0 +1,102 @@
|
||||
// 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));
|
||||
Reference in new issue
Block a user