diff options
Diffstat (limited to 'browser/components/migration/ChromeProfileMigrator.sys.mjs')
-rw-r--r-- | browser/components/migration/ChromeProfileMigrator.sys.mjs | 1018 |
1 files changed, 1018 insertions, 0 deletions
diff --git a/browser/components/migration/ChromeProfileMigrator.sys.mjs b/browser/components/migration/ChromeProfileMigrator.sys.mjs new file mode 100644 index 0000000000..fc20dc6e94 --- /dev/null +++ b/browser/components/migration/ChromeProfileMigrator.sys.mjs @@ -0,0 +1,1018 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 sts=2 et */ +/* 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 http://mozilla.org/MPL/2.0/. */ + +const AUTH_TYPE = { + SCHEME_HTML: 0, + SCHEME_BASIC: 1, + SCHEME_DIGEST: 2, +}; + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; +import { MigrationUtils } from "resource:///modules/MigrationUtils.sys.mjs"; +import { MigratorBase } from "resource:///modules/MigratorBase.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + ChromeMigrationUtils: "resource:///modules/ChromeMigrationUtils.sys.mjs", + FormHistory: "resource://gre/modules/FormHistory.sys.mjs", + PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", + Qihoo360seMigrationUtils: "resource:///modules/360seMigrationUtils.sys.mjs", +}); + +XPCOMUtils.defineLazyModuleGetters(lazy, { + NetUtil: "resource://gre/modules/NetUtil.jsm", +}); + +/** + * Converts an array of chrome bookmark objects into one our own places code + * understands. + * + * @param {object[]} items Chrome Bookmark items to be inserted on this parent + * @param {set} bookmarkURLAccumulator Accumulate all imported bookmark urls to be used for importing favicons + * @param {Function} errorAccumulator function that gets called with any errors + * thrown so we don't drop them on the floor. + * @returns {object[]} + */ +function convertBookmarks(items, bookmarkURLAccumulator, errorAccumulator) { + let itemsToInsert = []; + for (let item of items) { + try { + if (item.type == "url") { + if (item.url.trim().startsWith("chrome:")) { + // Skip invalid internal URIs. Creating an actual URI always reports + // messages to the console because Gecko has its own concept of how + // chrome:// URIs should be formed, so we avoid doing that. + continue; + } + if (item.url.trim().startsWith("edge:")) { + // Don't import internal Microsoft Edge URIs as they won't resolve within Firefox. + continue; + } + itemsToInsert.push({ url: item.url, title: item.name }); + bookmarkURLAccumulator.add({ url: item.url }); + } else if (item.type == "folder") { + let folderItem = { + type: lazy.PlacesUtils.bookmarks.TYPE_FOLDER, + title: item.name, + }; + folderItem.children = convertBookmarks( + item.children, + bookmarkURLAccumulator, + errorAccumulator + ); + itemsToInsert.push(folderItem); + } + } catch (ex) { + console.error(ex); + errorAccumulator(ex); + } + } + return itemsToInsert; +} + +/** + * Chrome profile migrator. This can also be used as a parent class for + * migrators for browsers that are variants of Chrome. + */ +export class ChromeProfileMigrator extends MigratorBase { + static get key() { + return "chrome"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-chrome"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/chrome.png"; + } + + get _chromeUserDataPathSuffix() { + return "Chrome"; + } + + _keychainServiceName = "Chrome Safe Storage"; + + _keychainAccountName = "Chrome"; + + async _getChromeUserDataPathIfExists() { + if (this._chromeUserDataPath) { + return this._chromeUserDataPath; + } + let path = await lazy.ChromeMigrationUtils.getDataPath( + this._chromeUserDataPathSuffix + ); + let exists = path && (await IOUtils.exists(path)); + if (exists) { + this._chromeUserDataPath = path; + } else { + this._chromeUserDataPath = null; + } + return this._chromeUserDataPath; + } + + async getResources(aProfile) { + let chromeUserDataPath = await this._getChromeUserDataPathIfExists(); + if (chromeUserDataPath) { + let profileFolder = chromeUserDataPath; + if (aProfile) { + profileFolder = PathUtils.join(chromeUserDataPath, aProfile.id); + } + if (await IOUtils.exists(profileFolder)) { + let possibleResourcePromises = [ + GetBookmarksResource(profileFolder, this.constructor.key), + GetHistoryResource(profileFolder), + GetFormdataResource(profileFolder), + ]; + if (lazy.ChromeMigrationUtils.supportsLoginsForPlatform) { + possibleResourcePromises.push( + this._GetPasswordsResource(profileFolder), + this._GetPaymentMethodsResource(profileFolder) + ); + } + + // Some of these Promises might reject due to things like database + // corruptions. We absorb those rejections here and filter them + // out so that we only try to import the resources that don't appear + // corrupted. + let possibleResources = await Promise.allSettled( + possibleResourcePromises + ); + return possibleResources + .filter(promise => { + return promise.status == "fulfilled" && promise.value !== null; + }) + .map(promise => promise.value); + } + } + return []; + } + + async getLastUsedDate() { + let sourceProfiles = await this.getSourceProfiles(); + if (!sourceProfiles) { + return new Date(0); + } + let chromeUserDataPath = await this._getChromeUserDataPathIfExists(); + if (!chromeUserDataPath) { + return new Date(0); + } + let datePromises = sourceProfiles.map(async profile => { + let basePath = PathUtils.join(chromeUserDataPath, profile.id); + let fileDatePromises = ["Bookmarks", "History", "Cookies"].map( + async leafName => { + let path = PathUtils.join(basePath, leafName); + let info = await IOUtils.stat(path).catch(() => null); + return info ? info.lastModified : 0; + } + ); + let dates = await Promise.all(fileDatePromises); + return Math.max(...dates); + }); + let datesOuter = await Promise.all(datePromises); + datesOuter.push(0); + return new Date(Math.max(...datesOuter)); + } + + async getSourceProfiles() { + if ("__sourceProfiles" in this) { + return this.__sourceProfiles; + } + + let chromeUserDataPath = await this._getChromeUserDataPathIfExists(); + if (!chromeUserDataPath) { + return []; + } + + let localState; + let profiles = []; + try { + localState = await lazy.ChromeMigrationUtils.getLocalState( + this._chromeUserDataPathSuffix + ); + let info_cache = localState.profile.info_cache; + for (let profileFolderName in info_cache) { + profiles.push({ + id: profileFolderName, + name: info_cache[profileFolderName].name || profileFolderName, + }); + } + } catch (e) { + // Avoid reporting NotFoundErrors from trying to get local state. + if (localState || e.name != "NotFoundError") { + console.error("Error detecting Chrome profiles: ", e); + } + // If we weren't able to detect any profiles above, fallback to the Default profile. + let defaultProfilePath = PathUtils.join(chromeUserDataPath, "Default"); + if (await IOUtils.exists(defaultProfilePath)) { + profiles = [ + { + id: "Default", + name: "Default", + }, + ]; + } + } + + let profileResources = await Promise.all( + profiles.map(async profile => ({ + profile, + resources: await this.getResources(profile), + })) + ); + + // Only list profiles from which any data can be imported + this.__sourceProfiles = profileResources + .filter(({ resources }) => { + return resources && !!resources.length; + }, this) + .map(({ profile }) => profile); + return this.__sourceProfiles; + } + + async _GetPasswordsResource(aProfileFolder) { + let loginPath = PathUtils.join(aProfileFolder, "Login Data"); + if (!(await IOUtils.exists(loginPath))) { + return null; + } + + let { + _chromeUserDataPathSuffix, + _keychainServiceName, + _keychainAccountName, + _keychainMockPassphrase = null, + } = this; + + let countQuery = `SELECT COUNT(*) FROM logins WHERE blacklisted_by_user = 0`; + + let countRows = await MigrationUtils.getRowsFromDBWithoutLocks( + loginPath, + "Chrome passwords", + countQuery + ); + + if (!countRows[0].getResultByName("COUNT(*)")) { + return null; + } + + return { + type: MigrationUtils.resourceTypes.PASSWORDS, + + async migrate(aCallback) { + let rows = await MigrationUtils.getRowsFromDBWithoutLocks( + loginPath, + "Chrome passwords", + `SELECT origin_url, action_url, username_element, username_value, + password_element, password_value, signon_realm, scheme, date_created, + times_used FROM logins WHERE blacklisted_by_user = 0` + ).catch(ex => { + console.error(ex); + aCallback(false); + }); + // If the promise was rejected we will have already called aCallback, + // so we can just return here. + if (!rows) { + return; + } + + // If there are no relevant rows, return before initializing crypto and + // thus prompting for Keychain access on macOS. + if (!rows.length) { + aCallback(true); + return; + } + + let crypto; + try { + if (AppConstants.platform == "win") { + let { ChromeWindowsLoginCrypto } = ChromeUtils.importESModule( + "resource:///modules/ChromeWindowsLoginCrypto.sys.mjs" + ); + crypto = new ChromeWindowsLoginCrypto(_chromeUserDataPathSuffix); + } else if (AppConstants.platform == "macosx") { + let { ChromeMacOSLoginCrypto } = ChromeUtils.importESModule( + "resource:///modules/ChromeMacOSLoginCrypto.sys.mjs" + ); + crypto = new ChromeMacOSLoginCrypto( + _keychainServiceName, + _keychainAccountName, + _keychainMockPassphrase + ); + } else { + aCallback(false); + return; + } + } catch (ex) { + // Handle the user canceling Keychain access or other OSCrypto errors. + console.error(ex); + aCallback(false); + return; + } + + let logins = []; + let fallbackCreationDate = new Date(); + for (let row of rows) { + try { + let origin_url = lazy.NetUtil.newURI( + row.getResultByName("origin_url") + ); + // Ignore entries for non-http(s)/ftp URLs because we likely can't + // use them anyway. + const kValidSchemes = new Set(["https", "http", "ftp"]); + if (!kValidSchemes.has(origin_url.scheme)) { + continue; + } + let loginInfo = { + username: row.getResultByName("username_value"), + password: await crypto.decryptData( + row.getResultByName("password_value"), + null + ), + origin: origin_url.prePath, + formActionOrigin: null, + httpRealm: null, + usernameElement: row.getResultByName("username_element"), + passwordElement: row.getResultByName("password_element"), + timeCreated: lazy.ChromeMigrationUtils.chromeTimeToDate( + row.getResultByName("date_created") + 0, + fallbackCreationDate + ).getTime(), + timesUsed: row.getResultByName("times_used") + 0, + }; + + switch (row.getResultByName("scheme")) { + case AUTH_TYPE.SCHEME_HTML: + let action_url = row.getResultByName("action_url"); + if (!action_url) { + // If there is no action_url, store the wildcard "" value. + // See the `formActionOrigin` IDL comments. + loginInfo.formActionOrigin = ""; + break; + } + let action_uri = lazy.NetUtil.newURI(action_url); + if (!kValidSchemes.has(action_uri.scheme)) { + continue; // This continues the outer for loop. + } + loginInfo.formActionOrigin = action_uri.prePath; + break; + case AUTH_TYPE.SCHEME_BASIC: + case AUTH_TYPE.SCHEME_DIGEST: + // signon_realm format is URIrealm, so we need remove URI + loginInfo.httpRealm = row + .getResultByName("signon_realm") + .substring(loginInfo.origin.length + 1); + break; + default: + throw new Error( + "Login data scheme type not supported: " + + row.getResultByName("scheme") + ); + } + logins.push(loginInfo); + } catch (e) { + console.error(e); + } + } + try { + if (logins.length) { + await MigrationUtils.insertLoginsWrapper(logins); + } + } catch (e) { + console.error(e); + } + if (crypto.finalize) { + crypto.finalize(); + } + aCallback(true); + }, + }; + } + async _GetPaymentMethodsResource(aProfileFolder) { + if ( + !Services.prefs.getBoolPref( + "browser.migrate.chrome.payment_methods.enabled", + false + ) + ) { + return null; + } + + let paymentMethodsPath = PathUtils.join(aProfileFolder, "Web Data"); + + if (!(await IOUtils.exists(paymentMethodsPath))) { + return null; + } + + let rows = await MigrationUtils.getRowsFromDBWithoutLocks( + paymentMethodsPath, + "Chrome Credit Cards", + "SELECT name_on_card, card_number_encrypted, expiration_month, expiration_year FROM credit_cards" + ).catch(ex => { + console.error(ex); + }); + + if (!rows?.length) { + return null; + } + + let { + _chromeUserDataPathSuffix, + _keychainServiceName, + _keychainAccountName, + _keychainMockPassphrase = null, + } = this; + + return { + type: MigrationUtils.resourceTypes.PAYMENT_METHODS, + + async migrate(aCallback) { + let crypto; + try { + if (AppConstants.platform == "win") { + let { ChromeWindowsLoginCrypto } = ChromeUtils.importESModule( + "resource:///modules/ChromeWindowsLoginCrypto.sys.mjs" + ); + crypto = new ChromeWindowsLoginCrypto(_chromeUserDataPathSuffix); + } else if (AppConstants.platform == "macosx") { + let { ChromeMacOSLoginCrypto } = ChromeUtils.importESModule( + "resource:///modules/ChromeMacOSLoginCrypto.sys.mjs" + ); + crypto = new ChromeMacOSLoginCrypto( + _keychainServiceName, + _keychainAccountName, + _keychainMockPassphrase + ); + } else { + aCallback(false); + return; + } + } catch (ex) { + // Handle the user canceling Keychain access or other OSCrypto errors. + console.error(ex); + aCallback(false); + return; + } + + let cards = []; + for (let row of rows) { + cards.push({ + "cc-name": row.getResultByName("name_on_card"), + "cc-number": await crypto.decryptData( + row.getResultByName("card_number_encrypted"), + null + ), + "cc-exp-month": parseInt( + row.getResultByName("expiration_month"), + 10 + ), + "cc-exp-year": parseInt(row.getResultByName("expiration_year"), 10), + }); + } + + await MigrationUtils.insertCreditCardsWrapper(cards); + aCallback(true); + }, + }; + } +} + +async function GetBookmarksResource(aProfileFolder, aBrowserKey) { + let bookmarksPath = PathUtils.join(aProfileFolder, "Bookmarks"); + let faviconsPath = PathUtils.join(aProfileFolder, "Favicons"); + + if (aBrowserKey === "chromium-360se") { + let localState = {}; + try { + localState = await lazy.ChromeMigrationUtils.getLocalState("360 SE"); + } catch (ex) { + console.error(ex); + } + + let alternativeBookmarks = + await lazy.Qihoo360seMigrationUtils.getAlternativeBookmarks({ + bookmarksPath, + localState, + }); + if (alternativeBookmarks.resource) { + return alternativeBookmarks.resource; + } + + bookmarksPath = alternativeBookmarks.path; + } + + if (!(await IOUtils.exists(bookmarksPath))) { + return null; + } + // check to read JSON bookmarks structure and see if any bookmarks exist else return null + // Parse Chrome bookmark file that is JSON format + let bookmarkJSON = await IOUtils.readJSON(bookmarksPath); + let other = bookmarkJSON.roots.other.children.length; + let bookmarkBar = bookmarkJSON.roots.bookmark_bar.children.length; + let synced = bookmarkJSON.roots.synced.children.length; + + if (!other && !bookmarkBar && !synced) { + return null; + } + return { + type: MigrationUtils.resourceTypes.BOOKMARKS, + + migrate(aCallback) { + return (async function () { + let gotErrors = false; + let errorGatherer = function () { + gotErrors = true; + }; + + let faviconRows = []; + try { + faviconRows = await MigrationUtils.getRowsFromDBWithoutLocks( + faviconsPath, + "Chrome Bookmark Favicons", + `select fav.id, fav.url, map.page_url, bit.image_data FROM favicons as fav + INNER JOIN favicon_bitmaps bit ON (fav.id = bit.icon_id) + INNER JOIN icon_mapping map ON (map.icon_id = bit.icon_id)` + ); + } catch (ex) { + console.error(ex); + } + // Create Hashmap for favicons + let faviconMap = new Map(); + for (let faviconRow of faviconRows) { + // First, try to normalize the URI: + try { + let uri = lazy.NetUtil.newURI( + faviconRow.getResultByName("page_url") + ); + faviconMap.set(uri.spec, { + faviconData: faviconRow.getResultByName("image_data"), + uri, + }); + } catch (e) { + // Couldn't parse the URI, so just skip it. + continue; + } + } + + let roots = bookmarkJSON.roots; + let bookmarkURLAccumulator = new Set(); + + // Importing bookmark bar items + if (roots.bookmark_bar.children && roots.bookmark_bar.children.length) { + // Toolbar + let parentGuid = lazy.PlacesUtils.bookmarks.toolbarGuid; + let bookmarks = convertBookmarks( + roots.bookmark_bar.children, + bookmarkURLAccumulator, + errorGatherer + ); + await MigrationUtils.insertManyBookmarksWrapper( + bookmarks, + parentGuid + ); + } + + // Importing Other Bookmarks items + if (roots.other.children && roots.other.children.length) { + // Other Bookmarks + let parentGuid = lazy.PlacesUtils.bookmarks.unfiledGuid; + let bookmarks = convertBookmarks( + roots.other.children, + bookmarkURLAccumulator, + errorGatherer + ); + await MigrationUtils.insertManyBookmarksWrapper( + bookmarks, + parentGuid + ); + } + + // Importing synced Bookmarks items + if (roots.synced.children && roots.synced.children.length) { + // Synced Bookmarks + let parentGuid = lazy.PlacesUtils.bookmarks.unfiledGuid; + let bookmarks = convertBookmarks( + roots.synced.children, + bookmarkURLAccumulator, + errorGatherer + ); + await MigrationUtils.insertManyBookmarksWrapper( + bookmarks, + parentGuid + ); + } + + // Find all favicons with associated bookmarks + let favicons = []; + for (let bookmark of bookmarkURLAccumulator) { + try { + let uri = lazy.NetUtil.newURI(bookmark.url); + let favicon = faviconMap.get(uri.spec); + if (favicon) { + favicons.push(favicon); + } + } catch (e) { + // Couldn't parse the bookmark URI, so just skip + continue; + } + } + + // Import Bookmark Favicons + MigrationUtils.insertManyFavicons(favicons); + if (gotErrors) { + throw new Error("The migration included errors."); + } + })().then( + () => aCallback(true), + () => aCallback(false) + ); + }, + }; +} + +async function GetHistoryResource(aProfileFolder) { + let historyPath = PathUtils.join(aProfileFolder, "History"); + if (!(await IOUtils.exists(historyPath))) { + return null; + } + let countQuery = "SELECT COUNT(*) FROM urls WHERE hidden = 0"; + + let countRows = await MigrationUtils.getRowsFromDBWithoutLocks( + historyPath, + "Chrome history", + countQuery + ); + if (!countRows[0].getResultByName("COUNT(*)")) { + return null; + } + return { + type: MigrationUtils.resourceTypes.HISTORY, + + migrate(aCallback) { + (async function () { + const LIMIT = Services.prefs.getIntPref( + "browser.migrate.chrome.history.limit" + ); + + let query = + "SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0"; + let maxAge = lazy.ChromeMigrationUtils.dateToChromeTime( + Date.now() - MigrationUtils.HISTORY_MAX_AGE_IN_MILLISECONDS + ); + query += " AND last_visit_time > " + maxAge; + + if (LIMIT) { + query += " ORDER BY last_visit_time DESC LIMIT " + LIMIT; + } + + let rows = await MigrationUtils.getRowsFromDBWithoutLocks( + historyPath, + "Chrome history", + query + ); + let pageInfos = []; + let fallbackVisitDate = new Date(); + for (let row of rows) { + try { + // if having typed_count, we changes transition type to typed. + let transition = lazy.PlacesUtils.history.TRANSITIONS.LINK; + if (row.getResultByName("typed_count") > 0) { + transition = lazy.PlacesUtils.history.TRANSITIONS.TYPED; + } + + pageInfos.push({ + title: row.getResultByName("title"), + url: new URL(row.getResultByName("url")), + visits: [ + { + transition, + date: lazy.ChromeMigrationUtils.chromeTimeToDate( + row.getResultByName("last_visit_time"), + fallbackVisitDate + ), + }, + ], + }); + } catch (e) { + console.error(e); + } + } + + if (pageInfos.length) { + await MigrationUtils.insertVisitsWrapper(pageInfos); + } + })().then( + () => { + aCallback(true); + }, + ex => { + console.error(ex); + aCallback(false); + } + ); + }, + }; +} + +async function GetFormdataResource(aProfileFolder) { + let formdataPath = PathUtils.join(aProfileFolder, "Web Data"); + if (!(await IOUtils.exists(formdataPath))) { + return null; + } + let countQuery = "SELECT COUNT(*) FROM autofill"; + + let countRows = await MigrationUtils.getRowsFromDBWithoutLocks( + formdataPath, + "Chrome formdata", + countQuery + ); + if (!countRows[0].getResultByName("COUNT(*)")) { + return null; + } + return { + type: MigrationUtils.resourceTypes.FORMDATA, + + async migrate(aCallback) { + let query = + "SELECT name, value, count, date_created, date_last_used FROM autofill"; + let rows = await MigrationUtils.getRowsFromDBWithoutLocks( + formdataPath, + "Chrome formdata", + query + ); + let addOps = []; + for (let row of rows) { + try { + let fieldname = row.getResultByName("name"); + let value = row.getResultByName("value"); + if (fieldname && value) { + addOps.push({ + op: "add", + fieldname, + value, + timesUsed: row.getResultByName("count"), + firstUsed: row.getResultByName("date_created") * 1000, + lastUsed: row.getResultByName("date_last_used") * 1000, + }); + } + } catch (e) { + console.error(e); + } + } + + try { + await lazy.FormHistory.update(addOps); + } catch (e) { + console.error(e); + aCallback(false); + return; + } + + aCallback(true); + }, + }; +} + +/** + * Chromium migrator + */ +export class ChromiumProfileMigrator extends ChromeProfileMigrator { + static get key() { + return "chromium"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-chromium"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/chromium.png"; + } + + _chromeUserDataPathSuffix = "Chromium"; + _keychainServiceName = "Chromium Safe Storage"; + _keychainAccountName = "Chromium"; +} + +/** + * Chrome Canary + * Not available on Linux + */ +export class CanaryProfileMigrator extends ChromeProfileMigrator { + static get key() { + return "canary"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-canary"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/canary.png"; + } + + get _chromeUserDataPathSuffix() { + return "Canary"; + } + + get _keychainServiceName() { + return "Chromium Safe Storage"; + } + + get _keychainAccountName() { + return "Chromium"; + } +} + +/** + * Chrome Dev - Linux only (not available in Mac and Windows) + */ +export class ChromeDevMigrator extends ChromeProfileMigrator { + static get key() { + return "chrome-dev"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-chrome-dev"; + } + + _chromeUserDataPathSuffix = "Chrome Dev"; + _keychainServiceName = "Chromium Safe Storage"; + _keychainAccountName = "Chromium"; +} + +/** + * Chrome Beta migrator + */ +export class ChromeBetaMigrator extends ChromeProfileMigrator { + static get key() { + return "chrome-beta"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-chrome-beta"; + } + + _chromeUserDataPathSuffix = "Chrome Beta"; + _keychainServiceName = "Chromium Safe Storage"; + _keychainAccountName = "Chromium"; +} + +/** + * Brave migrator + */ +export class BraveProfileMigrator extends ChromeProfileMigrator { + static get key() { + return "brave"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-brave"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/brave.png"; + } + + _chromeUserDataPathSuffix = "Brave"; + _keychainServiceName = "Brave Browser Safe Storage"; + _keychainAccountName = "Brave Browser"; +} + +/** + * Edge (Chromium-based) migrator + */ +export class ChromiumEdgeMigrator extends ChromeProfileMigrator { + static get key() { + return "chromium-edge"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-chromium-edge"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/edge.png"; + } + + _chromeUserDataPathSuffix = "Edge"; + _keychainServiceName = "Microsoft Edge Safe Storage"; + _keychainAccountName = "Microsoft Edge"; +} + +/** + * Edge Beta (Chromium-based) migrator + */ +export class ChromiumEdgeBetaMigrator extends ChromeProfileMigrator { + static get key() { + return "chromium-edge-beta"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-chromium-edge-beta"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/edgebeta.png"; + } + + _chromeUserDataPathSuffix = "Edge Beta"; + _keychainServiceName = "Microsoft Edge Safe Storage"; + _keychainAccountName = "Microsoft Edge"; +} + +/** + * Chromium 360 migrator + */ +export class Chromium360seMigrator extends ChromeProfileMigrator { + static get key() { + return "chromium-360se"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-chromium-360se"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/360.png"; + } + + _chromeUserDataPathSuffix = "360 SE"; + _keychainServiceName = "Microsoft Edge Safe Storage"; + _keychainAccountName = "Microsoft Edge"; +} + +/** + * Opera migrator + */ +export class OperaProfileMigrator extends ChromeProfileMigrator { + static get key() { + return "opera"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-opera"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/opera.png"; + } + + _chromeUserDataPathSuffix = "Opera"; + _keychainServiceName = "Opera Safe Storage"; + _keychainAccountName = "Opera"; + + getSourceProfiles() { + return null; + } +} + +/** + * Opera GX migrator + */ +export class OperaGXProfileMigrator extends ChromeProfileMigrator { + static get key() { + return "opera-gx"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-opera-gx"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/operagx.png"; + } + + _chromeUserDataPathSuffix = "Opera GX"; + _keychainServiceName = "Opera Safe Storage"; + _keychainAccountName = "Opera"; + + getSourceProfiles() { + return null; + } +} + +/** + * Vivaldi migrator + */ +export class VivaldiProfileMigrator extends ChromeProfileMigrator { + static get key() { + return "vivaldi"; + } + + static get displayNameL10nID() { + return "migration-wizard-migrator-display-name-vivaldi"; + } + + static get brandImage() { + return "chrome://browser/content/migration/brands/vivaldi.png"; + } + + _chromeUserDataPathSuffix = "Vivaldi"; + _keychainServiceName = "Vivaldi Safe Storage"; + _keychainAccountName = "Vivaldi"; +} |