From a90a5cba08fdf6c0ceb95101c275108a152a3aed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:35:37 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- .../resources/MiscDataBackupResource.sys.mjs | 135 ++++++++++++++------- 1 file changed, 94 insertions(+), 41 deletions(-) (limited to 'browser/components/backup/resources/MiscDataBackupResource.sys.mjs') diff --git a/browser/components/backup/resources/MiscDataBackupResource.sys.mjs b/browser/components/backup/resources/MiscDataBackupResource.sys.mjs index 97224f0e31..3d66114599 100644 --- a/browser/components/backup/resources/MiscDataBackupResource.sys.mjs +++ b/browser/components/backup/resources/MiscDataBackupResource.sys.mjs @@ -5,11 +5,19 @@ import { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs"; const lazy = {}; - ChromeUtils.defineESModuleGetters(lazy, { - Sqlite: "resource://gre/modules/Sqlite.sys.mjs", + ActivityStreamStorage: + "resource://activity-stream/lib/ActivityStreamStorage.sys.mjs", + ProfileAge: "resource://gre/modules/ProfileAge.sys.mjs", }); +const SNIPPETS_TABLE_NAME = "snippets"; +const FILES_FOR_BACKUP = [ + "enumerate_devices.txt", + "protections.sqlite", + "SiteSecurityServiceState.bin", +]; + /** * Class representing miscellaneous files for telemetry, site storage, * media device origin mapping, chrome privileged IndexedDB databases, @@ -25,57 +33,102 @@ export class MiscDataBackupResource extends BackupResource { } 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 files = ["enumerate_devices.txt", "SiteSecurityServiceState.bin"]; + await BackupResource.copyFiles(profilePath, stagingPath, files); 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(); - } - } + await BackupResource.copySqliteDatabases( + profilePath, + stagingPath, + sqliteDatabases + ); // 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. + // chrome-privileged IndexedDB databases under storage/permanent/chrome. + // Instead, we'll manually export any IndexedDB data we need to backup + // to a separate JSON file. + + // The first IndexedDB database we want to back up is the ActivityStream + // one - specifically, the "snippets" table, as this contains information + // on ASRouter impressions, blocked messages, message group impressions, + // etc. + let storage = new lazy.ActivityStreamStorage({ + storeNames: [SNIPPETS_TABLE_NAME], + }); + let snippetsTable = await storage.getDbTable(SNIPPETS_TABLE_NAME); + let snippetsObj = {}; + for (let key of await snippetsTable.getAllKeys()) { + snippetsObj[key] = await snippetsTable.get(key); + } + let snippetsBackupFile = PathUtils.join( + stagingPath, + "activity-stream-snippets.json" + ); + await IOUtils.writeJSON(snippetsBackupFile, snippetsObj); return null; } - async measure(profilePath = PathUtils.profileDir) { - const files = [ + async recover(_manifestEntry, recoveryPath, destProfilePath) { + await BackupResource.copyFiles( + recoveryPath, + destProfilePath, + FILES_FOR_BACKUP + ); + + // The times.json file, the one that powers ProfileAge, works hand in hand + // with the Telemetry client ID. We don't want to accidentally _overwrite_ + // a pre-existing times.json with data from a different profile, because + // then the client ID wouldn't match the times.json data anymore. + // + // The rule that we're following for backups and recoveries is that the + // recovered profile always inherits the client ID (and therefore the + // times.json) from the profile that _initiated recovery_. + // + // This means we want to copy the times.json file from the profile that's + // currently in use to the destProfilePath. + await BackupResource.copyFiles(PathUtils.profileDir, destProfilePath, [ "times.json", - "enumerate_devices.txt", - "protections.sqlite", - "SiteSecurityServiceState.bin", - ]; + ]); + + // We also want to write the recoveredFromBackup timestamp now. + let profileAge = await lazy.ProfileAge(destProfilePath); + await profileAge.recordRecoveredFromBackup(); + + // The activity-stream-snippets data will need to be written during the + // postRecovery phase, so we'll stash the path to the JSON file in the + // post recovery entry. + let snippetsBackupFile = PathUtils.join( + recoveryPath, + "activity-stream-snippets.json" + ); + return { snippetsBackupFile }; + } + + async postRecovery(postRecoveryEntry) { + let { snippetsBackupFile } = postRecoveryEntry; + // If for some reason, the activity-stream-snippets data file has been + // removed already, there's nothing to do. + if (!IOUtils.exists(snippetsBackupFile)) { + return; + } + + let snippetsData = await IOUtils.readJSON(snippetsBackupFile); + let storage = new lazy.ActivityStreamStorage({ + storeNames: [SNIPPETS_TABLE_NAME], + }); + let snippetsTable = await storage.getDbTable(SNIPPETS_TABLE_NAME); + for (let key in snippetsData) { + let value = snippetsData[key]; + await snippetsTable.set(key, value); + } + } + + async measure(profilePath = PathUtils.profileDir) { let fullSize = 0; - for (let filePath of files) { + for (let filePath of FILES_FOR_BACKUP) { let resourcePath = PathUtils.join(profilePath, filePath); let resourceSize = await BackupResource.getFileSize(resourcePath); if (Number.isInteger(resourceSize)) { -- cgit v1.2.3