From 0be03ebeb7148926ed79842c075eec41a45a0cf4 Mon Sep 17 00:00:00 2001 From: Jonathan Kingston Date: Thu, 24 Aug 2017 14:57:36 +0100 Subject: [PATCH] Remove tab counting code as also not needed, change tab counting to be per window. Prevent reopening of hidden tabs doesn't call itself. Fixes #750, Fixes #753, Fixes #756 --- webextension/js/background/backgroundLogic.js | 149 +++++++++--------- webextension/js/background/identityState.js | 97 +----------- webextension/js/background/messageHandler.js | 13 +- webextension/js/popup.js | 8 +- 4 files changed, 100 insertions(+), 167 deletions(-) diff --git a/webextension/js/background/backgroundLogic.js b/webextension/js/background/backgroundLogic.js index ecb86ca..2fd5c5c 100644 --- a/webextension/js/background/backgroundLogic.js +++ b/webextension/js/background/backgroundLogic.js @@ -52,6 +52,16 @@ const backgroundLogic = { }, async openTab(options) { + const userContextId = ("userContextId" in options) ? options.userContextId : 0; + const cookieStoreId = backgroundLogic.cookieStoreId(userContextId); + // Unhide all hidden tabs + this.showTabs({ + cookieStoreId + }); + return this.openNewTab(options); + }, + + async openNewTab(options) { let url = options.url || undefined; const userContextId = ("userContextId" in options) ? options.userContextId : 0; const active = ("nofocus" in options) ? options.nofocus : true; @@ -64,10 +74,6 @@ const backgroundLogic = { url = undefined; } - // Unhide all hidden tabs - this.showTabs({ - cookieStoreId - }); return browser.tabs.create({ url, active, @@ -76,60 +82,61 @@ const backgroundLogic = { }); }, - async getTabs(options) { - if (!("cookieStoreId" in options)) { - return new Error("getTabs must be called with cookieStoreId argument."); - } + checkArgs(requiredArguments, options, methodName) { + requiredArguments.forEach((argument) => { + if (!(argument in options)) { + return new Error(`${methodName} must be called with ${argument} argument.`); + } + }); + }, - const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(options.cookieStoreId); - await identityState.remapTabsIfMissing(options.cookieStoreId); - const isKnownContainer = await identityState._isKnownContainer(userContextId); - if (!isKnownContainer) { - return []; - } + async getTabs(options) { + const requiredArguments = ["cookieStoreId", "windowId"]; + this.checkArgs(requiredArguments, options, "getTabs"); + const { cookieStoreId, windowId } = options; const list = []; - const tabs = await this._containerTabs(options.cookieStoreId); + const tabs = await browser.tabs.query({ + cookieStoreId, + windowId + }); tabs.forEach((tab) => { list.push(identityState._createTabObject(tab)); }); - const containerState = await identityState.storageArea.get(options.cookieStoreId); + const containerState = await identityState.storageArea.get(cookieStoreId); return list.concat(containerState.hiddenTabs); }, async moveTabsToWindow(options) { - if (!("cookieStoreId" in options)) { - return new Error("moveTabsToWindow must be called with cookieStoreId argument."); - } + const requiredArguments = ["cookieStoreId", "windowId"]; + this.checkArgs(requiredArguments, options, "moveTabsToWindow"); + const { cookieStoreId, windowId } = options; - const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(options.cookieStoreId); - await identityState.remapTabsIfMissing(options.cookieStoreId); - if (!identityState._isKnownContainer(userContextId)) { - return null; - } + const list = await browser.tabs.query({ + cookieStoreId, + windowId + }); - const list = await identityState._matchTabsByContainer(options.cookieStoreId); - - const containerState = await identityState.storageArea.get(options.cookieStoreId); + const containerState = await identityState.storageArea.get(cookieStoreId); // Nothing to do if (list.length === 0 && containerState.hiddenTabs.length === 0) { return; } - const window = await browser.windows.create({ + const newWindowObj = await browser.windows.create({ tabId: list.shift().id }); browser.tabs.move(list.map((tab) => tab.id), { - windowId: window.id, + windowId: newWindowObj.id, index: -1 }); // Let's show the hidden tabs. for (let object of containerState.hiddenTabs) { // eslint-disable-line prefer-const browser.tabs.create(object.url || DEFAULT_TAB, { - windowId: window.id, - cookieStoreId: options.cookieStoreId + windowId: newWindowObj.id, + cookieStoreId }); } @@ -138,31 +145,46 @@ const backgroundLogic = { // Let's close all the normal tab in the new window. In theory it // should be only the first tab, but maybe there are addons doing // crazy stuff. - const tabs = browser.tabs.query({windowId: window.id}); + const tabs = browser.tabs.query({windowId: newWindowObj.id}); for (let tab of tabs) { // eslint-disable-line prefer-const - if (tabs.cookieStoreId !== options.cookieStoreId) { + if (tabs.cookieStoreId !== cookieStoreId) { browser.tabs.remove(tab.id); } } - return await identityState.storageArea.set(options.cookieStoreId, containerState); + return await identityState.storageArea.set(cookieStoreId, containerState); }, - async _closeTabs(userContextId) { + async _closeTabs(userContextId, windowId = false) { const cookieStoreId = this.cookieStoreId(userContextId); - const tabs = await this._containerTabs(cookieStoreId); + let tabs; + /* if we have no windowId we are going to close all this container (used for deleting) */ + if (windowId) { + tabs = await browser.tabs.query({ + cookieStoreId, + windowId + }); + } else { + await browser.tabs.query({ + cookieStoreId + }); + } const tabIds = tabs.map((tab) => tab.id); return browser.tabs.remove(tabIds); }, - async queryIdentitiesState() { + async queryIdentitiesState(windowId) { const identities = await browser.contextualIdentities.query({}); const identitiesOutput = {}; const identitiesPromise = identities.map(async function (identity) { - await identityState.remapTabsIfMissing(identity.cookieStoreId); - const containerState = await identityState.storageArea.get(identity.cookieStoreId); - identitiesOutput[identity.cookieStoreId] = { + const { cookieStoreId } = identity; + const containerState = await identityState.storageArea.get(cookieStoreId); + const openTabs = await browser.tabs.query({ + cookieStoreId, + windowId + }); + identitiesOutput[cookieStoreId] = { hasHiddenTabs: !!containerState.hiddenTabs.length, - hasOpenTabs: !!containerState.openTabs + hasOpenTabs: !!openTabs.length }; return; }); @@ -172,15 +194,15 @@ const backgroundLogic = { async sortTabs() { const windows = await browser.windows.getAll(); - for (let window of windows) { // eslint-disable-line prefer-const + for (let windowObj of windows) { // eslint-disable-line prefer-const // First the pinned tabs, then the normal ones. - await this._sortTabsInternal(window, true); - await this._sortTabsInternal(window, false); + await this._sortTabsInternal(windowObj, true); + await this._sortTabsInternal(windowObj, false); } }, - async _sortTabsInternal(window, pinnedTabs) { - const tabs = await browser.tabs.query({windowId: window.id}); + async _sortTabsInternal(windowObj, pinnedTabs) { + const tabs = await browser.tabs.query({windowId: windowObj.id}); let pos = 0; // Let's collect UCIs/tabs for this window. @@ -212,28 +234,22 @@ const backgroundLogic = { for (const tab of tabs) { ++pos; browser.tabs.move(tab.id, { - windowId: window.id, + windowId: windowObj.id, index: pos }); - //xulWindow.gBrowser.moveTabTo(tab, pos++); } }); }, async hideTabs(options) { - if (!("cookieStoreId" in options)) { - return new Error("hideTabs must be called with cookieStoreId option."); - } + const requiredArguments = ["cookieStoreId", "windowId"]; + this.checkArgs(requiredArguments, options, "hideTabs"); + const { cookieStoreId, windowId } = options; - const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(options.cookieStoreId); - await identityState.remapTabsIfMissing(options.cookieStoreId); - const isKnownContainer = await identityState._isKnownContainer(userContextId); - if (!isKnownContainer) { - return null; - } + const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(cookieStoreId); - const containerState = await identityState.storeHidden(options.cookieStoreId); - await this._closeTabs(userContextId); + const containerState = await identityState.storeHidden(cookieStoreId, windowId); + await this._closeTabs(userContextId, windowId); return containerState; }, @@ -243,17 +259,12 @@ const backgroundLogic = { } const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(options.cookieStoreId); - await identityState.remapTabsIfMissing(options.cookieStoreId); - if (!identityState._isKnownContainer(userContextId)) { - return null; - } - const promises = []; const containerState = await identityState.storageArea.get(options.cookieStoreId); for (let object of containerState.hiddenTabs) { // eslint-disable-line prefer-const - promises.push(this.openTab({ + promises.push(this.openNewTab({ userContextId: userContextId, url: object.url, nofocus: options.nofocus || false, @@ -269,12 +280,6 @@ const backgroundLogic = { cookieStoreId(userContextId) { return `firefox-container-${userContextId}`; - }, - - _containerTabs(cookieStoreId) { - return browser.tabs.query({ - cookieStoreId - }).catch((e) => {throw e;}); - }, + } }; diff --git a/webextension/js/background/identityState.js b/webextension/js/background/identityState.js index b43a2f6..a563d90 100644 --- a/webextension/js/background/identityState.js +++ b/webextension/js/background/identityState.js @@ -13,7 +13,10 @@ const identityState = { if (storageResponse && storeKey in storageResponse) { return storageResponse[storeKey]; } - return null; + const defaultContainerState = identityState._createIdentityState(); + await this.set(cookieStoreId, defaultContainerState); + + return defaultContainerState; }, set(cookieStoreId, data) { @@ -29,19 +32,13 @@ const identityState = { } }, - async _isKnownContainer(userContextId) { - const cookieStoreId = backgroundLogic.cookieStoreId(userContextId); - const state = await this.storageArea.get(cookieStoreId); - return !!state; - }, - _createTabObject(tab) { return Object.assign({}, tab); }, - async storeHidden(cookieStoreId) { + async storeHidden(cookieStoreId, windowId) { const containerState = await this.storageArea.get(cookieStoreId); - const tabsByContainer = await this._matchTabsByContainer(cookieStoreId); + const tabsByContainer = await browser.tabs.query({cookieStoreId, windowId}); tabsByContainer.forEach((tab) => { const tabObject = this._createTabObject(tab); // This tab is going to be closed. Let's mark this tabObject as @@ -54,89 +51,9 @@ const identityState = { return this.storageArea.set(cookieStoreId, containerState); }, - async containersCounts() { - let containersCounts = { // eslint-disable-line prefer-const - "shown": 0, - "hidden": 0, - "total": 0 - }; - const containers = await browser.contextualIdentities.query({}); - for (const id in containers) { - const container = containers[id]; - await this.remapTabsIfMissing(container.cookieStoreId); - const containerState = await this.storageArea.get(container.cookieStoreId); - if (containerState.openTabs > 0) { - ++containersCounts.shown; - ++containersCounts.total; - continue; - } else if (containerState.hiddenTabs.length > 0) { - ++containersCounts.hidden; - ++containersCounts.total; - continue; - } - } - return containersCounts; - }, - - async containerTabCount(cookieStoreId) { - // Returns the total of open and hidden tabs with this userContextId - let containerTabsCount = 0; - await identityState.remapTabsIfMissing(cookieStoreId); - const containerState = await this.storageArea.get(cookieStoreId); - containerTabsCount += containerState.openTabs; - containerTabsCount += containerState.hiddenTabs.length; - return containerTabsCount; - }, - - async totalContainerTabsCount() { - // Returns the number of total open tabs across ALL containers - let totalContainerTabsCount = 0; - const containers = await browser.contextualIdentities.query({}); - for (const id in containers) { - const container = containers[id]; - const cookieStoreId = container.cookieStoreId; - await identityState.remapTabsIfMissing(cookieStoreId); - totalContainerTabsCount += await this.storageArea.get(cookieStoreId).openTabs; - } - return totalContainerTabsCount; - }, - - async totalNonContainerTabsCount() { - // Returns the number of open tabs NOT IN a container - let totalNonContainerTabsCount = 0; - const tabs = await browser.tabs.query({}); - for (const tab of tabs) { - const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(tab.cookieStoreId); - if (userContextId === 0) { - ++totalNonContainerTabsCount; - } - } - return totalNonContainerTabsCount; - }, - - async remapTabsIfMissing(cookieStoreId) { - // We already know this cookieStoreId. - const containerState = await this.storageArea.get(cookieStoreId) || this._createIdentityState(); - - await this.storageArea.set(cookieStoreId, containerState); - await this.remapTabsFromUserContextId(cookieStoreId); - }, - - _matchTabsByContainer(cookieStoreId) { - return browser.tabs.query({cookieStoreId}); - }, - - async remapTabsFromUserContextId(cookieStoreId) { - const tabsByContainer = await this._matchTabsByContainer(cookieStoreId); - const containerState = await this.storageArea.get(cookieStoreId); - containerState.openTabs = tabsByContainer.length; - await this.storageArea.set(cookieStoreId, containerState); - }, - _createIdentityState() { return { - hiddenTabs: [], - openTabs: 0 + hiddenTabs: [] }; }, }; diff --git a/webextension/js/background/messageHandler.js b/webextension/js/background/messageHandler.js index e45a0fa..04af35c 100644 --- a/webextension/js/background/messageHandler.js +++ b/webextension/js/background/messageHandler.js @@ -45,23 +45,28 @@ const messageHandler = { backgroundLogic.showTabs({cookieStoreId: m.cookieStoreId}); break; case "hideTabs": - backgroundLogic.hideTabs({cookieStoreId: m.cookieStoreId}); + backgroundLogic.hideTabs({ + cookieStoreId: m.cookieStoreId, + windowId: m.windowId + }); break; case "checkIncompatibleAddons": // TODO break; case "moveTabsToWindow": response = backgroundLogic.moveTabsToWindow({ - cookieStoreId: m.cookieStoreId + cookieStoreId: m.cookieStoreId, + windowId: m.windowId }); break; case "getTabs": response = backgroundLogic.getTabs({ - cookieStoreId: m.cookieStoreId + cookieStoreId: m.cookieStoreId, + windowId: m.windowId }); break; case "queryIdentitiesState": - response = backgroundLogic.queryIdentitiesState(); + response = backgroundLogic.queryIdentitiesState(m.message.windowId); break; case "exemptContainerAssignment": response = assignManager._exemptTab(m); diff --git a/webextension/js/popup.js b/webextension/js/popup.js index edd3014..1aac9e2 100644 --- a/webextension/js/popup.js +++ b/webextension/js/popup.js @@ -190,7 +190,10 @@ const Logic = { const [identities, state] = await Promise.all([ browser.contextualIdentities.query({}), browser.runtime.sendMessage({ - method: "queryIdentitiesState" + method: "queryIdentitiesState", + message: { + windowId: browser.windows.WINDOW_ID_CURRENT + } }) ]); this._identities = identities.map((identity) => { @@ -654,6 +657,7 @@ Logic.registerPanel(P_CONTAINER_INFO, { try { browser.runtime.sendMessage({ method: identity.hasHiddenTabs ? "showTabs" : "hideTabs", + windowId: browser.windows.WINDOW_ID_CURRENT, cookieStoreId: Logic.currentCookieStoreId() }); window.close(); @@ -685,6 +689,7 @@ Logic.registerPanel(P_CONTAINER_INFO, { Logic.addEnterHandler(moveTabsEl, async function () { await browser.runtime.sendMessage({ method: "moveTabsToWindow", + windowId: browser.windows.WINDOW_ID_CURRENT, cookieStoreId: Logic.currentIdentity().cookieStoreId, }); window.close(); @@ -726,6 +731,7 @@ Logic.registerPanel(P_CONTAINER_INFO, { // Let's retrieve the list of tabs. const tabs = await browser.runtime.sendMessage({ method: "getTabs", + windowId: browser.windows.WINDOW_ID_CURRENT, cookieStoreId: Logic.currentIdentity().cookieStoreId }); return this.buildInfoTable(tabs);