From 5dae978ac02dbaad7d6f02b1de16e56b5bd63a5f Mon Sep 17 00:00:00 2001 From: Stefano Traverso Date: Mon, 30 Mar 2026 21:14:00 +0200 Subject: [PATCH] Add default/fallback container for unassigned sites When no specific container is assigned to a site, automatically open it in a user-configured fallback container. Configurable from the options page under a new "Default container" section. - assignManager: add _maybeFallbackContainerCookieStoreId() to look up the setting and verify the container still exists (auto-cleans if deleted) - onBeforeRequest: redirect to fallback container when no site assignment matches and the fallback is configured - options.html/options.js: new UI dropdown to select the fallback container Co-Authored-By: Claude Sonnet 4.6 --- src/js/background/assignManager.js | 31 +++++++++++++++++++++++++++- src/js/options.js | 33 ++++++++++++++++++++++++++++++ src/options.html | 10 +++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/js/background/assignManager.js b/src/js/background/assignManager.js index dee47ad38..875d28ad5 100644 --- a/src/js/background/assignManager.js +++ b/src/js/background/assignManager.js @@ -264,7 +264,11 @@ window.assignManager = { const siteIsolatedReloadInDefault = await this._maybeSiteIsolatedReloadInDefault(siteSettings, tab); - if (!siteIsolatedReloadInDefault) { + const fallbackCookieStoreId = (!siteSettings && !siteIsolatedReloadInDefault) + ? await this._maybeFallbackContainerCookieStoreId(tab) + : null; + + if (!siteIsolatedReloadInDefault && !fallbackCookieStoreId) { if (!siteSettings || userContextId === siteSettings.userContextId || this.storageArea.isExempted(options.url, tab.id)) { @@ -328,6 +332,15 @@ window.assignManager = { openTabId, tab.groupId ); + } else if (fallbackCookieStoreId) { + this.createTabWrapper( + options.url, + fallbackCookieStoreId, + tab.index + 1, + tab.active, + openTabId, + tab.groupId + ); } else { this.reloadPageInContainer( options.url, @@ -396,6 +409,22 @@ window.assignManager = { return currentContainerState && currentContainerState.isIsolated; }, + async _maybeFallbackContainerCookieStoreId(tab) { + const { fallbackContainerCookieStoreId } = await browser.storage.local.get("fallbackContainerCookieStoreId"); + if (!fallbackContainerCookieStoreId) return null; + // Already in the fallback container, no redirect needed + if (tab.cookieStoreId === fallbackContainerCookieStoreId) return null; + // Verify the container still exists + try { + await browser.contextualIdentities.get(fallbackContainerCookieStoreId); + return fallbackContainerCookieStoreId; + } catch { + // Container was deleted, clean up the setting + browser.storage.local.remove("fallbackContainerCookieStoreId"); + return null; + } + }, + maybeAddProxyListeners() { if (browser.proxy) { browser.proxy.onRequest.addListener(this.handleProxifiedRequest, {urls: [""]}); diff --git a/src/js/options.js b/src/js/options.js index 7679e2b31..524003ea9 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -59,6 +59,37 @@ async function changeTheme(event) { await browser.storage.local.set({currentThemeId: theme.selectedIndex}); } +async function setupDefaultContainerSelect() { + const select = document.querySelector("#defaultContainerSelect"); + const identities = await browser.contextualIdentities.query({}); + + const noneOption = document.createElement("option"); + noneOption.value = ""; + noneOption.textContent = browser.i18n.getMessage("defaultContainerNone"); + select.appendChild(noneOption); + + for (const identity of identities) { + const option = document.createElement("option"); + option.value = identity.cookieStoreId; + option.textContent = identity.name; + select.appendChild(option); + } + + const { fallbackContainerCookieStoreId } = await browser.storage.local.get("fallbackContainerCookieStoreId"); + if (fallbackContainerCookieStoreId) { + select.value = fallbackContainerCookieStoreId; + } +} + +async function changeDefaultContainer(event) { + const cookieStoreId = event.target.value || null; + if (cookieStoreId) { + await browser.storage.local.set({ fallbackContainerCookieStoreId: cookieStoreId }); + } else { + await browser.storage.local.remove("fallbackContainerCookieStoreId"); + } +} + async function setupOptions() { const { syncEnabled } = await browser.storage.local.get("syncEnabled"); const { replaceTabEnabled } = await browser.storage.local.get("replaceTabEnabled"); @@ -68,6 +99,7 @@ async function setupOptions() { document.querySelector("#replaceTabCheck").checked = !!replaceTabEnabled; document.querySelector("#changeTheme").selectedIndex = currentThemeId; setupContainerShortcutSelects(); + setupDefaultContainerSelect(); } async function setupContainerShortcutSelects () { @@ -124,6 +156,7 @@ document.addEventListener("DOMContentLoaded", setupOptions); document.querySelector("#syncCheck").addEventListener( "change", enableDisableSync); document.querySelector("#replaceTabCheck").addEventListener( "change", enableDisableReplaceTab); document.querySelector("#changeTheme").addEventListener( "change", changeTheme); +document.querySelector("#defaultContainerSelect").addEventListener("change", changeDefaultContainer); maybeShowPermissionsWarningIcon(); for (let i=0; i < NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { diff --git a/src/options.html b/src/options.html index 08544bff9..36dce2615 100644 --- a/src/options.html +++ b/src/options.html @@ -77,6 +77,16 @@

+

+
+ +

+
+