tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PreferencesBackupResource.sys.mjs (7952B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
      6 import { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
      7 
      8 const lazy = {};
      9 
     10 ChromeUtils.defineESModuleGetters(lazy, {
     11  ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
     12  SearchUtils: "moz-src:///toolkit/components/search/SearchUtils.sys.mjs",
     13 });
     14 
     15 const PROFILE_RESTORATION_DATE_PREF = "browser.backup.profile-restoration-date";
     16 
     17 /**
     18 * Class representing files that modify preferences and permissions within a user profile.
     19 */
     20 export class PreferencesBackupResource extends BackupResource {
     21  static get key() {
     22    return "preferences";
     23  }
     24 
     25  static get requiresEncryption() {
     26    return false;
     27  }
     28 
     29  /**
     30   * Adds prefs to the override map that are currently set but should not be
     31   * included in the backup.  Override them with null values to prevent
     32   * serialization.
     33   *
     34   * @param {nsIPrefOverrideMap} prefsOverrideMap
     35   * @returns {nsIPrefOverrideMap} prefsOverrideMap with ignored prefs added
     36   */
     37  static addPrefsToIgnoreInBackup(prefsOverrideMap) {
     38    // List of prefs we never backup.
     39    let kIgnoredPrefs = [
     40      "app.normandy.user_id",
     41      "toolkit.telemetry.cachedClientID",
     42      "toolkit.telemetry.cachedProfileGroupID",
     43      PROFILE_RESTORATION_DATE_PREF,
     44    ];
     45 
     46    const backupPrefs = Services.prefs.getChildList("browser.backup.");
     47    kIgnoredPrefs = kIgnoredPrefs.concat(backupPrefs);
     48 
     49    // Prefs with this prefix are always overriden.
     50    const kNimbusMetadataPrefPrefix = "nimbus.";
     51 
     52    for (const pref of kIgnoredPrefs) {
     53      if (Services.prefs.getPrefType(pref) !== Services.prefs.PREF_INVALID) {
     54        prefsOverrideMap.addEntry(pref, null);
     55      }
     56    }
     57 
     58    const nimbusPrefs = Services.prefs.getChildList(kNimbusMetadataPrefPrefix);
     59    for (const pref of nimbusPrefs) {
     60      prefsOverrideMap.addEntry(pref, null);
     61    }
     62 
     63    return prefsOverrideMap;
     64  }
     65 
     66  async backup(
     67    stagingPath,
     68    profilePath = PathUtils.profileDir,
     69    _isEncrypting = false
     70  ) {
     71    // These are files that can be simply copied into the staging folder using
     72    // IOUtils.copy.
     73    const simpleCopyFiles = [
     74      "xulstore.json",
     75      "containers.json",
     76      "handlers.json",
     77      "search.json.mozlz4",
     78      "user.js",
     79      "chrome",
     80    ];
     81    await BackupResource.copyFiles(profilePath, stagingPath, simpleCopyFiles);
     82 
     83    // prefs.js is a special case - we have a helper function to flush the
     84    // current prefs state to disk off of the main thread.
     85    let prefsDestPath = PathUtils.join(stagingPath, "prefs.js");
     86    let prefsDestFile = await IOUtils.getFile(prefsDestPath);
     87    await lazy.ExperimentAPI._rsLoader.withUpdateLock(async () => {
     88      await Services.prefs.backupPrefFile(
     89        prefsDestFile,
     90        PreferencesBackupResource.addPrefsToIgnoreInBackup(
     91          lazy.ExperimentAPI.manager.store.getOriginalPrefValuesForAllActiveEnrollments()
     92        )
     93      );
     94    });
     95 
     96    // During recovery, we need to recompute verification hashes for any
     97    // custom engines, but only for engines that were originally passing
     98    // verification. We'll store the profile path at backup time in our
     99    // ManifestEntry so that we can do that verification check at recover-time.
    100    return { profilePath };
    101  }
    102 
    103  async recover(manifestEntry, recoveryPath, destProfilePath) {
    104    const SEARCH_PREF_FILENAME = "search.json.mozlz4";
    105    const RECOVERY_SEARCH_PREF_PATH = PathUtils.join(
    106      recoveryPath,
    107      SEARCH_PREF_FILENAME
    108    );
    109 
    110    if (await IOUtils.exists(RECOVERY_SEARCH_PREF_PATH)) {
    111      // search.json.mozlz4 may contain hash values that need to be recomputed
    112      // now that the profile directory has changed.
    113      let searchPrefs = await IOUtils.readJSON(RECOVERY_SEARCH_PREF_PATH, {
    114        decompress: true,
    115      });
    116 
    117      // ... but we only want to do this for engines that had valid verification
    118      // hashes for the original profile path.
    119      const ORIGINAL_PROFILE_PATH = manifestEntry.profilePath;
    120 
    121      if (ORIGINAL_PROFILE_PATH) {
    122        searchPrefs.engines = searchPrefs.engines.map(engine => {
    123          if (engine._metaData.loadPathHash) {
    124            let loadPath = engine._loadPath;
    125            if (
    126              engine._metaData.loadPathHash ==
    127              lazy.SearchUtils.getVerificationHash(
    128                loadPath,
    129                ORIGINAL_PROFILE_PATH
    130              )
    131            ) {
    132              engine._metaData.loadPathHash =
    133                lazy.SearchUtils.getVerificationHash(loadPath, destProfilePath);
    134            }
    135          }
    136          return engine;
    137        });
    138 
    139        if (
    140          searchPrefs.metaData.defaultEngineIdHash &&
    141          searchPrefs.metaData.defaultEngineIdHash ==
    142            lazy.SearchUtils.getVerificationHash(
    143              searchPrefs.metaData.defaultEngineId,
    144              ORIGINAL_PROFILE_PATH
    145            )
    146        ) {
    147          searchPrefs.metaData.defaultEngineIdHash =
    148            lazy.SearchUtils.getVerificationHash(
    149              searchPrefs.metaData.defaultEngineId,
    150              destProfilePath
    151            );
    152        }
    153 
    154        if (
    155          searchPrefs.metaData.privateDefaultEngineIdHash &&
    156          searchPrefs.metaData.privateDefaultEngineIdHash ==
    157            lazy.SearchUtils.getVerificationHash(
    158              searchPrefs.metaData.privateDefaultEngineId,
    159              ORIGINAL_PROFILE_PATH
    160            )
    161        ) {
    162          searchPrefs.metaData.privateDefaultEngineIdHash =
    163            lazy.SearchUtils.getVerificationHash(
    164              searchPrefs.metaData.privateDefaultEngineId,
    165              destProfilePath
    166            );
    167        }
    168      }
    169 
    170      await IOUtils.writeJSON(
    171        PathUtils.join(destProfilePath, SEARCH_PREF_FILENAME),
    172        searchPrefs,
    173        { compress: true }
    174      );
    175    }
    176 
    177    const simpleCopyFiles = [
    178      "prefs.js",
    179      "xulstore.json",
    180      "containers.json",
    181      "handlers.json",
    182      "user.js",
    183      "chrome",
    184    ];
    185    await BackupResource.copyFiles(
    186      recoveryPath,
    187      destProfilePath,
    188      simpleCopyFiles
    189    );
    190 
    191    // Append browser.backup.scheduled.last-backup-file to prefs.js with the
    192    // current timestamp.
    193    const LINEBREAK = AppConstants.platform === "win" ? "\r\n" : "\n";
    194    let prefsFile = await IOUtils.getFile(destProfilePath);
    195    prefsFile.append("prefs.js");
    196    // We should always have recovered a prefs.js but, if we didn't for any
    197    // reason, we can still write the timestamp.  Since we are creating the
    198    // prefs.js file, we need to add the preamble.
    199    const includePreamble = !(await IOUtils.exists(prefsFile.path));
    200    let addToPrefsJs = includePreamble ? Services.prefs.prefsJsPreamble : "";
    201    addToPrefsJs += `user_pref("${PROFILE_RESTORATION_DATE_PREF}", ${Math.round(Date.now() / 1000)});${LINEBREAK}`;
    202    await IOUtils.writeUTF8(prefsFile.path, addToPrefsJs, {
    203      mode: "appendOrCreate",
    204    });
    205    return null;
    206  }
    207 
    208  async measure(profilePath = PathUtils.profileDir) {
    209    const files = [
    210      "prefs.js",
    211      "xulstore.json",
    212      "containers.json",
    213      "handlers.json",
    214      "search.json.mozlz4",
    215      "user.js",
    216    ];
    217    let fullSize = 0;
    218 
    219    for (let filePath of files) {
    220      let resourcePath = PathUtils.join(profilePath, filePath);
    221      let resourceSize = await BackupResource.getFileSize(resourcePath);
    222      if (Number.isInteger(resourceSize)) {
    223        fullSize += resourceSize;
    224      }
    225    }
    226 
    227    const chromeDirectoryPath = PathUtils.join(profilePath, "chrome");
    228    let chromeDirectorySize =
    229      await BackupResource.getDirectorySize(chromeDirectoryPath);
    230    if (Number.isInteger(chromeDirectorySize)) {
    231      fullSize += chromeDirectorySize;
    232    }
    233 
    234    Glean.browserBackup.preferencesSize.set(fullSize);
    235  }
    236 }