From b4d0115d22bf00d9754a819a9ff4b2e70f147947 Mon Sep 17 00:00:00 2001 From: Anton Dudakov Date: Fri, 21 Feb 2025 16:19:06 +0400 Subject: [PATCH 1/7] adds an ability to reopen current tab in the specific container with a shortcut --- src/js/background/backgroundLogic.js | 37 +++++++++++++--- src/manifest.json | 60 ++++++++++++++++++++++++++ test/features/reopen-shortcuts.test.js | 44 +++++++++++++++++++ 3 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 test/features/reopen-shortcuts.test.js diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index fb90ba6fc..4ad3a988b 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -13,9 +13,9 @@ const backgroundLogic = { ]), NUMBER_OF_KEYBOARD_SHORTCUTS: 10, unhideQueue: [], - init() { - browser.commands.onCommand.addListener(function (command) { + async init() { + browser.commands.onCommand.addListener(async function (command) { if (command === "sort_tabs") { backgroundLogic.sortTabs(); return; @@ -23,10 +23,33 @@ const backgroundLogic = { for (let i=0; i < backgroundLogic.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { const key = "open_container_" + i; + const reopenKey = "reopen_in_container_" + i; const cookieStoreId = identityState.keyboardShortcut[key]; + + if (cookieStoreId === "none") { + continue; + } + if (command === key) { - if (cookieStoreId === "none") return; browser.tabs.create({cookieStoreId}); + return; + } + + if (command === reopenKey) { + const currentTab = await browser.tabs.query({active: true, currentWindow: true}); + if (currentTab.length > 0) { + const tab = currentTab[0]; + + await browser.tabs.create({ + url: tab.url, + cookieStoreId: cookieStoreId, + index: tab.index + 1, + active: tab.active + }); + + await browser.tabs.remove(tab.id); + } + return; } } }); @@ -85,10 +108,14 @@ const backgroundLogic = { updateTranslationInManifest() { for (let index = 0; index < 10; index++) { - const ajustedIndex = index + 1; // We want to start from 1 instead of 0 in the UI. + const adjustedIndex = index + 1; browser.commands.update({ name: `open_container_${index}`, - description: browser.i18n.getMessage("containerShortcut", `${ajustedIndex}`) + description: browser.i18n.getMessage("containerShortcut", `${adjustedIndex}`) + }); + browser.commands.update({ + name: `reopen_in_container_${index}`, + description: browser.i18n.getMessage("reopenInContainerShortcut", `${adjustedIndex}`) }); } }, diff --git a/src/manifest.json b/src/manifest.json index 94de8ed5c..60c7c64e7 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -109,6 +109,66 @@ "default": "Ctrl+Shift+0" }, "description": "__MSG_containerShortcut__" + }, + "reopen_in_container_0": { + "suggested_key": { + "default": "Alt+Shift+1" + }, + "description": "__MSG_reopenInContainerShortcut__" + }, + "reopen_in_container_1": { + "suggested_key": { + "default": "Alt+Shift+2" + }, + "description": "__MSG_reopenInContainerShortcut__" + }, + "reopen_in_container_2": { + "suggested_key": { + "default": "Alt+Shift+3" + }, + "description": "__MSG_reopenInContainerShortcut__" + }, + "reopen_in_container_3": { + "suggested_key": { + "default": "Alt+Shift+4" + }, + "description": "__MSG_reopenInContainerShortcut__" + }, + "reopen_in_container_4": { + "suggested_key": { + "default": "Alt+Shift+5" + }, + "description": "__MSG_reopenInContainerShortcut__" + }, + "reopen_in_container_5": { + "suggested_key": { + "default": "Alt+Shift+6" + }, + "description": "__MSG_reopenInContainerShortcut__" + }, + "reopen_in_container_6": { + "suggested_key": { + "default": "Alt+Shift+7" + }, + "description": "__MSG_reopenInContainerShortcut__" + }, + "reopen_in_container_7": { + "suggested_key": { + "default": "Alt+Shift+8" + }, + "description": "__MSG_reopenInContainerShortcut__" + }, + "reopen_in_container_8": { + "suggested_key": { + "default": "Alt+Shift+9" + }, + "description": "__MSG_reopenInContainerShortcut__" + }, + "reopen_in_container_9": { + "suggested_key": { + "default": "Alt+Shift+0" + }, + "description": "__MSG_reopenInContainerShortcut__" } }, "browser_action": { diff --git a/test/features/reopen-shortcuts.test.js b/test/features/reopen-shortcuts.test.js new file mode 100644 index 000000000..ac3781cd8 --- /dev/null +++ b/test/features/reopen-shortcuts.test.js @@ -0,0 +1,44 @@ +const {initializeWithTab} = require("../common"); + +describe("Reopen Shortcuts Feature", function () { + beforeEach(async function () { + // Initialize with a tab in the default container + this.webExt = await initializeWithTab({ + cookieStoreId: "firefox-default", + url: "https://example.com" + }); + }); + + afterEach(function () { + this.webExt.destroy(); + }); + + describe("when using keyboard shortcut to reopen in container", function () { + beforeEach(async function () { + // Simulate the keyboard shortcut command + await this.webExt.background.browser.commands.onCommand.addListener.firstCall.args[0]("reopen_in_container_0"); + }); + + it("should open the page in the assigned container and close the original tab", async function () { + this.webExt.background.browser.tabs.create.should.have.been.calledWithMatch({ + url: "https://example.com", + cookieStoreId: "firefox-container-1", + index: 1, + active: true + }); + + this.webExt.background.browser.tabs.remove.should.have.been.called; + }); + }); + + describe("when container is set to 'none'", function () { + beforeEach(async function () { + await this.webExt.background.browser.commands.onCommand.addListener.firstCall.args[0]("reopen_in_container_9"); + }); + + it("should not reopen the tab", function () { + this.webExt.background.browser.tabs.create.should.not.have.been.called; + this.webExt.background.browser.tabs.remove.should.not.have.been.called; + }); + }); +}); \ No newline at end of file From fb5eb2c0dbfa9dc4a4da8facba53edf8956f1c11 Mon Sep 17 00:00:00 2001 From: Anton Dudakov Date: Tue, 15 Jul 2025 08:42:37 +0200 Subject: [PATCH 2/7] extracted reopenInContainer function which made it possible to remove async from the `init()` --- src/_locales | 2 +- src/js/background/backgroundLogic.js | 33 +++++++++++++++------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/_locales b/src/_locales index 30aab7b6d..6f74e6921 160000 --- a/src/_locales +++ b/src/_locales @@ -1 +1 @@ -Subproject commit 30aab7b6d876f02d22186bb534e84eefd8e1a2da +Subproject commit 6f74e692138082d0f6de4c40e6c2d1f5fe717005 diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index 4ad3a988b..f96c92d53 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -14,7 +14,7 @@ const backgroundLogic = { NUMBER_OF_KEYBOARD_SHORTCUTS: 10, unhideQueue: [], - async init() { + init() { browser.commands.onCommand.addListener(async function (command) { if (command === "sort_tabs") { backgroundLogic.sortTabs(); @@ -36,19 +36,7 @@ const backgroundLogic = { } if (command === reopenKey) { - const currentTab = await browser.tabs.query({active: true, currentWindow: true}); - if (currentTab.length > 0) { - const tab = currentTab[0]; - - await browser.tabs.create({ - url: tab.url, - cookieStoreId: cookieStoreId, - index: tab.index + 1, - active: tab.active - }); - - await browser.tabs.remove(tab.id); - } + backgroundLogic.reopenInContainer(cookieStoreId); return; } } @@ -89,6 +77,21 @@ const backgroundLogic = { browser.commands.reset("sort_tabs"); } } + + async reopenInContainer(cookieStoreId) { + const currentTab = await browser.tabs.query({ active: true, currentWindow: true }) + + if (currentTab.length > 0) { + const tab = currentTab[0]; + + browser.tabs.create({ + url: tab.url, + cookieStoreId: cookieStoreId, + index: tab.index + 1, + active: tab.active + }); + + browser.tabs.remove(tab.id); } }, @@ -108,7 +111,7 @@ const backgroundLogic = { updateTranslationInManifest() { for (let index = 0; index < 10; index++) { - const adjustedIndex = index + 1; + const adjustedIndex = index + 1; // We want to start from 1 instead of 0 in the UI. browser.commands.update({ name: `open_container_${index}`, description: browser.i18n.getMessage("containerShortcut", `${adjustedIndex}`) From 37a2b672249e774372655afae95f06f52b6208c7 Mon Sep 17 00:00:00 2001 From: Anton Dudakov Date: Tue, 15 Jul 2025 10:38:52 +0200 Subject: [PATCH 3/7] fixes merge conflicts --- src/js/background/backgroundLogic.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index f96c92d53..850f8346b 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -77,6 +77,8 @@ const backgroundLogic = { browser.commands.reset("sort_tabs"); } } + } + }, async reopenInContainer(cookieStoreId) { const currentTab = await browser.tabs.query({ active: true, currentWindow: true }) From eda79aaf05b268d539cbd3db90bb66184104e988 Mon Sep 17 00:00:00 2001 From: Anton Dudakov Date: Wed, 1 Oct 2025 18:45:59 +0400 Subject: [PATCH 4/7] address feedback --- src/js/background/backgroundLogic.js | 78 ++++++++++++++-------------- src/js/background/constants.js | 6 +++ src/js/background/identityState.js | 20 ++++--- src/js/background/index.html | 1 + src/manifest.json | 30 ----------- 5 files changed, 60 insertions(+), 75 deletions(-) create mode 100644 src/js/background/constants.js diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index 850f8346b..7a76e291e 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -1,3 +1,4 @@ +/* global MAC_CONSTANTS */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -11,33 +12,35 @@ const backgroundLogic = { "about:home", "about:blank" ]), - NUMBER_OF_KEYBOARD_SHORTCUTS: 10, + // Use shared constants for counts + // NOTE: Keep in sync with MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS unhideQueue: [], init() { browser.commands.onCommand.addListener(async function (command) { if (command === "sort_tabs") { backgroundLogic.sortTabs(); - return; - } - - for (let i=0; i < backgroundLogic.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { - const key = "open_container_" + i; - const reopenKey = "reopen_in_container_" + i; - const cookieStoreId = identityState.keyboardShortcut[key]; - - if (cookieStoreId === "none") { - continue; + } else if (command.startsWith(MAC_CONSTANTS.OPEN_CONTAINER_PREFIX)) { + for (let i = 0; i < MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { + const key = MAC_CONSTANTS.OPEN_CONTAINER_PREFIX + i; + const cookieStoreId = identityState.keyboardShortcut[key]; + if (command === key) { + if (cookieStoreId !== "none") { + browser.tabs.create({cookieStoreId}); + } + break; + } } - - if (command === key) { - browser.tabs.create({cookieStoreId}); - return; - } - - if (command === reopenKey) { - backgroundLogic.reopenInContainer(cookieStoreId); - return; + } else if (command.startsWith(MAC_CONSTANTS.REOPEN_IN_CONTAINER_PREFIX)) { + for (let i = 0; i < MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { + const key = MAC_CONSTANTS.REOPEN_IN_CONTAINER_PREFIX + i; + const cookieStoreId = identityState.keyboardShortcut[key]; + if (command === key) { + if (cookieStoreId !== "none") { + backgroundLogic.reopenInContainer(cookieStoreId); + } + break; + } } } }); @@ -81,7 +84,7 @@ const backgroundLogic = { }, async reopenInContainer(cookieStoreId) { - const currentTab = await browser.tabs.query({ active: true, currentWindow: true }) + const currentTab = await browser.tabs.query({active: true, currentWindow: true}); if (currentTab.length > 0) { const tab = currentTab[0]; @@ -112,14 +115,14 @@ const backgroundLogic = { }, updateTranslationInManifest() { - for (let index = 0; index < 10; index++) { + for (let index = 0; index < MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS; index++) { const adjustedIndex = index + 1; // We want to start from 1 instead of 0 in the UI. browser.commands.update({ - name: `open_container_${index}`, + name: `${MAC_CONSTANTS.OPEN_CONTAINER_PREFIX}${index}`, description: browser.i18n.getMessage("containerShortcut", `${adjustedIndex}`) }); browser.commands.update({ - name: `reopen_in_container_${index}`, + name: `${MAC_CONSTANTS.REOPEN_IN_CONTAINER_PREFIX}${index}`, description: browser.i18n.getMessage("reopenInContainerShortcut", `${adjustedIndex}`) }); } @@ -231,9 +234,9 @@ const backgroundLogic = { const protocol = new URL(url).protocol; // We can't open these we just have to throw them away if (protocol === "about:" - || protocol === "chrome:" - || protocol === "moz-extension:" - || protocol === "file:") { + || protocol === "chrome:" + || protocol === "moz-extension:" + || protocol === "file:") { return false; } return true; @@ -250,7 +253,7 @@ const backgroundLogic = { async getTabs(options) { const requiredArguments = ["cookieStoreId", "windowId"]; this.checkArgs(requiredArguments, options, "getTabs"); - const { cookieStoreId, windowId } = options; + const {cookieStoreId, windowId} = options; const list = []; const tabs = await browser.tabs.query({ @@ -294,7 +297,7 @@ const backgroundLogic = { async moveTabsToWindow(options) { const requiredArguments = ["cookieStoreId", "windowId"]; this.checkArgs(requiredArguments, options, "moveTabsToWindow"); - const { cookieStoreId, windowId } = options; + const {cookieStoreId, windowId} = options; const list = await browser.tabs.query({ cookieStoreId, @@ -305,7 +308,7 @@ const backgroundLogic = { // Nothing to do if (list.length === 0 && - containerState.hiddenTabs.length === 0) { + containerState.hiddenTabs.length === 0) { return; } let newWindowObj; @@ -316,7 +319,7 @@ const backgroundLogic = { // Pin the default tab in the new window so existing pinned tabs can be moved after it. // From the docs (https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/move): // Note that you can't move pinned tabs to a position after any unpinned tabs in a window, or move any unpinned tabs to a position before any pinned tabs. - await browser.tabs.update(newWindowObj.tabs[0].id, { pinned: true }); + await browser.tabs.update(newWindowObj.tabs[0].id, {pinned: true}); browser.tabs.move(list.map((tab) => tab.id), { windowId: newWindowObj.id, @@ -324,8 +327,7 @@ const backgroundLogic = { }); } else { // As we get a blank tab here we will need to await the tabs creation - newWindowObj = await browser.windows.create({ - }); + newWindowObj = await browser.windows.create({}); hiddenDefaultTabToClose = true; } @@ -386,7 +388,7 @@ const backgroundLogic = { const identities = await browser.contextualIdentities.query({}); const identitiesOutput = {}; const identitiesPromise = identities.map(async (identity) => { - const { cookieStoreId } = identity; + const {cookieStoreId} = identity; const containerState = await identityState.storageArea.get(cookieStoreId); const openTabs = await browser.tabs.query({ cookieStoreId, @@ -445,7 +447,7 @@ const backgroundLogic = { if (!map.has(tab.cookieStoreId)) { const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(tab.cookieStoreId); - map.set(tab.cookieStoreId, { order: userContextId, tabs: [] }); + map.set(tab.cookieStoreId, {order: userContextId, tabs: []}); } map.get(tab.cookieStoreId).tabs.push(tab); } @@ -464,7 +466,7 @@ const backgroundLogic = { const sortMap = new Map([...map.entries()].sort((a, b) => a[1].order > b[1].order)); // Let's move tabs. - for (const { tabs } of sortMap.values()) { + for (const {tabs} of sortMap.values()) { for (const tab of tabs) { ++pos; browser.tabs.move(tab.id, { @@ -488,7 +490,7 @@ const backgroundLogic = { async hideTabs(options) { const requiredArguments = ["cookieStoreId", "windowId"]; this.checkArgs(requiredArguments, options, "hideTabs"); - const { cookieStoreId, windowId } = options; + const {cookieStoreId, windowId} = options; const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(cookieStoreId); @@ -528,7 +530,7 @@ const backgroundLogic = { }, cookieStoreId(userContextId) { - if(userContextId === 0) return "firefox-default"; + if (userContextId === 0) return "firefox-default"; return `firefox-container-${userContextId}`; } }; diff --git a/src/js/background/constants.js b/src/js/background/constants.js new file mode 100644 index 000000000..27d558381 --- /dev/null +++ b/src/js/background/constants.js @@ -0,0 +1,6 @@ +// Shared constants for background scripts +window.MAC_CONSTANTS = { + OPEN_CONTAINER_PREFIX: "open_container_", + REOPEN_IN_CONTAINER_PREFIX: "reopen_in_container_", + NUMBER_OF_KEYBOARD_SHORTCUTS: 10, +}; \ No newline at end of file diff --git a/src/js/background/identityState.js b/src/js/background/identityState.js index 91142409e..1202c2a8a 100644 --- a/src/js/background/identityState.js +++ b/src/js/background/identityState.js @@ -1,3 +1,4 @@ +/* global MAC_CONSTANTS */ window.identityState = { keyboardShortcut: {}, storageArea: { @@ -50,18 +51,23 @@ window.identityState = { async loadKeyboardShortcuts () { const identities = await browser.contextualIdentities.query({}); - for (let i=0; i < backgroundLogic.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { - const key = "open_container_" + i; - const storageObject = await this.area.get(key); - if (storageObject[key]){ - identityState.keyboardShortcut[key] = storageObject[key]; + for (let i=0; i < MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { + const openKey = MAC_CONSTANTS.OPEN_CONTAINER_PREFIX + i; + const reopenKey = MAC_CONSTANTS.REOPEN_IN_CONTAINER_PREFIX + i; + const storageObject = await this.area.get(openKey); + + if (storageObject[openKey]){ + identityState.keyboardShortcut[openKey] = storageObject[openKey]; + identityState.keyboardShortcut[reopenKey] = storageObject[openKey]; continue; } if (identities[i]) { - identityState.keyboardShortcut[key] = identities[i].cookieStoreId; + identityState.keyboardShortcut[openKey] = identities[i].cookieStoreId; + identityState.keyboardShortcut[reopenKey] = identities[i].cookieStoreId; continue; } - identityState.keyboardShortcut[key] = "none"; + identityState.keyboardShortcut[openKey] = "none"; + identityState.keyboardShortcut[reopenKey] = "none"; } return identityState.keyboardShortcut; }, diff --git a/src/js/background/index.html b/src/js/background/index.html index 818dbb4a5..6922190bf 100644 --- a/src/js/background/index.html +++ b/src/js/background/index.html @@ -15,6 +15,7 @@ --> + diff --git a/src/manifest.json b/src/manifest.json index 60c7c64e7..6215b1c8f 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -111,63 +111,33 @@ "description": "__MSG_containerShortcut__" }, "reopen_in_container_0": { - "suggested_key": { - "default": "Alt+Shift+1" - }, "description": "__MSG_reopenInContainerShortcut__" }, "reopen_in_container_1": { - "suggested_key": { - "default": "Alt+Shift+2" - }, "description": "__MSG_reopenInContainerShortcut__" }, "reopen_in_container_2": { - "suggested_key": { - "default": "Alt+Shift+3" - }, "description": "__MSG_reopenInContainerShortcut__" }, "reopen_in_container_3": { - "suggested_key": { - "default": "Alt+Shift+4" - }, "description": "__MSG_reopenInContainerShortcut__" }, "reopen_in_container_4": { - "suggested_key": { - "default": "Alt+Shift+5" - }, "description": "__MSG_reopenInContainerShortcut__" }, "reopen_in_container_5": { - "suggested_key": { - "default": "Alt+Shift+6" - }, "description": "__MSG_reopenInContainerShortcut__" }, "reopen_in_container_6": { - "suggested_key": { - "default": "Alt+Shift+7" - }, "description": "__MSG_reopenInContainerShortcut__" }, "reopen_in_container_7": { - "suggested_key": { - "default": "Alt+Shift+8" - }, "description": "__MSG_reopenInContainerShortcut__" }, "reopen_in_container_8": { - "suggested_key": { - "default": "Alt+Shift+9" - }, "description": "__MSG_reopenInContainerShortcut__" }, "reopen_in_container_9": { - "suggested_key": { - "default": "Alt+Shift+0" - }, "description": "__MSG_reopenInContainerShortcut__" } }, From 2f6596259b06e2123a66eec5f4b0d20dc8461e21 Mon Sep 17 00:00:00 2001 From: Anton Dudakov Date: Thu, 26 Feb 2026 22:01:54 +0400 Subject: [PATCH 5/7] address feedback 02 --- src/js/background/backgroundLogic.js | 71 ++++++++++++++-------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index 7a76e291e..acc26dc83 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -12,35 +12,33 @@ const backgroundLogic = { "about:home", "about:blank" ]), - // Use shared constants for counts - // NOTE: Keep in sync with MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS + NUMBER_OF_KEYBOARD_SHORTCUTS: MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS, unhideQueue: [], init() { browser.commands.onCommand.addListener(async function (command) { if (command === "sort_tabs") { backgroundLogic.sortTabs(); - } else if (command.startsWith(MAC_CONSTANTS.OPEN_CONTAINER_PREFIX)) { - for (let i = 0; i < MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { - const key = MAC_CONSTANTS.OPEN_CONTAINER_PREFIX + i; - const cookieStoreId = identityState.keyboardShortcut[key]; - if (command === key) { - if (cookieStoreId !== "none") { - browser.tabs.create({cookieStoreId}); - } - break; - } + return; + } + + for (let i=0; i < backgroundLogic.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { + const key = MAC_CONSTANTS.OPEN_CONTAINER_PREFIX + i; + const reopenKey = MAC_CONSTANTS.REOPEN_IN_CONTAINER_PREFIX + i; + const cookieStoreId = identityState.keyboardShortcut[key]; + + if (cookieStoreId === "none") { + continue; } - } else if (command.startsWith(MAC_CONSTANTS.REOPEN_IN_CONTAINER_PREFIX)) { - for (let i = 0; i < MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { - const key = MAC_CONSTANTS.REOPEN_IN_CONTAINER_PREFIX + i; - const cookieStoreId = identityState.keyboardShortcut[key]; - if (command === key) { - if (cookieStoreId !== "none") { - backgroundLogic.reopenInContainer(cookieStoreId); - } - break; - } + + if (command === key) { + browser.tabs.create({cookieStoreId}); + return; + } + + if (command === reopenKey) { + backgroundLogic.reopenInContainer(cookieStoreId); + return; } } }); @@ -84,7 +82,7 @@ const backgroundLogic = { }, async reopenInContainer(cookieStoreId) { - const currentTab = await browser.tabs.query({active: true, currentWindow: true}); + const currentTab = await browser.tabs.query({ active: true, currentWindow: true }) if (currentTab.length > 0) { const tab = currentTab[0]; @@ -234,9 +232,9 @@ const backgroundLogic = { const protocol = new URL(url).protocol; // We can't open these we just have to throw them away if (protocol === "about:" - || protocol === "chrome:" - || protocol === "moz-extension:" - || protocol === "file:") { + || protocol === "chrome:" + || protocol === "moz-extension:" + || protocol === "file:") { return false; } return true; @@ -253,7 +251,7 @@ const backgroundLogic = { async getTabs(options) { const requiredArguments = ["cookieStoreId", "windowId"]; this.checkArgs(requiredArguments, options, "getTabs"); - const {cookieStoreId, windowId} = options; + const { cookieStoreId, windowId } = options; const list = []; const tabs = await browser.tabs.query({ @@ -297,7 +295,7 @@ const backgroundLogic = { async moveTabsToWindow(options) { const requiredArguments = ["cookieStoreId", "windowId"]; this.checkArgs(requiredArguments, options, "moveTabsToWindow"); - const {cookieStoreId, windowId} = options; + const { cookieStoreId, windowId } = options; const list = await browser.tabs.query({ cookieStoreId, @@ -308,7 +306,7 @@ const backgroundLogic = { // Nothing to do if (list.length === 0 && - containerState.hiddenTabs.length === 0) { + containerState.hiddenTabs.length === 0) { return; } let newWindowObj; @@ -319,7 +317,7 @@ const backgroundLogic = { // Pin the default tab in the new window so existing pinned tabs can be moved after it. // From the docs (https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/move): // Note that you can't move pinned tabs to a position after any unpinned tabs in a window, or move any unpinned tabs to a position before any pinned tabs. - await browser.tabs.update(newWindowObj.tabs[0].id, {pinned: true}); + await browser.tabs.update(newWindowObj.tabs[0].id, { pinned: true }); browser.tabs.move(list.map((tab) => tab.id), { windowId: newWindowObj.id, @@ -327,7 +325,8 @@ const backgroundLogic = { }); } else { // As we get a blank tab here we will need to await the tabs creation - newWindowObj = await browser.windows.create({}); + newWindowObj = await browser.windows.create({ + }); hiddenDefaultTabToClose = true; } @@ -388,7 +387,7 @@ const backgroundLogic = { const identities = await browser.contextualIdentities.query({}); const identitiesOutput = {}; const identitiesPromise = identities.map(async (identity) => { - const {cookieStoreId} = identity; + const { cookieStoreId } = identity; const containerState = await identityState.storageArea.get(cookieStoreId); const openTabs = await browser.tabs.query({ cookieStoreId, @@ -447,7 +446,7 @@ const backgroundLogic = { if (!map.has(tab.cookieStoreId)) { const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(tab.cookieStoreId); - map.set(tab.cookieStoreId, {order: userContextId, tabs: []}); + map.set(tab.cookieStoreId, { order: userContextId, tabs: [] }); } map.get(tab.cookieStoreId).tabs.push(tab); } @@ -466,7 +465,7 @@ const backgroundLogic = { const sortMap = new Map([...map.entries()].sort((a, b) => a[1].order > b[1].order)); // Let's move tabs. - for (const {tabs} of sortMap.values()) { + for (const { tabs } of sortMap.values()) { for (const tab of tabs) { ++pos; browser.tabs.move(tab.id, { @@ -490,7 +489,7 @@ const backgroundLogic = { async hideTabs(options) { const requiredArguments = ["cookieStoreId", "windowId"]; this.checkArgs(requiredArguments, options, "hideTabs"); - const {cookieStoreId, windowId} = options; + const { cookieStoreId, windowId } = options; const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(cookieStoreId); @@ -530,7 +529,7 @@ const backgroundLogic = { }, cookieStoreId(userContextId) { - if (userContextId === 0) return "firefox-default"; + if(userContextId === 0) return "firefox-default"; return `firefox-container-${userContextId}`; } }; From 1f0c52277385929d25bab5d1af7fdc2d3a94866a Mon Sep 17 00:00:00 2001 From: Anton Dudakov Date: Sun, 1 Mar 2026 11:21:06 +0400 Subject: [PATCH 6/7] address feedback 03 --- src/js/background/backgroundLogic.js | 3 ++- src/js/background/constants.js | 6 +++++- src/js/background/identityState.js | 27 ++++++++++++++------------ test/features/reopen-shortcuts.test.js | 2 +- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index acc26dc83..29e782562 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -1,8 +1,9 @@ -/* global MAC_CONSTANTS */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* global MAC_CONSTANTS */ + const DEFAULT_TAB = "about:newtab"; const backgroundLogic = { diff --git a/src/js/background/constants.js b/src/js/background/constants.js index 27d558381..f7f547cdf 100644 --- a/src/js/background/constants.js +++ b/src/js/background/constants.js @@ -1,6 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + // Shared constants for background scripts window.MAC_CONSTANTS = { OPEN_CONTAINER_PREFIX: "open_container_", REOPEN_IN_CONTAINER_PREFIX: "reopen_in_container_", NUMBER_OF_KEYBOARD_SHORTCUTS: 10, -}; \ No newline at end of file +}; diff --git a/src/js/background/identityState.js b/src/js/background/identityState.js index 1202c2a8a..8d3d52fb0 100644 --- a/src/js/background/identityState.js +++ b/src/js/background/identityState.js @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + /* global MAC_CONSTANTS */ window.identityState = { keyboardShortcut: {}, @@ -54,20 +58,19 @@ window.identityState = { for (let i=0; i < MAC_CONSTANTS.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { const openKey = MAC_CONSTANTS.OPEN_CONTAINER_PREFIX + i; const reopenKey = MAC_CONSTANTS.REOPEN_IN_CONTAINER_PREFIX + i; - const storageObject = await this.area.get(openKey); - if (storageObject[openKey]){ - identityState.keyboardShortcut[openKey] = storageObject[openKey]; - identityState.keyboardShortcut[reopenKey] = storageObject[openKey]; - continue; - } - if (identities[i]) { - identityState.keyboardShortcut[openKey] = identities[i].cookieStoreId; - identityState.keyboardShortcut[reopenKey] = identities[i].cookieStoreId; - continue; + for (const key of [openKey, reopenKey]) { + const storageObject = await this.area.get(key); + + if (storageObject[key]){ + identityState.keyboardShortcut[key] = storageObject[key]; + } else if (identities[i]) { + identityState.keyboardShortcut[key] = identities[i].cookieStoreId; + } else { + identityState.keyboardShortcut[key] = "none"; + } } - identityState.keyboardShortcut[openKey] = "none"; - identityState.keyboardShortcut[reopenKey] = "none"; + } return identityState.keyboardShortcut; }, diff --git a/test/features/reopen-shortcuts.test.js b/test/features/reopen-shortcuts.test.js index ac3781cd8..c9d0b9f9c 100644 --- a/test/features/reopen-shortcuts.test.js +++ b/test/features/reopen-shortcuts.test.js @@ -41,4 +41,4 @@ describe("Reopen Shortcuts Feature", function () { this.webExt.background.browser.tabs.remove.should.not.have.been.called; }); }); -}); \ No newline at end of file +}); From d456b988733cfce0e90bdc5889e0911444a92350 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Mar 2026 08:13:56 +0000 Subject: [PATCH 7/7] Fix command handler to look up cookieStoreId per command type Address bakulf's review: each command (open_container/reopen_in_container) now looks up its own cookieStoreId from identityState.keyboardShortcut instead of sharing the open_container key's value. Also use shorthand property in reopenInContainer and fix indentation/semicolon. https://claude.ai/code/session_01KZXcX3hBia3UyqEuJttWJR --- src/js/background/backgroundLogic.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index 29e782562..34c7696cf 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -26,19 +26,19 @@ const backgroundLogic = { for (let i=0; i < backgroundLogic.NUMBER_OF_KEYBOARD_SHORTCUTS; i++) { const key = MAC_CONSTANTS.OPEN_CONTAINER_PREFIX + i; const reopenKey = MAC_CONSTANTS.REOPEN_IN_CONTAINER_PREFIX + i; - const cookieStoreId = identityState.keyboardShortcut[key]; - - if (cookieStoreId === "none") { - continue; - } - if (command === key) { - browser.tabs.create({cookieStoreId}); + const cookieStoreId = identityState.keyboardShortcut[key]; + if (cookieStoreId && cookieStoreId !== "none") { + browser.tabs.create({cookieStoreId}); + } return; } if (command === reopenKey) { - backgroundLogic.reopenInContainer(cookieStoreId); + const cookieStoreId = identityState.keyboardShortcut[reopenKey]; + if (cookieStoreId && cookieStoreId !== "none") { + backgroundLogic.reopenInContainer(cookieStoreId); + } return; } } @@ -83,14 +83,14 @@ const backgroundLogic = { }, async reopenInContainer(cookieStoreId) { - const currentTab = await browser.tabs.query({ active: true, currentWindow: true }) + const currentTab = await browser.tabs.query({ active: true, currentWindow: true }); if (currentTab.length > 0) { const tab = currentTab[0]; browser.tabs.create({ url: tab.url, - cookieStoreId: cookieStoreId, + cookieStoreId, index: tab.index + 1, active: tab.active });