summaryrefslogtreecommitdiffstats
path: root/browser/components/backup/resources/PlacesBackupResource.sys.mjs
blob: 3a9433e67cf74646fde6607e9a27e87e513b7ff4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* 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",
});

XPCOMUtils.defineLazyPreferenceGetter(
  lazy,
  "isBrowsingHistoryEnabled",
  "places.history.enabled",
  true
);
XPCOMUtils.defineLazyPreferenceGetter(
  lazy,
  "isSanitizeOnShutdownEnabled",
  "privacy.sanitize.sanitizeOnShutdown",
  false
);

const BOOKMARKS_BACKUP_FILENAME = "bookmarks.jsonlz4";

/**
 * 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;
  }

  static get priority() {
    return 1;
  }

  async backup(stagingPath, profilePath = PathUtils.profileDir) {
    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_BACKUP_FILENAME
      );
      await lazy.BookmarkJSONUtils.exportToFile(bookmarksBackupFile, {
        compress: true,
      });
      return { bookmarksOnly: true };
    }

    // These are copied in parallel because they're attached[1], and we don't
    // want them to get out of sync with one another.
    //
    // [1]: https://www.sqlite.org/lang_attach.html
    await Promise.all([
      BackupResource.copySqliteDatabases(profilePath, stagingPath, [
        "places.sqlite",
      ]),
      BackupResource.copySqliteDatabases(profilePath, stagingPath, [
        "favicons.sqlite",
      ]),
    ]);

    return null;
  }

  async recover(manifestEntry, recoveryPath, destProfilePath) {
    if (!manifestEntry) {
      const simpleCopyFiles = ["places.sqlite", "favicons.sqlite"];
      await BackupResource.copyFiles(
        recoveryPath,
        destProfilePath,
        simpleCopyFiles
      );
    } else {
      const { bookmarksOnly } = manifestEntry;

      /**
       * If the recovery file only has bookmarks backed up, pass the file path to postRecovery()
       * so that we can import all bookmarks into the new profile once it's been launched and restored.
       */
      if (bookmarksOnly) {
        let bookmarksBackupPath = PathUtils.join(
          recoveryPath,
          BOOKMARKS_BACKUP_FILENAME
        );
        return { bookmarksBackupPath };
      }
    }

    return null;
  }

  async postRecovery(postRecoveryEntry) {
    if (postRecoveryEntry?.bookmarksBackupPath) {
      await lazy.BookmarkJSONUtils.importFromFile(
        postRecoveryEntry.bookmarksBackupPath,
        {
          replace: true,
        }
      );
    }
  }

  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);
  }
}