Merge b8cf955e1f
into aca51cc11c
This commit is contained in:
commit
5e5c8a0913
4 changed files with 167 additions and 1 deletions
|
@ -335,6 +335,93 @@ 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 [
|
||||||
|
{ isIsolated },
|
||||||
|
sitesByContainer
|
||||||
|
] = await Promise.all([
|
||||||
|
identityState.storageArea.get(cookieStoreId),
|
||||||
|
assignManager.storageArea.getAssignedSites(userContextId)
|
||||||
|
]);
|
||||||
|
const sites = Object.values(sitesByContainer).map(({ neverAsk, hostname }) => ({ neverAsk, hostname }));
|
||||||
|
return ({
|
||||||
|
color,
|
||||||
|
icon,
|
||||||
|
name,
|
||||||
|
isolated: isIsolated && true, // either `true` or `undefined`
|
||||||
|
sites
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
async restoreIdentitiesState(identities) {
|
||||||
|
const backup = await browser.contextualIdentities.query({});
|
||||||
|
const incomplete = [];
|
||||||
|
let allSucceed = true;
|
||||||
|
const identitiesPromise = identities.map(async ({ color, icon, name, isolated, sites }) => {
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
typeof color !== "string" ||
|
||||||
|
typeof icon !== "string" ||
|
||||||
|
typeof name !== "string" ||
|
||||||
|
(isolated !== true && isolated !== undefined) ||
|
||||||
|
!Array.isArray((sites))
|
||||||
|
)
|
||||||
|
throw new Error("Corrupted container backup");
|
||||||
|
const identity = await browser.contextualIdentities.create({ color, icon, name });
|
||||||
|
try {
|
||||||
|
await identityState.storageArea.get(identity.cookieStoreId); // to create identity state
|
||||||
|
const userContextId = this.getUserContextIdFromCookieStoreId(identity.cookieStoreId);
|
||||||
|
for (const { neverAsk, hostname } of sites) {
|
||||||
|
if (typeof neverAsk !== "boolean" || typeof hostname !== "string" || hostname === "")
|
||||||
|
throw new Error("Corrupted site association");
|
||||||
|
const pageUrl = `http://${hostname}`; // protocol doesn't really matter here
|
||||||
|
await assignManager.storageArea.set(pageUrl, {
|
||||||
|
neverAsk,
|
||||||
|
userContextId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isolated)
|
||||||
|
await identityState.storageArea.set(identity.cookieStoreId, { isIsolated: "locked" });
|
||||||
|
} catch (err) {
|
||||||
|
incomplete.push(name); // site association damaged
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
throw new Error("Some containers couldn't be created");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Importation succeed, remove old identities
|
||||||
|
await Promise.all(
|
||||||
|
backup.map(async (identity) => {
|
||||||
|
await identityState.storageArea.remove(identity.cookieStoreId);
|
||||||
|
await browser.contextualIdentities.remove(identity.cookieStoreId);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return { created: created.length, incomplete };
|
||||||
|
},
|
||||||
|
|
||||||
async queryIdentitiesState(windowId) {
|
async queryIdentitiesState(windowId) {
|
||||||
const identities = await browser.contextualIdentities.query({});
|
const identities = await browser.contextualIdentities.query({});
|
||||||
const identitiesOutput = {};
|
const identitiesOutput = {};
|
||||||
|
|
|
@ -78,6 +78,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;
|
||||||
|
|
|
@ -53,6 +53,56 @@ async function enableDisableReplaceTab() {
|
||||||
await browser.storage.local.set({replaceTabEnabled: !!checkbox.checked});
|
await browser.storage.local.set({replaceTabEnabled: !!checkbox.checked});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function backupContainers() {
|
||||||
|
const backupLink = document.getElementById("containers-save-link");
|
||||||
|
const backupResult = document.getElementById("containers-save-result");
|
||||||
|
try {
|
||||||
|
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();
|
||||||
|
backupResult.textContent = "";
|
||||||
|
} catch (err) {
|
||||||
|
backupResult.textContent = browser.i18n.getMessage("backupFailure", [String(err.message || err)]);
|
||||||
|
backupResult.style.color = "red";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restoreContainers(event) {
|
||||||
|
const restoreInput = event.currentTarget;
|
||||||
|
const restoreResult = document.getElementById("containers-restore-result");
|
||||||
|
event.preventDefault();
|
||||||
|
if (restoreInput.files.length) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = async () => {
|
||||||
|
try {
|
||||||
|
const identitiesState = JSON.parse(reader.result);
|
||||||
|
const { created: restoredCount, incomplete } = await browser.runtime.sendMessage({
|
||||||
|
method: "restoreIdentitiesState",
|
||||||
|
identities: identitiesState
|
||||||
|
});
|
||||||
|
if (incomplete.length === 0) {
|
||||||
|
restoreResult.textContent = browser.i18n.getMessage("containersRestored", [String(restoredCount)]);
|
||||||
|
restoreResult.style.color = "green";
|
||||||
|
} else {
|
||||||
|
restoreResult.textContent = browser.i18n.getMessage("containersPartiallyRestored", [String(restoredCount), String(incomplete.join(", "))]);
|
||||||
|
restoreResult.style.color = "orange";
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Cannot restore containers list: %s", err.message || err);
|
||||||
|
restoreResult.textContent = browser.i18n.getMessage("containersRestorationFailed");
|
||||||
|
restoreResult.style.color = "red";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(restoreInput.files.item(0));
|
||||||
|
}
|
||||||
|
restoreInput.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
async function changeTheme(event) {
|
async function changeTheme(event) {
|
||||||
const theme = event.currentTarget;
|
const theme = event.currentTarget;
|
||||||
await browser.storage.local.set({currentTheme: theme.value});
|
await browser.storage.local.set({currentTheme: theme.value});
|
||||||
|
@ -123,6 +173,7 @@ browser.permissions.onRemoved.addListener(resetPermissionsUi);
|
||||||
document.addEventListener("DOMContentLoaded", setupOptions);
|
document.addEventListener("DOMContentLoaded", setupOptions);
|
||||||
document.querySelector("#syncCheck").addEventListener( "change", enableDisableSync);
|
document.querySelector("#syncCheck").addEventListener( "change", enableDisableSync);
|
||||||
document.querySelector("#replaceTabCheck").addEventListener( "change", enableDisableReplaceTab);
|
document.querySelector("#replaceTabCheck").addEventListener( "change", enableDisableReplaceTab);
|
||||||
|
document.querySelector("#containersRestoreInput").addEventListener( "change", restoreContainers);
|
||||||
document.querySelector("#changeTheme").addEventListener( "change", changeTheme);
|
document.querySelector("#changeTheme").addEventListener( "change", changeTheme);
|
||||||
|
|
||||||
maybeShowPermissionsWarningIcon();
|
maybeShowPermissionsWarningIcon();
|
||||||
|
@ -132,8 +183,12 @@ for (let i=0; i < NUMBER_OF_KEYBOARD_SHORTCUTS; i++) {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelectorAll("[data-btn-id]").forEach(btn => {
|
document.querySelectorAll("[data-btn-id]").forEach(btn => {
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", e => {
|
||||||
switch (btn.dataset.btnId) {
|
switch (btn.dataset.btnId) {
|
||||||
|
case "containers-save-button":
|
||||||
|
e.preventDefault();
|
||||||
|
backupContainers();
|
||||||
|
break;
|
||||||
case "reset-onboarding":
|
case "reset-onboarding":
|
||||||
resetOnboarding();
|
resetOnboarding();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -59,6 +59,24 @@
|
||||||
<p><em data-i18n-message-id="replaceTabDescription"></em></p>
|
<p><em data-i18n-message-id="replaceTabDescription"></em></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3 data-i18n-message-id="backup"></h3>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<fieldset>
|
||||||
|
<legend data-i18n-message-id="restoreLegend"></legend>
|
||||||
|
<input id="containersRestoreInput" type="file">
|
||||||
|
<p><em id="containers-restore-result"></em></p>
|
||||||
|
<p data-i18n-message-id="warningConfigOverride"></p>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend data-i18n-message-id="saveLegend"></legend>
|
||||||
|
<a id="containers-save-link" href="#" style="display: none;"></a>
|
||||||
|
<button data-i18n-message-id="saveButton" data-btn-id="containers-save-button"></button>
|
||||||
|
<p><em id="containers-save-result"></em></p>
|
||||||
|
<p data-i18n-message-id="noteWontBackupCookies"></p>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
TODO
|
TODO
|
||||||
- Add data-i18n
|
- Add data-i18n
|
||||||
|
|
Loading…
Add table
Reference in a new issue