diff --git a/src/js/background/assignManager.js b/src/js/background/assignManager.js index 765432d..7098f62 100644 --- a/src/js/background/assignManager.js +++ b/src/js/background/assignManager.js @@ -61,9 +61,9 @@ const assignManager = { if (storageResponse && siteStoreKey in storageResponse) { resolve(storageResponse[siteStoreKey]); } - resolve(()=> { throw new Error (siteStoreKey, " does not exist"); }); + resolve(null); }).catch((e) => { - throw e; // reject(e); + reject(e); }); }); }, @@ -101,8 +101,11 @@ const assignManager = { const siteConfigs = await this.area.get(); for(const urlKey of Object.keys(siteConfigs)) { if (urlKey.includes("siteContainerMap@@_")) { - // For some reason this is stored as string... lets check them both as that - if (!!userContextId && String(siteConfigs[urlKey].userContextId) !== String(userContextId)) { + // For some reason this is stored as string... lets check + // them both as that + if (!!userContextId && + String(siteConfigs[urlKey].userContextId) + !== String(userContextId)) { continue; } const site = siteConfigs[urlKey]; @@ -119,7 +122,8 @@ const assignManager = { _neverAsk(m) { const pageUrl = m.pageUrl; if (m.neverAsk === true) { - // If we have existing data and for some reason it hasn't been deleted etc lets update it + // If we have existing data and for some reason it hasn't been + // deleted etc lets update it this.storageArea.get(pageUrl).then((siteSettings) => { if (siteSettings) { siteSettings.neverAsk = true; @@ -138,7 +142,8 @@ const assignManager = { return true; }, - // Before a request is handled by the browser we decide if we should route through a different container + // Before a request is handled by the browser we decide if we should + // route through a different container async onBeforeRequest(options) { if (options.frameId !== 0 || options.tabId === -1) { return {}; @@ -150,13 +155,14 @@ const assignManager = { ]); let container; try { - container = await browser.contextualIdentities.get(backgroundLogic.cookieStoreId(siteSettings.userContextId)); + container = await browser.contextualIdentities + .get(backgroundLogic.cookieStoreId(siteSettings.userContextId)); } catch (e) { container = false; } - // The container we have in the assignment map isn't present any more so lets remove it - // then continue the existing load + // 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 {}; @@ -173,7 +179,8 @@ const assignManager = { const openTabId = removeTab ? tab.openerTabId : tab.id; if (!this.canceledRequests[tab.id]) { - // we decided to cancel the request at this point, register canceled request + // we decided to cancel the request at this point, register + // canceled request this.canceledRequests[tab.id] = { requestIds: { [options.requestId]: true @@ -183,8 +190,10 @@ const assignManager = { } }; - // since webRequest onCompleted and onErrorOccurred are not 100% reliable (see #1120) - // we register a timer here to cleanup canceled requests, just to make sure we don't + // since webRequest onCompleted and onErrorOccurred are not 100% + // reliable (see #1120) + // we register a timer here to cleanup canceled requests, just to + // make sure we don't // end up in a situation where certain urls in a tab.id stay canceled setTimeout(() => { if (this.canceledRequests[tab.id]) { @@ -196,10 +205,12 @@ const assignManager = { if (this.canceledRequests[tab.id].requestIds[options.requestId] || this.canceledRequests[tab.id].urls[options.url]) { // same requestId or url from the same tab - // this is a redirect that we have to cancel early to prevent opening two tabs + // this is a redirect that we have to cancel early to prevent + // opening two tabs cancelEarly = true; } - // we decided to cancel the request at this point, register canceled request + // we decided to cancel the request at this point, register canceled + // request this.canceledRequests[tab.id].requestIds[options.requestId] = true; this.canceledRequests[tab.id].urls[options.url] = true; if (cancelEarly) { @@ -221,15 +232,27 @@ const assignManager = { this.calculateContextMenu(tab); /* Removal of existing tabs: - We aim to open the new assigned container tab / warning prompt in it's own tab: - - As the history won't span from one container to another it seems most sane to not try and reopen a tab on history.back() - - When users open a new tab themselves we want to make sure we don't end up with three tabs as per: https://github.com/mozilla/testpilot-containers/issues/421 - If we are coming from an internal url that are used for the new tab page (NEW_TAB_PAGES), we can safely close as user is unlikely losing history - Detecting redirects on "new tab" opening actions is pretty hard as we don't get tab history: - - Redirects happen from Short URLs and tracking links that act as a gateway - - Extensions don't provide a way to history crawl for tabs, we could inject content scripts to do this - however they don't run on about:blank so this would likely be just as hacky. - We capture the time the tab was created and close if it was within the timeout to try to capture pages which haven't had user interaction or history. + We aim to open the new assigned container tab / warning prompt in + it's own tab: + - As the history won't span from one container to another it + seems most sane to not try and reopen a tab on history.back() + - When users open a new tab themselves we want to make sure we + don't end up with three tabs as per: + https://github.com/mozilla/testpilot-containers/issues/421 + If we are coming from an internal url that are used for the new + tab page (NEW_TAB_PAGES), we can safely close as user is unlikely + losing history + Detecting redirects on "new tab" opening actions is pretty hard + as we don't get tab history: + - Redirects happen from Short URLs and tracking links that act as + a gateway + - Extensions don't provide a way to history crawl for tabs, we + could inject content scripts to do this + however they don't run on about:blank so this would likely be + just as hacky. + We capture the time the tab was created and close if it was within + the timeout to try to capture pages which haven't had user + interaction or history. */ if (removeTab) { browser.tabs.remove(tab.id); @@ -241,10 +264,13 @@ const assignManager = { init() { browser.contextMenus.onClicked.addListener((info, tab) => { - info.bookmarkId ? this._onClickedBookmark(info) : this._onClickedHandler(info, tab); + info.bookmarkId ? + this._onClickedBookmark(info) : + this._onClickedHandler(info, tab); }); - // Before a request is handled by the browser we decide if we should route through a different container + // 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); @@ -266,21 +292,29 @@ const assignManager = { }, async resetBookmarksMenuItem() { - const hasPermission = await browser.permissions.contains({permissions: ["bookmarks"]}); + const hasPermission = await browser.permissions.contains({ + permissions: ["bookmarks"] + }); if (this.hadBookmark === hasPermission) { return; } this.hadBookmark = hasPermission; if (hasPermission) { this.initBookmarksMenu(); - browser.contextualIdentities.onCreated.addListener(this.contextualIdentityCreated); - browser.contextualIdentities.onUpdated.addListener(this.contextualIdentityUpdated); - browser.contextualIdentities.onRemoved.addListener(this.contextualIdentityRemoved); + browser.contextualIdentities.onCreated + .addListener(this.contextualIdentityCreated); + browser.contextualIdentities.onUpdated + .addListener(this.contextualIdentityUpdated); + browser.contextualIdentities.onRemoved + .addListener(this.contextualIdentityRemoved); } else { this.removeBookmarksMenu(); - browser.contextualIdentities.onCreated.removeListener(this.contextualIdentityCreated); - browser.contextualIdentities.onUpdated.removeListener(this.contextualIdentityUpdated); - browser.contextualIdentities.onRemoved.removeListener(this.contextualIdentityRemoved); + browser.contextualIdentities.onCreated + .removeListener(this.contextualIdentityCreated); + browser.contextualIdentities.onUpdated + .removeListener(this.contextualIdentityUpdated); + browser.contextualIdentities.onRemoved + .removeListener(this.contextualIdentityRemoved); } }, @@ -289,19 +323,25 @@ const assignManager = { parentId: assignManager.OPEN_IN_CONTAINER, id: changeInfo.contextualIdentity.cookieStoreId, title: changeInfo.contextualIdentity.name, - icons: { "16": `img/usercontext.svg#${changeInfo.contextualIdentity.icon}` } + icons: { "16": `img/usercontext.svg#${ + changeInfo.contextualIdentity.icon + }` } }); }, contextualIdentityUpdated(changeInfo) { - browser.contextMenus.update(changeInfo.contextualIdentity.cookieStoreId, { - title: changeInfo.contextualIdentity.name, - icons: { "16": `img/usercontext.svg#${changeInfo.contextualIdentity.icon}` } - }); + browser.contextMenus.update( + changeInfo.contextualIdentity.cookieStoreId, { + title: changeInfo.contextualIdentity.name, + icons: { "16": `img/usercontext.svg#${ + changeInfo.contextualIdentity.icon}` } + }); }, contextualIdentityRemoved(changeInfo) { - browser.contextMenus.remove(changeInfo.contextualIdentity.cookieStoreId); + browser.contextMenus.remove( + changeInfo.contextualIdentity.cookieStoreId + ); }, async _onClickedHandler(info, tab) { @@ -317,7 +357,9 @@ const assignManager = { } else { remove = true; } - await this._setOrRemoveAssignment(tab.id, info.pageUrl, userContextId, remove); + await this._setOrRemoveAssignment( + tab.id, info.pageUrl, userContextId, remove + ); break; case this.MENU_MOVE_ID: backgroundLogic.moveTabsToWindow({ @@ -338,7 +380,8 @@ const assignManager = { async _onClickedBookmark(info) { async function _getBookmarksFromInfo(info) { - const [bookmarkTreeNode] = await browser.bookmarks.get(info.bookmarkId); + const [bookmarkTreeNode] = + await browser.bookmarks.get(info.bookmarkId); if (bookmarkTreeNode.type === "folder") { return await browser.bookmarks.getChildren(bookmarkTreeNode.id); } @@ -347,8 +390,10 @@ const assignManager = { const bookmarks = await _getBookmarksFromInfo(info); for (const bookmark of bookmarks) { - // Some checks on the urls from https://github.com/Rob--W/bookmark-container-tab/ thanks! - if ( !/^(javascript|place):/i.test(bookmark.url) && bookmark.type !== "folder") { + // Some checks on the urls from + // https://github.com/Rob--W/bookmark-container-tab/ thanks! + if ( !/^(javascript|place):/i.test(bookmark.url) && + bookmark.type !== "folder") { const openInReaderMode = bookmark.url.startsWith("about:reader"); if(openInReaderMode) { try { @@ -376,7 +421,9 @@ const assignManager = { if (!("cookieStoreId" in tab)) { return false; } - return backgroundLogic.getUserContextIdFromCookieStoreId(tab.cookieStoreId); + return backgroundLogic.getUserContextIdFromCookieStoreId( + tab.cookieStoreId + ); }, isTabPermittedAssign(tab) { diff --git a/src/js/background/sync.js b/src/js/background/sync.js index 15447db..41497bf 100644 --- a/src/js/background/sync.js +++ b/src/js/background/sync.js @@ -1,4 +1,4 @@ -const SYNC_DEBUG = true; +const SYNC_DEBUG = false; const sync = { storageArea: { @@ -39,28 +39,32 @@ const sync = { async backup(options) { if (SYNC_DEBUG) console.log("backup"); // remove listeners to avoid an infinite loop! - browser.storage.onChanged.removeListener(sync.storageArea.onChangedListener); + browser.storage.onChanged.removeListener( + sync.storageArea.onChangedListener); removeContextualIdentityListeners(); - - await updateSyncIdentities(); - await updateCookieStoreIdMap(); - await updateSyncSiteAssignments(); - if (options && options.uuid) - await updateDeletedIdentityList(options.uuid); - if (options && options.siteStoreKey) - await addToDeletedSitesList(options.siteStoreKey); - if (options && options.undelete) - await removeFromDeletedSitesList(options.undelete); + try { + await updateSyncIdentities(); + await updateCookieStoreIdMap(); + await updateSyncSiteAssignments(); + if (options && options.uuid) + await updateDeletedIdentityList(options.uuid); + if (options && options.siteStoreKey) + await addToDeletedSitesList(options.siteStoreKey); + if (options && options.undelete) + await removeFromDeletedSitesList(options.undelete); - if (SYNC_DEBUG) { - const storage = await sync.storageArea.get(); - console.log("in sync: ", storage); - const localStorage = await browser.storage.local.get(); - console.log("inLocal:", localStorage); + if (SYNC_DEBUG) { + const storage = await sync.storageArea.get(); + console.log("in sync: ", storage); + const localStorage = await browser.storage.local.get(); + console.log("inLocal:", localStorage); + } + } catch (error) { + console.error("Error backing up", error); } - - await browser.storage.onChanged.addListener(sync.storageArea.onChangedListener); - await addContextualIdentityListeners(); + browser.storage.onChanged.addListener( + sync.storageArea.onChangedListener); + addContextualIdentityListeners(); async function updateSyncIdentities() { const identities = await browser.contextualIdentities.query({}); @@ -111,7 +115,8 @@ const sync = { async cleanup() { console.log("cleanupSync"); - browser.storage.onChanged.removeListener(sync.storageArea.onChangedListener); + browser.storage.onChanged.removeListener( + sync.storageArea.onChangedListener); const identitiesList = await sync.storageArea.getStoredObject("identities"); const cookieStoreIDmap = @@ -127,7 +132,8 @@ const sync = { console.log("removed ", cookieStoreId, " from sync list"); } } - await browser.storage.onChanged.addListener(sync.storageArea.onChangedListener); + await browser.storage.onChanged.addListener( + sync.storageArea.onChangedListener); }, onChangedListener(changes, areaName) { @@ -212,7 +218,8 @@ async function restoreFirstRun() { async function reconcileIdentitiesByName(){ console.log("reconcileIdentitiesByName"); const localIdentities = await browser.contextualIdentities.query({}); - const syncIdentities = await sync.storageArea.getStoredObject("identities"); + const syncIdentities = + await sync.storageArea.getStoredObject("identities"); const cookieStoreIDmap = await sync.storageArea.getStoredObject("cookieStoreIDmap"); for (const syncIdentity of syncIdentities) { @@ -311,7 +318,8 @@ async function reconcileSiteAssignments() { assignedSite, cookieStoreIDmap, ){ - const syncCookieStoreId = "firefox-container-" + assignedSite.userContextId; + const syncCookieStoreId = + "firefox-container-" + assignedSite.userContextId; return cookieStoreIDmap[syncCookieStoreId]; } } @@ -331,7 +339,8 @@ async function setAssignmentWithUUID (newUUID, assignedSite, urlKey) { } async function runSync() { - browser.storage.onChanged.removeListener(sync.storageArea.onChangedListener); + browser.storage.onChanged.removeListener( + sync.storageArea.onChangedListener); removeContextualIdentityListeners(); console.log("runSync"); await identityState.storageArea.cleanup(); @@ -416,7 +425,8 @@ async function reconcileIdentitiesByUUID() { function findIdentityFromSync(cookieStoreId, identitiesList){ for (const identity of identitiesList) { const { name, color, icon } = identity; - if (identity.cookieStoreId === cookieStoreId) return { name, color, icon }; + if (identity.cookieStoreId === cookieStoreId) + return { name, color, icon }; } } diff --git a/src/js/background/test.js b/src/js/background/test.js index 0cdee0a..97a6c53 100644 --- a/src/js/background/test.js +++ b/src/js/background/test.js @@ -1,10 +1,12 @@ browser.tests = { async runAll() { await this.test1(); + await this.test2(); }, async test1() { await browser.tests.stopSyncListeners(); + console.log("Testing new install with no sync"); // sync state on install: no sync data await browser.storage.sync.clear(); @@ -52,6 +54,123 @@ browser.tests = { console.log("Finished!"); }, + async test2() { + await browser.tests.stopSyncListeners(); + console.log("Testing sync differing"); + + // sync state on install: no sync data + await browser.storage.sync.clear(); + const syncData = { + "identities": [ + { + "name": "Personal", + "icon": "fingerprint", + "iconUrl": "resource://usercontext-content/fingerprint.svg", + "color": "red", + "colorCode": "#37adff", + "cookieStoreId": "firefox-container-146" + }, + { + "name": "Oscar", + "icon": "dollar", + "iconUrl": "resource://usercontext-content/dollar.svg", + "color": "green", + "colorCode": "#51cd00", + "cookieStoreId": "firefox-container-147" + }, + { + "name": "Mozilla", + "icon": "pet", + "iconUrl": "resource://usercontext-content/briefcase.svg", + "color": "red", + "colorCode": "#ff613d", + "cookieStoreId": "firefox-container-148" + }, + { + "name": "Groceries, obviously", + "icon": "cart", + "iconUrl": "resource://usercontext-content/cart.svg", + "color": "pink", + "colorCode": "#ffcb00", + "cookieStoreId": "firefox-container-149" + }, + { + "name": "Facebook", + "icon": "fence", + "iconUrl": "resource://usercontext-content/fence.svg", + "color": "toolbar", + "colorCode": "#7c7c7d", + "cookieStoreId": "firefox-container-150" + } + ], + "cookieStoreIDmap": { + "firefox-container-146": "22ded543-5173-44a5-a47a-8813535945ca", + "firefox-container-147": "63e5212f-0858-418e-b5a3-09c2dea61fcd", + "firefox-container-148": "71335417-158e-4d74-a55b-e9e9081601ec", + "firefox-container-149": "59c4e5f7-fe3b-435a-ae60-1340db31a91b", + "firefox-container-150": "3dc916fb-8c0a-4538-9758-73ef819a45f7" + }, + "assignedSites": {} + }; + await browser.storage.sync.set(syncData); + await browser.storage.local.clear(); + const localData = { + "browserActionBadgesClicked": [ "6.1.1" ], + "containerTabsOpened": 7, + "identitiesState@@_firefox-default": { "hiddenTabs": [] }, + "onboarding-stage": 5 + }; + await this.removeAllContainers(); + console.log("TEST_CONTAINERS.length", TEST_CONTAINERS.length); + for (let i=0; i < TEST_CONTAINERS.length; i++) { + //build identities + const newIdentity = + await browser.contextualIdentities.create(TEST_CONTAINERS[i]); + // fill identies with site assignments + if (TEST_ASSIGNMENTS[i]) { + localData[TEST_ASSIGNMENTS[i]] = { + "userContextId": + String( + newIdentity.cookieStoreId.replace(/^firefox-container-/, "") + ), + "neverAsk": true + }; + } + } + await browser.storage.local.set(localData); + console.log("local storage set: ", await browser.storage.local.get()); + + await sync.initSync(); + + const getSync = await browser.storage.sync.get(); + const getAssignedSites = + await assignManager.storageArea.getAssignedSites(); + const identities = await browser.contextualIdentities.query({}); + const localCookieStoreIDmap = + await identityState.getCookieStoreIDuuidMap(); + console.log(getSync.cookieStoreIDmap); + console.assert( + Object.keys(getSync.cookieStoreIDmap).length === 6, + "cookieStoreIDmap should have 6 entries" + ); + + console.assert( + Object.keys(localCookieStoreIDmap).length === 7, + "localCookieStoreIDmap should have 7 entries" + ); + + console.assert( + identities.length === 6, + "There should be 6 identities" + ); + + console.assert( + Object.keys(getAssignedSites).length === 5, + "There should be 5 site assignments" + ); + console.log("Finished!"); + }, + async removeAllContainers() { const identities = await browser.contextualIdentities.query({}); for (const identity of identities) { @@ -99,19 +218,13 @@ const TEST_CONTAINERS = [ }, ]; - -browser.resetMAC1 = async function () { - // for debugging and testing: remove all containers except the default 4 and the first one created - browser.tests.stopSyncListeners(); - - // sync state on install: no sync data - await browser.storage.sync.clear(); - - // FF1: no sync, Only default containers and 1 extra - browser.storage.local.clear(); - const localData = {"browserActionBadgesClicked":["6.1.1"],"containerTabsOpened":6,"identitiesState@@_firefox-container-1":{"hiddenTabs":[]},"identitiesState@@_firefox-container-2":{"hiddenTabs":[]},"identitiesState@@_firefox-container-3":{"hiddenTabs":[]},"identitiesState@@_firefox-container-4":{"hiddenTabs":[]},"identitiesState@@_firefox-container-6":{"hiddenTabs":[]},"identitiesState@@_firefox-default":{"hiddenTabs":[]},"onboarding-stage":5,"siteContainerMap@@_twitter.com":{"userContextId":"1","neverAsk":true},"siteContainerMap@@_www.facebook.com":{"userContextId":"2","neverAsk":true},"siteContainerMap@@_www.linkedin.com":{"userContextId":"4","neverAsk":false}}; - browser.storage.local.set(localData); -}; +const TEST_ASSIGNMENTS = [ + "siteContainerMap@@_developer.mozilla.org", + "siteContainerMap@@_twitter.com", + "siteContainerMap@@_www.facebook.com", + "siteContainerMap@@_www.linkedin.com", + "siteContainerMap@@_reddit.com" +]; browser.resetMAC2 = async function () { // for debugging and testing: remove all containers except the default 4 and the first one created diff --git a/test/features/sync.test.js b/test/features/sync.test.js index 9a91c98..ddc87a0 100644 --- a/test/features/sync.test.js +++ b/test/features/sync.test.js @@ -65,44 +65,4 @@ describe("Sync", () => { console.log("!!!b"); }); - it("should sync for the first time", async () => { - const mozContainer = await background.browser.contextualIdentities.create({ - name:"Test", - color:"green", - icon:"pet" - }); - console.log(await background.browser.contextualIdentities.query({})); - await helper.browser.initSyncTest({localStorage:SYNC_TEST_1_LOCAL}); - console.log(await background.browser.storage.local.get()); - for (const containerName of SYNC_TEST_CONTAINERS) { - const storageKeyString = "identitiesState@@_" + containerName; - const answer = await background.browser.storage.local.get(storageKeyString); - expect(answer[storageKeyString].hasOwnProperty("macAddonUUID")).to.be.true; - } - const storageKeyString = "identitiesState@@_" + mozContainer.cookieStoreId; - const answer = await background.browser.storage.local.get(storageKeyString); - expect(answer[storageKeyString].hasOwnProperty("macAddonUUID")).to.be.true; - }); -}); - -const SYNC_TEST_1_LOCAL = { - "browserActionBadgesClicked":["6.1.1"], - "containerTabsOpened":6, - "identitiesState@@_firefox-container-1":{"hiddenTabs":[]}, - "identitiesState@@_firefox-container-2":{"hiddenTabs":[]}, - "identitiesState@@_firefox-container-3":{"hiddenTabs":[]}, - "identitiesState@@_firefox-container-4":{"hiddenTabs":[]}, - "identitiesState@@_firefox-container-6":{"hiddenTabs":[]}, - "identitiesState@@_firefox-default":{"hiddenTabs":[]}, - "onboarding-stage":5, - "siteContainerMap@@_twitter.com":{"userContextId":"1","neverAsk":true}, - "siteContainerMap@@_www.facebook.com":{"userContextId":"2","neverAsk":true}, - "siteContainerMap@@_www.linkedin.com":{"userContextId":"4","neverAsk":false} -}; - -const SYNC_TEST_CONTAINERS = [ - "firefox-container-1", - "firefox-container-2", - "firefox-container-3", - "firefox-container-4" -]; \ No newline at end of file +}); \ No newline at end of file