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

@@ -19,6 +19,8 @@ h1 { padding: 10px 12px 6px; font-size: 15px; font-weight: 600; border-bottom: 1
.toggle:checked::after { left: 16px; background: #fff; }
.regen { background: none; border: 1px solid #555; color: #aaa; border-radius: 4px; padding: 2px 6px; font-size: 11px; cursor: pointer; flex-shrink: 0; }
.regen:hover { border-color: #888; color: #ddd; }
.del { background: none; border: none; color: #664444; font-size: 15px; cursor: pointer; flex-shrink: 0; padding: 0 2px; line-height: 1; }
.del:hover { color: #ff613d; }
.actions { padding: 8px 12px; border-top: 1px solid #333; }
#regen-all { width: 100%; padding: 6px; background: #333; border: 1px solid #555; color: #ccc; border-radius: 4px; cursor: pointer; font-size: 12px; }
#regen-all:hover { background: #444; color: #fff; }
@@ -29,6 +31,18 @@ h1 { padding: 10px 12px 6px; font-size: 15px; font-weight: 600; border-bottom: 1
.danger { background: #3a1a1a; border: 1px solid #663333; color: #ff613d; }
.danger:hover { background: #4a2020; color: #ff8866; }
.gear { background: none; border: none; color: #666; font-size: 15px; cursor: pointer; flex-shrink: 0; padding: 0 2px; line-height: 1; }
.gear:hover, .gear.active { color: #4a9eff; }
.vector-panel { background: #252535; border-bottom: 1px solid #333; padding: 6px 12px 6px 30px; }
.vector-row { display: flex; align-items: center; gap: 6px; padding: 2px 0; }
.vector-label { flex: 1; font-size: 11px; color: #aaa; }
.toggle-sm { width: 26px !important; height: 14px !important; }
.toggle-sm::after { width: 10px !important; height: 10px !important; }
.toggle-sm:checked::after { left: 12px !important; }
.toggle.inherited { opacity: 0.5; }
.vector-reset { background: none; border: none; color: #666; font-size: 12px; cursor: pointer; padding: 0 2px; visibility: visible; }
.vector-reset:hover { color: #4a9eff; }
.dot-blue { background: #37adff; }
.dot-turquoise { background: #00c79a; }
.dot-green { background: #51cd00; }

View File

@@ -1,3 +1,18 @@
const VECTORS = {
canvas: "Canvas",
webgl: "WebGL",
audio: "Audio",
navigator: "Navigator",
screen: "Screen",
timezone: "Timezone",
webrtc: "WebRTC",
fonts: "Fonts",
clientRects: "Client Rects",
plugins: "Plugins",
battery: "Battery",
connection: "Connection"
};
async function loadContainers() {
const containers = await browser.runtime.sendMessage({ type: "getContainerList" });
const list = document.getElementById("container-list");
@@ -38,6 +53,13 @@ async function loadContainers() {
});
row.appendChild(toggle);
const gear = document.createElement("button");
gear.className = "gear";
gear.textContent = "\u2699";
gear.title = "Vector settings";
gear.addEventListener("click", () => toggleSettings(c.cookieStoreId, row, gear));
row.appendChild(gear);
const regen = document.createElement("button");
regen.className = "regen";
regen.textContent = "New";
@@ -53,10 +75,98 @@ async function loadContainers() {
});
row.appendChild(regen);
const del = document.createElement("button");
del.className = "del";
del.textContent = "\u00D7";
del.title = "Delete container";
del.addEventListener("click", async () => {
if (!confirm(`Delete container "${c.name}"? This removes all cookies and data for this site.`)) return;
await browser.runtime.sendMessage({
type: "deleteContainer",
cookieStoreId: c.cookieStoreId
});
const panel = row.nextElementSibling;
if (panel && panel.classList.contains("vector-panel")) panel.remove();
row.remove();
});
row.appendChild(del);
list.appendChild(row);
}
}
async function toggleSettings(cookieStoreId, row, gearBtn) {
const existing = row.nextElementSibling;
if (existing && existing.classList.contains("vector-panel")) {
existing.remove();
gearBtn.classList.remove("active");
return;
}
gearBtn.classList.add("active");
const panel = document.createElement("div");
panel.className = "vector-panel";
const { global, overrides } = await browser.runtime.sendMessage({
type: "getContainerVectors",
cookieStoreId
});
for (const [key, label] of Object.entries(VECTORS)) {
const item = document.createElement("div");
item.className = "vector-row";
const span = document.createElement("span");
span.className = "vector-label";
span.textContent = label;
item.appendChild(span);
const hasOverride = overrides[key] !== undefined && overrides[key] !== null;
const globalEnabled = global[key] !== false;
const effectiveValue = hasOverride ? overrides[key] : globalEnabled;
const toggle = document.createElement("input");
toggle.type = "checkbox";
toggle.className = "toggle toggle-sm";
toggle.checked = effectiveValue;
if (!hasOverride) toggle.classList.add("inherited");
toggle.addEventListener("change", async () => {
overrides[key] = toggle.checked;
toggle.classList.remove("inherited");
await browser.runtime.sendMessage({
type: "setContainerVectors",
cookieStoreId,
vectors: overrides
});
});
item.appendChild(toggle);
const resetBtn = document.createElement("button");
resetBtn.className = "vector-reset";
resetBtn.textContent = "\u21A9";
resetBtn.title = "Reset to global default";
if (!hasOverride) resetBtn.style.visibility = "hidden";
resetBtn.addEventListener("click", async () => {
delete overrides[key];
toggle.checked = globalEnabled;
toggle.classList.add("inherited");
resetBtn.style.visibility = "hidden";
await browser.runtime.sendMessage({
type: "setContainerVectors",
cookieStoreId,
vectors: overrides
});
});
item.appendChild(resetBtn);
panel.appendChild(item);
}
row.after(panel);
}
document.getElementById("regen-all").addEventListener("click", async (e) => {
e.target.textContent = "Regenerating...";
await browser.runtime.sendMessage({ type: "regenerateAll" });