Compare commits

...
Sign in to create a new pull request.

5 commits

Author SHA1 Message Date
Minigugus
38af2a385f
Merge branch 'master' into master 2020-02-14 13:37:28 +01:00
Minigugus
1f2810e3ec Merge branch 'master' of github.com:mozilla/multi-account-containers 2020-02-02 14:45:25 +01:00
Minigugus
1f01f93674 Added backup of site associations 2020-02-02 14:22:12 +01:00
Minigugus
9476b6acf6 Fixed options input bug 2020-02-02 14:15:00 +01:00
Minigugus
3255d23ddf #1427 - Added backup/restore buttons in the Options UI 2019-10-14 00:26:49 +02:00
4 changed files with 108 additions and 3 deletions

View file

@ -215,6 +215,66 @@ const backgroundLogic = {
return browser.tabs.remove(tabIds); return browser.tabs.remove(tabIds);
}, },
async backupIdentitiesState() {
const identities = await browser.contextualIdentities.query({});
return Promise.all(
identities.map(async ({ cookieStoreId, color, icon, name }) => {
const userContextId = this.getUserContextIdFromCookieStoreId(cookieStoreId);
const sitesByContainer = await assignManager.storageArea.getByContainer(userContextId);
const sites = Object.values(sitesByContainer).map(site => {
site = Object.assign({}, site); // create a copy
delete site.userContextId;
return site;
});
return ({ color, icon, name, sites });
})
);
},
async restoreIdentitiesState(identities) {
const backup = await browser.contextualIdentities.query({});
let allSucceed = true;
const identitiesPromise = identities.map(async ({ color, icon, name, sites }) => {
try {
const identity = await browser.contextualIdentities.create({ color, icon, name });
try {
await identityState.storageArea.get(identity.cookieStoreId);
const userContextId = this.getUserContextIdFromCookieStoreId(identity.cookieStoreId);
for (const site of sites) {
const pageUrl = `http://${site.hostname}`; // protocol doesn't really matter here
const data = Object.assign({}, site, { userContextId });
delete data.hostname;
await assignManager.storageArea.set(pageUrl, data);
}
} catch (err) {
// TODO warn the user some associations of sites could not be recovered
}
return identity;
} catch (err) {
allSucceed = false;
return null;
}
});
const created = await Promise.all(identitiesPromise);
if (!allSucceed) { // Importation failed, restore previous state
await Promise.all(
created.map(async (identityOrNull) => {
if (identityOrNull) {
await identityState.storageArea.remove(identityOrNull.cookieStoreId);
await browser.contextualIdentities.remove(identityOrNull.cookieStoreId);
}
})
);
} else { // Importation succeed, remove old identities
await Promise.all(
backup.map(async (identity) => {
await identityState.storageArea.remove(identity.cookieStoreId);
await browser.contextualIdentities.remove(identity.cookieStoreId);
})
);
}
},
async queryIdentitiesState(windowId) { async queryIdentitiesState(windowId) {
const identities = await browser.contextualIdentities.query({}); const identities = await browser.contextualIdentities.query({});
const identitiesOutput = {}; const identitiesOutput = {};

View file

@ -67,6 +67,12 @@ const messageHandler = {
windowId: m.windowId windowId: m.windowId
}); });
break; break;
case "backupIdentitiesState":
response = backgroundLogic.backupIdentitiesState();
break;
case "restoreIdentitiesState":
response = backgroundLogic.restoreIdentitiesState(m.identities);
break;
case "queryIdentitiesState": case "queryIdentitiesState":
response = backgroundLogic.queryIdentitiesState(m.message.windowId); response = backgroundLogic.queryIdentitiesState(m.message.windowId);
break; break;

View file

@ -23,6 +23,34 @@ async function enableDisableSync() {
} }
async function restoreOptions() { async function restoreOptions() {
const backupLink = document.getElementById("containers-save-link");
document.getElementById("containers-save-button").addEventListener("click", async () => {
const content = JSON.stringify(
await browser.runtime.sendMessage({
method: "backupIdentitiesState"
})
);
backupLink.href = `data:application/json;base64,${btoa(content)}`;
backupLink.download = `containers-backup-${(new Date()).toISOString()}.json`;
backupLink.click();
}, { capture: true, passive: false });
const restoreInput = document.getElementById("containers-restore-input");
restoreInput.addEventListener("change", () => {
if (restoreInput.files.length) {
const reader = new FileReader();
reader.onloadend = async () => {
const identitiesState = JSON.parse(reader.result);
await browser.runtime.sendMessage({
method: "restoreIdentitiesState",
identities: identitiesState
});
};
reader.readAsText(restoreInput.files.item(0));
}
restoreInput.value = "";
});
const hasPermission = await browser.permissions.contains({permissions: ["bookmarks"]}); const hasPermission = await browser.permissions.contains({permissions: ["bookmarks"]});
const { syncEnabled } = await browser.storage.local.get("syncEnabled"); const { syncEnabled } = await browser.storage.local.get("syncEnabled");
if (hasPermission) { if (hasPermission) {

View file

@ -1,5 +1,3 @@
<!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@ -17,7 +15,20 @@
Enable Sync Enable Sync
</label> </label>
<p>This setting allows you to sync your containers and site assignments across devices.</p> <p>This setting allows you to sync your containers and site assignments across devices.</p>
<fieldset>
<legend>Restore</legend>
<input id="containers-restore-input" type="file">
<p><strong>WARNING !</strong> This operation will erase current configuration with the imported one. All cookies will be discarded.</p>
</fieldset>
<fieldset>
<legend>Save</legend>
<a id="containers-save-link" href="#" style="display: none;"></a>
<button id="containers-save-button">Backup</button>
<p>NOTE : Backup containers names, icons and colors, but <em>not</em> containers' cookies.</p>
</fieldset>
</form> </form>
<script src="js/options.js"></script> <script src="js/options.js"></script>
</body> </body>
</html> </html>