summaryrefslogtreecommitdiffstats
path: root/browser/components/migration/ChromeProfileMigrator.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/migration/ChromeProfileMigrator.js')
-rw-r--r--browser/components/migration/ChromeProfileMigrator.js557
1 files changed, 0 insertions, 557 deletions
diff --git a/browser/components/migration/ChromeProfileMigrator.js b/browser/components/migration/ChromeProfileMigrator.js
deleted file mode 100644
index ec0c8d444..000000000
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ /dev/null
@@ -1,557 +0,0 @@
-/* -*- 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"); /* globals OS */
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
-
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
- "resource://gre/modules/OSCrypto.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);
-}
-
-/**
- * Convert Date object to Chrome time format
- *
- * @param aDate
- * Date object or integer equivalent
- * @return Chrome time
- * @note For details on Chrome time, see chromeTimeToDate.
- */
-function dateToChromeTime(aDate) {
- return (aDate * 10000 + S100NS_FROM1601TO1970) / S100NS_PER_MS;
-}
-
-/**
- * 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 MigrationUtils.insertBookmarkWrapper({
- parentGuid, url: item.url, title: item.name
- });
- } else if (item.type == "folder") {
- let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
- 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 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
- this.__sourceProfiles = profiles.filter(function(profile) {
- let resources = this.getResources(profile);
- return resources && resources.length > 0;
- }, this);
- return this.__sourceProfiles;
- }
-});
-
-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(prefsFile, -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 = function() { gotErrors = true };
- let jsonStream = yield new Promise((resolve, reject) => {
- let options = {
- uri: NetUtil.newURI(bookmarksFile),
- loadUsingSystemPrincipal: true
- };
- NetUtil.asyncFetch(options, (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 new Error("The migration included errors.");
- }
- }.bind(this)).then(() => aCallback(true),
- () => aCallback(false));
- }
- };
-}
-
-function GetHistoryResource(aProfileFolder) {
- let historyFile = aProfileFolder.clone();
- historyFile.append("History");
- if (!historyFile.exists())
- return null;
-
- return {
- type: MigrationUtils.resourceTypes.HISTORY,
-
- migrate(aCallback) {
- Task.spawn(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 = 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 =
- yield MigrationUtils.getRowsFromDBWithoutLocks(historyFile.path, "Chrome history", query);
- 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) => {
- MigrationUtils.insertVisitsWrapper(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: Task.async(function* (aCallback) {
- // We don't support decrypting cookies yet so only import plaintext ones.
- let rows = yield MigrationUtils.getRowsFromDBWithoutLocks(cookiesFile.path, "Chrome cookies",
- `SELECT host_key, name, value, path, expires_utc, secure, httponly, encrypted_value
- 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;
- }
-
- 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);
- }
-
- 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);
- }
- }
- aCallback(true);
- }),
- };
-}
-
-function GetWindowsPasswordsResource(aProfileFolder) {
- let loginFile = aProfileFolder.clone();
- loginFile.append("Login Data");
- if (!loginFile.exists())
- return null;
-
- return {
- type: MigrationUtils.resourceTypes.PASSWORDS,
-
- migrate: Task.async(function* (aCallback) {
- let rows = yield MigrationUtils.getRowsFromDBWithoutLocks(loginFile.path, "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;
- }
- let crypto = new OSCrypto();
-
- 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: crypto.
- decryptData(crypto.arrayToString(row.getResultByName("password_value")),
- null),
- hostname: origin_url.prePath,
- formSubmitURL: 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:
- let action_url = NetUtil.newURI(row.getResultByName("action_url"));
- if (!kValidSchemes.has(action_url.scheme)) {
- continue; // This continues the outer for loop.
- }
- loginInfo.formSubmitURL = 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"));
- }
- MigrationUtils.insertLoginWrapper(loginInfo);
- } catch (e) {
- Cu.reportError(e);
- }
- }
- 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() {
- 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);