forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			757 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			757 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- 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/. */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| const AUTH_TYPE = {
 | |
|   SCHEME_HTML: 0,
 | |
|   SCHEME_BASIC: 1,
 | |
|   SCHEME_DIGEST: 2,
 | |
| };
 | |
| 
 | |
| const { AppConstants } = ChromeUtils.import(
 | |
|   "resource://gre/modules/AppConstants.jsm"
 | |
| );
 | |
| const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 | |
| const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
 | |
| const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 | |
| const { ChromeMigrationUtils } = ChromeUtils.import(
 | |
|   "resource:///modules/ChromeMigrationUtils.jsm"
 | |
| );
 | |
| const { MigrationUtils, MigratorPrototype } = ChromeUtils.import(
 | |
|   "resource:///modules/MigrationUtils.jsm"
 | |
| );
 | |
| 
 | |
| ChromeUtils.defineModuleGetter(
 | |
|   this,
 | |
|   "PlacesUtils",
 | |
|   "resource://gre/modules/PlacesUtils.jsm"
 | |
| );
 | |
| 
 | |
| ChromeUtils.defineModuleGetter(
 | |
|   this,
 | |
|   "PlacesUIUtils",
 | |
|   "resource:///modules/PlacesUIUtils.jsm"
 | |
| );
 | |
| 
 | |
| /**
 | |
|  * Converts an array of chrome bookmark objects into one our own places code
 | |
|  * understands.
 | |
|  *
 | |
|  * @param   items
 | |
|  *          bookmark items to be inserted on this parent
 | |
|  * @param   errorAccumulator
 | |
|  *          function that gets called with any errors thrown so we don't drop them on the floor.
 | |
|  */
 | |
| function convertBookmarks(items, 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 });
 | |
|       } else if (item.type == "folder") {
 | |
|         let folderItem = {
 | |
|           type: PlacesUtils.bookmarks.TYPE_FOLDER,
 | |
|           title: item.name,
 | |
|         };
 | |
|         folderItem.children = convertBookmarks(item.children, errorAccumulator);
 | |
|         itemsToInsert.push(folderItem);
 | |
|       }
 | |
|     } catch (ex) {
 | |
|       Cu.reportError(ex);
 | |
|       errorAccumulator(ex);
 | |
|     }
 | |
|   }
 | |
|   return itemsToInsert;
 | |
| }
 | |
| 
 | |
| function ChromeProfileMigrator() {
 | |
|   this._chromeUserDataPathSuffix = "Chrome";
 | |
| }
 | |
| 
 | |
| ChromeProfileMigrator.prototype = Object.create(MigratorPrototype);
 | |
| 
 | |
| ChromeProfileMigrator.prototype._keychainServiceName = "Chrome Safe Storage";
 | |
| ChromeProfileMigrator.prototype._keychainAccountName = "Chrome";
 | |
| 
 | |
| ChromeProfileMigrator.prototype._getChromeUserDataPathIfExists = async function() {
 | |
|   if (this._chromeUserDataPath) {
 | |
|     return this._chromeUserDataPath;
 | |
|   }
 | |
|   let path = ChromeMigrationUtils.getDataPath(this._chromeUserDataPathSuffix);
 | |
|   let exists = await OS.File.exists(path);
 | |
|   if (exists) {
 | |
|     this._chromeUserDataPath = path;
 | |
|   } else {
 | |
|     this._chromeUserDataPath = null;
 | |
|   }
 | |
|   return this._chromeUserDataPath;
 | |
| };
 | |
| 
 | |
| ChromeProfileMigrator.prototype.getResources = async function Chrome_getResources(
 | |
|   aProfile
 | |
| ) {
 | |
|   let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
 | |
|   if (chromeUserDataPath) {
 | |
|     let profileFolder = OS.Path.join(chromeUserDataPath, aProfile.id);
 | |
|     if (await OS.File.exists(profileFolder)) {
 | |
|       let localePropertySuffix = MigrationUtils._getLocalePropertyForBrowser(
 | |
|         this.getBrowserKey()
 | |
|       ).replace(/^source-name-/, "");
 | |
|       let possibleResourcePromises = [
 | |
|         GetBookmarksResource(
 | |
|           profileFolder,
 | |
|           localePropertySuffix,
 | |
|           this.getBrowserKey()
 | |
|         ),
 | |
|         GetHistoryResource(profileFolder),
 | |
|         GetCookiesResource(profileFolder),
 | |
|       ];
 | |
|       if (ChromeMigrationUtils.supportsLoginsForPlatform) {
 | |
|         possibleResourcePromises.push(
 | |
|           this._GetPasswordsResource(profileFolder)
 | |
|         );
 | |
|       }
 | |
|       let possibleResources = await Promise.all(possibleResourcePromises);
 | |
|       return possibleResources.filter(r => r != null);
 | |
|     }
 | |
|   }
 | |
|   return [];
 | |
| };
 | |
| 
 | |
| ChromeProfileMigrator.prototype.getLastUsedDate = async function Chrome_getLastUsedDate() {
 | |
|   let sourceProfiles = await this.getSourceProfiles();
 | |
|   let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
 | |
|   if (!chromeUserDataPath) {
 | |
|     return new Date(0);
 | |
|   }
 | |
|   let datePromises = sourceProfiles.map(async profile => {
 | |
|     let basePath = OS.Path.join(chromeUserDataPath, profile.id);
 | |
|     let fileDatePromises = ["Bookmarks", "History", "Cookies"].map(
 | |
|       async leafName => {
 | |
|         let path = OS.Path.join(basePath, leafName);
 | |
|         let info = await OS.File.stat(path).catch(() => null);
 | |
|         return info ? info.lastModificationDate : 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));
 | |
| };
 | |
| 
 | |
| ChromeProfileMigrator.prototype.getSourceProfiles = async function Chrome_getSourceProfiles() {
 | |
|   if ("__sourceProfiles" in this) {
 | |
|     return this.__sourceProfiles;
 | |
|   }
 | |
| 
 | |
|   let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
 | |
|   if (!chromeUserDataPath) {
 | |
|     return [];
 | |
|   }
 | |
| 
 | |
|   let localState;
 | |
|   let profiles = [];
 | |
|   try {
 | |
|     localState = await 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") {
 | |
|       Cu.reportError("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;
 | |
| };
 | |
| 
 | |
| Object.defineProperty(ChromeProfileMigrator.prototype, "sourceLocked", {
 | |
|   get: function Chrome_sourceLocked() {
 | |
|     // There is an exclusive lock on some SQLite databases. Assume they are locked for now.
 | |
|     return true;
 | |
|   },
 | |
| });
 | |
| 
 | |
| async function GetBookmarksResource(
 | |
|   aProfileFolder,
 | |
|   aLocalePropertySuffix,
 | |
|   aBrowserKey
 | |
| ) {
 | |
|   let bookmarksPath = OS.Path.join(aProfileFolder, "Bookmarks");
 | |
|   if (!(await OS.File.exists(bookmarksPath))) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   return {
 | |
|     type: MigrationUtils.resourceTypes.BOOKMARKS,
 | |
| 
 | |
|     migrate(aCallback) {
 | |
|       return (async function() {
 | |
|         let gotErrors = false;
 | |
|         let errorGatherer = function() {
 | |
|           gotErrors = true;
 | |
|         };
 | |
|         // Parse Chrome bookmark file that is JSON format
 | |
|         let bookmarkJSON = await OS.File.read(bookmarksPath, {
 | |
|           encoding: "UTF-8",
 | |
|         });
 | |
|         let roots = JSON.parse(bookmarkJSON).roots;
 | |
|         let histogramBookmarkRoots = 0;
 | |
| 
 | |
|         // Importing bookmark bar items
 | |
|         if (roots.bookmark_bar.children && roots.bookmark_bar.children.length) {
 | |
|           // Toolbar
 | |
|           histogramBookmarkRoots |=
 | |
|             MigrationUtils.SOURCE_BOOKMARK_ROOTS_BOOKMARKS_TOOLBAR;
 | |
|           let parentGuid = PlacesUtils.bookmarks.toolbarGuid;
 | |
|           let bookmarks = convertBookmarks(
 | |
|             roots.bookmark_bar.children,
 | |
|             errorGatherer
 | |
|           );
 | |
|           if (
 | |
|             !Services.prefs.getBoolPref("browser.toolbars.bookmarks.2h2020") &&
 | |
|             !MigrationUtils.isStartupMigration &&
 | |
|             PlacesUtils.getChildCountForFolder(
 | |
|               PlacesUtils.bookmarks.toolbarGuid
 | |
|             ) > PlacesUIUtils.NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE
 | |
|           ) {
 | |
|             parentGuid = await MigrationUtils.createImportedBookmarksFolder(
 | |
|               aLocalePropertySuffix,
 | |
|               parentGuid
 | |
|             );
 | |
|           }
 | |
|           await MigrationUtils.insertManyBookmarksWrapper(
 | |
|             bookmarks,
 | |
|             parentGuid
 | |
|           );
 | |
|           PlacesUIUtils.maybeToggleBookmarkToolbarVisibilityAfterMigration();
 | |
|         }
 | |
| 
 | |
|         // Importing bookmark menu items
 | |
|         if (roots.other.children && roots.other.children.length) {
 | |
|           // Bookmark menu
 | |
|           histogramBookmarkRoots |=
 | |
|             MigrationUtils.SOURCE_BOOKMARK_ROOTS_BOOKMARKS_MENU;
 | |
|           let parentGuid = PlacesUtils.bookmarks.menuGuid;
 | |
|           let bookmarks = convertBookmarks(roots.other.children, errorGatherer);
 | |
|           if (
 | |
|             !Services.prefs.getBoolPref("browser.toolbars.bookmarks.2h2020") &&
 | |
|             !MigrationUtils.isStartupMigration &&
 | |
|             PlacesUtils.getChildCountForFolder(PlacesUtils.bookmarks.menuGuid) >
 | |
|               PlacesUIUtils.NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE
 | |
|           ) {
 | |
|             parentGuid = await MigrationUtils.createImportedBookmarksFolder(
 | |
|               aLocalePropertySuffix,
 | |
|               parentGuid
 | |
|             );
 | |
|           }
 | |
|           await MigrationUtils.insertManyBookmarksWrapper(
 | |
|             bookmarks,
 | |
|             parentGuid
 | |
|           );
 | |
|         }
 | |
|         if (gotErrors) {
 | |
|           throw new Error("The migration included errors.");
 | |
|         }
 | |
|         Services.telemetry
 | |
|           .getKeyedHistogramById("FX_MIGRATION_BOOKMARKS_ROOTS")
 | |
|           .add(aBrowserKey, histogramBookmarkRoots);
 | |
|       })().then(
 | |
|         () => aCallback(true),
 | |
|         () => aCallback(false)
 | |
|       );
 | |
|     },
 | |
|   };
 | |
| }
 | |
| 
 | |
| async function GetHistoryResource(aProfileFolder) {
 | |
|   let historyPath = OS.Path.join(aProfileFolder, "History");
 | |
|   if (!(await OS.File.exists(historyPath))) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   return {
 | |
|     type: MigrationUtils.resourceTypes.HISTORY,
 | |
| 
 | |
|     migrate(aCallback) {
 | |
|       (async function() {
 | |
|         const MAX_AGE_IN_DAYS = Services.prefs.getIntPref(
 | |
|           "browser.migrate.chrome.history.maxAgeInDays"
 | |
|         );
 | |
|         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";
 | |
|         if (MAX_AGE_IN_DAYS) {
 | |
|           let maxAge = ChromeMigrationUtils.dateToChromeTime(
 | |
|             Date.now() - MAX_AGE_IN_DAYS * 24 * 60 * 60 * 1000
 | |
|           );
 | |
|           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 = PlacesUtils.history.TRANSITIONS.LINK;
 | |
|             if (row.getResultByName("typed_count") > 0) {
 | |
|               transition = PlacesUtils.history.TRANSITIONS.TYPED;
 | |
|             }
 | |
| 
 | |
|             pageInfos.push({
 | |
|               title: row.getResultByName("title"),
 | |
|               url: new URL(row.getResultByName("url")),
 | |
|               visits: [
 | |
|                 {
 | |
|                   transition,
 | |
|                   date: ChromeMigrationUtils.chromeTimeToDate(
 | |
|                     row.getResultByName("last_visit_time"),
 | |
|                     fallbackVisitDate
 | |
|                   ),
 | |
|                 },
 | |
|               ],
 | |
|             });
 | |
|           } catch (e) {
 | |
|             Cu.reportError(e);
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (pageInfos.length) {
 | |
|           await MigrationUtils.insertVisitsWrapper(pageInfos);
 | |
|         }
 | |
|       })().then(
 | |
|         () => {
 | |
|           aCallback(true);
 | |
|         },
 | |
|         ex => {
 | |
|           Cu.reportError(ex);
 | |
|           aCallback(false);
 | |
|         }
 | |
|       );
 | |
|     },
 | |
|   };
 | |
| }
 | |
| 
 | |
| async function GetCookiesResource(aProfileFolder) {
 | |
|   let cookiesPath = OS.Path.join(aProfileFolder, "Cookies");
 | |
|   if (!(await OS.File.exists(cookiesPath))) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   return {
 | |
|     type: MigrationUtils.resourceTypes.COOKIES,
 | |
| 
 | |
|     async migrate(aCallback) {
 | |
|       // Get columns names and set is_sceure, is_httponly fields accordingly.
 | |
|       let columns = await MigrationUtils.getRowsFromDBWithoutLocks(
 | |
|         cookiesPath,
 | |
|         "Chrome cookies",
 | |
|         `PRAGMA table_info(cookies)`
 | |
|       ).catch(ex => {
 | |
|         Cu.reportError(ex);
 | |
|         aCallback(false);
 | |
|       });
 | |
|       // If the promise was rejected we will have already called aCallback,
 | |
|       // so we can just return here.
 | |
|       if (!columns) {
 | |
|         return;
 | |
|       }
 | |
|       columns = columns.map(c => c.getResultByName("name"));
 | |
|       let isHttponly = columns.includes("is_httponly")
 | |
|         ? "is_httponly"
 | |
|         : "httponly";
 | |
|       let isSecure = columns.includes("is_secure") ? "is_secure" : "secure";
 | |
| 
 | |
|       let source_scheme = columns.includes("source_scheme")
 | |
|         ? "source_scheme"
 | |
|         : `"${Ci.nsICookie.SCHEME_UNSET}" as source_scheme`;
 | |
| 
 | |
|       // We don't support decrypting cookies yet so only import plaintext ones.
 | |
|       let rows = await MigrationUtils.getRowsFromDBWithoutLocks(
 | |
|         cookiesPath,
 | |
|         "Chrome cookies",
 | |
|         `SELECT host_key, name, value, path, expires_utc, ${isSecure}, ${isHttponly}, encrypted_value, ${source_scheme}
 | |
|         FROM cookies
 | |
|         WHERE length(encrypted_value) = 0`
 | |
|       ).catch(ex => {
 | |
|         Cu.reportError(ex);
 | |
|         aCallback(false);
 | |
|       });
 | |
| 
 | |
|       // If the promise was rejected we will have already called aCallback,
 | |
|       // so we can just return here.
 | |
|       if (!rows) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       let fallbackExpiryDate = 0;
 | |
|       for (let row of rows) {
 | |
|         let host_key = row.getResultByName("host_key");
 | |
|         if (host_key.match(/^\./)) {
 | |
|           // 1st character of host_key may be ".", so we have to remove it
 | |
|           host_key = host_key.substr(1);
 | |
|         }
 | |
| 
 | |
|         let schemeType = Ci.nsICookie.SCHEME_UNSET;
 | |
|         switch (row.getResultByName("source_scheme")) {
 | |
|           case 1:
 | |
|             schemeType = Ci.nsICookie.SCHEME_HTTP;
 | |
|             break;
 | |
|           case 2:
 | |
|             schemeType = Ci.nsICookie.SCHEME_HTTPS;
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         try {
 | |
|           let expiresUtc =
 | |
|             ChromeMigrationUtils.chromeTimeToDate(
 | |
|               row.getResultByName("expires_utc"),
 | |
|               fallbackExpiryDate
 | |
|             ) / 1000;
 | |
|           // No point adding cookies that don't have a valid expiry.
 | |
|           if (!expiresUtc) {
 | |
|             continue;
 | |
|           }
 | |
| 
 | |
|           Services.cookies.add(
 | |
|             host_key,
 | |
|             row.getResultByName("path"),
 | |
|             row.getResultByName("name"),
 | |
|             row.getResultByName("value"),
 | |
|             row.getResultByName(isSecure),
 | |
|             row.getResultByName(isHttponly),
 | |
|             false,
 | |
|             parseInt(expiresUtc),
 | |
|             {},
 | |
|             Ci.nsICookie.SAMESITE_NONE,
 | |
|             schemeType
 | |
|           );
 | |
|         } catch (e) {
 | |
|           Cu.reportError(e);
 | |
|         }
 | |
|       }
 | |
|       aCallback(true);
 | |
|     },
 | |
|   };
 | |
| }
 | |
| 
 | |
| ChromeProfileMigrator.prototype._GetPasswordsResource = async function(
 | |
|   aProfileFolder
 | |
| ) {
 | |
|   let loginPath = OS.Path.join(aProfileFolder, "Login Data");
 | |
|   if (!(await OS.File.exists(loginPath))) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   let {
 | |
|     _chromeUserDataPathSuffix,
 | |
|     _keychainServiceName,
 | |
|     _keychainAccountName,
 | |
|     _keychainMockPassphrase = null,
 | |
|   } = this;
 | |
| 
 | |
|   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 => {
 | |
|         Cu.reportError(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.import(
 | |
|             "resource:///modules/ChromeWindowsLoginCrypto.jsm"
 | |
|           );
 | |
|           crypto = new ChromeWindowsLoginCrypto(_chromeUserDataPathSuffix);
 | |
|         } else if (AppConstants.platform == "macosx") {
 | |
|           let { ChromeMacOSLoginCrypto } = ChromeUtils.import(
 | |
|             "resource:///modules/ChromeMacOSLoginCrypto.jsm"
 | |
|           );
 | |
|           crypto = new ChromeMacOSLoginCrypto(
 | |
|             _keychainServiceName,
 | |
|             _keychainAccountName,
 | |
|             _keychainMockPassphrase
 | |
|           );
 | |
|         } else {
 | |
|           aCallback(false);
 | |
|           return;
 | |
|         }
 | |
|       } catch (ex) {
 | |
|         // Handle the user canceling Keychain access or other OSCrypto errors.
 | |
|         Cu.reportError(ex);
 | |
|         aCallback(false);
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       let logins = [];
 | |
|       let fallbackCreationDate = new Date();
 | |
|       for (let row of rows) {
 | |
|         try {
 | |
|           let origin_url = 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: 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 = 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) {
 | |
|           Cu.reportError(e);
 | |
|         }
 | |
|       }
 | |
|       try {
 | |
|         if (logins.length) {
 | |
|           await MigrationUtils.insertLoginsWrapper(logins);
 | |
|         }
 | |
|       } catch (e) {
 | |
|         Cu.reportError(e);
 | |
|       }
 | |
|       if (crypto.finalize) {
 | |
|         crypto.finalize();
 | |
|       }
 | |
|       aCallback(true);
 | |
|     },
 | |
|   };
 | |
| };
 | |
| 
 | |
| ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator";
 | |
| ChromeProfileMigrator.prototype.contractID =
 | |
|   "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
 | |
| ChromeProfileMigrator.prototype.classID = Components.ID(
 | |
|   "{4cec1de4-1671-4fc3-a53e-6c539dc77a26}"
 | |
| );
 | |
| 
 | |
| /**
 | |
|  *  Chromium migration
 | |
|  **/
 | |
| function ChromiumProfileMigrator() {
 | |
|   this._chromeUserDataPathSuffix = "Chromium";
 | |
|   this._keychainServiceName = "Chromium Safe Storage";
 | |
|   this._keychainAccountName = "Chromium";
 | |
| }
 | |
| 
 | |
| ChromiumProfileMigrator.prototype = Object.create(
 | |
|   ChromeProfileMigrator.prototype
 | |
| );
 | |
| ChromiumProfileMigrator.prototype.classDescription =
 | |
|   "Chromium Profile Migrator";
 | |
| ChromiumProfileMigrator.prototype.contractID =
 | |
|   "@mozilla.org/profile/migrator;1?app=browser&type=chromium";
 | |
| ChromiumProfileMigrator.prototype.classID = Components.ID(
 | |
|   "{8cece922-9720-42de-b7db-7cef88cb07ca}"
 | |
| );
 | |
| 
 | |
| var EXPORTED_SYMBOLS = ["ChromeProfileMigrator", "ChromiumProfileMigrator"];
 | |
| 
 | |
| /**
 | |
|  * Chrome Canary
 | |
|  * Not available on Linux
 | |
|  **/
 | |
| function CanaryProfileMigrator() {
 | |
|   this._chromeUserDataPathSuffix = "Canary";
 | |
| }
 | |
| CanaryProfileMigrator.prototype = Object.create(
 | |
|   ChromeProfileMigrator.prototype
 | |
| );
 | |
| CanaryProfileMigrator.prototype.classDescription =
 | |
|   "Chrome Canary Profile Migrator";
 | |
| CanaryProfileMigrator.prototype.contractID =
 | |
|   "@mozilla.org/profile/migrator;1?app=browser&type=canary";
 | |
| CanaryProfileMigrator.prototype.classID = Components.ID(
 | |
|   "{4bf85aa5-4e21-46ca-825f-f9c51a5e8c76}"
 | |
| );
 | |
| 
 | |
| if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
 | |
|   EXPORTED_SYMBOLS.push("CanaryProfileMigrator");
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Chrome Dev - Linux only (not available in Mac and Windows)
 | |
|  */
 | |
| function ChromeDevMigrator() {
 | |
|   this._chromeUserDataPathSuffix = "Chrome Dev";
 | |
| }
 | |
| ChromeDevMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
 | |
| ChromeDevMigrator.prototype.classDescription = "Chrome Dev Profile Migrator";
 | |
| ChromeDevMigrator.prototype.contractID =
 | |
|   "@mozilla.org/profile/migrator;1?app=browser&type=chrome-dev";
 | |
| ChromeDevMigrator.prototype.classID = Components.ID(
 | |
|   "{7370a02a-4886-42c3-a4ec-d48c726ec30a}"
 | |
| );
 | |
| 
 | |
| if (AppConstants.platform != "win" && AppConstants.platform != "macosx") {
 | |
|   EXPORTED_SYMBOLS.push("ChromeDevMigrator");
 | |
| }
 | |
| 
 | |
| function ChromeBetaMigrator() {
 | |
|   this._chromeUserDataPathSuffix = "Chrome Beta";
 | |
| }
 | |
| ChromeBetaMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
 | |
| ChromeBetaMigrator.prototype.classDescription = "Chrome Beta Profile Migrator";
 | |
| ChromeBetaMigrator.prototype.contractID =
 | |
|   "@mozilla.org/profile/migrator;1?app=browser&type=chrome-beta";
 | |
| ChromeBetaMigrator.prototype.classID = Components.ID(
 | |
|   "{47f75963-840b-4950-a1f0-d9c1864f8b8e}"
 | |
| );
 | |
| 
 | |
| if (AppConstants.platform != "macosx") {
 | |
|   EXPORTED_SYMBOLS.push("ChromeBetaMigrator");
 | |
| }
 | |
| 
 | |
| function ChromiumEdgeMigrator() {
 | |
|   this._chromeUserDataPathSuffix = "Edge";
 | |
|   this._keychainServiceName = "Microsoft Edge Safe Storage";
 | |
|   this._keychainAccountName = "Microsoft Edge";
 | |
| }
 | |
| ChromiumEdgeMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
 | |
| ChromiumEdgeMigrator.prototype.classDescription =
 | |
|   "Chromium Edge Profile Migrator";
 | |
| ChromiumEdgeMigrator.prototype.contractID =
 | |
|   "@mozilla.org/profile/migrator;1?app=browser&type=chromium-edge";
 | |
| ChromiumEdgeMigrator.prototype.classID = Components.ID(
 | |
|   "{3c7f6b7c-baa9-4338-acfa-04bf79f1dcf1}"
 | |
| );
 | |
| 
 | |
| function ChromiumEdgeBetaMigrator() {
 | |
|   this._chromeUserDataPathSuffix = "Edge Beta";
 | |
|   this._keychainServiceName = "Microsoft Edge Safe Storage";
 | |
|   this._keychainAccountName = "Microsoft Edge";
 | |
| }
 | |
| ChromiumEdgeBetaMigrator.prototype = Object.create(
 | |
|   ChromiumEdgeMigrator.prototype
 | |
| );
 | |
| ChromiumEdgeBetaMigrator.prototype.classDescription =
 | |
|   "Chromium Edge Beta Profile Migrator";
 | |
| ChromiumEdgeBetaMigrator.prototype.contractID =
 | |
|   "@mozilla.org/profile/migrator;1?app=browser&type=chromium-edge-beta";
 | |
| ChromiumEdgeBetaMigrator.prototype.classID = Components.ID(
 | |
|   "{0fc3d48a-c1c3-4871-b58f-a8b47d1555fb}"
 | |
| );
 | |
| 
 | |
| if (AppConstants.platform == "macosx" || AppConstants.platform == "win") {
 | |
|   EXPORTED_SYMBOLS.push("ChromiumEdgeMigrator", "ChromiumEdgeBetaMigrator");
 | |
| }
 | 
