forked from mirrors/gecko-dev
		
	 ae4301ef96
			
		
	
	
		ae4301ef96
		
	
	
	
	
		
			
			MozReview-Commit-ID: 89f0YCxxgC8 --HG-- extra : rebase_source : 53efa0f0258728df54fc17a773cbf74c712b51e4
		
			
				
	
	
		
			611 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			611 lines
		
	
	
	
		
			22 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 { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 | |
| 
 | |
| const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
 | |
| 
 | |
| const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
 | |
| const S100NS_PER_MS = 10;
 | |
| 
 | |
| const AUTH_TYPE = {
 | |
|   SCHEME_HTML: 0,
 | |
|   SCHEME_BASIC: 1,
 | |
|   SCHEME_DIGEST: 2
 | |
| };
 | |
| 
 | |
| Cu.import("resource://gre/modules/AppConstants.jsm");
 | |
| Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 | |
| Cu.import("resource://gre/modules/Services.jsm");
 | |
| Cu.import("resource://gre/modules/NetUtil.jsm");
 | |
| Cu.import("resource://gre/modules/FileUtils.jsm");
 | |
| Cu.import("resource://gre/modules/osfile.jsm");
 | |
| Cu.import("resource://gre/modules/Console.jsm");
 | |
| Cu.import("resource://gre/modules/Task.jsm");
 | |
| Cu.import("resource:///modules/MigrationUtils.jsm");
 | |
| 
 | |
| XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
 | |
|                                   "resource://gre/modules/PlacesUtils.jsm");
 | |
| XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
 | |
|                                   "resource://gre/modules/OSCrypto.jsm");
 | |
| XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
 | |
|                                   "resource://gre/modules/Sqlite.jsm");
 | |
| /**
 | |
|  * Get an nsIFile instance representing the expected location of user data
 | |
|  * for this copy of Chrome/Chromium/Canary on different OSes.
 | |
|  * @param subfoldersWin {Array} an array of subfolders to use for Windows
 | |
|  * @param subfoldersOSX {Array} an array of subfolders to use for OS X
 | |
|  * @param subfoldersUnix {Array} an array of subfolders to use for *nix systems
 | |
|  * @returns {nsIFile} the place we expect data to live. Might not actually exist!
 | |
|  */
 | |
| function getDataFolder(subfoldersWin, subfoldersOSX, subfoldersUnix) {
 | |
|   let dirServiceID, subfolders;
 | |
|   if (AppConstants.platform == "win") {
 | |
|     dirServiceID = "LocalAppData";
 | |
|     subfolders = subfoldersWin.concat(["User Data"]);
 | |
|   } else if (AppConstants.platform == "macosx") {
 | |
|     dirServiceID = "ULibDir";
 | |
|     subfolders = ["Application Support"].concat(subfoldersOSX);
 | |
|   } else {
 | |
|     dirServiceID = "Home";
 | |
|     subfolders = [".config"].concat(subfoldersUnix);
 | |
|   }
 | |
|   return FileUtils.getDir(dirServiceID, subfolders, false);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Convert Chrome time format to Date object
 | |
|  *
 | |
|  * @param   aTime
 | |
|  *          Chrome time
 | |
|  * @return  converted Date object
 | |
|  * @note    Google Chrome uses FILETIME / 10 as time.
 | |
|  *          FILETIME is based on same structure of Windows.
 | |
|  */
 | |
| function chromeTimeToDate(aTime)
 | |
| {
 | |
|   return new Date((aTime * S100NS_PER_MS - S100NS_FROM1601TO1970 ) / 10000);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Insert bookmark items into specific folder.
 | |
|  *
 | |
|  * @param   parentGuid
 | |
|  *          GUID of the folder where items will be inserted
 | |
|  * @param   items
 | |
|  *          bookmark items to be inserted
 | |
|  * @param   errorAccumulator
 | |
|  *          function that gets called with any errors thrown so we don't drop them on the floor.
 | |
|  */
 | |
| function* insertBookmarkItems(parentGuid, items, errorAccumulator) {
 | |
|   for (let item of items) {
 | |
|     try {
 | |
|       if (item.type == "url") {
 | |
|         if (item.url.trim().startsWith("chrome:")) {
 | |
|           // Skip invalid chrome URIs. Creating an actual URI always reports
 | |
|           // messages to the console, so we avoid doing that.
 | |
|           continue;
 | |
|         }
 | |
|         yield PlacesUtils.bookmarks.insert({
 | |
|           parentGuid, url: item.url, title: item.name
 | |
|         });
 | |
|       } else if (item.type == "folder") {
 | |
|         let newFolderGuid = (yield PlacesUtils.bookmarks.insert({
 | |
|           parentGuid, type: PlacesUtils.bookmarks.TYPE_FOLDER, title: item.name
 | |
|         })).guid;
 | |
| 
 | |
|         yield insertBookmarkItems(newFolderGuid, item.children, errorAccumulator);
 | |
|       }
 | |
|     } catch (e) {
 | |
|       Cu.reportError(e);
 | |
|       errorAccumulator(e);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| function ChromeProfileMigrator() {
 | |
|   let chromeUserDataFolder =
 | |
|     getDataFolder(["Google", "Chrome"], ["Google", "Chrome"], ["google-chrome"]);
 | |
|   this._chromeUserDataFolder = chromeUserDataFolder.exists() ?
 | |
|     chromeUserDataFolder : null;
 | |
| }
 | |
| 
 | |
| ChromeProfileMigrator.prototype = Object.create(MigratorPrototype);
 | |
| 
 | |
| ChromeProfileMigrator.prototype.getResources =
 | |
|   function Chrome_getResources(aProfile) {
 | |
|     if (this._chromeUserDataFolder) {
 | |
|       let profileFolder = this._chromeUserDataFolder.clone();
 | |
|       profileFolder.append(aProfile.id);
 | |
|       if (profileFolder.exists()) {
 | |
|         let possibleResources = [
 | |
|           GetBookmarksResource(profileFolder),
 | |
|           GetHistoryResource(profileFolder),
 | |
|           GetCookiesResource(profileFolder),
 | |
|         ];
 | |
|         if (AppConstants.platform == "win") {
 | |
|           possibleResources.push(GetWindowsPasswordsResource(profileFolder));
 | |
|         }
 | |
|         return possibleResources.filter(r => r != null);
 | |
|       }
 | |
|     }
 | |
|     return [];
 | |
|   };
 | |
| 
 | |
| ChromeProfileMigrator.prototype.getLastUsedDate =
 | |
|   function Chrome_getLastUsedDate() {
 | |
|     let datePromises = this.sourceProfiles.map(profile => {
 | |
|       let profileFolder = this._chromeUserDataFolder.clone();
 | |
|       let basePath = OS.Path.join(this._chromeUserDataFolder.path, profile.id);
 | |
|       let fileDatePromises = ["Bookmarks", "History", "Cookies"].map(leafName => {
 | |
|         let path = OS.Path.join(basePath, leafName);
 | |
|         return OS.File.stat(path).catch(_ => null).then(info => {
 | |
|           return info ? info.lastModificationDate : 0;
 | |
|         });
 | |
|       });
 | |
|       return Promise.all(fileDatePromises).then(dates => {
 | |
|         return Math.max.apply(Math, dates);
 | |
|       });
 | |
|     });
 | |
|     return Promise.all(datePromises).then(dates => {
 | |
|       dates.push(0);
 | |
|       return new Date(Math.max.apply(Math, dates));
 | |
|     });
 | |
|   };
 | |
| 
 | |
| Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", {
 | |
|   get: function Chrome_sourceProfiles() {
 | |
|     if ("__sourceProfiles" in this)
 | |
|       return this.__sourceProfiles;
 | |
| 
 | |
|     if (!this._chromeUserDataFolder)
 | |
|       return [];
 | |
| 
 | |
|     let profiles = [];
 | |
|     try {
 | |
|       // Local State is a JSON file that contains profile info.
 | |
|       let localState = this._chromeUserDataFolder.clone();
 | |
|       localState.append("Local State");
 | |
|       if (!localState.exists())
 | |
|         throw new Error("Chrome's 'Local State' file does not exist.");
 | |
|       if (!localState.isReadable())
 | |
|         throw new Error("Chrome's 'Local State' file could not be read.");
 | |
| 
 | |
|       let fstream = Cc[FILE_INPUT_STREAM_CID].createInstance(Ci.nsIFileInputStream);
 | |
|       fstream.init(localState, -1, 0, 0);
 | |
|       let inputStream = NetUtil.readInputStreamToString(fstream, fstream.available(),
 | |
|                                                         { charset: "UTF-8" });
 | |
|       let info_cache = JSON.parse(inputStream).profile.info_cache;
 | |
|       for (let profileFolderName in info_cache) {
 | |
|         let profileFolder = this._chromeUserDataFolder.clone();
 | |
|         profileFolder.append(profileFolderName);
 | |
|         profiles.push({
 | |
|           id: profileFolderName,
 | |
|           name: info_cache[profileFolderName].name || profileFolderName,
 | |
|         });
 | |
|       }
 | |
|     } catch (e) {
 | |
|       Cu.reportError("Error detecting Chrome profiles: " + e);
 | |
|       // If we weren't able to detect any profiles above, fallback to the Default profile.
 | |
|       let defaultProfileFolder = this._chromeUserDataFolder.clone();
 | |
|       defaultProfileFolder.append("Default");
 | |
|       if (defaultProfileFolder.exists()) {
 | |
|         profiles = [{
 | |
|           id: "Default",
 | |
|           name: "Default",
 | |
|         }];
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Only list profiles from which any data can be imported
 | |
|     return this.__sourceProfiles = profiles.filter(function(profile) {
 | |
|       let resources = this.getResources(profile);
 | |
|       return resources && resources.length > 0;
 | |
|     }, this);
 | |
|   }
 | |
| });
 | |
| 
 | |
| Object.defineProperty(ChromeProfileMigrator.prototype, "sourceHomePageURL", {
 | |
|   get: function Chrome_sourceHomePageURL() {
 | |
|     let prefsFile = this._chromeUserDataFolder.clone();
 | |
|     prefsFile.append("Preferences");
 | |
|     if (prefsFile.exists()) {
 | |
|       // XXX reading and parsing JSON is synchronous.
 | |
|       let fstream = Cc[FILE_INPUT_STREAM_CID].
 | |
|                     createInstance(Ci.nsIFileInputStream);
 | |
|       fstream.init(file, -1, 0, 0);
 | |
|       try {
 | |
|         return JSON.parse(
 | |
|           NetUtil.readInputStreamToString(fstream, fstream.available(),
 | |
|                                           { charset: "UTF-8" })
 | |
|             ).homepage;
 | |
|       }
 | |
|       catch (e) {
 | |
|         Cu.reportError("Error parsing Chrome's preferences file: " + e);
 | |
|       }
 | |
|     }
 | |
|     return "";
 | |
|   }
 | |
| });
 | |
| 
 | |
| 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;
 | |
|   },
 | |
| });
 | |
| 
 | |
| function GetBookmarksResource(aProfileFolder) {
 | |
|   let bookmarksFile = aProfileFolder.clone();
 | |
|   bookmarksFile.append("Bookmarks");
 | |
|   if (!bookmarksFile.exists())
 | |
|     return null;
 | |
| 
 | |
|   return {
 | |
|     type: MigrationUtils.resourceTypes.BOOKMARKS,
 | |
| 
 | |
|     migrate: function(aCallback) {
 | |
|       return Task.spawn(function* () {
 | |
|         let gotErrors = false;
 | |
|         let errorGatherer = () => gotErrors = true;
 | |
|         let jsonStream = yield new Promise(resolve =>
 | |
|           NetUtil.asyncFetch({ uri: NetUtil.newURI(bookmarksFile),
 | |
|                                loadUsingSystemPrincipal: true
 | |
|                              },
 | |
|                              (inputStream, resultCode) => {
 | |
|                                if (Components.isSuccessCode(resultCode)) {
 | |
|                                  resolve(inputStream);
 | |
|                                } else {
 | |
|                                  reject(new Error("Could not read Bookmarks file"));
 | |
|                                }
 | |
|                              }
 | |
|           )
 | |
|         );
 | |
| 
 | |
|         // Parse Chrome bookmark file that is JSON format
 | |
|         let bookmarkJSON = NetUtil.readInputStreamToString(
 | |
|           jsonStream, jsonStream.available(), { charset : "UTF-8" });
 | |
|         let roots = JSON.parse(bookmarkJSON).roots;
 | |
| 
 | |
|         // Importing bookmark bar items
 | |
|         if (roots.bookmark_bar.children &&
 | |
|             roots.bookmark_bar.children.length > 0) {
 | |
|           // Toolbar
 | |
|           let parentGuid = PlacesUtils.bookmarks.toolbarGuid;
 | |
|           if (!MigrationUtils.isStartupMigration) {
 | |
|             parentGuid =
 | |
|               yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
 | |
|           }
 | |
|           yield insertBookmarkItems(parentGuid, roots.bookmark_bar.children, errorGatherer);
 | |
|         }
 | |
| 
 | |
|         // Importing bookmark menu items
 | |
|         if (roots.other.children &&
 | |
|             roots.other.children.length > 0) {
 | |
|           // Bookmark menu
 | |
|           let parentGuid = PlacesUtils.bookmarks.menuGuid;
 | |
|           if (!MigrationUtils.isStartupMigration) {
 | |
|             parentGuid =
 | |
|               yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
 | |
|           }
 | |
|           yield insertBookmarkItems(parentGuid, roots.other.children, errorGatherer);
 | |
|         }
 | |
|         if (gotErrors) {
 | |
|           throw "The migration included errors.";
 | |
|         }
 | |
|       }.bind(this)).then(() => aCallback(true),
 | |
|                           e => aCallback(false));
 | |
|     }
 | |
|   };
 | |
| }
 | |
| 
 | |
| function GetHistoryResource(aProfileFolder) {
 | |
|   let historyFile = aProfileFolder.clone();
 | |
|   historyFile.append("History");
 | |
|   if (!historyFile.exists())
 | |
|     return null;
 | |
| 
 | |
|   function getRows(dbOptions) {
 | |
|     const RETRYLIMIT = 10;
 | |
|     const RETRYINTERVAL = 100;
 | |
|     return Task.spawn(function* innerGetRows() {
 | |
|       let rows = null;
 | |
|       for (let retryCount = RETRYLIMIT; retryCount && !rows; retryCount--) {
 | |
|         // Attempt to get the rows. If this succeeds, we will bail out of the loop,
 | |
|         // close the database in a failsafe way, and pass the rows back.
 | |
|         // If fetching the rows throws, we will wait RETRYINTERVAL ms
 | |
|         // and try again. This will repeat a maximum of RETRYLIMIT times.
 | |
|         let db;
 | |
|         let didOpen = false;
 | |
|         let exceptionSeen;
 | |
|         try {
 | |
|           db = yield Sqlite.openConnection(dbOptions);
 | |
|           didOpen = true;
 | |
|           rows = yield db.execute(`SELECT url, title, last_visit_time, typed_count
 | |
|                                    FROM urls WHERE hidden = 0`);
 | |
|         } catch (ex) {
 | |
|           if (!exceptionSeen) {
 | |
|             Cu.reportError(ex);
 | |
|           }
 | |
|           exceptionSeen = ex;
 | |
|         } finally {
 | |
|           try {
 | |
|             if (didOpen) {
 | |
|               yield db.close();
 | |
|             }
 | |
|           } catch (ex) {}
 | |
|         }
 | |
|         if (exceptionSeen) {
 | |
|           yield new Promise(resolve => setTimeout(resolve, RETRYINTERVAL));
 | |
|         }
 | |
|       }
 | |
|       if (!rows) {
 | |
|         throw new Error("Couldn't get rows from the Chrome history database.");
 | |
|       }
 | |
|       return rows;
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   return {
 | |
|     type: MigrationUtils.resourceTypes.HISTORY,
 | |
| 
 | |
|     migrate(aCallback) {
 | |
|       Task.spawn(function* () {
 | |
|         let dbOptions = {
 | |
|           readOnly: true,
 | |
|           ignoreLockingMode: true,
 | |
|           path: historyFile.path
 | |
|         };
 | |
| 
 | |
|         let rows = yield getRows(dbOptions);
 | |
|         let places = [];
 | |
|         for (let row of rows) {
 | |
|           try {
 | |
|             // if having typed_count, we changes transition type to typed.
 | |
|             let transType = PlacesUtils.history.TRANSITION_LINK;
 | |
|             if (row.getResultByName("typed_count") > 0)
 | |
|               transType = PlacesUtils.history.TRANSITION_TYPED;
 | |
| 
 | |
|             places.push({
 | |
|               uri: NetUtil.newURI(row.getResultByName("url")),
 | |
|               title: row.getResultByName("title"),
 | |
|               visits: [{
 | |
|                 transitionType: transType,
 | |
|                 visitDate: chromeTimeToDate(
 | |
|                              row.getResultByName(
 | |
|                                "last_visit_time")) * 1000,
 | |
|               }],
 | |
|             });
 | |
|           } catch (e) {
 | |
|             Cu.reportError(e);
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (places.length > 0) {
 | |
|           yield new Promise((resolve, reject) => {
 | |
|             PlacesUtils.asyncHistory.updatePlaces(places, {
 | |
|               _success: false,
 | |
|               handleResult: function() {
 | |
|                 // Importing any entry is considered a successful import.
 | |
|                 this._success = true;
 | |
|               },
 | |
|               handleError: function() {},
 | |
|               handleCompletion: function() {
 | |
|                 if (this._success) {
 | |
|                   resolve();
 | |
|                 } else {
 | |
|                   reject(new Error("Couldn't add visits"));
 | |
|                 }
 | |
|               }
 | |
|             });
 | |
|           });
 | |
|         }
 | |
|       }).then(() => { aCallback(true); },
 | |
|               ex => {
 | |
|                 Cu.reportError(ex);
 | |
|                 aCallback(false);
 | |
|               });
 | |
|     }
 | |
|   };
 | |
| }
 | |
| 
 | |
| function GetCookiesResource(aProfileFolder) {
 | |
|   let cookiesFile = aProfileFolder.clone();
 | |
|   cookiesFile.append("Cookies");
 | |
|   if (!cookiesFile.exists())
 | |
|     return null;
 | |
| 
 | |
|   return {
 | |
|     type: MigrationUtils.resourceTypes.COOKIES,
 | |
| 
 | |
|     migrate: function(aCallback) {
 | |
|       let dbConn = Services.storage.openUnsharedDatabase(cookiesFile);
 | |
|       // We don't support decrypting cookies yet so only import plaintext ones.
 | |
|       let stmt = dbConn.createAsyncStatement(`
 | |
|         SELECT host_key, name, value, path, expires_utc, secure, httponly, encrypted_value
 | |
|         FROM cookies
 | |
|         WHERE length(encrypted_value) = 0`);
 | |
| 
 | |
|       stmt.executeAsync({
 | |
|         handleResult : function(aResults) {
 | |
|           for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
 | |
|             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);
 | |
|             }
 | |
| 
 | |
|             try {
 | |
|               let expiresUtc =
 | |
|                 chromeTimeToDate(row.getResultByName("expires_utc")) / 1000;
 | |
|               Services.cookies.add(host_key,
 | |
|                                    row.getResultByName("path"),
 | |
|                                    row.getResultByName("name"),
 | |
|                                    row.getResultByName("value"),
 | |
|                                    row.getResultByName("secure"),
 | |
|                                    row.getResultByName("httponly"),
 | |
|                                    false,
 | |
|                                    parseInt(expiresUtc),
 | |
|                                    {});
 | |
|             } catch (e) {
 | |
|               Cu.reportError(e);
 | |
|             }
 | |
|           }
 | |
|         },
 | |
| 
 | |
|         handleError : function(aError) {
 | |
|           Cu.reportError("Async statement execution returned with '" +
 | |
|                          aError.result + "', '" + aError.message + "'");
 | |
|         },
 | |
| 
 | |
|         handleCompletion : function(aReason) {
 | |
|           dbConn.asyncClose();
 | |
|           aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
 | |
|         },
 | |
|       });
 | |
|       stmt.finalize();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| function GetWindowsPasswordsResource(aProfileFolder) {
 | |
|   let loginFile = aProfileFolder.clone();
 | |
|   loginFile.append("Login Data");
 | |
|   if (!loginFile.exists())
 | |
|     return null;
 | |
| 
 | |
|   return {
 | |
|     type: MigrationUtils.resourceTypes.PASSWORDS,
 | |
| 
 | |
|     migrate(aCallback) {
 | |
|       let dbConn = Services.storage.openUnsharedDatabase(loginFile);
 | |
|       let stmt = dbConn.createAsyncStatement(`
 | |
|         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`);
 | |
|       let crypto = new OSCrypto();
 | |
| 
 | |
|       stmt.executeAsync({
 | |
|         _rowToLoginInfo(row) {
 | |
|           let loginInfo = {
 | |
|             username: row.getResultByName("username_value"),
 | |
|             password: crypto.
 | |
|                       decryptData(crypto.arrayToString(row.getResultByName("password_value")),
 | |
|                                                        null),
 | |
|             hostName: NetUtil.newURI(row.getResultByName("origin_url")).prePath,
 | |
|             submitURL: null,
 | |
|             httpRealm: null,
 | |
|             usernameElement: row.getResultByName("username_element"),
 | |
|             passwordElement: row.getResultByName("password_element"),
 | |
|             timeCreated: chromeTimeToDate(row.getResultByName("date_created") + 0).getTime(),
 | |
|             timesUsed: row.getResultByName("times_used") + 0,
 | |
|           };
 | |
| 
 | |
|           switch (row.getResultByName("scheme")) {
 | |
|             case AUTH_TYPE.SCHEME_HTML:
 | |
|               loginInfo.submitURL = NetUtil.newURI(row.getResultByName("action_url")).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.hostName.length + 1);
 | |
|               break;
 | |
|             default:
 | |
|               throw new Error("Login data scheme type not supported: " +
 | |
|                               row.getResultByName("scheme"));
 | |
|           }
 | |
| 
 | |
|           return loginInfo;
 | |
|         },
 | |
| 
 | |
|         handleResult(aResults) {
 | |
|           for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
 | |
|             try {
 | |
|               let loginInfo = this._rowToLoginInfo(row);
 | |
|               let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
 | |
| 
 | |
|               login.init(loginInfo.hostName, loginInfo.submitURL, loginInfo.httpRealm,
 | |
|                          loginInfo.username, loginInfo.password, loginInfo.usernameElement,
 | |
|                          loginInfo.passwordElement);
 | |
|               login.QueryInterface(Ci.nsILoginMetaInfo);
 | |
|               login.timeCreated = loginInfo.timeCreated;
 | |
|               login.timeLastUsed = loginInfo.timeCreated;
 | |
|               login.timePasswordChanged = loginInfo.timeCreated;
 | |
|               login.timesUsed = loginInfo.timesUsed;
 | |
| 
 | |
|               // Add the login only if there's not an existing entry
 | |
|               let logins = Services.logins.findLogins({}, login.hostname,
 | |
|                                                       login.formSubmitURL,
 | |
|                                                       login.httpRealm);
 | |
| 
 | |
|               // Bug 1187190: Password changes should be propagated depending on timestamps.
 | |
|               if (!logins.some(l => login.matches(l, true))) {
 | |
|                 Services.logins.addLogin(login);
 | |
|               }
 | |
|             } catch (e) {
 | |
|               Cu.reportError(e);
 | |
|             }
 | |
|           }
 | |
|         },
 | |
| 
 | |
|         handleError(aError) {
 | |
|           Cu.reportError("Async statement execution returned with '" +
 | |
|                          aError.result + "', '" + aError.message + "'");
 | |
|         },
 | |
| 
 | |
|         handleCompletion(aReason) {
 | |
|           dbConn.asyncClose();
 | |
|           aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
 | |
|           crypto.finalize();
 | |
|         },
 | |
|       });
 | |
|       stmt.finalize();
 | |
|     }
 | |
|   };
 | |
| }
 | |
| 
 | |
| 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() {
 | |
|   let chromiumUserDataFolder = getDataFolder(["Chromium"], ["Chromium"], ["chromium"]);
 | |
|   this._chromeUserDataFolder = chromiumUserDataFolder.exists() ? chromiumUserDataFolder : null;
 | |
| }
 | |
| 
 | |
| 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 componentsArray = [ChromeProfileMigrator, ChromiumProfileMigrator];
 | |
| 
 | |
| /**
 | |
|  * Chrome Canary
 | |
|  * Not available on Linux
 | |
|  **/
 | |
| function CanaryProfileMigrator() {
 | |
|   let chromeUserDataFolder = getDataFolder(["Google", "Chrome SxS"], ["Google", "Chrome Canary"]);
 | |
|   this._chromeUserDataFolder = chromeUserDataFolder.exists() ? chromeUserDataFolder : null;
 | |
| }
 | |
| 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") {
 | |
|   componentsArray.push(CanaryProfileMigrator);
 | |
| }
 | |
| 
 | |
| this.NSGetFactory = XPCOMUtils.generateNSGetFactory(componentsArray);
 |