diff options
Diffstat (limited to 'browser/components/backup/content')
5 files changed, 233 insertions, 0 deletions
diff --git a/browser/components/backup/content/BackupManifest.1.schema.json b/browser/components/backup/content/BackupManifest.1.schema.json new file mode 100644 index 0000000000..51418988fe --- /dev/null +++ b/browser/components/backup/content/BackupManifest.1.schema.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "file:///BackupManifest.schema.json", + "title": "BackupManifest", + "description": "A schema for the backup-manifest.json file for profile backups created by the BackupService", + "type": "object", + "properties": { + "version": { + "type": "integer", + "description": "Version of the backup manifest structure" + }, + "meta": { + "type": "object", + "description": "Metadata about the backup", + "properties": { + "date": { + "type": "string", + "format": "date-time", + "description": "Date and time that the backup was created" + }, + "appName": { + "type": "string", + "description": "Name of the application that the backup was created for." + }, + "appVersion": { + "type": "string", + "description": "Full version string for the app instance that the backup was created on" + }, + "buildID": { + "type": "string", + "description": "Build ID for the app instance that the backup was created on" + }, + "profileName": { + "type": "string", + "description": "The name given to the profile that was backed up" + }, + "machineName": { + "type": "string", + "description": "The name of the machine that the backup was created on" + }, + "osName": { + "type": "string", + "description": "The OS name that the backup was created on" + }, + "osVersion": { + "type": "string", + "description": "The OS version that the backup was created on" + }, + "accountID": { + "type": "string", + "description": "The ID for the account that the user profile was signed into when backing up. Optional." + }, + "accountEmail": { + "type": "string", + "description": "The email address for the account that the user profile was signed into when backing up. Optional." + }, + "legacyClientID": { + "type": "string", + "description": "The legacy telemetry client ID for the profile that the backup was created on." + } + }, + "required": [ + "date", + "appName", + "appVersion", + "buildID", + "profileName", + "machineName", + "osName", + "osVersion", + "legacyClientID" + ] + }, + "resources": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + }, + "required": ["version", "resources", "meta"] +} diff --git a/browser/components/backup/content/backup-settings.mjs b/browser/components/backup/content/backup-settings.mjs new file mode 100644 index 0000000000..c34d87dbc7 --- /dev/null +++ b/browser/components/backup/content/backup-settings.mjs @@ -0,0 +1,47 @@ +/* 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/. */ + +import { html } from "chrome://global/content/vendor/lit.all.mjs"; +import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; + +/** + * The widget for managing the BackupService that is embedded within the main + * document of about:settings / about:preferences. + */ +export default class BackupSettings extends MozLitElement { + static properties = { + backupServiceState: { type: Object }, + }; + + /** + * Creates a BackupSettings instance and sets the initial default + * state. + */ + constructor() { + super(); + this.backupServiceState = { + backupInProgress: false, + }; + } + + /** + * Dispatches the BackupUI:InitWidget custom event upon being attached to the + * DOM, which registers with BackupUIChild for BackupService state updates. + */ + connectedCallback() { + super.connectedCallback(); + this.dispatchEvent( + new CustomEvent("BackupUI:InitWidget", { bubbles: true }) + ); + } + + render() { + return html`<div> + Backup in progress: + ${this.backupServiceState.backupInProgress ? "Yes" : "No"} + </div>`; + } +} + +customElements.define("backup-settings", BackupSettings); diff --git a/browser/components/backup/content/backup-settings.stories.mjs b/browser/components/backup/content/backup-settings.stories.mjs new file mode 100644 index 0000000000..2a87c361bc --- /dev/null +++ b/browser/components/backup/content/backup-settings.stories.mjs @@ -0,0 +1,32 @@ +/* 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/. */ + +// eslint-disable-next-line import/no-unresolved +import { html } from "lit.all.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "./backup-settings.mjs"; + +export default { + title: "Domain-specific UI Widgets/Backup/Backup Settings", + component: "backup-settings", + argTypes: {}, +}; + +const Template = ({ backupServiceState }) => html` + <backup-settings .backupServiceState=${backupServiceState}></backup-settings> +`; + +export const BackingUpNotInProgress = Template.bind({}); +BackingUpNotInProgress.args = { + backupServiceState: { + backupInProgress: false, + }, +}; + +export const BackingUpInProgress = Template.bind({}); +BackingUpInProgress.args = { + backupServiceState: { + backupInProgress: true, + }, +}; diff --git a/browser/components/backup/content/debug.html b/browser/components/backup/content/debug.html index 5d6517cf2a..55034d4a5c 100644 --- a/browser/components/backup/content/debug.html +++ b/browser/components/backup/content/debug.html @@ -36,7 +36,25 @@ <section id="controls"> <h2>Controls</h2> <button id="create-backup">Create backup</button> + <p> + Clicking "Create backup" will create a backup, and then attempt to + show an OS notification with the total time it took to create it. This + notification may not appear if your OS has not granted the browser to + display notifications. + </p> + <p id="last-backup-status"></p> <button id="open-backup-folder">Open backups folder</button> + <button id="recover-from-staging"> + Recover from staging folder and launch + </button> + <p> + Clicking "Recover from staging folder and launch" will open a file + picker to allow you to select a staging folder. Once selected, a new + user profile will be created and the data stores from the staging + folder will be copied into that new profile. The new profile will then + be launched. + </p> + <p id="last-recovery-status"></p> </section> </main> diff --git a/browser/components/backup/content/debug.js b/browser/components/backup/content/debug.js index fd673818c0..7a2cea9640 100644 --- a/browser/components/backup/content/debug.js +++ b/browser/components/backup/content/debug.js @@ -26,13 +26,31 @@ let DebugUI = { } }, + secondsToHms(seconds) { + let h = Math.floor(seconds / 3600); + let m = Math.floor((seconds % 3600) / 60); + let s = Math.floor((seconds % 3600) % 60); + return `${h}h ${m}m ${s}s`; + }, + async onButtonClick(button) { switch (button.id) { case "create-backup": { let service = BackupService.get(); + let lastBackupStatus = document.querySelector("#last-backup-status"); + lastBackupStatus.textContent = "Creating backup..."; + + let then = Cu.now(); button.disabled = true; await service.createBackup(); + let totalTimeSeconds = (Cu.now() - then) / 1000; button.disabled = false; + new Notification(`Backup created`, { + body: `Total time ${this.secondsToHms(totalTimeSeconds)}`, + }); + lastBackupStatus.textContent = `Backup created - total time: ${this.secondsToHms( + totalTimeSeconds + )}`; break; } case "open-backup-folder": { @@ -52,6 +70,42 @@ let DebugUI = { break; } + case "recover-from-staging": { + let backupsDir = PathUtils.join(PathUtils.profileDir, "backups"); + let fp = Cc["@mozilla.org/filepicker;1"].createInstance( + Ci.nsIFilePicker + ); + fp.init( + window.browsingContext, + "Choose a staging folder", + Ci.nsIFilePicker.modeGetFolder + ); + fp.displayDirectory = await IOUtils.getDirectory(backupsDir); + let result = await new Promise(resolve => fp.open(resolve)); + if (result == Ci.nsIFilePicker.returnCancel) { + break; + } + + let path = fp.file.path; + let lastRecoveryStatus = document.querySelector( + "#last-recovery-status" + ); + lastRecoveryStatus.textContent = "Recovering from backup..."; + + let service = BackupService.get(); + try { + let newProfile = await service.recoverFromBackup( + path, + true /* shouldLaunch */ + ); + lastRecoveryStatus.textContent = `Created profile ${newProfile.name} at ${newProfile.rootDir.path}`; + } catch (e) { + lastRecoveryStatus.textContent( + `Failed to recover: ${e.message} Check the console for the full exception.` + ); + throw e; + } + } } }, }; |