Fix cross-compartment crashes, add per-container vector settings

- Replace Intl.DateTimeFormat and RTCPeerConnection constructor overrides
  with safe non-constructor approaches to fix Discord and other complex apps
- Add per-container vector toggles (gear icon in popup)
- Add per-container delete button in popup
- Fix Reset All not removing orphaned containers
- Popup now only shows managed containers
- Bump version to 0.5.3
This commit is contained in:
sal
2026-03-04 22:40:08 -06:00
parent 464a570201
commit 0435d06bbc
6 changed files with 297 additions and 87 deletions

View File

@@ -85,8 +85,16 @@ async function registerForContainer(cookieStoreId, profile) {
async function buildProfileAndRegister(cookieStoreId, seed) {
const profile = generateFingerprintProfile(seed);
const vsStored = await browser.storage.local.get("vectorSettings");
profile.vectors = vsStored.vectorSettings || {};
const stored = await browser.storage.local.get(["vectorSettings", "containerSettings"]);
const globalVectors = stored.vectorSettings || {};
const containerSettings = stored.containerSettings || {};
const containerVectors = containerSettings[cookieStoreId]?.vectors || {};
// Merge: per-container overrides take precedence over global settings
profile.vectors = { ...globalVectors };
for (const [key, val] of Object.entries(containerVectors)) {
if (val !== null) profile.vectors[key] = val;
}
// Cache profile for HTTP header spoofing
containerProfiles[cookieStoreId] = {
@@ -278,6 +286,8 @@ browser.runtime.onMessage.addListener((message, sender) => {
if (message.type === "importSettings") return handleImportSettings(message.data);
if (message.type === "getAutoPruneSettings") return handleGetAutoPruneSettings();
if (message.type === "setAutoPruneSettings") return handleSetAutoPruneSettings(message.settings);
if (message.type === "getContainerVectors") return handleGetContainerVectors(message.cookieStoreId);
if (message.type === "setContainerVectors") return handleSetContainerVectors(message.cookieStoreId, message.vectors);
});
async function handleGetContainerList() {
@@ -291,15 +301,23 @@ async function handleGetContainerList() {
reverseDomainMap[cid] = domain;
}
return containers.map(c => ({
name: c.name,
cookieStoreId: c.cookieStoreId,
color: c.color,
icon: c.icon,
domain: reverseDomainMap[c.cookieStoreId] || null,
enabled: (settings[c.cookieStoreId]?.enabled !== false),
hasSeed: !!seeds[c.cookieStoreId]
}));
// Only show containers we manage (in domainMap or have a seed)
const ourContainerIds = new Set([
...Object.values(domainMap),
...Object.keys(seeds)
]);
return containers
.filter(c => ourContainerIds.has(c.cookieStoreId))
.map(c => ({
name: c.name,
cookieStoreId: c.cookieStoreId,
color: c.color,
icon: c.icon,
domain: reverseDomainMap[c.cookieStoreId] || null,
enabled: (settings[c.cookieStoreId]?.enabled !== false),
hasSeed: !!seeds[c.cookieStoreId]
}));
}
async function handleToggle(cookieStoreId, enabled) {
@@ -359,11 +377,21 @@ async function handleResetAll() {
delete registeredScripts[key];
}
// Remove all ContainSite-managed containers
// Collect all container IDs we know about (domainMap + seeds)
const stored = await browser.storage.local.get("containerSeeds");
const seeds = stored.containerSeeds || {};
const ourContainerIds = new Set([
...Object.values(domainMap),
...Object.keys(seeds),
...managedContainerIds
]);
// Remove all containers that are ours OR look like domain-named containers
// (catches orphans from previous installs/reloads)
const containers = await browser.contextualIdentities.query({});
const ourContainerIds = new Set(Object.values(domainMap));
const domainPattern = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;
for (const c of containers) {
if (ourContainerIds.has(c.cookieStoreId)) {
if (ourContainerIds.has(c.cookieStoreId) || domainPattern.test(c.name)) {
try { await browser.contextualIdentities.remove(c.cookieStoreId); } catch(e) {}
}
}
@@ -402,15 +430,34 @@ async function handlePruneContainers() {
}
async function handleDeleteContainer(cookieStoreId) {
// Unregister content script
if (registeredScripts[cookieStoreId]) {
try { await registeredScripts[cookieStoreId].unregister(); } catch(e) {}
delete registeredScripts[cookieStoreId];
}
try {
await browser.contextualIdentities.remove(cookieStoreId);
} catch(e) {}
// Clean up domainMap
for (const [domain, cid] of Object.entries(domainMap)) {
if (cid === cookieStoreId) {
delete domainMap[domain];
}
}
await saveDomainMap();
managedContainerIds.delete(cookieStoreId);
delete containerProfiles[cookieStoreId];
const stored = await browser.storage.local.get(["containerSeeds", "containerSettings"]);
const seeds = stored.containerSeeds || {};
const settings = stored.containerSettings || {};
delete seeds[cookieStoreId];
delete settings[cookieStoreId];
await browser.storage.local.set({ containerSeeds: seeds, containerSettings: settings });
await updateBadge();
return { ok: true };
}
@@ -483,6 +530,28 @@ async function handleSetAutoPruneSettings(settings) {
return { ok: true };
}
async function handleGetContainerVectors(cookieStoreId) {
const stored = await browser.storage.local.get(["vectorSettings", "containerSettings"]);
const globalVectors = stored.vectorSettings || {};
const containerSettings = stored.containerSettings || {};
const containerVectors = containerSettings[cookieStoreId]?.vectors || {};
return { global: globalVectors, overrides: containerVectors };
}
async function handleSetContainerVectors(cookieStoreId, vectors) {
const stored = await browser.storage.local.get(["containerSettings", "containerSeeds"]);
const settings = stored.containerSettings || {};
settings[cookieStoreId] = { ...settings[cookieStoreId], vectors };
await browser.storage.local.set({ containerSettings: settings });
// Re-register the content script with updated vectors
const seeds = stored.containerSeeds || {};
if (seeds[cookieStoreId] && settings[cookieStoreId]?.enabled !== false) {
await buildProfileAndRegister(cookieStoreId, seeds[cookieStoreId]);
}
return { ok: true };
}
async function runAutoPrune() {
const stored = await browser.storage.local.get("autoPrune");
const settings = stored.autoPrune || { enabled: false, days: 30 };