From 01384f20f5ae885edbb3fa3b739ca37d6e9add6e Mon Sep 17 00:00:00 2001 From: Matteo Ferrando Date: Wed, 25 Feb 2026 13:05:30 -0400 Subject: [PATCH 1/2] Add "always open in no container" option (#503) Allow users to assign a site to "No Container" so it always opens outside any container. When a URL assigned to no container is visited inside a container tab, it redirects to a default (non-container) tab. Co-Authored-By: Claude Sonnet 4.5 --- src/js/background/assignManager.js | 29 ++++++++++++--------- src/js/pageAction.js | 41 ++++++++++++++++++++++++++++++ src/js/popup.js | 40 +++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 12 deletions(-) diff --git a/src/js/background/assignManager.js b/src/js/background/assignManager.js index dee47ad38..d84313648 100644 --- a/src/js/background/assignManager.js +++ b/src/js/background/assignManager.js @@ -137,6 +137,7 @@ window.assignManager = { const macConfigs = await this.area.get(); for(const configKey of Object.keys(macConfigs)) { if (configKey.includes("siteContainerMap@@_")) { + if (String(macConfigs[configKey].userContextId) === "0") continue; const cookieStoreId = "firefox-container-" + macConfigs[configKey].userContextId; const match = identitiesList.find( @@ -228,19 +229,22 @@ window.assignManager = { browser.tabs.get(options.tabId), this.storageArea.get(options.url) ]); + const isDefaultContainerAssignment = siteSettings && String(siteSettings.userContextId) === "0"; let container; - try { - container = await browser.contextualIdentities - .get(backgroundLogic.cookieStoreId(siteSettings.userContextId)); - } catch { - container = false; - } + if (!isDefaultContainerAssignment) { + try { + container = await browser.contextualIdentities + .get(backgroundLogic.cookieStoreId(siteSettings.userContextId)); + } catch { + container = false; + } - // The container we have in the assignment map isn't present any - // more so lets remove it then continue the existing load - if (siteSettings && !container) { - this.deleteContainer(siteSettings.userContextId); - return {}; + // The container we have in the assignment map isn't present any + // more so lets remove it then continue the existing load + if (siteSettings && !container) { + this.deleteContainer(siteSettings.userContextId); + return {}; + } } const userContextId = this.getUserContextIdFromCookieStore(tab); @@ -267,6 +271,7 @@ window.assignManager = { if (!siteIsolatedReloadInDefault) { if (!siteSettings || userContextId === siteSettings.userContextId + || (isDefaultContainerAssignment && tab.cookieStoreId === "firefox-default") || this.storageArea.isExempted(options.url, tab.id)) { return {}; } @@ -320,7 +325,7 @@ window.assignManager = { } } - if (siteIsolatedReloadInDefault) { + if (siteIsolatedReloadInDefault || isDefaultContainerAssignment) { this.reloadPageInDefaultContainer( options.url, tab.index + 1, diff --git a/src/js/pageAction.js b/src/js/pageAction.js index 8aa51e476..64fa38156 100644 --- a/src/js/pageAction.js +++ b/src/js/pageAction.js @@ -2,6 +2,47 @@ async function init() { const fragment = document.createDocumentFragment(); const identities = await browser.contextualIdentities.query({}); + const currentTab = await Utils.currentTab(); + + { + const tr = document.createElement("tr"); + tr.classList.add("menu-item", "hover-highlight"); + const td = document.createElement("td"); + td.innerHTML = Utils.escaped` + + No Container + + `; + + tr.appendChild(td); + fragment.appendChild(tr); + + Utils.addEnterHandler(tr, async () => { + if (currentTab.cookieStoreId !== "firefox-default") { + await browser.runtime.sendMessage({ + method: "assignAndReloadInContainer", + url: currentTab.url, + currentUserContextId: false, + newUserContextId: 0, + tabIndex: currentTab.index + 1, + active: currentTab.active, + groupId: currentTab.groupId + }); + } else { + await Utils.setOrRemoveAssignment( + currentTab.id, + currentTab.url, + 0, + false + ); + } + window.close(); + }); + } + for (const identity of identities) { const tr = document.createElement("tr"); tr.classList.add("menu-item", "hover-highlight"); diff --git a/src/js/popup.js b/src/js/popup.js index 7b1f4705e..39c0a9610 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -1385,6 +1385,46 @@ Logic.registerPanel(ALWAYS_OPEN_IN_PICKER, { document.getElementById("new-container-div").innerHTML = ""; + { + const tr = document.createElement("tr"); + tr.classList.add("menu-item", "hover-highlight", "keyboard-nav"); + tr.setAttribute("tabindex", "0"); + const td = document.createElement("td"); + + td.innerHTML = Utils.escaped` + + No Container`; + + tr.appendChild(td); + fragment.appendChild(tr); + + Utils.addEnterHandler(tr, async () => { + const currentTab = await Utils.currentTab(); + if (currentTab.cookieStoreId !== "firefox-default") { + await browser.runtime.sendMessage({ + method: "assignAndReloadInContainer", + url: currentTab.url, + currentUserContextId: false, + newUserContextId: 0, + tabIndex: currentTab.index + 1, + active: currentTab.active, + groupId: currentTab.groupId + }); + } else { + await Utils.setOrRemoveAssignment( + currentTab.id, + currentTab.url, + 0, + false + ); + } + window.close(); + }); + } + for (const identity of identities) { const tr = document.createElement("tr"); tr.classList.add("menu-item", "hover-highlight", "keyboard-nav"); From 73bc9b9be8e976b1afec5fb93cc377428e742e9b Mon Sep 17 00:00:00 2001 From: Matteo Ferrando Date: Wed, 25 Feb 2026 13:57:15 -0400 Subject: [PATCH 2/2] Add "No Container" to Manage Containers for viewing/removing assignments Co-Authored-By: Claude Sonnet 4.5 --- src/js/popup.js | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/js/popup.js b/src/js/popup.js index 39c0a9610..38e92f1cc 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -381,7 +381,7 @@ const Logic = { }, getAssignmentObjectByContainer(userContextId) { - if (!userContextId) { + if (!userContextId && userContextId !== 0 && userContextId !== "0") { return {}; } return browser.runtime.sendMessage({ @@ -1185,6 +1185,31 @@ Logic.registerPanel(MANAGE_CONTAINERS_PICKER, { const identities = Logic.identities(); + { + const noContainerIdentity = { + name: "No Container", + cookieStoreId: "firefox-default", + }; + const tr = document.createElement("tr"); + tr.classList.add("menu-item", "hover-highlight", "keyboard-nav"); + tr.setAttribute("tabindex", "0"); + const td = document.createElement("td"); + + td.innerHTML = Utils.escaped` + + No Container`; + + tr.appendChild(td); + fragment.appendChild(tr); + + Utils.addEnterHandler(tr, () => { + Logic.showPanel(P_CONTAINER_ASSIGNMENTS, noContainerIdentity); + }); + } + for (const identity of identities) { const tr = document.createElement("tr"); tr.classList.add("menu-item", "hover-highlight", "keyboard-nav"); @@ -1472,22 +1497,27 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, { // This method is called when the panel is shown. async prepare() { const identity = Logic.currentIdentity(); + const isNoContainer = identity.cookieStoreId === "firefox-default"; // Populating the panel: name and icon document.getElementById("edit-assignments-title").textContent = identity.name; - const userContextId = Logic.currentUserContextId(); + const userContextId = isNoContainer ? "0" : Logic.currentUserContextId(); const assignments = await Logic.getAssignmentObjectByContainer(userContextId); - this.showAssignedContainers(assignments); + this.showAssignedContainers(assignments, isNoContainer); return Promise.resolve(null); }, - showAssignedContainers(assignments) { + showAssignedContainers(assignments, isNoContainer) { const closeContEl = document.querySelector("#close-container-assignment-panel"); Utils.addEnterHandler(closeContEl, () => { - const identity = Logic.currentIdentity(); - Logic.showPanel(P_CONTAINER_EDIT, identity, false, false); + if (isNoContainer) { + Logic.showPanel(MANAGE_CONTAINERS_PICKER, null, false, false); + } else { + const identity = Logic.currentIdentity(); + Logic.showPanel(P_CONTAINER_EDIT, identity, false, false); + } }); const assignmentPanel = document.getElementById("edit-sites-assigned"); @@ -1518,12 +1548,12 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, { trElement.getElementsByClassName("favicon")[0].appendChild(Utils.createFavIconElement(assumedUrl)); const deleteButton = trElement.querySelector(".trash-button"); Utils.addEnterHandler(deleteButton, async () => { - const userContextId = Logic.currentUserContextId(); + const userContextId = isNoContainer ? "0" : Logic.currentUserContextId(); // Lets show the message to the current tab // const currentTab = await Utils.currentTab(); Utils.setOrRemoveAssignment(false, assumedUrl, userContextId, true); delete assignments[siteKey]; - this.showAssignedContainers(assignments); + this.showAssignedContainers(assignments, isNoContainer); }); const resetButton = trElement.querySelector(".reset-button"); Utils.addEnterHandler(resetButton, async () => {