Files
tubestation/browser/components/backup/ArchiveUtils.sys.mjs
Mike Conley 660509b6b4 Bug 1897498 - Add a mechanism for exporting backup snapshots to a single file archive. r=Gijs,valentin,backup-reviewers,frontend-codestyle-reviewers,Standard8,kpatenio
There are a number of interesting things going on this patch that I think are worth highlighting
here for my reviewers:

1. The single-file archive format is an HTML file that uses an inlined multipart/mixed MIME
   message within a HTML document comment in order to embed the backup data into the archive.
2. We use the multipart/mixed nsIStreamConverter to extract the JSON and binary data from
   the MIME block.
3. We use a Archive Worker to do the archive creation, allowing us to do the work of construction
   off of the main thread.
4. The Archive Worker is only parsing the header and getting the byte offset of the MIME block.
   Extraction is happening in the parent process. This is mainly for simplicity for now, since
   the Archive Worker cannot invoke an nsIStreamConverter. Down the line, if we determine that
   we'd prefer the Archive Worker do the base64 decoding off of the main thread, we may need
   to use a Message Channel to send the byte sfrom the nsIStreamConverter to it, and add
   stream-writing support to IOUtils so that the Archive Worker can take care of sending the
   decoded bytes to disk.
5. The patch doesn't expose the extraction mechanism in any way except through the debug
   interface right now. That will come down the line. In the meantime, this mechanism
   can be manually tested in the debug interface by creating a backup, which should also
   create an "archive.html" file in the backups folder. Using the "Extract from archive"
   button in the debug tool will let you select that HTML file and extract the ZIP as
   a file in the backups folder called "extraction.zip".
6. The test template contains Unicode characters because certain locales might involve
   us writing Unicode characters in the HTML template when generating the archive. The
   fun part about that is calculating where the byte offset is for the MIME block! See
   the comment in the Archive.worker.mjs script for how that works.

Differential Revision: https://phabricator.services.mozilla.com/D211588
2024-06-11 13:05:58 +00:00

92 lines
2.6 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/. */
// This module expects to be able to load in both main-thread module contexts,
// as well as ChromeWorker contexts. Do not ChromeUtils.importESModule
// anything there at the top-level that's not compatible with both contexts.
export const ArchiveUtils = {
/**
* Convert an array containing only two bytes unsigned numbers to a base64
* encoded string.
*
* @param {number[]} anArray
* The array that needs to be converted.
* @returns {string}
* The string representation of the array.
*/
arrayToBase64(anArray) {
let result = "";
let bytes = new Uint8Array(anArray);
for (let i = 0; i < bytes.length; i++) {
result += String.fromCharCode(bytes[i]);
}
return btoa(result);
},
/**
* Convert a base64 encoded string to an Uint8Array.
*
* @param {string} base64Str
* The base64 encoded string that needs to be converted.
* @returns {Uint8Array[]}
* The array representation of the string.
*/
stringToArray(base64Str) {
let binaryStr = atob(base64Str);
let len = binaryStr.length;
let bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryStr.charCodeAt(i);
}
return bytes;
},
/**
* The version of the single-file archive that this version of the
* application is expected to produce. Versions greater than this are not
* interpretable by the application, and will cause an exception to be
* thrown when loading the archive.
*
* Note: Until we can interpolate strings in our templates, changing this
* value will require manual changes to the archive.template.html version
* number in the header, as well as any test templates.
*
* @type {number}
*/
get ARCHIVE_FILE_VERSION() {
return 1;
},
/**
* The HTML document comment start block, also indicating the start of the
* inline MIME message block.
*
* @type {string}
*/
get INLINE_MIME_START_MARKER() {
return "<!-- Begin inline MIME --";
},
/**
* The HTML document comment end block, also indicating the end of the
* inline MIME message block.
*
* @type {string}
*/
get INLINE_MIME_END_MARKER() {
return "---- End inline MIME -->";
},
/**
* The maximum number of bytes to read and encode when constructing the
* single-file archive.
*
* @type {number}
*/
get ARCHIVE_CHUNK_MAX_BYTES_SIZE() {
return 1048576; // 2 ^ 20 bytes, per guidance from security engineering.
},
};