added some checks on sync and some error handling for bad site assignment info

This commit is contained in:
Kendall Werts 2020-01-09 16:36:26 -06:00
parent 9de6df6157
commit 5ae7a395a1
4 changed files with 285 additions and 163 deletions

View file

@ -107,6 +107,31 @@ const assignManager = {
}
return sites;
},
/*
* Looks for abandoned site assignments. If there is no identity with
* the site assignment's userContextId (cookieStoreId), then the assignment
* is removed.
*/
async cleanup() {
const identitiesList = await browser.contextualIdentities.query({});
const macConfigs = await this.area.get();
for(const configKey of Object.keys(macConfigs)) {
if (configKey.includes("siteContainerMap@@_")) {
const cookieStoreId =
"firefox-container-" + macConfigs[configKey].userContextId;
const match = identitiesList.find(
localIdentity => localIdentity.cookieStoreId === cookieStoreId
);
if (!match) {
await this.remove(configKey.replace(/^siteContainerMap@@_/, "https://"));
continue;
}
}
}
}
},
_neverAsk(m) {

View file

@ -51,9 +51,7 @@ const sync = {
async backup(options) {
// remove listeners to avoid an infinite loop!
await browser.storage.onChanged.removeListener(
sync.storageArea.onChangedListener);
await removeContextualIdentityListeners();
await sync.checkForListenersMaybeRemove();
await updateSyncIdentities();
await updateCookieStoreIdMap();
@ -71,9 +69,8 @@ const sync = {
console.log("inLocal:", localStorage);
console.log("idents: ", await browser.contextualIdentities.query({}));
}
browser.storage.onChanged.addListener(
sync.storageArea.onChangedListener);
addContextualIdentityListeners();
await sync.checkForListenersMaybeAdd();
async function updateSyncIdentities() {
const identities = await browser.contextualIdentities.query({});
@ -93,9 +90,8 @@ const sync = {
}
async function updateDeletedIdentityList(deletedIdentityUUID) {
let { deletedIdentityList } =
await sync.storageArea.get("deletedIdentityList");
if (!deletedIdentityList) deletedIdentityList = [];
const deletedIdentityList =
await sync.storageArea.getDeletedIdentityList();
if (
deletedIdentityList.find(element => element === deletedIdentityUUID)
) return;
@ -104,74 +100,91 @@ const sync = {
}
async function addToDeletedSitesList(siteStoreKey) {
let { deletedSiteList } =
await sync.storageArea.get("deletedSiteList");
if (!deletedSiteList) deletedSiteList = [];
const deletedSiteList =
await sync.storageArea.getDeletedSiteList();
if (deletedSiteList.find(element => element === siteStoreKey)) return;
deletedSiteList.push(siteStoreKey);
await sync.storageArea.set({ deletedSiteList });
}
async function removeFromDeletedSitesList(siteStoreKey) {
let { deletedSiteList } =
await sync.storageArea.get("deletedSiteList");
if (!deletedSiteList) return;
deletedSiteList = deletedSiteList
const deletedSiteList =
await sync.storageArea.getDeletedSiteList();
const newDeletedSiteList = deletedSiteList
.filter(element => element !== siteStoreKey);
await sync.storageArea.set({ deletedSiteList });
await sync.storageArea.set({ deletedSiteList: newDeletedSiteList });
}
},
/*
* Ensures all sync info matches. But maybe we shouldn't even use
* sync info that doesn't match.
*/
async cleanup() {
console.log("cleanupSync");
browser.storage.onChanged.removeListener(
sync.storageArea.onChangedListener);
const identitiesList =
await sync.storageArea.getIdentities();
const cookieStoreIDmap =
await sync.storageArea.getCookieStoreIDMap();
for(const cookieStoreId of Object.keys(cookieStoreIDmap)) {
const match = identitiesList
.find(syncIdentity =>
syncIdentity.cookieStoreId === cookieStoreId
);
if (!match) {
delete cookieStoreIDmap[cookieStoreId];
await sync.storageArea.set({ cookieStoreIDmap });
console.log("removed ", cookieStoreId, " from sync list");
}
}
await browser.storage.onChanged.addListener(
sync.storageArea.onChangedListener);
},
onChangedListener(changes, areaName) {
if (areaName === "sync") sync.runSync();
if (areaName === "sync") sync.errorHandledRunSync();
},
async addToDeletedList(changeInfo) {
const identity = changeInfo.contextualIdentity;
console.log("addToDeletedList", identity.cookieStoreId);
const deletedUUID =
await identityState.lookupMACaddonUUID(identity.cookieStoreId);
await identityState.storageArea.remove(identity.cookieStoreId);
console.log(deletedUUID);
sync.storageArea.backup({uuid: deletedUUID});
},
async checkSyncForMismatches() {
const cookieStoreIDmap = await this.getCookieStoreIDMap();
const identities = await this.getIdentities();
for (const cookieStoreId of Object.keys(cookieStoreIDmap)) {
const match = identities.find(identity =>
identity.cookieStoreId === cookieStoreId
);
if (!match) return false;
// if one has no match, this is bad data.
console.log("Bad Data, skipping");
}
return !(Object.entries(cookieStoreIDmap).length === 0);
}
},
init() {
const errorHandledRunSync = () => {
this.runSync().catch((error)=> {
console.error("Error from runSync", error);
});
};
browser.runtime.onInstalled.addListener(errorHandledRunSync);
browser.runtime.onStartup.addListener(errorHandledRunSync);
browser.runtime.onInstalled.addListener(this.errorHandledRunSync);
browser.runtime.onStartup.addListener(this.errorHandledRunSync);
},
async errorHandledRunSync () {
sync.runSync().catch(async (error)=> {
console.error("Error from runSync", error);
sync.checkForListenersMaybeAdd();
});
},
async checkForListenersMaybeAdd() {
const hasStorageListener =
await browser.storage.onChanged.hasListener(
sync.storageArea.onChangedListener
);
if (! await hasContextualIdentityListeners()) {
addContextualIdentityListeners();
}
if (! hasStorageListener) {
browser.storage.onChanged.addListener(
sync.storageArea.onChangedListener);
}
},
async checkForListenersMaybeRemove() {
const hasStorageListener =
await browser.storage.onChanged.hasListener(
sync.storageArea.onChangedListener
);
if (await hasContextualIdentityListeners()) {
removeContextualIdentityListeners();
}
if (hasStorageListener) {
browser.storage.onChanged.removeListener(
sync.storageArea.onChangedListener);
}
},
async runSync() {
@ -182,21 +195,19 @@ const sync = {
console.log("inLocal: ", localInfo);
console.log("indents: ", await browser.contextualIdentities.query({}));
}
browser.storage.onChanged.removeListener(
sync.storageArea.onChangedListener);
removeContextualIdentityListeners();
await sync.checkForListenersMaybeRemove();
console.log("runSync");
await identityState.storageArea.cleanup();
if (await sync.storageArea.hasSyncStorage()){
await sync.storageArea.cleanup();
console.log("storage found, attempting to restore ...");
await restore();
return;
}
console.log("no sync storage, backing up...");
await identityState.storageArea.cleanup();
await assignManager.storageArea.cleanup();
const hasSyncStorage = await sync.storageArea.hasSyncStorage();
const dataIsReliable = await sync.storageArea.checkSyncForMismatches();
if (hasSyncStorage && dataIsReliable) await restore();
await sync.storageArea.backup();
return; },
return;
},
};
sync.init();
@ -205,7 +216,7 @@ async function restore() {
console.log("restore");
await reconcileIdentities();
await reconcileSiteAssignments();
await sync.storageArea.backup();
return;
}
/*
@ -224,7 +235,13 @@ async function reconcileIdentities(){
const deletedCookieStoreId =
await identityState.lookupCookieStoreId(deletedUUID);
if (deletedCookieStoreId){
await browser.contextualIdentities.remove(deletedCookieStoreId);
try{
await browser.contextualIdentities.remove(deletedCookieStoreId);
} catch (error) {
// if the identity we are deleting is not there, that's fine.
console.error("Error deleting contextualIdentity", deletedCookieStoreId);
continue;
}
}
}
@ -291,23 +308,32 @@ async function ifUUIDMatch(syncIdentity, localCookieStoreID) {
icon: syncIdentity.icon
};
if (SYNC_DEBUG) {
const getIdent =
try {
const getIdent =
await browser.contextualIdentities.get(localCookieStoreID);
if (getIdent.name !== identityInfo.name) {
console.log(getIdent.name, "Change name: ", identityInfo.name);
}
if (getIdent.color !== identityInfo.color) {
console.log(getIdent.name, "Change color: ", identityInfo.color);
}
if (getIdent.icon !== identityInfo.icon) {
console.log(getIdent.name, "Change icon: ", identityInfo.icon);
if (getIdent.name !== identityInfo.name) {
console.log(getIdent.name, "Change name: ", identityInfo.name);
}
if (getIdent.color !== identityInfo.color) {
console.log(getIdent.name, "Change color: ", identityInfo.color);
}
if (getIdent.icon !== identityInfo.icon) {
console.log(getIdent.name, "Change icon: ", identityInfo.icon);
}
} catch (error) {
//if this fails, there is probably differing sync info.
console.error("Error getting info on CI", error);
}
}
try {
// update the local container with the sync data
await browser.contextualIdentities
.update(localCookieStoreID, identityInfo);
return;
await browser.contextualIdentities
.update(localCookieStoreID, identityInfo);
return;
} catch (error) {
// If this fails, sync info is off.
console.error("Error udpating CI", error);
}
}
async function ifNoMatch(syncIdentity){
@ -351,34 +377,43 @@ async function reconcileSiteAssignments() {
for(const urlKey of Object.keys(assignedSitesFromSync)) {
const assignedSite = assignedSitesFromSync[urlKey];
const syncUUID =
await lookupSyncSiteAssigmentIdentityUUID(
assignedSite, cookieStoreIDmap, urlKey
);
if (syncUUID) {
try{
const syncUUID =
await lookupSyncSiteAssigmentIdentityUUID(
assignedSite, cookieStoreIDmap, urlKey
);
if (syncUUID) {
// Sync is truth.
// Not even looking it up. Just overwrite
console.log("new assignment ", assignedSite, ": ",
assignedSite.userContextId);
const newUUID = cookieStoreIDmap[
"firefox-container-" + assignedSite.userContextId
];
await setAssignmentWithUUID(newUUID, assignedSite, urlKey);
continue;
}
console.log("new assignment ", assignedSite, ": ",
assignedSite.userContextId);
const newUUID = cookieStoreIDmap[
"firefox-container-" + assignedSite.userContextId
];
// if there's no syncUUID, something is wrong, since these site
// assignments are from sync
throw new Error("Sync storage not aligned");
await setAssignmentWithUUID(newUUID, assignedSite, urlKey);
continue;
}
} catch (error) {
// this is probably old or incorrect site info in Sync
// skip and move on.
//console.error("Error assigning site", error);
}
}
async function lookupSyncSiteAssigmentIdentityUUID(
assignedSite,
cookieStoreIDmap,
urlKey
){
const syncCookieStoreId =
"firefox-container-" + assignedSite.userContextId;
return cookieStoreIDmap[syncCookieStoreId];
const uuid = cookieStoreIDmap[syncCookieStoreId];
if (!uuid) throw new Error (`No syncUUID found for : ${urlKey}`);
return uuid;
}
}
@ -414,4 +449,13 @@ function removeContextualIdentityListeners(listenerList) {
browser.contextualIdentities.onCreated.removeListener(listenerList[0]);
browser.contextualIdentities.onRemoved.removeListener(listenerList[1]);
browser.contextualIdentities.onUpdated.removeListener(listenerList[2]);
}
}
async function hasContextualIdentityListeners(listenerList) {
if(!listenerList) listenerList = syncCIListenerList;
return (
await browser.contextualIdentities.onCreated.hasListener(listenerList[0]) &&
await browser.contextualIdentities.onRemoved.hasListener(listenerList[1]) &&
await browser.contextualIdentities.onUpdated.hasListener(listenerList[2])
);
}

View file

@ -1,16 +1,32 @@
browser.tests = {
async runAll() {
await this.testIdentityStateCleanup();
await this.test1();
await this.testReconcileSiteAssignments();
await this.testInitialSync();
await this.test2();
await this.dupeTest();
},
// more unit tests
// if site assignment has no valid cookieStoreId, delete on local
async resetForManualTests() {
await browser.tests.stopSyncListeners();
console.log("reset");
await this.setState({}, LOCAL_DATA, TEST_CONTAINERS, []);
},
async testIdentityStateCleanup() {
await browser.tests.stopSyncListeners();
console.log("Testing the cleanup of local storage");
await this.setState({}, LOCAL_DATA, TEST_CONTAINERS);
await this.setState({}, LOCAL_DATA, TEST_CONTAINERS, []);
await browser.storage.local.set({
"identitiesState@@_firefox-container-5": {
"hiddenTabs": []
}
});
console.log("local storage set: ", await browser.storage.local.get());
await identityState.storageArea.cleanup();
@ -28,11 +44,76 @@ browser.tests = {
}
console.log("Finished!");
},
async test1() {
async testAssignManagerCleanup() {
await browser.tests.stopSyncListeners();
console.log("Testing the cleanup of local storage");
await this.setState({}, LOCAL_DATA, TEST_CONTAINERS, []);
await browser.storage.local.set({
"siteContainerMap@@_www.goop.com": {
"userContextId": "5",
"neverAsk": true,
"hostname": "www.goop.com"
}
});
console.log("local storage set: ", await browser.storage.local.get());
await assignManager.storageArea.cleanup();
await browser.storage.local.set({
"identitiesState@@_firefox-container-5": {
"hiddenTabs": []
}
});
const macConfigs = await browser.storage.local.get();
const assignments = [];
for(const configKey of Object.keys(macConfigs)) {
if (configKey.includes("siteContainerMap@@_")) {
assignments.push(configKey);
}
}
console.assert(assignments.length === 0, "There should be 0 identity entries");
console.log("Finished!");
},
async testReconcileSiteAssignments() {
await browser.tests.stopSyncListeners();
console.log("Testing reconciling Site Assignments");
const newSyncData = DUPE_TEST_SYNC;
// add some bad data.
newSyncData.assignedSites["siteContainerMap@@_www.linkedin.com"] = {
"userContextId": "1",
"neverAsk": true,
"hostname": "www.linkedin.com"
};
newSyncData.deletedSiteList = ["siteContainerMap@@_www.google.com"];
await this.setState(
newSyncData,
LOCAL_DATA,
TEST_CONTAINERS,
SITE_ASSIGNMENT_TEST
);
await sync.runSync();
const assignedSites = await assignManager.storageArea.getAssignedSites();
console.assert(Object.keys(assignedSites).length === 5, "There should be 5 assigned sites");
console.log("Finished!");
},
async testInitialSync() {
await browser.tests.stopSyncListeners();
console.log("Testing new install with no sync");
await this.setState({}, LOCAL_DATA, TEST_CONTAINERS);
await this.setState({}, LOCAL_DATA, TEST_CONTAINERS, []);
await sync.runSync();
@ -106,7 +187,7 @@ browser.tests = {
async dupeTest() {
await browser.tests.stopSyncListeners();
console.log("Test state from duped sync");
console.log("Test state from sync that duped everything initially");
await this.setState(
DUPE_TEST_SYNC,
@ -176,30 +257,42 @@ browser.tests = {
}
return false;
},
async setState(syncData, localData, indentityData, assignmentData){
/*
* Sets the state of sync storage, local storage, and the identities.
* SyncDat and localData (without identities or site assignments) get
* set to sync and local storage respectively. IdentityData creates
* new identities (after all have been removed), and assignmentData
* is used along with the new identities' cookieStoreIds to create
* site assignments in local storage.
*/
async setState(syncData, localData, identityData, assignmentData){
await this.removeAllContainers();
await browser.storage.sync.clear();
await browser.storage.sync.set(syncData);
await browser.storage.local.clear();
for (let i=0; i < indentityData.length; i++) {
await browser.storage.local.set(localData);
for (let i=0; i < identityData.length; i++) {
//build identities
const newIdentity =
await browser.contextualIdentities.create(indentityData[i]);
await browser.contextualIdentities.create(identityData[i]);
// fill identies with site assignments
if (assignmentData && assignmentData[i]) {
localData[assignmentData[i]] = {
const data = {
"userContextId":
String(
newIdentity.cookieStoreId.replace(/^firefox-container-/, "")
),
"neverAsk": true
};
await browser.storage.local.set({[assignmentData[i]]: data});
}
}
await browser.storage.local.set(localData);
console.log("local storage set: ", await browser.storage.local.get());
return;
},
async removeAllContainers() {
const identities = await browser.contextualIdentities.query({});
for (const identity of identities) {
@ -254,6 +347,7 @@ const TEST_ASSIGNMENTS = [
"siteContainerMap@@_www.linkedin.com",
"siteContainerMap@@_reddit.com"
];
const LOCAL_DATA = {
"browserActionBadgesClicked": [ "6.1.1" ],
"containerTabsOpened": 7,
@ -313,23 +407,6 @@ const SYNC_DATA = {
},
"assignedSites": {}
};
browser.resetMAC2 = async function () {
// for debugging and testing: remove all containers except the default 4 and the first one created
browser.tests.stopSyncListeners();
// sync state after FF1 (default + 1)
await browser.storage.sync.clear();
const syncData = {"cookieStoreIDmap":{"firefox-container-1":"4dc76734-5b71-4f2e-85d0-1cb199ae3821","firefox-container-2":"30308b8d-393c-4375-b9a1-afc59f0dea79","firefox-container-3":"7419c94d-85d7-4d76-94c0-bacef1de722f","firefox-container-4":"2b9fe881-e552-4df9-8cab-922f4688bb68","firefox-container-6":"db7f622e-682b-4556-968a-6e2542ff3b26"},"assignedSites":{"siteContainerMap@@_twitter.com":{"userContextId":"1","neverAsk":!0},"siteContainerMap@@_www.facebook.com":{"userContextId":"2","neverAsk":!0},"siteContainerMap@@_www.linkedin.com":{"userContextId":"4","neverAsk":!1}},"identities":[{"name":"Personal","icon":"fingerprint","iconUrl":"resource://usercontext-content/fingerprint.svg","color":"blue","colorCode":"#37adff","cookieStoreId":"firefox-container-1"},{"name":"Work","icon":"briefcase","iconUrl":"resource://usercontext-content/briefcase.svg","color":"orange","colorCode":"#ff9f00","cookieStoreId":"firefox-container-2"},{"name":"Banking","icon":"dollar","iconUrl":"resource://usercontext-content/dollar.svg","color":"green","colorCode":"#51cd00","cookieStoreId":"firefox-container-3"},{"name":"Shopping","icon":"cart","iconUrl":"resource://usercontext-content/cart.svg","color":"pink","colorCode":"#ff4bda","cookieStoreId":"firefox-container-4"},{"name":"Container #01","icon":"chill","iconUrl":"resource://usercontext-content/chill.svg","color":"green","colorCode":"#51cd00","cookieStoreId":"firefox-container-6"}]};
browser.storage.sync.set(syncData);
// FF2 (intial sync w/ default 4 + 1 with some changes)
browser.contextualIdentities.update("firefox-container-2", {color:"purple"});
browser.contextualIdentities.update("firefox-container-4", {icon:"pet"});
browser.storage.local.clear();
const localData = {"browserActionBadgesClicked":["6.1.1"],"containerTabsOpened":7,"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@@_developer.mozilla.org":{"userContextId":"6","neverAsk":!1},"siteContainerMap@@_twitter.com":{"userContextId":"1","neverAsk":!0},"siteContainerMap@@_www.linkedin.com":{"userContextId":"4","neverAsk":!1}};
browser.storage.local.set(localData);
};
const DUPE_TEST_SYNC = {
"identities": [
@ -439,6 +516,13 @@ const DUPE_TEST_ASSIGNMENTS = [
"siteContainerMap@@_www.linkedin.com"
];
const SITE_ASSIGNMENT_TEST = [
"siteContainerMap@@_developer.mozilla.org",
"siteContainerMap@@_www.facebook.com",
"siteContainerMap@@_www.google.com",
"siteContainerMap@@_bugzilla.mozilla.org"
];
const DUPE_TEST_IDENTS = [
{
"name": "Personal",
@ -471,38 +555,3 @@ const DUPE_TEST_IDENTS = [
"color": "yellow",
}
];
browser.resetMAC3 = async function () {
// for debugging and testing: remove all containers except the default 4 and the first one created
browser.tests.stopSyncListeners();
// sync state after FF2 synced
await browser.storage.sync.clear();
const syncData = {"assignedSites":{"siteContainerMap@@_developer.mozilla.org":{"userContextId":"6","neverAsk":!1,"hostname":"developer.mozilla.org"},"siteContainerMap@@_twitter.com":{"userContextId":"1","neverAsk":!0,"hostname":"twitter.com"},"siteContainerMap@@_www.facebook.com":{"userContextId":"2","neverAsk":!0,"hostname":"www.facebook.com"},"siteContainerMap@@_www.linkedin.com":{"userContextId":"4","neverAsk":!1,"hostname":"www.linkedin.com"},"siteContainerMap@@_reddit.com": {"userContextId": "7","neverAsk": true}},"cookieStoreIDmap":{"firefox-container-1":"4dc76734-5b71-4f2e-85d0-1cb199ae3821","firefox-container-2":"30308b8d-393c-4375-b9a1-afc59f0dea79","firefox-container-3":"7419c94d-85d7-4d76-94c0-bacef1de722f","firefox-container-4":"2b9fe881-e552-4df9-8cab-922f4688bb68","firefox-container-6":"db7f622e-682b-4556-968a-6e2542ff3b26","firefox-container-7":"ceb06672-76c0-48c4-959e-f3a3ee8358b6"},"identities":[{"name":"Personal","icon":"fingerprint","iconUrl":"resource://usercontext-content/fingerprint.svg","color":"blue","colorCode":"#37adff","cookieStoreId":"firefox-container-1"},{"name":"Work","icon":"briefcase","iconUrl":"resource://usercontext-content/briefcase.svg","color":"orange","colorCode":"#ff9f00","cookieStoreId":"firefox-container-2"},{"name":"Banking","icon":"dollar","iconUrl":"resource://usercontext-content/dollar.svg","color":"purple","colorCode":"#af51f5","cookieStoreId":"firefox-container-3"},{"name":"Shopping","icon":"cart","iconUrl":"resource://usercontext-content/cart.svg","color":"pink","colorCode":"#ff4bda","cookieStoreId":"firefox-container-4"},{"name":"Container #01","icon":"chill","iconUrl":"resource://usercontext-content/chill.svg","color":"green","colorCode":"#51cd00","cookieStoreId":"firefox-container-6"},{"name":"Container #02","icon":"vacation","iconUrl":"resource://usercontext-content/vacation.svg","color":"yellow","colorCode":"#ffcb00","cookieStoreId":"firefox-container-7"}]};
browser.storage.sync.set(syncData);
// FF1 with updates from FF2 (intial sync w/ default 4 + 1 with some changes)
browser.contextualIdentities.update("firefox-container-3", {color:"purple", icon:"fruit"});
//browser.contextualIdentities.create({name: "Container #02", icon: "vacation", color: "yellow"});
browser.storage.local.clear();
const localData = {"beenSynced":!0,"browserActionBadgesClicked":["6.1.1"],"containerTabsOpened":7,"identitiesState@@_firefox-container-1":{"hiddenTabs":[],"macAddonUUID":"4dc76734-5b71-4f2e-85d0-1cb199ae3821"},"identitiesState@@_firefox-container-2":{"hiddenTabs":[],"macAddonUUID":"30308b8d-393c-4375-b9a1-afc59f0dea79"},"identitiesState@@_firefox-container-3":{"hiddenTabs":[],"macAddonUUID":"7419c94d-85d7-4d76-94c0-bacef1de722f"},"identitiesState@@_firefox-container-4":{"hiddenTabs":[],"macAddonUUID":"2b9fe881-e552-4df9-8cab-922f4688bb68"},"identitiesState@@_firefox-container-6":{"hiddenTabs":[],"macAddonUUID":"db7f622e-682b-4556-968a-6e2542ff3b26"},"identitiesState@@_firefox-default":{"hiddenTabs":[]},"onboarding-stage":5,"siteContainerMap@@_developer.mozilla.org":{"userContextId":"6","neverAsk":!1},"siteContainerMap@@_twitter.com":{"userContextId":"1","neverAsk":!0},"siteContainerMap@@_www.facebook.com":{"userContextId":"2","neverAsk":!0},"siteContainerMap@@_www.linkedin.com":{"userContextId":"4","neverAsk":!1}};
browser.storage.local.set(localData);
};
browser.resetMAC4 = async function () {
// for debugging and testing: remove all containers except the default 4 and the first one created
browser.tests.stopSyncListeners();
// sync state after FF2 synced
await browser.storage.sync.clear();
const syncData = {"assignedSites":{"siteContainerMap@@_developer.mozilla.org":{"userContextId":"6","neverAsk":!1,"hostname":"developer.mozilla.org"},"siteContainerMap@@_twitter.com":{"userContextId":"1","neverAsk":!0,"hostname":"twitter.com"},"siteContainerMap@@_www.facebook.com":{"userContextId":"2","neverAsk":!0,"hostname":"www.facebook.com"},"siteContainerMap@@_www.linkedin.com":{"userContextId":"4","neverAsk":!1,"hostname":"www.linkedin.com"},"siteContainerMap@@_reddit.com": {"userContextId": "7","neverAsk": true}},"cookieStoreIDmap":{"firefox-container-1":"4dc76734-5b71-4f2e-85d0-1cb199ae3821","firefox-container-2":"30308b8d-393c-4375-b9a1-afc59f0dea79","firefox-container-3":"7419c94d-85d7-4d76-94c0-bacef1de722f","firefox-container-4":"2b9fe881-e552-4df9-8cab-922f4688bb68","firefox-container-6":"db7f622e-682b-4556-968a-6e2542ff3b26","firefox-container-7":"ceb06672-76c0-48c4-959e-f3a3ee8358b6"},"identities":[{"name":"Personal","icon":"fingerprint","iconUrl":"resource://usercontext-content/fingerprint.svg","color":"blue","colorCode":"#37adff","cookieStoreId":"firefox-container-1"},{"name":"Work","icon":"briefcase","iconUrl":"resource://usercontext-content/briefcase.svg","color":"orange","colorCode":"#ff9f00","cookieStoreId":"firefox-container-2"},{"name":"Banking","icon":"dollar","iconUrl":"resource://usercontext-content/dollar.svg","color":"purple","colorCode":"#af51f5","cookieStoreId":"firefox-container-3"},{"name":"Shopping","icon":"cart","iconUrl":"resource://usercontext-content/cart.svg","color":"pink","colorCode":"#ff4bda","cookieStoreId":"firefox-container-4"},{"name":"Container #01","icon":"chill","iconUrl":"resource://usercontext-content/chill.svg","color":"green","colorCode":"#51cd00","cookieStoreId":"firefox-container-6"},{"name":"Container #02","icon":"vacation","iconUrl":"resource://usercontext-content/vacation.svg","color":"yellow","colorCode":"#ffcb00","cookieStoreId":"firefox-container-7"}]};
browser.storage.sync.set(syncData);
// FF1 with updates from FF2 (intial sync w/ default 4 + 1 with some changes)
browser.contextualIdentities.update("firefox-container-3", {color:"purple", icon:"fruit"});
//browser.contextualIdentities.create({name: "Container #02", icon: "vacation", color: "yellow"});
browser.storage.local.clear();
const localData = {"beenSynced":!0,"browserActionBadgesClicked":["6.1.1"],"containerTabsOpened":7,"identitiesState@@_firefox-container-1":{"hiddenTabs":[],"macAddonUUID":"4dc76734-5b71-4f2e-85d0-1cb199ae3821"},"identitiesState@@_firefox-container-2":{"hiddenTabs":[],"macAddonUUID":"30308b8d-393c-4375-b9a1-afc59f0dea79"},"identitiesState@@_firefox-container-3":{"hiddenTabs":[],"macAddonUUID":"7419c94d-85d7-4d76-94c0-bacef1de722f"},"identitiesState@@_firefox-container-4":{"hiddenTabs":[],"macAddonUUID":"2b9fe881-e552-4df9-8cab-922f4688bb68"},"identitiesState@@_firefox-container-6":{"hiddenTabs":[],"macAddonUUID":"db7f622e-682b-4556-968a-6e2542ff3b26"},"identitiesState@@_firefox-default":{"hiddenTabs":[]},"onboarding-stage":5,"siteContainerMap@@_developer.mozilla.org":{"userContextId":"6","neverAsk":!1},"siteContainerMap@@_twitter.com":{"userContextId":"1","neverAsk":!0},"siteContainerMap@@_www.facebook.com":{"userContextId":"2","neverAsk":!0},"siteContainerMap@@_www.linkedin.com":{"userContextId":"4","neverAsk":!1}};
browser.storage.local.set(localData);
};

View file

@ -724,8 +724,12 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
however it allows us to have a tabindex before the first selected item
*/
const focusHandler = () => {
list.querySelector("tr .clickable").focus();
document.removeEventListener("focus", focusHandler);
const identityList = list.querySelector("tr .clickable");
if (identityList) {
// otherwise this throws an error when there are no containers present.
identityList.focus();
document.removeEventListener("focus", focusHandler);
}
};
document.addEventListener("focus", focusHandler);
/* If the user mousedown's first then remove the focus handler */