diff --git a/README.md b/README.md index 1b5e37f..def3d7d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ For more info, see: ## Development 1. `npm install` -2. `./node_modules/.bin/web-ext run -s src/ +2. `./node_modules/.bin/web-ext run -s src/` ### Testing TBD diff --git a/package.json b/package.json index 5a7ff73..ca8eb7d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "testpilot-containers", "title": "Multi-Account Containers", "description": "Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.", - "version": "5.0.0", + "version": "5.0.1", "author": "Andrea Marchesini, Luke Crouch and Jonathan Kingston", "bugs": { "url": "https://github.com/mozilla/testpilot-containers/issues" diff --git a/src/js/background/assignManager.js b/src/js/background/assignManager.js index 7040b4f..bcc8dc2 100644 --- a/src/js/background/assignManager.js +++ b/src/js/background/assignManager.js @@ -132,7 +132,7 @@ const assignManager = { // The container we have in the assignment map isn't present any more so lets remove it // then continue the existing load - if (!container) { + if (siteSettings && !container) { this.deleteContainer(siteSettings.userContextId); return {}; } @@ -147,15 +147,31 @@ const assignManager = { || (messageHandler.lastCreatedTab && messageHandler.lastCreatedTab.id === tab.id); const openTabId = removeTab ? tab.openerTabId : tab.id; + + // we decided to cancel the request at this point, register it as canceled request as early as possible + if (!this.canceledRequests[options.requestId]) { + this.canceledRequests[options.requestId] = true; + // register a cleanup for handled requestIds + // all relevant requests that come in that timeframe with the same requestId will be canceled + setTimeout(() => { + delete this.canceledRequests[options.requestId]; + }, 2000); + } else { + // if we see a request for the same requestId at this point then this is a redirect that we have to cancel to prevent opening two tabs + return { + cancel: true + }; + } + this.reloadPageInContainer( - options.url, - userContextId, - siteSettings.userContextId, - tab.index + 1, - tab.active, - siteSettings.neverAsk, - openTabId - ); + options.url, + userContextId, + siteSettings.userContextId, + tab.index + 1, + tab.active, + siteSettings.neverAsk, + openTabId + ); this.calculateContextMenu(tab); /* Removal of existing tabs: @@ -183,6 +199,7 @@ const assignManager = { }); // Before a request is handled by the browser we decide if we should route through a different container + this.canceledRequests = {}; browser.webRequest.onBeforeRequest.addListener((options) => { return this.onBeforeRequest(options); },{urls: [""], types: ["main_frame"]}, ["blocking"]); diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index 36bcb47..04906d8 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -131,9 +131,13 @@ const backgroundLogic = { let newWindowObj; let hiddenDefaultTabToClose; if (list.length) { - newWindowObj = await browser.windows.create({ - tabId: list.shift().id - }); + newWindowObj = await browser.windows.create(); + + // 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 }); + browser.tabs.move(list.map((tab) => tab.id), { windowId: newWindowObj.id, index: -1 diff --git a/src/js/background/messageHandler.js b/src/js/background/messageHandler.js index 1971953..6e5fced 100644 --- a/src/js/background/messageHandler.js +++ b/src/js/background/messageHandler.js @@ -72,6 +72,42 @@ const messageHandler = { return response; }); + // Handles external messages from webextensions + const externalExtensionAllowed = {}; + browser.runtime.onMessageExternal.addListener(async (message, sender) => { + if (!externalExtensionAllowed[sender.id]) { + const extensionInfo = await browser.management.get(sender.id); + if (!extensionInfo.permissions.includes("contextualIdentities")) { + throw new Error("Missing contextualIdentities permission"); + } + externalExtensionAllowed[sender.id] = true; + } + let response; + switch (message.method) { + case "getAssignment": + if (typeof message.url === "undefined") { + throw new Error("Missing message.url"); + } + response = assignManager.storageArea.get(message.url); + break; + default: + throw new Error("Unknown message.method"); + } + return response; + }); + // Delete externalExtensionAllowed if add-on installs/updates; permissions might change + browser.management.onInstalled.addListener(extensionInfo => { + if (externalExtensionAllowed[extensionInfo.id]) { + delete externalExtensionAllowed[extensionInfo.id]; + } + }); + // Delete externalExtensionAllowed if add-on uninstalls; not needed anymore + browser.management.onUninstalled.addListener(extensionInfo => { + if (externalExtensionAllowed[extensionInfo.id]) { + delete externalExtensionAllowed[extensionInfo.id]; + } + }); + if (browser.contextualIdentities.onRemoved) { browser.contextualIdentities.onRemoved.addListener(({contextualIdentity}) => { const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(contextualIdentity.cookieStoreId); diff --git a/src/js/popup.js b/src/js/popup.js index a644322..a9e3d26 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -524,7 +524,8 @@ Logic.registerPanel(P_CONTAINERS_LIST, { previous(); break; default: - if (e.keyCode >= 49 && e.keyCode <= 57) { + if ((e.keyCode >= 49 && e.keyCode <= 57) && + Logic._currentPanel === "containersList") { const element = selectables[e.keyCode - 48]; if (element) { element.click(); diff --git a/src/manifest.json b/src/manifest.json index 199f593..3963e24 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Firefox Multi-Account Containers", - "version": "5.0.0", + "version": "5.0.1", "description": "Multi-Account Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.", "icons": { @@ -11,6 +11,7 @@ "applications": { "gecko": { + "id": "@testpilot-containers", "strict_min_version": "57.0" } }, @@ -25,6 +26,7 @@ "contextualIdentities", "history", "idle", + "management", "storage", "tabs", "webRequestBlocking",