247 lines
8 KiB
JavaScript
247 lines
8 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
BackupService: "resource:///modules/backup/BackupService.sys.mjs",
|
|
ERRORS: "chrome://browser/content/backup/backup-constants.mjs",
|
|
});
|
|
|
|
ChromeUtils.defineLazyGetter(lazy, "logConsole", function () {
|
|
return console.createInstance({
|
|
prefix: "BackupUIParent",
|
|
maxLogLevel: Services.prefs.getBoolPref("browser.backup.log", false)
|
|
? "Debug"
|
|
: "Warn",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* A JSWindowActor that is responsible for marshalling information between
|
|
* the BackupService singleton and any registered UI widgets that need to
|
|
* represent data from that service.
|
|
*/
|
|
export class BackupUIParent extends JSWindowActorParent {
|
|
/**
|
|
* A reference to the BackupService singleton instance.
|
|
*
|
|
* @type {BackupService}
|
|
*/
|
|
#bs;
|
|
|
|
/**
|
|
* Create a BackupUIParent instance. If a BackupUIParent is instantiated
|
|
* before BrowserGlue has a chance to initialize the BackupService, this
|
|
* constructor will cause it to initialize first.
|
|
*/
|
|
constructor() {
|
|
super();
|
|
// We use init() rather than get(), since it's possible to load
|
|
// about:preferences before the service has had a chance to init itself
|
|
// via BrowserGlue.
|
|
this.#bs = lazy.BackupService.init();
|
|
}
|
|
|
|
/**
|
|
* Called once the BackupUIParent/BackupUIChild pair have been connected.
|
|
*/
|
|
actorCreated() {
|
|
this.#bs.addEventListener("BackupService:StateUpdate", this);
|
|
// Note that loadEncryptionState is an async function.
|
|
// This function is no-op if the encryption state was already loaded.
|
|
this.#bs.loadEncryptionState();
|
|
}
|
|
|
|
/**
|
|
* Called once the BackupUIParent/BackupUIChild pair have been disconnected.
|
|
*/
|
|
didDestroy() {
|
|
this.#bs.removeEventListener("BackupService:StateUpdate", this);
|
|
}
|
|
|
|
/**
|
|
* Handles events fired by the BackupService.
|
|
*
|
|
* @param {Event} event
|
|
* The event that the BackupService emitted.
|
|
*/
|
|
handleEvent(event) {
|
|
if (event.type == "BackupService:StateUpdate") {
|
|
this.sendState();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles messages sent by BackupUIChild.
|
|
*
|
|
* @param {ReceiveMessageArgument} message
|
|
* The message received from the BackupUIChild.
|
|
* @returns {
|
|
* null |
|
|
* {success: boolean, errorCode: number} |
|
|
* {path: string, fileName: string, iconURL: string|null}
|
|
* }
|
|
* Returns either a success object, a file details object, or null.
|
|
*/
|
|
async receiveMessage(message) {
|
|
if (message.name == "RequestState") {
|
|
this.sendState();
|
|
} else if (message.name == "EnableScheduledBackups") {
|
|
try {
|
|
let { parentDirPath, password } = message.data;
|
|
if (parentDirPath) {
|
|
this.#bs.setParentDirPath(parentDirPath);
|
|
}
|
|
if (password) {
|
|
await this.#bs.enableEncryption(password);
|
|
Glean.browserBackup.passwordAdded.record();
|
|
}
|
|
this.#bs.setScheduledBackups(true);
|
|
} catch (e) {
|
|
lazy.logConsole.error(`Failed to enable scheduled backups`, e);
|
|
return { success: false, errorCode: e.cause || lazy.ERRORS.UNKNOWN };
|
|
}
|
|
/**
|
|
* TODO: (Bug 1900125) we should create a backup at the specified dir path once we turn on
|
|
* scheduled backups. The backup folder in the chosen directory should contain
|
|
* the archive file, which we create using BackupService.createArchive implemented in
|
|
* Bug 1897498.
|
|
*/
|
|
return { success: true };
|
|
} else if (message.name == "DisableScheduledBackups") {
|
|
try {
|
|
if (this.#bs.state.encryptionEnabled) {
|
|
await this.#bs.disableEncryption();
|
|
}
|
|
await this.#bs.deleteLastBackup();
|
|
} catch (e) {
|
|
// no-op so that scheduled backups can still be turned off
|
|
}
|
|
this.#bs.setScheduledBackups(false);
|
|
} else if (message.name == "ShowFilepicker") {
|
|
let { win, filter, displayDirectoryPath } = message.data;
|
|
|
|
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
|
|
|
let mode = filter
|
|
? Ci.nsIFilePicker.modeOpen
|
|
: Ci.nsIFilePicker.modeGetFolder;
|
|
fp.init(win, "", mode);
|
|
|
|
if (filter) {
|
|
fp.appendFilters(Ci.nsIFilePicker[filter]);
|
|
}
|
|
|
|
if (displayDirectoryPath) {
|
|
try {
|
|
let exists = await IOUtils.exists(displayDirectoryPath);
|
|
if (exists) {
|
|
fp.displayDirectory = await IOUtils.getFile(displayDirectoryPath);
|
|
}
|
|
} catch (_) {
|
|
// If the file can not be found we will skip setting the displayDirectory.
|
|
}
|
|
}
|
|
|
|
let result = await new Promise(resolve => fp.open(resolve));
|
|
|
|
if (result === Ci.nsIFilePicker.returnCancel) {
|
|
return null;
|
|
}
|
|
|
|
let path = fp.file.path;
|
|
let iconURL = this.#bs.getIconFromFilePath(path);
|
|
let filename = PathUtils.filename(path);
|
|
|
|
return {
|
|
path,
|
|
filename,
|
|
iconURL,
|
|
};
|
|
} else if (message.name == "GetBackupFileInfo") {
|
|
let { backupFile } = message.data;
|
|
try {
|
|
await this.#bs.getBackupFileInfo(backupFile);
|
|
} catch (e) {
|
|
/**
|
|
* TODO: (Bug 1905156) display a localized version of error in the restore dialog.
|
|
*/
|
|
}
|
|
} else if (message.name == "RestoreFromBackupChooseFile") {
|
|
const window = this.browsingContext.topChromeWindow;
|
|
this.#bs.filePickerForRestore(window);
|
|
} else if (message.name == "RestoreFromBackupFile") {
|
|
let { backupFile, backupPassword } = message.data;
|
|
try {
|
|
await this.#bs.recoverFromBackupArchive(
|
|
backupFile,
|
|
backupPassword,
|
|
true /* shouldLaunch */
|
|
);
|
|
} catch (e) {
|
|
lazy.logConsole.error(`Failed to restore file: ${backupFile}`, e);
|
|
return { success: false, errorCode: e.cause || lazy.ERRORS.UNKNOWN };
|
|
}
|
|
return { success: true };
|
|
} else if (message.name == "EnableEncryption") {
|
|
try {
|
|
await this.#bs.enableEncryption(message.data.password);
|
|
Glean.browserBackup.passwordAdded.record();
|
|
} catch (e) {
|
|
lazy.logConsole.error(`Failed to enable encryption`, e);
|
|
return { success: false, errorCode: e.cause || lazy.ERRORS.UNKNOWN };
|
|
}
|
|
/**
|
|
* TODO: (Bug 1901640) after enabling encryption, recreate the backup,
|
|
* this time with sensitive data.
|
|
*/
|
|
return { success: true };
|
|
} else if (message.name == "DisableEncryption") {
|
|
try {
|
|
await this.#bs.disableEncryption();
|
|
Glean.browserBackup.passwordRemoved.record();
|
|
} catch (e) {
|
|
lazy.logConsole.error(`Failed to disable encryption`, e);
|
|
return { success: false, errorCode: e.cause || lazy.ERRORS.UNKNOWN };
|
|
}
|
|
/**
|
|
* TODO: (Bug 1901640) after disabling encryption, recreate the backup,
|
|
* this time without sensitive data.
|
|
*/
|
|
return { success: true };
|
|
} else if (message.name == "RerunEncryption") {
|
|
try {
|
|
let { password } = message.data;
|
|
|
|
await this.#bs.disableEncryption();
|
|
await this.#bs.enableEncryption(password);
|
|
Glean.browserBackup.passwordChanged.record();
|
|
} catch (e) {
|
|
lazy.logConsole.error(`Failed to rerun encryption`, e);
|
|
return { success: false, errorCode: e.cause || lazy.ERRORS.UNKNOWN };
|
|
}
|
|
/**
|
|
* TODO: (Bug 1901640) after enabling encryption, recreate the backup,
|
|
* this time with the new password.
|
|
*/
|
|
return { success: true };
|
|
} else if (message.name == "ShowBackupLocation") {
|
|
this.#bs.showBackupLocation();
|
|
} else if (message.name == "EditBackupLocation") {
|
|
const window = this.browsingContext.topChromeWindow;
|
|
this.#bs.editBackupLocation(window);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Sends the StateUpdate message to the BackupUIChild, along with the most
|
|
* recent state object from BackupService.
|
|
*/
|
|
sendState() {
|
|
this.sendAsyncMessage("StateUpdate", { state: this.#bs.state });
|
|
}
|
|
}
|