tor-browser

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

FileMigrators.sys.mjs (10159B)


      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 http://mozilla.org/MPL/2.0/. */
      4 
      5 const lazy = {};
      6 
      7 ChromeUtils.defineESModuleGetters(lazy, {
      8  BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.sys.mjs",
      9  BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.sys.mjs",
     10  LoginCSVImport: "resource://gre/modules/LoginCSVImport.sys.mjs",
     11  MigrationWizardConstants:
     12    "chrome://browser/content/migration/migration-wizard-constants.mjs",
     13 });
     14 
     15 ChromeUtils.defineLazyGetter(lazy, "gFluentStrings", function () {
     16  return new Localization([
     17    "branding/brand.ftl",
     18    "browser/migrationWizard.ftl",
     19  ]);
     20 });
     21 
     22 /**
     23 * Base class for a migration that involves reading a single file off of
     24 * the disk that the user picks using a file picker. The file might be
     25 * generated by another browser or some other application.
     26 */
     27 export class FileMigratorBase {
     28  /**
     29   * This must be overridden to return a simple string identifier for the
     30   * migrator, for example "password-csv". This key is what
     31   * is used as an identifier when calling MigrationUtils.getFileMigrator.
     32   *
     33   * @type {string}
     34   */
     35  static get key() {
     36    throw new Error("FileMigrator.key must be overridden.");
     37  }
     38 
     39  /**
     40   * This must be overridden to return a Fluent string ID mapping to the display
     41   * name for this migrator. These strings should be defined in migrationWizard.ftl.
     42   *
     43   * @type {string}
     44   */
     45  static get displayNameL10nID() {
     46    throw new Error("FileMigrator.displayNameL10nID must be overridden.");
     47  }
     48 
     49  /**
     50   * This getter should get overridden to return an icon url to represent the
     51   * file to be imported from. By default, this will just use the default Favicon
     52   * image.
     53   *
     54   * @type {string}
     55   */
     56  static get brandImage() {
     57    return "chrome://global/skin/icons/defaultFavicon.svg";
     58  }
     59 
     60  /**
     61   * Returns true if the migrator is configured to be enabled.
     62   *
     63   * @type {boolean}
     64   *   true if the migrator should be shown in the migration wizard.
     65   */
     66  get enabled() {
     67    throw new Error("FileMigrator.enabled must be overridden.");
     68  }
     69 
     70  /**
     71   * This getter should be overridden to return a Fluent string ID for what
     72   * the migration wizard header should be while the file migration is
     73   * underway.
     74   *
     75   * @type {string}
     76   */
     77  get progressHeaderL10nID() {
     78    throw new Error("FileMigrator.progressHeaderL10nID must be overridden.");
     79  }
     80 
     81  /**
     82   * This getter should be overridden to return a Fluent string ID for what
     83   * the migration wizard header should be while the file migration is
     84   * done.
     85   *
     86   * @type {string}
     87   */
     88  get successHeaderL10nID() {
     89    throw new Error("FileMigrator.progressHeaderL10nID must be overridden.");
     90  }
     91 
     92  /**
     93   * @typedef {object} FilePickerConfiguration
     94   * @property {string} title
     95   *   The title that should be assigned to the native file picker window.
     96   * @property {FilePickerConfigurationFilter[]} filters
     97   *   One or more extension filters that should be applied to the native
     98   *   file picker window to make selection easier.
     99   */
    100 
    101  /**
    102   * @typedef {object} FilePickerConfigurationFilter
    103   * @property {string} title
    104   *   The title for the filter. Example: "CSV Files"
    105   * @property {string} extensionPattern
    106   *   A matching pattern for the filter. Example: "*.csv"
    107   */
    108 
    109  /**
    110   * A subclass of FileMigratorBase will eventually open a native file picker
    111   * for the user to select the file from their file system.
    112   *
    113   * Subclasses need to override this method in order to configure the
    114   * native file picker.
    115   *
    116   * @returns {Promise<FilePickerConfiguration>}
    117   */
    118  async getFilePickerConfig() {
    119    throw new Error("FileMigrator.getFilePickerConfig must be overridden.");
    120  }
    121 
    122  /**
    123   * Returns a list of one or more resource types that should appear to be
    124   * in progress of migrating while the file migration occurs. Notably,
    125   * this does not need to match the resource types that are returned by
    126   * `FileMigratorBase.migrate`.
    127   *
    128   * @type {string[]}
    129   *   An array of resource types from the
    130   *   MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES set.
    131   */
    132  get displayedResourceTypes() {
    133    throw new Error("FileMigrator.displayedResourceTypes must be overridden");
    134  }
    135 
    136  /**
    137   * Called to perform the file migration once the user makes a selection
    138   * from the native file picker. This will not be called if the user
    139   * chooses to cancel the native file picker.
    140   *
    141   * @param {string} _filePath
    142   *   The path that the user selected from the native file picker.
    143   */
    144  async migrate(_filePath) {
    145    throw new Error("FileMigrator.migrate must be overridden.");
    146  }
    147 }
    148 
    149 /**
    150 * A file migrator for importing passwords from CSV or TSV files. CSV
    151 * files are more common, so this is what we show as the file type for
    152 * the display name, but this FileMigrator accepts both.
    153 */
    154 export class PasswordFileMigrator extends FileMigratorBase {
    155  static get key() {
    156    return "file-password-csv";
    157  }
    158 
    159  static get displayNameL10nID() {
    160    return "migration-wizard-migrator-display-name-file-password-csv";
    161  }
    162 
    163  static get brandImage() {
    164    return "chrome://branding/content/document.ico";
    165  }
    166 
    167  get enabled() {
    168    return true;
    169  }
    170 
    171  get displayedResourceTypes() {
    172    return [
    173      lazy.MigrationWizardConstants.DISPLAYED_FILE_RESOURCE_TYPES
    174        .PASSWORDS_FROM_FILE,
    175    ];
    176  }
    177 
    178  get progressHeaderL10nID() {
    179    return "migration-passwords-from-file-progress-header";
    180  }
    181 
    182  get successHeaderL10nID() {
    183    return "migration-passwords-from-file-success-header";
    184  }
    185 
    186  async getFilePickerConfig() {
    187    let [title, csvFilterTitle, tsvFilterTitle] =
    188      await lazy.gFluentStrings.formatValues([
    189        { id: "migration-passwords-from-file-picker-title" },
    190        { id: "migration-passwords-from-file-csv-filter-title" },
    191        { id: "migration-passwords-from-file-tsv-filter-title" },
    192      ]);
    193 
    194    return {
    195      title,
    196      filters: [
    197        {
    198          title: csvFilterTitle,
    199          extensionPattern: "*.csv",
    200        },
    201        {
    202          title: tsvFilterTitle,
    203          extensionPattern: "*.tsv",
    204        },
    205      ],
    206    };
    207  }
    208 
    209  async migrate(filePath) {
    210    try {
    211      let summary = await lazy.LoginCSVImport.importFromCSV(filePath);
    212      let newEntries = 0;
    213      let updatedEntries = 0;
    214      for (let entry of summary) {
    215        if (entry.result == "added") {
    216          newEntries++;
    217        } else if (entry.result == "modified") {
    218          updatedEntries++;
    219        }
    220      }
    221      let [newMessage, updatedMessage] = await lazy.gFluentStrings.formatValues(
    222        [
    223          {
    224            id: "migration-wizard-progress-success-new-passwords",
    225            args: { newEntries },
    226          },
    227          {
    228            id: "migration-wizard-progress-success-updated-passwords",
    229            args: { updatedEntries },
    230          },
    231        ]
    232      );
    233 
    234      Services.prefs.setBoolPref(
    235        "browser.migrate.interactions.csvpasswords",
    236        true
    237      );
    238 
    239      return {
    240        [lazy.MigrationWizardConstants.DISPLAYED_FILE_RESOURCE_TYPES
    241          .PASSWORDS_NEW]: newMessage,
    242        [lazy.MigrationWizardConstants.DISPLAYED_FILE_RESOURCE_TYPES
    243          .PASSWORDS_UPDATED]: updatedMessage,
    244      };
    245    } catch (e) {
    246      console.error(e);
    247 
    248      let errorMessage = await lazy.gFluentStrings.formatValue(
    249        "migration-passwords-from-file-no-valid-data"
    250      );
    251      throw new Error(errorMessage);
    252    }
    253  }
    254 }
    255 
    256 /**
    257 * A file migrator for importing bookmarks from a HTML or JSON file.
    258 *
    259 * @class BookmarksFileMigrator
    260 * @augments {FileMigratorBase}
    261 */
    262 export class BookmarksFileMigrator extends FileMigratorBase {
    263  static get key() {
    264    return "file-bookmarks";
    265  }
    266 
    267  static get displayNameL10nID() {
    268    return "migration-wizard-migrator-display-name-file-bookmarks";
    269  }
    270 
    271  static get brandImage() {
    272    return "chrome://branding/content/document.ico";
    273  }
    274 
    275  get enabled() {
    276    return Services.prefs.getBoolPref(
    277      "browser.migrate.bookmarks-file.enabled",
    278      false
    279    );
    280  }
    281 
    282  get displayedResourceTypes() {
    283    return [
    284      lazy.MigrationWizardConstants.DISPLAYED_FILE_RESOURCE_TYPES
    285        .BOOKMARKS_FROM_FILE,
    286    ];
    287  }
    288 
    289  get progressHeaderL10nID() {
    290    return "migration-bookmarks-from-file-progress-header";
    291  }
    292 
    293  get successHeaderL10nID() {
    294    return "migration-bookmarks-from-file-success-header";
    295  }
    296 
    297  async getFilePickerConfig() {
    298    let [title, htmlFilterTitle, jsonFilterTitle] =
    299      await lazy.gFluentStrings.formatValues([
    300        { id: "migration-bookmarks-from-file-picker-title" },
    301        { id: "migration-bookmarks-from-file-html-filter-title" },
    302        { id: "migration-bookmarks-from-file-json-filter-title" },
    303      ]);
    304 
    305    return {
    306      title,
    307      filters: [
    308        {
    309          title: htmlFilterTitle,
    310          extensionPattern: "*.html",
    311        },
    312        {
    313          title: jsonFilterTitle,
    314          extensionPattern: "*.json",
    315        },
    316      ],
    317    };
    318  }
    319 
    320  async migrate(filePath) {
    321    try {
    322      let pathCheck = filePath.toLowerCase();
    323      let importedCount;
    324 
    325      if (pathCheck.endsWith("html")) {
    326        importedCount = await lazy.BookmarkHTMLUtils.importFromFile(filePath);
    327      } else if (pathCheck.endsWith("json") || pathCheck.endsWith("jsonlz4")) {
    328        importedCount = await lazy.BookmarkJSONUtils.importFromFile(filePath);
    329      }
    330 
    331      if (!importedCount) {
    332        // The catch will cause us to show a default error message.
    333        throw new Error();
    334      }
    335 
    336      let importedMessage = await lazy.gFluentStrings.formatValue(
    337        "migration-wizard-progress-success-new-bookmarks",
    338        {
    339          newEntries: importedCount,
    340        }
    341      );
    342      return {
    343        [lazy.MigrationWizardConstants.DISPLAYED_FILE_RESOURCE_TYPES
    344          .BOOKMARKS_FROM_FILE]: importedMessage,
    345      };
    346    } catch (e) {
    347      console.error(e);
    348 
    349      let errorMessage = await lazy.gFluentStrings.formatValue(
    350        "migration-bookmarks-from-file-no-valid-data"
    351      );
    352      throw new Error(errorMessage);
    353    }
    354  }
    355 }