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 @@
+
+
+