Bug 1901520 - Part 1: Send localized template down to the archive worker. r=backup-reviewers,fluent-reviewers,kpatenio,bolsson

Differential Revision: https://phabricator.services.mozilla.com/D213974
This commit is contained in:
Mike Conley 2024-06-25 00:35:43 +00:00
parent 9805f87c5f
commit 02d7da6f8b
5 changed files with 174 additions and 22 deletions

View File

@ -89,10 +89,10 @@ class ArchiveWorker {
* Arguments that are described in more detail below.
* @param {string} params.archivePath
* The path on the file system to write the single-file archive.
* @param {string} params.templateURI
* A URI pointing to the HTML template that will be used for the viewable
* part of the document. The inlined MIME message will be appended after
* the contents of this template.
* @param {string} params.markup
* The HTML markup to insert into the archive file before the HTML
* comment block. This is the markup that will be rendered if the HTML
* file is opened in a web browser.
* @param {object} params.backupMetadata
* The metadata associated with this backup. This is a copy of the metadata
* object that is contained within the compressed backups' manifest.
@ -106,7 +106,7 @@ class ArchiveWorker {
*/
async constructArchive({
archivePath,
templateURI,
markup,
backupMetadata,
compressedBackupSnapshotPath,
encryptionArgs,
@ -120,15 +120,6 @@ class ArchiveWorker {
);
}
// We can get at the template content by using a sync XHR, which is fine to
// to do in a Worker.
let templateXhr = new XMLHttpRequest();
// Using a synchronous XHR in a worker is fine.
templateXhr.open("GET", templateURI, false);
templateXhr.responseType = "text";
templateXhr.send(null);
let template = templateXhr.responseText;
let boundary = this.#generateBoundary();
let jsonBlock;
@ -156,7 +147,7 @@ class ArchiveWorker {
//
// This isn't supposed to be some kind of generalized MIME message
// generator, so we're happy to construct it by hand here.
await IOUtils.writeUTF8(archivePath, template);
await IOUtils.writeUTF8(archivePath, markup);
await IOUtils.writeUTF8(
archivePath,
`
@ -310,7 +301,8 @@ ${ArchiveUtils.INLINE_MIME_END_MARKER}
let textDecoder = new TextDecoder();
let decodedHeader = textDecoder.decode(headerBuffer);
const EXPECTED_HEADER = /^<!DOCTYPE html>\n<!-- Version: (\d+) -->\n/;
const EXPECTED_HEADER =
/^<!DOCTYPE html>[\r\n]+<!-- Version: (\d+) -->[\r\n]+/;
let headerMatches = decodedHeader.match(EXPECTED_HEADER);
if (!headerMatches) {
throw new Error("Corrupt archive header");

View File

@ -68,6 +68,13 @@ ChromeUtils.defineLazyGetter(lazy, "gFluentStrings", function () {
);
});
ChromeUtils.defineLazyGetter(lazy, "gDOMLocalization", function () {
return new DOMLocalization([
"branding/brand.ftl",
"preview/backupSettings.ftl",
]);
});
ChromeUtils.defineLazyGetter(lazy, "defaultParentDirPath", function () {
return Services.dirsvc.get("Docs", Ci.nsIFile).path;
});
@ -1171,6 +1178,76 @@ export class BackupService extends EventTarget {
}
}
/**
* Given a URI to an HTML template for the single-file backup archive,
* produces the static markup that will then be used as the beginning of that
* single-file backup archive.
*
* @param {string} templateURI
* A URI pointing at a template for the HTML content for the page. This is
* what is visible if the file is loaded in a web browser.
* @param {boolean} isEncrypted
* True if the template should indicate that the backup is encrypted.
* @param {object} backupMetadata
* The metadata for the backup, which is also stored in the backup manifest
* of the compressed backup snapshot.
* @returns {Promise<string>}
*/
async renderTemplate(templateURI, isEncrypted, backupMetadata) {
let templateResponse = await fetch(templateURI);
let templateString = await templateResponse.text();
let templateDOM = new DOMParser().parseFromString(
templateString,
"text/html"
);
// Set the lang attribute on the <html> element
templateDOM.documentElement.setAttribute(
"lang",
Services.locale.appLocaleAsBCP47
);
// TODO: insert download link (bug 1903117)
let supportLinkHref =
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"recover-from-backup";
let supportLink = templateDOM.querySelector("#support-link");
supportLink.href = supportLinkHref;
let encStateNode = templateDOM.querySelector("#encryption-state");
lazy.gDOMLocalization.setAttributes(
encStateNode,
isEncrypted
? "backup-file-encryption-state-encrypted"
: "backup-file-encryption-state-not-encrypted"
);
let lastBackedUpNode = templateDOM.querySelector("#last-backed-up");
lazy.gDOMLocalization.setArgs(lastBackedUpNode, {
// It's very unlikely that backupMetadata.date isn't a valid Date string,
// but if it _is_, then Fluent will cause us to crash in debug builds.
// We fallback to the current date if all else fails.
date: new Date(backupMetadata.date).getTime() || new Date().getTime(),
});
let creationDeviceNode = templateDOM.querySelector("#creation-device");
lazy.gDOMLocalization.setArgs(creationDeviceNode, {
machineName: backupMetadata.machineName,
});
try {
await lazy.gDOMLocalization.translateFragment(
templateDOM.documentElement
);
} catch (_) {
// This shouldn't happen, but we don't want a missing locale string to
// cause backup creation to fail.
}
let serializer = new XMLSerializer();
return serializer.serializeToString(templateDOM);
}
/**
* Creates a portable, potentially encrypted single-file archive containing
* a compressed backup snapshot. The single-file archive is a specially
@ -1203,6 +1280,12 @@ export class BackupService extends EventTarget {
backupMetadata,
options = {}
) {
let markup = await this.renderTemplate(
templateURI,
!!encState,
backupMetadata
);
let worker = new lazy.BasePromiseWorker(
"resource:///modules/backup/Archive.worker.mjs",
{ type: "module" }
@ -1225,7 +1308,7 @@ export class BackupService extends EventTarget {
await worker.post("constructArchive", [
{
archivePath,
templateURI,
markup,
backupMetadata,
compressedBackupSnapshotPath,
encryptionArgs,

View File

@ -15,13 +15,36 @@
#endif
<!DOCTYPE html>
<!-- Version: 1 -->
<html lang="{{locale}}">
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{title}}</title>
<title data-l10n-id="backup-file-title"></title>
</head>
<body>
<p>This is placeholder content for now.</p>
<h1 data-l10n-id="backup-file-header"></h1>
<p data-l10n-id="backup-file-intro">
<a data-l10n-name="backup-file-support-link" id="support-link" target="_blank"></a>
</p>
<ul>
<li id="last-backed-up" data-l10n-id="backup-file-last-backed-up"></li>
<li id="encryption-state"></li>
<li id="creation-device" data-l10n-id="backup-file-creation-device"></li>
</ul>
<hr />
<section class="moz-browser">
<h2 data-l10n-id="backup-file-how-to-restore-header"></h2>
<ol>
<li data-l10n-id="backup-file-moz-browser-restore-step-1"></li>
<li data-l10n-id="backup-file-moz-browser-restore-step-2"></li>
<li data-l10n-id="backup-file-moz-browser-restore-step-3"></li>
</ol>
</section>
<section class="other-browser">
<ol>
<li><span data-l10n-id="backup-file-other-browser-restore-step-1"></span><button id="download-moz-browser" data-l10n-id="backup-file-download-moz-browser-button"></button></li>
<li data-l10n-id="backup-file-other-browser-restore-step-2"></li>
</ol>
</section>
</body>
</html>

View File

@ -1,12 +1,37 @@
<!DOCTYPE html>
<!-- Version: 1 -->
<html lang="en">
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>This is a test title</title>
<title data-l10n-id="backup-file-title"></title>
</head>
<body>
<h1 data-l10n-id="backup-file-header"></h1>
<p data-l10n-id="backup-file-intro">
<a data-l10n-name="backup-file-support-link" id="support-link"></a>
</p>
<ul>
<li id="last-backed-up" data-l10n-id="backup-file-last-backed-up"></li>
<li id="encryption-state"></li>
<li id="creation-device" data-l10n-id="backup-file-creation-device"></li>
</ul>
<hr />
<section class="moz-browser">
<h2 data-l10n-id="backup-file-how-to-restore-header"></h2>
<ol>
<li data-l10n-id="backup-file-moz-browser-restore-step-1"></li>
<li data-l10n-id="backup-file-moz-browser-restore-step-2"></li>
<li data-l10n-id="backup-file-moz-browser-restore-step-3"></li>
</ol>
</section>
<section class="other-browser">
<ol>
<li><span data-l10n-id="backup-file-other-browser-restore-step-1"></span> <a id="download-moz-browser" data-l10n-id="backup-file-download-moz-browser-button"></a></li>
<li data-l10n-id="backup-file-other-browser-restore-step-2"></li>
</ol>
</section>
<h1>This is some text.</h1>
<p>I'm also including some unicode characters: 🐶🐶🐶🐶🐶🐶🐶🐶🐶🐶🐶🐶</p>
<p>Here are some Arabic glyphs: أهلاً بك!</p>

View File

@ -84,4 +84,33 @@ disable-backup-encryption-support-link = What will be backed up?
disable-backup-encryption-cancel-button = Cancel
disable-backup-encryption-confirm-button = Remove password
## These strings are inserted into the generated single-file backup archive.
## The single-file backup archive is a specially-crafted, static HTML file
## that is placed within a user specified directory (the Documents folder by
## default) within a folder labelled with the "backup-folder-name" string.
backup-file-header = { -brand-short-name } is ready to be restored
backup-file-title = Restore { -brand-short-name }
backup-file-intro = Get back to browsing and recover all your bookmarks, history, and other data. <a data-l10n-name="backup-file-support-link">Learn more</a>
# Variables:
# $date (string) - Date to be formatted based on locale
backup-file-last-backed-up = <strong>Last backed up:</strong> { DATETIME($date, timeStyle: "short") }, { DATETIME($date, dateStyle: "short") }
backup-file-encryption-state-encrypted = Encrypted
backup-file-encryption-state-not-encrypted = Not encrypted
# Variables:
# $machineName (String) - Name of the machine that the backup was created on.
backup-file-creation-device = Created on { $machineName }
backup-file-how-to-restore-header = How to restore your data:
backup-file-moz-browser-restore-step-1 = Go to Settings > Backup
backup-file-moz-browser-restore-step-2 = Under “Restore”, click “Choose backup file”
backup-file-moz-browser-restore-step-3 = Restart { -brand-short-name } when asked
backup-file-other-browser-restore-step-1 = Download and install { -brand-short-name }:
backup-file-download-moz-browser-button = Download { -brand-short-name }
backup-file-other-browser-restore-step-2 = Open { -brand-short-name } and restore your backup
##