Bug 1895943 - Implement file picker and save location for backups. r=backup-reviewers,mconley,fluent-reviewers,flod,firefox-desktop-core-reviewers

1. Allows for selecting a custom path or the default (Documents) path for saving backups. The selection is passed from the "turn-on-scheduled-backups" dialog to "BackupService".
2. After pressing the "Choose" button in the dialog, a filepicker will appear so that a folder can be selected.
3. Once the dialog is confirmed, the absolute path is saved to a pref called "browser.backup.location" and saved in the service state.

Other changes:
- Added the Documents folder as the default save location
- Added an onUpdate function for "browser.backup.location" that passes the updated BackupService state to registered widgets (backup settings section, dialogs)
- Added Storybook entries and tests for the newly updated input and filepicker

Figma: https://www.figma.com/design/vNbX4c0ws0L1qr0mxpKvsW/Fx-Backup?node-id=147-4568&t=tILUMKfg8c6Ed1Ul-0 (turn on backup dialog)

Differential Revision: https://phabricator.services.mozilla.com/D210850
This commit is contained in:
kpatenio
2024-06-10 21:41:40 +00:00
parent 027089b9b5
commit f5e604a1bf
13 changed files with 504 additions and 39 deletions

View File

@@ -442,6 +442,7 @@ let JSWINDOWACTORS = {
events: {
"BackupUI:InitWidget": { wantUntrusted: true },
"BackupUI:ToggleScheduledBackups": { wantUntrusted: true },
"BackupUI:ShowFilepicker": { wantUntrusted: true },
},
},
matches: ["about:preferences*", "about:settings*"],

View File

@@ -6,6 +6,7 @@ import * as DefaultBackupResources from "resource:///modules/backup/BackupResour
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const BACKUP_DIR_PREF_NAME = "browser.backup.location";
const SCHEDULED_BACKUPS_ENABLED_PREF_NAME = "browser.backup.scheduled.enabled";
const lazy = {};
@@ -37,6 +38,13 @@ ChromeUtils.defineLazyGetter(lazy, "ZipWriter", () =>
Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter", "open")
);
ChromeUtils.defineLazyGetter(lazy, "gFluentStrings", function () {
return new Localization(
["branding/brand.ftl", "preview/backupSettings.ftl"],
true
);
});
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"scheduledBackupsPref",
@@ -50,6 +58,20 @@ XPCOMUtils.defineLazyPreferenceGetter(
}
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"backupDirPref",
BACKUP_DIR_PREF_NAME,
Services.dirsvc.get("Docs", Ci.nsIFile)
.path /* Default directory is Documents */,
async function onUpdateLocationDirPath(_pref, _prevVal, newVal) {
let bs = BackupService.get();
if (bs) {
await bs.onUpdateLocationDirPath(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
@@ -71,6 +93,13 @@ export class BackupService extends EventTarget {
*/
#resources = new Map();
/**
* The name of the backup folder. Should be localized.
*
* @see BACKUP_DIR_NAME
*/
static #backupFolderName = null;
/**
* Set to true if a backup is currently in progress. Causes stateUpdate()
* to be called.
@@ -112,7 +141,12 @@ export class BackupService extends EventTarget {
* @type {object}
*/
#_state = {
backupFilePath: "Documents", // TODO: make save location configurable (bug 1895943)
backupDirPath: lazy.backupDirPref,
defaultParent: {
path: BackupService.DEFAULT_PARENT_DIR_PATH,
fileName: PathUtils.filename(BackupService.DEFAULT_PARENT_DIR_PATH),
iconURL: this.getIconFromFilePath(BackupService.DEFAULT_PARENT_DIR_PATH),
},
backupInProgress: false,
scheduledBackupsEnabled: lazy.scheduledBackupsPref,
encryptionEnabled: false,
@@ -152,6 +186,29 @@ export class BackupService extends EventTarget {
*/
#encState = undefined;
/**
* The path of the default parent directory for saving backups.
* The current default is the Documents directory.
*
* @returns {string} The path of the default parent directory
*/
static get DEFAULT_PARENT_DIR_PATH() {
return Services.dirsvc.get("Docs", Ci.nsIFile).path;
}
/**
* The localized name for the user's backup folder.
*
* @returns {string} The localized backup folder name
*/
static get BACKUP_DIR_NAME() {
if (!BackupService.#backupFolderName) {
BackupService.#backupFolderName =
lazy.gFluentStrings.formatValueSync("backup-folder-name");
}
return BackupService.#backupFolderName;
}
/**
* The name of the folder within the profile folder where this service reads
* and writes state to.
@@ -924,6 +981,65 @@ export class BackupService extends EventTarget {
}
}
/**
* Sets the parent directory of the backups folder. Calling this function will update
* browser.backup.location.
*
* @param {string} parentDirPath directory path
*/
setParentDirPath(parentDirPath) {
try {
if (!parentDirPath || !PathUtils.filename(parentDirPath)) {
throw new Error("Parent directory path is invalid.");
}
// Recreate the backups path with the new parent directory.
let fullPath = PathUtils.join(
parentDirPath,
BackupService.BACKUP_DIR_NAME
);
Services.prefs.setStringPref(BACKUP_DIR_PREF_NAME, fullPath);
} catch (e) {
lazy.logConsole.error(
`Failed to set parent directory ${parentDirPath}. ${e}`
);
}
}
/**
* Updates backupDirPath in the backup service state. Should be called every time the value
* for browser.backup.location changes.
*
* @param {string} newDirPath the new directory path for storing backups
*/
async onUpdateLocationDirPath(newDirPath) {
lazy.logConsole.debug(`Updating backup location to ${newDirPath}`);
this.#_state.backupDirPath = newDirPath;
this.stateUpdate();
}
/**
* Returns the moz-icon URL of a file. To get the moz-icon URL, the
* file path is convered to a fileURI. If there is a problem retreiving
* the moz-icon due to an invalid file path, return null instead.
*
* @param {string} path Path of the file to read its icon from.
* @returns {string|null} The moz-icon URL of the specified file, or
* null if the icon cannot be retreived.
*/
getIconFromFilePath(path) {
if (!path) {
return null;
}
try {
let fileURI = PathUtils.toFileURI(path);
return `moz-icon:${fileURI}?size=16`;
} catch (e) {
return null;
}
}
/**
* Sets browser.backup.scheduled.enabled to true or false.
*

View File

@@ -19,7 +19,7 @@ export class BackupUIChild extends JSWindowActorChild {
* @param {Event} event
* The custom event that the widget fired.
*/
handleEvent(event) {
async 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
@@ -31,6 +31,31 @@ export class BackupUIChild extends JSWindowActorChild {
this.sendAsyncMessage("RequestState");
} else if (event.type == "BackupUI:ToggleScheduledBackups") {
this.sendAsyncMessage("ToggleScheduledBackups", event.detail);
} else if (event.type == "BackupUI:ShowFilepicker") {
let targetNodeName = event.target.nodeName;
let { path, filename, iconURL } = await this.sendQuery("ShowFilepicker", {
win: event.detail?.win,
});
let widgets = ChromeUtils.nondeterministicGetWeakSetKeys(
this.#inittedWidgets
);
for (let widget of widgets) {
if (widget.isConnected && widget.nodeName == targetNodeName) {
widget.dispatchEvent(
new CustomEvent("BackupUI:SelectNewFilepickerPath", {
bubbles: true,
detail: {
path,
filename,
iconURL,
},
})
);
break;
}
}
}
}

View File

@@ -66,12 +66,49 @@ export class BackupUIParent extends JSWindowActorParent {
* @param {ReceiveMessageArgument} message
* The message received from the BackupUIChild.
*/
receiveMessage(message) {
async receiveMessage(message) {
if (message.name == "RequestState") {
this.sendState();
} else if (message.name == "ToggleScheduledBackups") {
this.#bs.setScheduledBackups(message.data?.isScheduledBackupsEnabled);
let { isScheduledBackupsEnabled, parentDirPath } = message.data;
if (isScheduledBackupsEnabled && parentDirPath) {
this.#bs.setParentDirPath(parentDirPath);
}
this.#bs.setScheduledBackups(isScheduledBackupsEnabled);
return true;
/**
* 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.
*/
} else if (message.name == "ShowFilepicker") {
let { win } = message.data;
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
fp.init(win, "", Ci.nsIFilePicker.modeGetFolder);
let result = await new Promise(resolve => fp.open(resolve));
if (result === Ci.nsIFilePicker.returnCancel) {
return false;
}
let path = fp.file.path;
let iconURL = this.#bs.getIconFromFilePath(path);
let filename = PathUtils.filename(path);
return {
path,
filename,
iconURL,
};
}
return null;
}
/**

View File

@@ -36,8 +36,9 @@ export default class BackupSettings extends MozLitElement {
constructor() {
super();
this.backupServiceState = {
backupFilePath: "Documents", // TODO: make save location configurable (bug 1895943)
backupDirPath: "",
backupInProgress: false,
defaultParent: {},
scheduledBackupsEnabled: false,
};
}
@@ -66,6 +67,7 @@ export default class BackupSettings extends MozLitElement {
bubbles: true,
composed: true,
detail: {
...event.detail,
isScheduledBackupsEnabled: true,
},
})
@@ -108,9 +110,12 @@ export default class BackupSettings extends MozLitElement {
}
turnOnScheduledBackupsDialogTemplate() {
let { fileName, path, iconURL } = this.backupServiceState.defaultParent;
return html`<dialog id="turn-on-scheduled-backups-dialog">
<turn-on-scheduled-backups
.backupFilePath=${this.backupServiceState.backupFilePath}
defaultlabel=${fileName}
defaultpath=${path}
defaulticonurl=${iconURL}
></turn-on-scheduled-backups>
</dialog>`;
}

View File

@@ -22,8 +22,12 @@ const Template = ({ backupServiceState }) => html`
export const BackingUpNotInProgress = Template.bind({});
BackingUpNotInProgress.args = {
backupServiceState: {
backupFilePath: "Documents",
backupDirPath: "/Some/User/Documents",
backupInProgress: false,
defaultParent: {
path: "/Some/User/Documents",
fileName: "Documents",
},
scheduledBackupsEnabled: false,
},
};
@@ -31,8 +35,12 @@ BackingUpNotInProgress.args = {
export const BackingUpInProgress = Template.bind({});
BackingUpInProgress.args = {
backupServiceState: {
backupFilePath: "Documents",
backupDirPath: "/Some/User/Documents",
backupInProgress: true,
defaultParent: {
path: "/Some/User/Documents",
fileName: "Documents",
},
scheduledBackupsEnabled: false,
},
};
@@ -40,8 +48,12 @@ BackingUpInProgress.args = {
export const ScheduledBackupsEnabled = Template.bind({});
ScheduledBackupsEnabled.args = {
backupServiceState: {
backupFilePath: "Documents",
backupDirPath: "/Some/User/Documents",
backupInProgress: false,
defaultParent: {
path: "/Some/User/Documents",
fileName: "Documents",
},
scheduledBackupsEnabled: true,
},
};

View File

@@ -53,9 +53,22 @@
align-items: center;
}
#backup-location-filepicker-input {
margin: 0;
.backup-location-filepicker-input {
flex: 1;
margin-block: var(--space-xsmall);
margin-inline-start: 0;
padding-inline-start: var(--space-xxlarge);
background-repeat: no-repeat;
background-size: var(--icon-size-default);
background-position: var(--space-small) 50%;
/* For the placeholder icon */
fill: currentColor;
-moz-context-properties: fill;
&:dir(rtl) {
background-position: calc(100% - var(--space-small)) 50%;
}
}
}

View File

@@ -10,8 +10,15 @@ import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
* scheduled backups.
*/
export default class TurnOnScheduledBackups extends MozLitElement {
#placeholderIconURL = "chrome://global/skin/icons/page-portrait.svg";
static properties = {
backupFilePath: { type: String },
defaultIconURL: { type: String, reflect: true },
defaultLabel: { type: String, reflect: true },
defaultPath: { type: String, reflect: true },
_newIconURL: { type: String },
_newLabel: { type: String },
_newPath: { type: String },
showPasswordOptions: { type: Boolean, reflect: true },
};
@@ -19,15 +26,22 @@ export default class TurnOnScheduledBackups extends MozLitElement {
return {
cancelButtonEl: "#backup-turn-on-scheduled-cancel-button",
confirmButtonEl: "#backup-turn-on-scheduled-confirm-button",
filePathButtonEl: "#backup-location-filepicker-button",
filePathInputCustomEl: "#backup-location-filepicker-input-custom",
filePathInputDefaultEl: "#backup-location-filepicker-input-default",
passwordOptionsCheckboxEl: "#sensitive-data-checkbox-input",
passwordOptionsExpandedEl: "#passwords",
recommendedFolderInputEl: "#backup-location-filepicker-input",
};
}
constructor() {
super();
this.backupFilePath = null;
this.defaultIconURL = "";
this.defaultLabel = "";
this.defaultPath = "";
this._newIconURL = "";
this._newLabel = "";
this._newPath = "";
this.showPasswordOptions = false;
}
@@ -40,10 +54,28 @@ export default class TurnOnScheduledBackups extends MozLitElement {
this.dispatchEvent(
new CustomEvent("BackupUI:InitWidget", { bubbles: true })
);
this.addEventListener("BackupUI:SelectNewFilepickerPath", this);
}
handleChooseLocation() {
// TODO: show file picker (bug 1895943)
handleEvent(event) {
if (event.type == "BackupUI:SelectNewFilepickerPath") {
let { path, filename, iconURL } = event.detail;
this._newPath = path;
this._newLabel = filename;
this._newIconURL = iconURL;
}
}
async handleChooseLocation() {
this.dispatchEvent(
new CustomEvent("BackupUI:ShowFilepicker", {
bubbles: true,
detail: {
win: window.browsingContext,
},
})
);
}
handleCancel() {
@@ -53,14 +85,12 @@ export default class TurnOnScheduledBackups extends MozLitElement {
composed: true,
})
);
this.showPasswordOptions = false;
this.passwordOptionsCheckboxEl.checked = false;
this.resetChanges();
}
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).
*/
@@ -68,16 +98,62 @@ export default class TurnOnScheduledBackups extends MozLitElement {
new CustomEvent("turnOnScheduledBackups", {
bubbles: true,
composed: true,
detail: {
parentDirPath: this._newPath || this.defaultPath,
},
})
);
this.showPasswordOptions = false;
this.passwordOptionsCheckboxEl.checked = false;
this.resetChanges();
}
handleTogglePasswordOptions() {
this.showPasswordOptions = this.passwordOptionsCheckboxEl?.checked;
}
resetChanges() {
this._newPath = "";
this._newIconURL = "";
this._newLabel = "";
this.showPasswordOptions = false;
this.passwordOptionsCheckboxEl.checked = false;
}
defaultFilePathInputTemplate() {
let filename = this.defaultLabel;
let iconURL = this.defaultIconURL || this.#placeholderIconURL;
return html`
<input
id="backup-location-filepicker-input-default"
class="backup-location-filepicker-input"
type="text"
readonly
data-l10n-id="turn-on-scheduled-backups-location-default-folder"
data-l10n-args=${JSON.stringify({
recommendedFolder: filename,
})}
data-l10n-attrs="value"
style=${`background-image: url(${iconURL})`}
/>
`;
}
customFilePathInputTemplate() {
let filename = this._newLabel;
let iconURL = this._newIconURL || this.#placeholderIconURL;
return html`
<input
id="backup-location-filepicker-input-custom"
class="backup-location-filepicker-input"
type="text"
readonly
value=${filename}
style=${`background-image: url(${iconURL})`}
/>
`;
}
allOptionsTemplate() {
return html`
<fieldset id="all-controls">
@@ -87,18 +163,10 @@ export default class TurnOnScheduledBackups extends MozLitElement {
for="backup-location-filepicker-input"
data-l10n-id="turn-on-scheduled-backups-location-label"
></label>
<!-- TODO: show folder icon (bug 1895943) -->
<div id="backup-location-filepicker">
<input
id="backup-location-filepicker-input"
type="text"
readonly
data-l10n-id="turn-on-scheduled-backups-location-default-folder"
data-l10n-args=${JSON.stringify({
recommendedFolder: this.backupFilePath,
})}
data-l10n-attrs="value"
/>
${!this._newPath
? this.defaultFilePathInputTemplate()
: this.customFilePathInputTemplate()}
<moz-button
id="backup-location-filepicker-button"
@click=${this.handleChooseLocation}

View File

@@ -3,7 +3,7 @@
* 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 { html, ifDefined } from "lit.all.mjs";
import "chrome://global/content/elements/moz-card.mjs";
import "./turn-on-scheduled-backups.mjs";
@@ -16,15 +16,26 @@ export default {
argTypes: {},
};
const Template = ({ backupFilePath }) => html`
const Template = ({ defaultPath, _newPath, defaultLabel, _newLabel }) => html`
<moz-card style="width: 27.8rem;">
<turn-on-scheduled-backups
.backupFilePath=${backupFilePath}
defaultPath=${defaultPath}
_newPath=${ifDefined(_newPath)}
defaultLabel=${defaultLabel}
_newLabel=${ifDefined(_newLabel)}
></turn-on-scheduled-backups>
</moz-card>
`;
export const RecommendedFolder = Template.bind({});
RecommendedFolder.args = {
backupFilePath: "Documents",
export const Default = Template.bind({});
Default.args = {
defaultPath: "/Some/User/Documents",
defaultLabel: "Documents",
};
export const CustomLocation = Template.bind({});
CustomLocation.args = {
...Default.args,
_newPath: "/Some/Test/Custom/Dir",
_newLabel: "Dir",
};

View File

@@ -3,6 +3,19 @@
"use strict";
const { BackupService } = ChromeUtils.importESModule(
"resource:///modules/backup/BackupService.sys.mjs"
);
const { MockFilePicker } = SpecialPowers;
add_setup(async () => {
MockFilePicker.init(window.browsingContext);
registerCleanupFunction(() => {
MockFilePicker.cleanup();
});
});
/**
* Tests that the section for controlling backup in about:preferences is
* visible, but can also be hidden via a pref.
@@ -87,6 +100,119 @@ add_task(async function test_turn_on_scheduled_backups_confirm() {
});
});
/**
* Tests that the turn on scheduled backups dialog displays the default input field
* and a filepicker to choose a custom backup file path, updates the input field to show
* that path, and sets browser.backup.location to the path from the settings page.
*/
add_task(async function test_turn_on_custom_location_filepicker() {
await BrowserTestUtils.withNewTab("about:preferences", async browser => {
const mockCustomParentDir = await IOUtils.createUniqueDirectory(
PathUtils.tempDir,
"settings-custom-dir-test"
);
const dummyFile = Cc["@mozilla.org/file/local;1"].createInstance(
Ci.nsIFile
);
dummyFile.initWithPath(mockCustomParentDir);
let filePickerShownPromise = new Promise(resolve => {
MockFilePicker.showCallback = () => {
Assert.ok(true, "Filepicker shown");
MockFilePicker.setFiles([dummyFile]);
resolve();
};
});
MockFilePicker.returnValue = MockFilePicker.returnOK;
// After setting up mocks, start testing components
let settings = browser.contentDocument.querySelector("backup-settings");
let turnOnButton = settings.scheduledBackupsButtonEl;
Assert.ok(
turnOnButton,
"Button to turn on scheduled backups should be found"
);
await settings.updateComplete;
let turnOnScheduledBackups = settings.turnOnScheduledBackupsEl;
Assert.ok(
turnOnScheduledBackups,
"turn-on-scheduled-backups should be found"
);
// First verify the default input value and dir path button
let filePathInputDefault = turnOnScheduledBackups.filePathInputDefaultEl;
let filePathButton = turnOnScheduledBackups.filePathButtonEl;
const documentsPath = BackupService.DEFAULT_PARENT_DIR_PATH;
Assert.ok(
filePathInputDefault,
"Default input for choosing a file path should be found"
);
Assert.equal(
filePathInputDefault.value,
`${PathUtils.filename(documentsPath)} (recommended)`,
"Default input displays the expected text"
);
Assert.ok(
filePathButton,
"Button for choosing a file path should be found"
);
// Next, verify the filepicker and updated dialog
let inputUpdatePromise = BrowserTestUtils.waitForCondition(
() => turnOnScheduledBackups.filePathInputCustomEl
);
filePathButton.click();
await filePickerShownPromise;
await turnOnScheduledBackups.updateComplete;
info("Waiting for file path input to update");
await inputUpdatePromise;
Assert.ok("Input should have been updated");
let filePathInputCustom = turnOnScheduledBackups.filePathInputCustomEl;
Assert.equal(
filePathInputCustom.value,
PathUtils.filename(mockCustomParentDir),
"Input should display file path from filepicker"
);
// Now close the dialog by confirming choices and verify that backup settings are saved
let confirmButton = turnOnScheduledBackups.confirmButtonEl;
Assert.ok(confirmButton, "Confirm button should be found");
let confirmButtonPromise = BrowserTestUtils.waitForEvent(
window,
"turnOnScheduledBackups"
);
confirmButton.click();
await confirmButtonPromise;
await settings.updateComplete;
// Backup folder should be joined with the updated path
let locationPrefVal = Services.prefs.getStringPref(
"browser.backup.location"
);
Assert.equal(
locationPrefVal,
PathUtils.join(mockCustomParentDir, BackupService.BACKUP_DIR_NAME),
"Backup location pref should be updated"
);
await IOUtils.remove(mockCustomParentDir, {
ignoreAbsent: true,
recursive: true,
});
});
});
/**
* Tests that the turn off scheduled backups dialog can set
* browser.backup.scheduled.enabled to false from the settings page.

View File

@@ -38,8 +38,12 @@
*/
add_task(async function test_turnOnScheduledBackupsDialog() {
let settings = document.getElementById("test-backup-settings");
const testDefaultName = "test-default-path";
settings.backupServiceState = {
scheduledBackupsEnabled: false,
defaultParent: {
path: PathUtils.join(PathUtils.tempDir, testDefaultName),
fileName: testDefaultName,
}
}
await settings.updateComplete;
@@ -73,7 +77,12 @@
*/
add_task(async function test_turnOffScheduledBackupsDialog() {
let settings = document.getElementById("test-backup-settings");
const testDefaultName = "test-default-path";
settings.backupServiceState = {
defaultParent: {
path: PathUtils.join(PathUtils.tempDir, testDefaultName),
fileName: testDefaultName,
},
scheduledBackupsEnabled: true,
}

View File

@@ -9,6 +9,8 @@
src="chrome://browser/content/backup/turn-on-scheduled-backups.mjs"
type="module"
></script>
<link rel="localization" href="preview/backupSettings.ftl"/>
<link rel="localization" href="branding/brand.ftl"/>
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
<script>
@@ -84,6 +86,41 @@
ok(turnOnScheduledBackups.passwordOptionsExpandedEl, "Passwords expanded options should be found");
})
/**
* Tests that the dialog displays a default save location for backups and updates to a custom one
* if there is one selected.
*/
add_task(async function test_inputs() {
let turnOnScheduledBackups = document.getElementById("test-turn-on-scheduled-backups");
let inputDefault = turnOnScheduledBackups.filePathInputDefaultEl;
ok(inputDefault, "Default input should be found");
let promise = BrowserTestUtils.waitForCondition(() => inputDefault.value);
/* Normally we would pass in the default attributes, but for this test, we will
* hardcode them since file paths vary across different platforms.
*/
const defaultPathFilename = "testdefaultpath";
let defaultPath = PathUtils.join(PathUtils.tempDir, defaultPathFilename);
turnOnScheduledBackups.defaultPath = defaultPath;
turnOnScheduledBackups.defaultLabel = defaultPathFilename;
await turnOnScheduledBackups.updateComplete;
await promise;
is(inputDefault.value, `${defaultPathFilename} (recommended)`, "Default input should not be empty and should contain part of the default path");
// Now pretend a custom file path was selected
const newPathFilename = "testnewpath";
let newPath = PathUtils.join(PathUtils.tempDir, newPathFilename);
turnOnScheduledBackups._newPath = newPath;
turnOnScheduledBackups._newLabel = newPathFilename;
await turnOnScheduledBackups.updateComplete;
let inputCustom = turnOnScheduledBackups.filePathInputCustomEl;
ok(inputCustom, "Input should be updated");
is(inputCustom.value, newPathFilename, "Input value should be set to the new path");
})
</script>
</head>
<body>

View File

@@ -2,6 +2,11 @@
# 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/.
# This string is used to name the folder that users will save backups to.
# "Restore" is an action and intended for prompting users to select this folder
# when following backup restoration steps.
backup-folder-name = Restore { -brand-product-name }
settings-data-backup-header = Backup
settings-data-backup-toggle = Manage backup