summaryrefslogtreecommitdiffstats
path: root/browser/components/backup/resources
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/components/backup/resources/AddonsBackupResource.sys.mjs100
-rw-r--r--browser/components/backup/resources/BackupResource.sys.mjs83
-rw-r--r--browser/components/backup/resources/CookiesBackupResource.sys.mjs25
-rw-r--r--browser/components/backup/resources/CredentialsAndSecurityBackupResource.sys.mjs53
-rw-r--r--browser/components/backup/resources/FormHistoryBackupResource.sys.mjs25
-rw-r--r--browser/components/backup/resources/MiscDataBackupResource.sys.mjs101
-rw-r--r--browser/components/backup/resources/PlacesBackupResource.sys.mjs91
-rw-r--r--browser/components/backup/resources/PreferencesBackupResource.sys.mjs98
-rw-r--r--browser/components/backup/resources/SessionStoreBackupResource.sys.mjs53
9 files changed, 618 insertions, 11 deletions
diff --git a/browser/components/backup/resources/AddonsBackupResource.sys.mjs b/browser/components/backup/resources/AddonsBackupResource.sys.mjs
new file mode 100644
index 0000000000..83b97ed2f2
--- /dev/null
+++ b/browser/components/backup/resources/AddonsBackupResource.sys.mjs
@@ -0,0 +1,100 @@
+/* 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 { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
+
+/**
+ * Backup for addons and extensions files and data.
+ */
+export class AddonsBackupResource extends BackupResource {
+ static get key() {
+ return "addons";
+ }
+
+ static get requiresEncryption() {
+ return false;
+ }
+
+ async measure(profilePath = PathUtils.profileDir) {
+ // Report the total size of the extension json files.
+ const jsonFiles = [
+ "extensions.json",
+ "extension-settings.json",
+ "extension-preferences.json",
+ "addonStartup.json.lz4",
+ ];
+ let extensionsJsonSize = 0;
+ for (const filePath of jsonFiles) {
+ let resourcePath = PathUtils.join(profilePath, filePath);
+ let resourceSize = await BackupResource.getFileSize(resourcePath);
+ if (Number.isInteger(resourceSize)) {
+ extensionsJsonSize += resourceSize;
+ }
+ }
+ Glean.browserBackup.extensionsJsonSize.set(extensionsJsonSize);
+
+ // Report the size of permissions store data, if present.
+ let extensionStorePermissionsDataPath = PathUtils.join(
+ profilePath,
+ "extension-store-permissions",
+ "data.safe.bin"
+ );
+ let extensionStorePermissionsDataSize = await BackupResource.getFileSize(
+ extensionStorePermissionsDataPath
+ );
+ if (Number.isInteger(extensionStorePermissionsDataSize)) {
+ Glean.browserBackup.extensionStorePermissionsDataSize.set(
+ extensionStorePermissionsDataSize
+ );
+ }
+
+ // Report the size of extensions storage sync database.
+ let storageSyncPath = PathUtils.join(profilePath, "storage-sync-v2.sqlite");
+ let storageSyncSize = await BackupResource.getFileSize(storageSyncPath);
+ Glean.browserBackup.storageSyncSize.set(storageSyncSize);
+
+ // Report the total size of XPI files in the extensions directory.
+ let extensionsXpiDirectoryPath = PathUtils.join(profilePath, "extensions");
+ let extensionsXpiDirectorySize = await BackupResource.getDirectorySize(
+ extensionsXpiDirectoryPath,
+ {
+ shouldExclude: (filePath, fileType) =>
+ fileType !== "regular" || !filePath.endsWith(".xpi"),
+ }
+ );
+ Glean.browserBackup.extensionsXpiDirectorySize.set(
+ extensionsXpiDirectorySize
+ );
+
+ // Report the total size of the browser extension data.
+ let browserExtensionDataPath = PathUtils.join(
+ profilePath,
+ "browser-extension-data"
+ );
+ let browserExtensionDataSize = await BackupResource.getDirectorySize(
+ browserExtensionDataPath
+ );
+ Glean.browserBackup.browserExtensionDataSize.set(browserExtensionDataSize);
+
+ // Report the size of all moz-extension IndexedDB databases.
+ let defaultStoragePath = PathUtils.join(profilePath, "storage", "default");
+ let extensionsStorageSize = await BackupResource.getDirectorySize(
+ defaultStoragePath,
+ {
+ shouldExclude: (filePath, _fileType, parentPath) => {
+ if (
+ parentPath == defaultStoragePath &&
+ !PathUtils.filename(filePath).startsWith("moz-extension")
+ ) {
+ return true;
+ }
+ return false;
+ },
+ }
+ );
+ if (Number.isInteger(extensionsStorageSize)) {
+ Glean.browserBackup.extensionsStorageSize.set(extensionsStorageSize);
+ }
+ }
+}
diff --git a/browser/components/backup/resources/BackupResource.sys.mjs b/browser/components/backup/resources/BackupResource.sys.mjs
index bde3f0669c..d851eb5199 100644
--- a/browser/components/backup/resources/BackupResource.sys.mjs
+++ b/browser/components/backup/resources/BackupResource.sys.mjs
@@ -3,7 +3,19 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// Convert from bytes to kilobytes (not kibibytes).
-const BYTES_IN_KB = 1000;
+export const BYTES_IN_KB = 1000;
+
+/**
+ * Convert bytes to the nearest 10th kilobyte to make the measurements fuzzier.
+ *
+ * @param {number} bytes - size in bytes.
+ * @returns {number} - size in kilobytes rounded to the nearest 10th kilobyte.
+ */
+export function bytesToFuzzyKilobytes(bytes) {
+ let sizeInKb = Math.ceil(bytes / BYTES_IN_KB);
+ let nearestTenthKb = Math.round(sizeInKb / 10) * 10;
+ return Math.max(nearestTenthKb, 1);
+}
/**
* An abstract class representing a set of data within a user profile
@@ -23,6 +35,21 @@ export class BackupResource {
}
/**
+ * This must be overridden to return a boolean indicating whether the
+ * resource requires encryption when being backed up. Encryption should be
+ * required for particularly sensitive data, such as passwords / credentials,
+ * cookies, or payment methods. If you're not sure, talk to someone from the
+ * Privacy team.
+ *
+ * @type {boolean}
+ */
+ static get requiresEncryption() {
+ throw new Error(
+ "BackupResource::requiresEncryption needs to be overridden."
+ );
+ }
+
+ /**
* Get the size of a file.
*
* @param {string} filePath - path to a file.
@@ -40,21 +67,25 @@ export class BackupResource {
return null;
}
- let sizeInKb = Math.ceil(size / BYTES_IN_KB);
- // Make the measurement fuzzier by rounding to the nearest 10kb.
- let nearestTenthKb = Math.round(sizeInKb / 10) * 10;
+ let nearestTenthKb = bytesToFuzzyKilobytes(size);
- return Math.max(nearestTenthKb, 1);
+ return nearestTenthKb;
}
/**
* Get the total size of a directory.
*
* @param {string} directoryPath - path to a directory.
+ * @param {object} options - A set of additional optional parameters.
+ * @param {Function} [options.shouldExclude] - an optional callback which based on file path and file type should return true
+ * if the file should be excluded from the computed directory size.
* @returns {Promise<number|null>} - the size of all descendants of the directory in kilobytes, or null if the
* directory does not exist, the path is not a directory or the size is unknown.
*/
- static async getDirectorySize(directoryPath) {
+ static async getDirectorySize(
+ directoryPath,
+ { shouldExclude = () => false } = {}
+ ) {
if (!(await IOUtils.exists(directoryPath))) {
return null;
}
@@ -75,15 +106,20 @@ export class BackupResource {
childFilePath
);
+ if (shouldExclude(childFilePath, childType, directoryPath)) {
+ continue;
+ }
+
if (childSize >= 0) {
- let sizeInKb = Math.ceil(childSize / BYTES_IN_KB);
- // Make the measurement fuzzier by rounding to the nearest 10kb.
- let nearestTenthKb = Math.round(sizeInKb / 10) * 10;
- size += Math.max(nearestTenthKb, 1);
+ let nearestTenthKb = bytesToFuzzyKilobytes(childSize);
+
+ size += nearestTenthKb;
}
if (childType == "directory") {
- let childDirectorySize = await this.getDirectorySize(childFilePath);
+ let childDirectorySize = await this.getDirectorySize(childFilePath, {
+ shouldExclude,
+ });
if (Number.isInteger(childDirectorySize)) {
size += childDirectorySize;
}
@@ -106,4 +142,29 @@ export class BackupResource {
async measure(profilePath) {
throw new Error("BackupResource::measure needs to be overridden.");
}
+
+ /**
+ * Perform a safe copy of the resource(s) and write them into the backup
+ * database. The Promise should resolve with an object that can be serialized
+ * to JSON, as it will be written to the manifest file. This same object will
+ * be deserialized and passed to restore() when restoring the backup. This
+ * object can be null if no additional information is needed to restore the
+ * backup.
+ *
+ * @param {string} stagingPath
+ * The path to the staging folder where copies of the datastores for this
+ * BackupResource should be written to.
+ * @param {string} [profilePath=null]
+ * This is null if the backup is being run on the currently running user
+ * profile. If, however, the backup is being run on a different user profile
+ * (for example, it's being run from a BackgroundTask on a user profile that
+ * just shut down, or during test), then this is a string set to that user
+ * profile path.
+ *
+ * @returns {Promise<object|null>}
+ */
+ // eslint-disable-next-line no-unused-vars
+ async backup(stagingPath, profilePath = null) {
+ throw new Error("BackupResource::backup must be overridden");
+ }
}
diff --git a/browser/components/backup/resources/CookiesBackupResource.sys.mjs b/browser/components/backup/resources/CookiesBackupResource.sys.mjs
new file mode 100644
index 0000000000..8b988fd532
--- /dev/null
+++ b/browser/components/backup/resources/CookiesBackupResource.sys.mjs
@@ -0,0 +1,25 @@
+/* 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 { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
+
+/**
+ * Class representing Cookies database within a user profile.
+ */
+export class CookiesBackupResource extends BackupResource {
+ static get key() {
+ return "cookies";
+ }
+
+ static get requiresEncryption() {
+ return true;
+ }
+
+ async measure(profilePath = PathUtils.profileDir) {
+ let cookiesDBPath = PathUtils.join(profilePath, "cookies.sqlite");
+ let cookiesSize = await BackupResource.getFileSize(cookiesDBPath);
+
+ Glean.browserBackup.cookiesSize.set(cookiesSize);
+ }
+}
diff --git a/browser/components/backup/resources/CredentialsAndSecurityBackupResource.sys.mjs b/browser/components/backup/resources/CredentialsAndSecurityBackupResource.sys.mjs
new file mode 100644
index 0000000000..89069de826
--- /dev/null
+++ b/browser/components/backup/resources/CredentialsAndSecurityBackupResource.sys.mjs
@@ -0,0 +1,53 @@
+/* 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 { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
+
+/**
+ * Class representing files needed for logins, payment methods and form autofill within a user profile.
+ */
+export class CredentialsAndSecurityBackupResource extends BackupResource {
+ static get key() {
+ return "credentials_and_security";
+ }
+
+ static get requiresEncryption() {
+ return true;
+ }
+
+ async measure(profilePath = PathUtils.profileDir) {
+ const securityFiles = ["cert9.db", "pkcs11.txt"];
+ let securitySize = 0;
+
+ for (let filePath of securityFiles) {
+ let resourcePath = PathUtils.join(profilePath, filePath);
+ let resourceSize = await BackupResource.getFileSize(resourcePath);
+ if (Number.isInteger(resourceSize)) {
+ securitySize += resourceSize;
+ }
+ }
+
+ Glean.browserBackup.securityDataSize.set(securitySize);
+
+ const credentialsFiles = [
+ "key4.db",
+ "logins.json",
+ "logins-backup.json",
+ "autofill-profiles.json",
+ "credentialstate.sqlite",
+ "signedInUser.json",
+ ];
+ let credentialsSize = 0;
+
+ for (let filePath of credentialsFiles) {
+ let resourcePath = PathUtils.join(profilePath, filePath);
+ let resourceSize = await BackupResource.getFileSize(resourcePath);
+ if (Number.isInteger(resourceSize)) {
+ credentialsSize += resourceSize;
+ }
+ }
+
+ Glean.browserBackup.credentialsDataSize.set(credentialsSize);
+ }
+}
diff --git a/browser/components/backup/resources/FormHistoryBackupResource.sys.mjs b/browser/components/backup/resources/FormHistoryBackupResource.sys.mjs
new file mode 100644
index 0000000000..cb314eb34d
--- /dev/null
+++ b/browser/components/backup/resources/FormHistoryBackupResource.sys.mjs
@@ -0,0 +1,25 @@
+/* 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 { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
+
+/**
+ * Class representing Form history database within a user profile.
+ */
+export class FormHistoryBackupResource extends BackupResource {
+ static get key() {
+ return "formhistory";
+ }
+
+ static get requiresEncryption() {
+ return false;
+ }
+
+ async measure(profilePath = PathUtils.profileDir) {
+ let formHistoryDBPath = PathUtils.join(profilePath, "formhistory.sqlite");
+ let formHistorySize = await BackupResource.getFileSize(formHistoryDBPath);
+
+ Glean.browserBackup.formHistorySize.set(formHistorySize);
+ }
+}
diff --git a/browser/components/backup/resources/MiscDataBackupResource.sys.mjs b/browser/components/backup/resources/MiscDataBackupResource.sys.mjs
new file mode 100644
index 0000000000..97224f0e31
--- /dev/null
+++ b/browser/components/backup/resources/MiscDataBackupResource.sys.mjs
@@ -0,0 +1,101 @@
+/* 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 { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
+});
+
+/**
+ * Class representing miscellaneous files for telemetry, site storage,
+ * media device origin mapping, chrome privileged IndexedDB databases,
+ * and Mozilla Accounts within a user profile.
+ */
+export class MiscDataBackupResource extends BackupResource {
+ static get key() {
+ return "miscellaneous";
+ }
+
+ static get requiresEncryption() {
+ return false;
+ }
+
+ async backup(stagingPath, profilePath = PathUtils.profileDir) {
+ const files = [
+ "times.json",
+ "enumerate_devices.txt",
+ "SiteSecurityServiceState.bin",
+ ];
+
+ for (let fileName of files) {
+ let sourcePath = PathUtils.join(profilePath, fileName);
+ let destPath = PathUtils.join(stagingPath, fileName);
+ if (await IOUtils.exists(sourcePath)) {
+ await IOUtils.copy(sourcePath, destPath, { recursive: true });
+ }
+ }
+
+ const sqliteDatabases = ["protections.sqlite"];
+
+ for (let fileName of sqliteDatabases) {
+ let sourcePath = PathUtils.join(profilePath, fileName);
+ let destPath = PathUtils.join(stagingPath, fileName);
+ let connection;
+
+ try {
+ connection = await lazy.Sqlite.openConnection({
+ path: sourcePath,
+ readOnly: true,
+ });
+
+ await connection.backup(destPath);
+ } finally {
+ await connection.close();
+ }
+ }
+
+ // Bug 1890585 - we don't currently have the ability to copy the
+ // chrome-privileged IndexedDB databases under storage/permanent/chrome, so
+ // we'll just skip that for now.
+
+ return null;
+ }
+
+ async measure(profilePath = PathUtils.profileDir) {
+ const files = [
+ "times.json",
+ "enumerate_devices.txt",
+ "protections.sqlite",
+ "SiteSecurityServiceState.bin",
+ ];
+
+ let fullSize = 0;
+
+ for (let filePath of files) {
+ let resourcePath = PathUtils.join(profilePath, filePath);
+ let resourceSize = await BackupResource.getFileSize(resourcePath);
+ if (Number.isInteger(resourceSize)) {
+ fullSize += resourceSize;
+ }
+ }
+
+ let chromeIndexedDBDirPath = PathUtils.join(
+ profilePath,
+ "storage",
+ "permanent",
+ "chrome"
+ );
+ let chromeIndexedDBDirSize = await BackupResource.getDirectorySize(
+ chromeIndexedDBDirPath
+ );
+ if (Number.isInteger(chromeIndexedDBDirSize)) {
+ fullSize += chromeIndexedDBDirSize;
+ }
+
+ Glean.browserBackup.miscDataSize.set(fullSize);
+ }
+}
diff --git a/browser/components/backup/resources/PlacesBackupResource.sys.mjs b/browser/components/backup/resources/PlacesBackupResource.sys.mjs
new file mode 100644
index 0000000000..1955406f51
--- /dev/null
+++ b/browser/components/backup/resources/PlacesBackupResource.sys.mjs
@@ -0,0 +1,91 @@
+/* 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 { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.sys.mjs",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
+ Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
+});
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "isBrowsingHistoryEnabled",
+ "places.history.enabled",
+ true
+);
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "isSanitizeOnShutdownEnabled",
+ "privacy.sanitize.sanitizeOnShutdown",
+ false
+);
+
+/**
+ * Class representing Places database related files within a user profile.
+ */
+export class PlacesBackupResource extends BackupResource {
+ static get key() {
+ return "places";
+ }
+
+ static get requiresEncryption() {
+ return false;
+ }
+
+ async backup(stagingPath, profilePath = PathUtils.profileDir) {
+ const sqliteDatabases = ["places.sqlite", "favicons.sqlite"];
+ let canBackupHistory =
+ !lazy.PrivateBrowsingUtils.permanentPrivateBrowsing &&
+ !lazy.isSanitizeOnShutdownEnabled &&
+ lazy.isBrowsingHistoryEnabled;
+
+ /**
+ * Do not backup places.sqlite and favicons.sqlite if users have history disabled, want history cleared on shutdown or are using permanent private browsing mode.
+ * Instead, export all existing bookmarks to a compressed JSON file that we can read when restoring the backup.
+ */
+ if (!canBackupHistory) {
+ let bookmarksBackupFile = PathUtils.join(
+ stagingPath,
+ "bookmarks.jsonlz4"
+ );
+ await lazy.BookmarkJSONUtils.exportToFile(bookmarksBackupFile, {
+ compress: true,
+ });
+ return { bookmarksOnly: true };
+ }
+
+ for (let fileName of sqliteDatabases) {
+ let sourcePath = PathUtils.join(profilePath, fileName);
+ let destPath = PathUtils.join(stagingPath, fileName);
+ let connection;
+
+ try {
+ connection = await lazy.Sqlite.openConnection({
+ path: sourcePath,
+ readOnly: true,
+ });
+
+ await connection.backup(destPath);
+ } finally {
+ await connection.close();
+ }
+ }
+ return null;
+ }
+
+ async measure(profilePath = PathUtils.profileDir) {
+ let placesDBPath = PathUtils.join(profilePath, "places.sqlite");
+ let faviconsDBPath = PathUtils.join(profilePath, "favicons.sqlite");
+ let placesDBSize = await BackupResource.getFileSize(placesDBPath);
+ let faviconsDBSize = await BackupResource.getFileSize(faviconsDBPath);
+
+ Glean.browserBackup.placesSize.set(placesDBSize);
+ Glean.browserBackup.faviconsSize.set(faviconsDBSize);
+ }
+}
diff --git a/browser/components/backup/resources/PreferencesBackupResource.sys.mjs b/browser/components/backup/resources/PreferencesBackupResource.sys.mjs
new file mode 100644
index 0000000000..012c0bf91e
--- /dev/null
+++ b/browser/components/backup/resources/PreferencesBackupResource.sys.mjs
@@ -0,0 +1,98 @@
+/* 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 { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
+import { Sqlite } from "resource://gre/modules/Sqlite.sys.mjs";
+
+/**
+ * Class representing files that modify preferences and permissions within a user profile.
+ */
+export class PreferencesBackupResource extends BackupResource {
+ static get key() {
+ return "preferences";
+ }
+
+ static get requiresEncryption() {
+ return false;
+ }
+
+ async backup(stagingPath, profilePath = PathUtils.profileDir) {
+ // These are files that can be simply copied into the staging folder using
+ // IOUtils.copy.
+ const simpleCopyFiles = [
+ "xulstore.json",
+ "containers.json",
+ "handlers.json",
+ "search.json.mozlz4",
+ "user.js",
+ "chrome",
+ ];
+
+ for (let fileName of simpleCopyFiles) {
+ let sourcePath = PathUtils.join(profilePath, fileName);
+ let destPath = PathUtils.join(stagingPath, fileName);
+ if (await IOUtils.exists(sourcePath)) {
+ await IOUtils.copy(sourcePath, destPath, { recursive: true });
+ }
+ }
+
+ const sqliteDatabases = ["permissions.sqlite", "content-prefs.sqlite"];
+
+ for (let fileName of sqliteDatabases) {
+ let sourcePath = PathUtils.join(profilePath, fileName);
+ let destPath = PathUtils.join(stagingPath, fileName);
+ let connection;
+
+ try {
+ connection = await Sqlite.openConnection({
+ path: sourcePath,
+ });
+
+ await connection.backup(destPath);
+ } finally {
+ await connection.close();
+ }
+ }
+
+ // prefs.js is a special case - we have a helper function to flush the
+ // current prefs state to disk off of the main thread.
+ let prefsDestPath = PathUtils.join(stagingPath, "prefs.js");
+ let prefsDestFile = await IOUtils.getFile(prefsDestPath);
+ await Services.prefs.backupPrefFile(prefsDestFile);
+
+ return null;
+ }
+
+ async measure(profilePath = PathUtils.profileDir) {
+ const files = [
+ "prefs.js",
+ "xulstore.json",
+ "permissions.sqlite",
+ "content-prefs.sqlite",
+ "containers.json",
+ "handlers.json",
+ "search.json.mozlz4",
+ "user.js",
+ ];
+ let fullSize = 0;
+
+ for (let filePath of files) {
+ let resourcePath = PathUtils.join(profilePath, filePath);
+ let resourceSize = await BackupResource.getFileSize(resourcePath);
+ if (Number.isInteger(resourceSize)) {
+ fullSize += resourceSize;
+ }
+ }
+
+ const chromeDirectoryPath = PathUtils.join(profilePath, "chrome");
+ let chromeDirectorySize = await BackupResource.getDirectorySize(
+ chromeDirectoryPath
+ );
+ if (Number.isInteger(chromeDirectorySize)) {
+ fullSize += chromeDirectorySize;
+ }
+
+ Glean.browserBackup.preferencesSize.set(fullSize);
+ }
+}
diff --git a/browser/components/backup/resources/SessionStoreBackupResource.sys.mjs b/browser/components/backup/resources/SessionStoreBackupResource.sys.mjs
new file mode 100644
index 0000000000..fa5dcca848
--- /dev/null
+++ b/browser/components/backup/resources/SessionStoreBackupResource.sys.mjs
@@ -0,0 +1,53 @@
+/* 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 {
+ BackupResource,
+ bytesToFuzzyKilobytes,
+} from "resource:///modules/backup/BackupResource.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
+});
+
+/**
+ * Class representing Session store related files within a user profile.
+ */
+export class SessionStoreBackupResource extends BackupResource {
+ static get key() {
+ return "sessionstore";
+ }
+
+ static get requiresEncryption() {
+ // Session store data does not require encryption, but if encryption is
+ // disabled, then session cookies will be cleared from the backup before
+ // writing it to the disk.
+ return false;
+ }
+
+ async measure(profilePath = PathUtils.profileDir) {
+ // Get the current state of the session store JSON and
+ // measure it's uncompressed size.
+ let sessionStoreJson = lazy.SessionStore.getCurrentState(true);
+ let sessionStoreSize = new TextEncoder().encode(
+ JSON.stringify(sessionStoreJson)
+ ).byteLength;
+ let sessionStoreNearestTenthKb = bytesToFuzzyKilobytes(sessionStoreSize);
+
+ Glean.browserBackup.sessionStoreSize.set(sessionStoreNearestTenthKb);
+
+ let sessionStoreBackupsDirectoryPath = PathUtils.join(
+ profilePath,
+ "sessionstore-backups"
+ );
+ let sessionStoreBackupsDirectorySize =
+ await BackupResource.getDirectorySize(sessionStoreBackupsDirectoryPath);
+
+ Glean.browserBackup.sessionStoreBackupsDirectorySize.set(
+ sessionStoreBackupsDirectorySize
+ );
+ }
+}