diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 932bd49d749f..12eb28acb447 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -3052,6 +3052,8 @@ pref("browser.mailto.prompt.os", true); // Pref to initialize the BackupService soon after startup. pref("browser.backup.enabled", true); +// Pref to control whether scheduled backups run or not. +pref("browser.backup.scheduled.enabled", false); // Pref to control the visibility of the backup section in about:preferences pref("browser.backup.preferences.ui.enabled", false); // The number of SQLite database pages to backup per step. diff --git a/browser/components/BrowserGlue.sys.mjs b/browser/components/BrowserGlue.sys.mjs index 050e9c9554d9..c913509fd70c 100644 --- a/browser/components/BrowserGlue.sys.mjs +++ b/browser/components/BrowserGlue.sys.mjs @@ -439,6 +439,7 @@ let JSWINDOWACTORS = { esModuleURI: "resource:///actors/BackupUIChild.sys.mjs", events: { "BackupUI:InitWidget": { wantUntrusted: true }, + "BackupUI:ScheduledBackupsConfirm": { wantUntrusted: true }, }, }, matches: ["about:preferences*", "about:settings*"], diff --git a/browser/components/backup/BackupService.sys.mjs b/browser/components/backup/BackupService.sys.mjs index 44745dca41b0..8ed2238222e7 100644 --- a/browser/components/backup/BackupService.sys.mjs +++ b/browser/components/backup/BackupService.sys.mjs @@ -4,7 +4,9 @@ import * as DefaultBackupResources from "resource:///modules/backup/BackupResources.sys.mjs"; import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; +const SCHEDULED_BACKUPS_ENABLED_PREF_NAME = "browser.backup.scheduled.enabled"; const lazy = {}; ChromeUtils.defineLazyGetter(lazy, "logConsole", function () { @@ -33,6 +35,19 @@ ChromeUtils.defineLazyGetter(lazy, "ZipWriter", () => Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter", "open") ); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "scheduledBackupsPref", + SCHEDULED_BACKUPS_ENABLED_PREF_NAME, + false, + function onUpdateScheduledBackups(_pref, _prevVal, newVal) { + let bs = BackupService.get(); + if (bs) { + bs.onUpdateScheduledBackups(newVal); + } + } +); + /** * The BackupService class orchestrates the scheduling and creation of profile * backups. It also does most of the heavy lifting for the restoration of a @@ -94,7 +109,11 @@ export class BackupService extends EventTarget { * * @type {object} */ - #_state = { backupInProgress: false }; + #_state = { + backupFilePath: "Documents", // TODO: make save location configurable (bug 1895943) + backupInProgress: false, + scheduledBackupsEnabled: lazy.scheduledBackupsPref, + }; /** * A Promise that will resolve once the postRecovery steps are done. It will @@ -844,6 +863,35 @@ export class BackupService extends EventTarget { } } + /** + * Sets browser.backup.scheduled.enabled to true or false. + * + * @param { boolean } shouldEnableScheduledBackups true if scheduled backups should be enabled. Else, false. + */ + setScheduledBackups(shouldEnableScheduledBackups) { + Services.prefs.setBoolPref( + SCHEDULED_BACKUPS_ENABLED_PREF_NAME, + shouldEnableScheduledBackups + ); + } + + /** + * Updates scheduledBackupsEnabled in the backup service state. Should be called every time + * the value for browser.backup.scheduled.enabled changes. + * + * @param {boolean} isScheduledBackupsEnabled True if scheduled backups are enabled. Else false. + */ + onUpdateScheduledBackups(isScheduledBackupsEnabled) { + if (this.#_state.scheduledBackupsEnabled != isScheduledBackupsEnabled) { + lazy.logConsole.debug( + "Updating scheduled backups", + isScheduledBackupsEnabled + ); + this.#_state.scheduledBackupsEnabled = isScheduledBackupsEnabled; + this.stateUpdate(); + } + } + /** * Take measurements of the current profile state for Telemetry. * diff --git a/browser/components/backup/actors/BackupUIChild.sys.mjs b/browser/components/backup/actors/BackupUIChild.sys.mjs index 25d013fa8ecf..668debaa6ee0 100644 --- a/browser/components/backup/actors/BackupUIChild.sys.mjs +++ b/browser/components/backup/actors/BackupUIChild.sys.mjs @@ -13,20 +13,24 @@ export class BackupUIChild extends JSWindowActorChild { #inittedWidgets = new WeakSet(); /** - * Handles BackupUI:InitWidget custom events fired by widgets that want to - * register with BackupUIChild. Firing this event sends a message to the - * parent to request the BackupService state which will result in a - * `backupServiceState` property of the widget to be set when that state is - * received. Subsequent state updates will also cause that state property to - * be set. + * Handles custom events fired by widgets that want to register with + * BackupUIChild. * * @param {Event} event - * The BackupUI:InitWidget custom event that the widget fired. + * The custom event that the widget fired. */ handleEvent(event) { + /** + * BackupUI:InitWidget sends a message to the parent to request the BackupService state + * which will result in a `backupServiceState` property of the widget to be set when that + * state is received. Subsequent state updates will also cause that state property to + * be set. + */ if (event.type == "BackupUI:InitWidget") { this.#inittedWidgets.add(event.target); this.sendAsyncMessage("RequestState"); + } else if (event.type == "BackupUI:ScheduledBackupsConfirm") { + this.sendAsyncMessage("ScheduledBackupsConfirm"); } } diff --git a/browser/components/backup/actors/BackupUIParent.sys.mjs b/browser/components/backup/actors/BackupUIParent.sys.mjs index e4d0f3aace85..f97d00cbdc8c 100644 --- a/browser/components/backup/actors/BackupUIParent.sys.mjs +++ b/browser/components/backup/actors/BackupUIParent.sys.mjs @@ -69,6 +69,8 @@ export class BackupUIParent extends JSWindowActorParent { receiveMessage(message) { if (message.name == "RequestState") { this.sendState(); + } else if (message.name == "ScheduledBackupsConfirm") { + this.#bs.setScheduledBackups(true); } } diff --git a/browser/components/backup/content/backup-settings.css b/browser/components/backup/content/backup-settings.css new file mode 100644 index 000000000000..e78596b00a76 --- /dev/null +++ b/browser/components/backup/content/backup-settings.css @@ -0,0 +1,7 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +#turn-on-scheduled-backups-dialog { + width: 27.8rem; +} diff --git a/browser/components/backup/content/backup-settings.mjs b/browser/components/backup/content/backup-settings.mjs index c34d87dbc7a8..2992a7e1e7e1 100644 --- a/browser/components/backup/content/backup-settings.mjs +++ b/browser/components/backup/content/backup-settings.mjs @@ -5,6 +5,9 @@ import { html } from "chrome://global/content/vendor/lit.all.mjs"; import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "chrome://browser/content/backup/turn-on-scheduled-backups.mjs"; + /** * The widget for managing the BackupService that is embedded within the main * document of about:settings / about:preferences. @@ -14,14 +17,24 @@ export default class BackupSettings extends MozLitElement { backupServiceState: { type: Object }, }; + static get queries() { + return { + scheduledBackupsButtonEl: "#backup-toggle-scheduled-button", + turnOnScheduledBackupsDialogEl: "#turn-on-scheduled-backups-dialog", + turnOnScheduledBackupsEl: "turn-on-scheduled-backups", + }; + } + /** - * Creates a BackupSettings instance and sets the initial default + * Creates a BackupPreferences instance and sets the initial default * state. */ constructor() { super(); this.backupServiceState = { + backupFilePath: "Documents", // TODO: make save location configurable (bug 1895943) backupInProgress: false, + scheduledBackupsEnabled: false, }; } @@ -34,13 +47,68 @@ export default class BackupSettings extends MozLitElement { this.dispatchEvent( new CustomEvent("BackupUI:InitWidget", { bubbles: true }) ); + + this.addEventListener("scheduledBackupsCancel", this); + this.addEventListener("scheduledBackupsConfirm", this); + } + + handleEvent(event) { + switch (event.type) { + case "scheduledBackupsConfirm": + this.turnOnScheduledBackupsDialogEl.close(); + this.dispatchEvent( + new CustomEvent("BackupUI:ScheduledBackupsConfirm", { + bubbles: true, + composed: true, + }) + ); + break; + case "scheduledBackupsCancel": + this.turnOnScheduledBackupsDialogEl.close(); + break; + } + } + + handleShowScheduledBackups() { + if ( + !this.backupServiceState.scheduledBackupsEnabled && + this.turnOnScheduledBackupsDialogEl + ) { + this.turnOnScheduledBackupsDialogEl.showModal(); + } + } + + turnOnScheduledBackupsDialogTemplate() { + return html` + + `; } render() { - return html`
- Backup in progress: - ${this.backupServiceState.backupInProgress ? "Yes" : "No"} -
`; + return html` + +
+
+ Backup in progress: + ${this.backupServiceState.backupInProgress ? "Yes" : "No"} +
+ + ${this.turnOnScheduledBackupsDialogTemplate()} + + +
`; } } diff --git a/browser/components/backup/content/backup-settings.stories.mjs b/browser/components/backup/content/backup-settings.stories.mjs index e125cfbeff70..dafd8193cf54 100644 --- a/browser/components/backup/content/backup-settings.stories.mjs +++ b/browser/components/backup/content/backup-settings.stories.mjs @@ -6,6 +6,9 @@ import { html } from "lit.all.mjs"; import "./backup-settings.mjs"; +window.MozXULElement.insertFTLIfNeeded("locales-preview/backupSettings.ftl"); +window.MozXULElement.insertFTLIfNeeded("branding/brand.ftl"); + export default { title: "Domain-specific UI Widgets/Backup/Backup Settings", component: "backup-settings", @@ -19,13 +22,17 @@ const Template = ({ backupServiceState }) => html` export const BackingUpNotInProgress = Template.bind({}); BackingUpNotInProgress.args = { backupServiceState: { + backupFilePath: "Documents", backupInProgress: false, + scheduledBackupsEnabled: false, }, }; export const BackingUpInProgress = Template.bind({}); BackingUpInProgress.args = { backupServiceState: { + backupFilePath: "Documents", backupInProgress: true, + scheduledBackupsEnabled: false, }, }; diff --git a/browser/components/backup/content/turn-on-scheduled-backups.css b/browser/components/backup/content/turn-on-scheduled-backups.css new file mode 100644 index 000000000000..baa5d61776b9 --- /dev/null +++ b/browser/components/backup/content/turn-on-scheduled-backups.css @@ -0,0 +1,109 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +@import url("chrome://global/skin/in-content/common.css"); + +:host { + --margin-inline-start-checkbox-content: calc(var(--checkbox-margin-inline) + var(--checkbox-size)); +} + +#backup-turn-on-scheduled-wrapper { + display: grid; + grid-template-areas: + "header" + "content" + "button-group"; + grid-template-rows: auto auto auto; + line-height: 1.5; + + #backup-turn-on-scheduled-header { + grid-area: header; + font-size: var(--font-size-large); + font-weight: var(--font-weight); + margin: 0; + } + + & fieldset { + border: none; + margin: 0; + padding: 0; + } + + #backup-turn-on-scheduled-content { + display: flex; + flex-direction: column; + grid-area: content; + margin-block-start: var(--space-small); + margin-block-end: var(--space-large); + row-gap: var(--space-large); + } + + #all-controls { + display: flex; + flex-direction: column; + row-gap: var(--space-xlarge); + } + + #backup-location-controls { + display: flex; + flex-direction: column; + row-gap: var(--space-xsmall); + + #backup-location-filepicker { + display: flex; + column-gap: var(--space-small); + align-items: center; + } + + #backup-location-filepicker-input { + margin: 0; + flex: 1; + } + } + + #sensitive-data-controls { + display: flex; + flex-direction: column; + row-gap: var(--space-large); + + #sensitive-data-checkbox-label { + display: flex; + gap: var(--checkbox-margin-inline); + align-items: center; + } + + #sensitive-data-checkbox-label > input { + margin: 0; + } + + #sensitive-data-checkbox { + display: flex; + flex-direction: column; + row-gap: var(--space-xsmall); + } + + #sensitive-data-checkbox > span { + margin-inline-start: var(--margin-inline-start-checkbox-content); + } + } + + #passwords { + display: flex; + flex-direction: column; + margin-inline-start: var(--margin-inline-start-checkbox-content); + + & > #new-password-label, #repeat-password-label { + display: flex; + flex-direction: column; + } + + & > #new-password-label > input, #repeat-password-label > input { + margin-inline-start: 0px; + } + } + + #backup-turn-on-scheduled-button-group { + grid-area: button-group; + } +} diff --git a/browser/components/backup/content/turn-on-scheduled-backups.mjs b/browser/components/backup/content/turn-on-scheduled-backups.mjs new file mode 100644 index 000000000000..02ebc7dcd8eb --- /dev/null +++ b/browser/components/backup/content/turn-on-scheduled-backups.mjs @@ -0,0 +1,212 @@ +/* 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 showing available options when users want to turn on + * scheduled backups. + */ +export default class TurnOnScheduledBackups extends MozLitElement { + static properties = { + backupFilePath: { type: String }, + showPasswordOptions: { type: Boolean, reflect: true }, + }; + + static get queries() { + return { + cancelButtonEl: "#backup-turn-on-scheduled-cancel-button", + confirmButtonEl: "#backup-turn-on-scheduled-confirm-button", + passwordOptionsCheckboxEl: "#sensitive-data-checkbox-input", + passwordOptionsExpandedEl: "#passwords", + recommendedFolderInputEl: "#backup-location-filepicker-input", + }; + } + + constructor() { + super(); + this.backupFilePath = null; + this.showPasswordOptions = 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 }) + ); + } + + handleChooseLocation() { + // TODO: show file picker (bug 1895943) + } + + handleCancel() { + this.dispatchEvent( + new CustomEvent("scheduledBackupsCancel", { + bubbles: true, + composed: true, + }) + ); + this.showPasswordOptions = false; + this.passwordOptionsCheckboxEl.checked = false; + } + + handleConfirm() { + /** + * TODO: + * We should pass save location to BackupUIParent here (bug 1895943). + * If encryption is enabled via this dialog, ensure a password is set and pass it to BackupUIParent (bug 1895981). + * Before confirmation, verify passwords match and FxA format rules (bug 1896772). + */ + this.dispatchEvent( + new CustomEvent("scheduledBackupsConfirm", { + bubbles: true, + composed: true, + }) + ); + this.showPasswordOptions = false; + this.passwordOptionsCheckboxEl.checked = false; + } + + handleTogglePasswordOptions() { + this.showPasswordOptions = this.passwordOptionsCheckboxEl?.checked; + } + + allOptionsTemplate() { + return html` +
+
+ + +
+ + +
+
+ +
+
+ + +
+ + ${this.showPasswordOptions ? this.passwordOptionsTemplate() : null} +
+
+ `; + } + + passwordOptionsTemplate() { + return html` +
+ + +
+ `; + } + + contentTemplate() { + return html` +
+

+
+
+ + +
+ ${this.allOptionsTemplate()} +
+ + + + + +
+ `; + } + + render() { + return html` + + ${this.contentTemplate()} + `; + } +} + +customElements.define("turn-on-scheduled-backups", TurnOnScheduledBackups); diff --git a/browser/components/backup/content/turn-on-scheduled-backups.stories.mjs b/browser/components/backup/content/turn-on-scheduled-backups.stories.mjs new file mode 100644 index 000000000000..865bf65068f5 --- /dev/null +++ b/browser/components/backup/content/turn-on-scheduled-backups.stories.mjs @@ -0,0 +1,30 @@ +/* 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"; +import "chrome://global/content/elements/moz-card.mjs"; +import "./turn-on-scheduled-backups.mjs"; + +window.MozXULElement.insertFTLIfNeeded("locales-preview/backupSettings.ftl"); +window.MozXULElement.insertFTLIfNeeded("branding/brand.ftl"); + +export default { + title: "Domain-specific UI Widgets/Backup/Turn On Scheduled Backups", + component: "turn-on-scheduled-backups", + argTypes: {}, +}; + +const Template = ({ backupFilePath }) => html` + + + +`; + +export const RecommendedFolder = Template.bind({}); +RecommendedFolder.args = { + backupFilePath: "Documents", +}; diff --git a/browser/components/backup/jar.mn b/browser/components/backup/jar.mn index 94c670afaf5e..aaeb2b807151 100644 --- a/browser/components/backup/jar.mn +++ b/browser/components/backup/jar.mn @@ -8,4 +8,7 @@ browser.jar: content/browser/backup/debug.js (content/debug.js) #endif content/browser/backup/BackupManifest.1.schema.json (content/BackupManifest.1.schema.json) + content/browser/backup/backup-settings.css (content/backup-settings.css) content/browser/backup/backup-settings.mjs (content/backup-settings.mjs) + content/browser/backup/turn-on-scheduled-backups.css (content/turn-on-scheduled-backups.css) + content/browser/backup/turn-on-scheduled-backups.mjs (content/turn-on-scheduled-backups.mjs) diff --git a/browser/components/backup/tests/browser/browser.toml b/browser/components/backup/tests/browser/browser.toml index f222c3b82549..2fec03c48fc5 100644 --- a/browser/components/backup/tests/browser/browser.toml +++ b/browser/components/backup/tests/browser/browser.toml @@ -2,6 +2,7 @@ prefs = [ "browser.backup.enabled=true", "browser.backup.preferences.ui.enabled=true", + "browser.backup.scheduled.enabled=false", ] ["browser_settings.js"] diff --git a/browser/components/backup/tests/browser/browser_settings.js b/browser/components/backup/tests/browser/browser_settings.js index b33dbec7bdf6..2dddc9d15c72 100644 --- a/browser/components/backup/tests/browser/browser_settings.js +++ b/browser/components/backup/tests/browser/browser_settings.js @@ -38,3 +38,51 @@ add_task(async function test_preferences_visibility() { await SpecialPowers.popPrefEnv(); }); + +/** + * Tests that the turn on scheduled backups dialog can set + * browser.backup.scheduled.enabled to true from the settings page. + */ +add_task(async function test_turn_on_scheduled_backups_confirm() { + await BrowserTestUtils.withNewTab("about:preferences", async browser => { + let settings = browser.contentDocument.querySelector("backup-settings"); + + await settings.updateComplete; + + let turnOnButton = settings.scheduledBackupsButtonEl; + + Assert.ok( + turnOnButton, + "Button to turn on scheduled backups should be found" + ); + + turnOnButton.click(); + + await settings.updateComplete; + + let turnOnScheduledBackups = settings.turnOnScheduledBackupsEl; + + Assert.ok( + turnOnScheduledBackups, + "turn-on-scheduled-backups should be found" + ); + + let confirmButton = turnOnScheduledBackups.confirmButtonEl; + let promise = BrowserTestUtils.waitForEvent( + window, + "scheduledBackupsConfirm" + ); + + Assert.ok(confirmButton, "Confirm button should be found"); + + confirmButton.click(); + + await promise; + await settings.updateComplete; + + let scheduledPrefVal = Services.prefs.getBoolPref( + "browser.backup.scheduled.enabled" + ); + Assert.ok(scheduledPrefVal, "Scheduled backups pref should be true"); + }); +}); diff --git a/browser/components/backup/tests/chrome/chrome.toml b/browser/components/backup/tests/chrome/chrome.toml index b0c01b336f64..0e1cc0dc14aa 100644 --- a/browser/components/backup/tests/chrome/chrome.toml +++ b/browser/components/backup/tests/chrome/chrome.toml @@ -1,4 +1,9 @@ [DEFAULT] +prefs = [ + "browser.backup.scheduled.enabled=false", +] skip-if = ["os == 'android'"] ["test_backup_settings.html"] + +["test_turn_on_scheduled_backups.html"] diff --git a/browser/components/backup/tests/chrome/test_backup_settings.html b/browser/components/backup/tests/chrome/test_backup_settings.html index 3619f8a1f4bc..fad7137d5151 100644 --- a/browser/components/backup/tests/chrome/test_backup_settings.html +++ b/browser/components/backup/tests/chrome/test_backup_settings.html @@ -31,6 +31,41 @@ settings.remove(); }); + + /** + * Tests that the dialog for turning on scheduled backups can be displayed + * from settings, or hidden if cancelled. + */ + add_task(async function test_turnOnScheduledBackupsDialog() { + let settings = document.getElementById("test-backup-settings"); + settings.backupServiceState = { + scheduledBackupsEnabled: false, + } + + await settings.updateComplete; + + let turnOnButton = settings.scheduledBackupsButtonEl; + let dialog = settings.turnOnScheduledBackupsDialogEl; + + ok(turnOnButton, "Button to turn on scheduled backups should be found"); + ok(!dialog.open, "Dialog should not be open"); + + turnOnButton.click(); + await settings.updateComplete; + + ok(dialog?.open, "Dialog should be open"); + + let turnOnScheduledBackups = dialog.querySelector("turn-on-scheduled-backups"); + ok(turnOnScheduledBackups, "turn-on-scheduled-backups should be found"); + + let cancelButton = turnOnScheduledBackups.shadowRoot.getElementById("backup-turn-on-scheduled-cancel-button"); + ok(cancelButton, "Cancel button should be found"); + + cancelButton.click(); + await settings.updateComplete; + + ok(!dialog.open, "Dialog should not be open"); + }); diff --git a/browser/components/backup/tests/chrome/test_turn_on_scheduled_backups.html b/browser/components/backup/tests/chrome/test_turn_on_scheduled_backups.html new file mode 100644 index 000000000000..054df24ff756 --- /dev/null +++ b/browser/components/backup/tests/chrome/test_turn_on_scheduled_backups.html @@ -0,0 +1,96 @@ + + + + + Tests for the turn-on-scheduled-backups component + + + + + + + +

+ +

+
+
diff --git a/browser/locales-preview/backupSettings.ftl b/browser/locales-preview/backupSettings.ftl
index 351a60998bda..f3d36f9c28d6 100644
--- a/browser/locales-preview/backupSettings.ftl
+++ b/browser/locales-preview/backupSettings.ftl
@@ -3,3 +3,33 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 settings-data-backup-header = Backup
+settings-data-backup-toggle = Manage backup
+
+## These strings are displayed in a modal when users want to turn on scheduled backups.
+
+turn-on-scheduled-backups-header = Turn on backup
+turn-on-scheduled-backups-description = { -brand-short-name } will create a snapshot of your data every 60 minutes. You can restore it if there’s a problem or you get a new device.
+turn-on-scheduled-backups-support-link = What will be backed up?
+
+# "Location" refers to the save location or a folder where users want backups stored.
+turn-on-scheduled-backups-location-label = Location
+# Variables:
+#   $recommendedFolder (String) - Name of the recommended folder for saving backups
+turn-on-scheduled-backups-location-default-folder =
+    .value = { $recommendedFolder } (recommended)
+turn-on-scheduled-backups-location-choose-button =
+    { PLATFORM() ->
+        [macos] Choose…
+        *[other] Browse…
+    }
+
+turn-on-scheduled-backups-encryption-label = Back up your sensitive data
+turn-on-scheduled-backups-encryption-description = Back up your passwords, payment methods, and cookies with encryption.
+turn-on-scheduled-backups-encryption-create-password-label = Password
+# Users will be prompted to re-type a password, to ensure that the password is entered correctly.
+turn-on-scheduled-backups-encryption-repeat-password-label = Repeat password
+
+turn-on-scheduled-backups-cancel-button = Cancel
+turn-on-scheduled-backups-confirm-button = Turn on backup
+
+##