--- a/browser/components/migration/ChromeMigrationUtils.jsm
+++ b/browser/components/migration/ChromeMigrationUtils.jsm
@@ -1,20 +1,18 @@
/* 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";
this.EXPORTED_SYMBOLS = ["ChromeMigrationUtils"];
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
-const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
this.ChromeMigrationUtils = {
_chromeUserDataPath: null,
@@ -32,17 +30,20 @@ this.ChromeMigrationUtils = {
// }
_extensionLocaleStrings: {},
/**
* Get all extensions installed in a specific profile.
* @param {String} profileId - A Chrome user profile ID. For example, "Profile 1".
* @returns {Array} All installed Chrome extensions information.
*/
- async getExtensionList(profileId = this.getLastUsedProfileId()) {
+ async getExtensionList(profileId) {
+ if (profileId === undefined) {
+ profileId = await this.getLastUsedProfileId();
+ }
let path = this.getExtensionPath(profileId);
let iterator = new OS.File.DirectoryIterator(path);
let extensionList = [];
await iterator.forEach(async entry => {
if (entry.isDir) {
let extensionInformation = await this.getExtensionInformation(entry.name, profileId);
if (extensionInformation) {
extensionList.push(extensionInformation);
@@ -53,17 +54,20 @@ this.ChromeMigrationUtils = {
},
/**
* Get information of a specific Chrome extension.
* @param {String} extensionId - The extension ID.
* @param {String} profileId - The user profile's ID.
* @retruns {Object} The Chrome extension information.
*/
- async getExtensionInformation(extensionId, profileId = this.getLastUsedProfileId()) {
+ async getExtensionInformation(extensionId, profileId) {
+ if (profileId === undefined) {
+ profileId = await this.getLastUsedProfileId();
+ }
let extensionInformation = null;
try {
let manifestPath = this.getExtensionPath(profileId);
manifestPath = OS.Path.join(manifestPath, extensionId);
// If there are multiple sub-directories in the extension directory,
// read the files in the latest directory.
let directories = await this._getSortedByVersionSubDirectoryNames(manifestPath);
if (!directories[0]) {
@@ -146,50 +150,44 @@ this.ChromeMigrationUtils = {
},
/**
* Check that a specific extension is installed or not.
* @param {String} extensionId - The extension ID.
* @param {String} profileId - The user profile's ID.
* @returns {Boolean} Return true if the extension is installed otherwise return false.
*/
- async isExtensionInstalled(extensionId, profileId = this.getLastUsedProfileId()) {
+ async isExtensionInstalled(extensionId, profileId) {
+ if (profileId === undefined) {
+ profileId = await this.getLastUsedProfileId();
+ }
let extensionPath = this.getExtensionPath(profileId);
let isInstalled = await OS.File.exists(OS.Path.join(extensionPath, extensionId));
return isInstalled;
},
/**
* Get the last used user profile's ID.
* @returns {String} The last used user profile's ID.
*/
- getLastUsedProfileId() {
- let localState = this.getLocalState();
+ async getLastUsedProfileId() {
+ let localState = await this.getLocalState();
return localState ? localState.profile.last_used : "Default";
},
/**
* Get the local state file content.
* @returns {Object} The JSON-based content.
*/
- getLocalState() {
- let localStateFile = new FileUtils.File(this.getChromeUserDataPath());
- localStateFile.append("Local State");
- if (!localStateFile.exists())
- throw new Error("Chrome's 'Local State' file does not exist.");
- if (!localStateFile.isReadable())
- throw new Error("Chrome's 'Local State' file could not be read.");
-
+ async getLocalState() {
let localState = null;
try {
- let fstream = Cc[FILE_INPUT_STREAM_CID].createInstance(Ci.nsIFileInputStream);
- fstream.init(localStateFile, -1, 0, 0);
- let inputStream = NetUtil.readInputStreamToString(fstream, fstream.available(),
- { charset: "UTF-8" });
- localState = JSON.parse(inputStream);
+ let localStatePath = OS.Path.join(this.getChromeUserDataPath(), "Local State");
+ let localStateJson = await OS.File.read(localStatePath, { encoding: "utf-8" });
+ localState = JSON.parse(localStateJson);
} catch (ex) {
Cu.reportError(ex);
throw ex;
}
return localState;
},
/**
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -3,29 +3,26 @@
/* 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/FileUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/ChromeMigrationUtils.jsm");
Cu.import("resource:///modules/MigrationUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
@@ -87,147 +84,159 @@ function convertBookmarks(items, errorAc
Cu.reportError(ex);
errorAccumulator(ex);
}
}
return itemsToInsert;
}
function ChromeProfileMigrator() {
- let path = ChromeMigrationUtils.getDataPath("Chrome");
- let chromeUserDataFolder = new FileUtils.File(path);
- this._chromeUserDataFolder = chromeUserDataFolder.exists() ?
- chromeUserDataFolder : null;
+ this._chromeUserDataPathSuffix = "Chrome";
}
ChromeProfileMigrator.prototype = Object.create(MigratorPrototype);
+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 =
- function Chrome_getResources(aProfile) {
- if (this._chromeUserDataFolder) {
- let profileFolder = this._chromeUserDataFolder.clone();
- profileFolder.append(aProfile.id);
- if (profileFolder.exists()) {
- let possibleResources = [
+ 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 possibleResourcePromises = [
GetBookmarksResource(profileFolder),
GetHistoryResource(profileFolder),
GetCookiesResource(profileFolder),
];
if (AppConstants.platform == "win") {
- possibleResources.push(GetWindowsPasswordsResource(profileFolder));
+ possibleResourcePromises.push(GetWindowsPasswordsResource(profileFolder));
}
+ let possibleResources = await Promise.all(possibleResourcePromises);
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 => {
+ 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);
- return OS.File.stat(path).catch(() => null).then(info => {
- return info ? info.lastModificationDate : 0;
- });
+ let info = await OS.File.stat(path).catch(() => null);
+ return info ? info.lastModificationDate : 0;
});
- return Promise.all(fileDatePromises).then(dates => {
- return Math.max.apply(Math, dates);
- });
+ let dates = await Promise.all(fileDatePromises);
+ return Math.max(...dates);
});
- return Promise.all(datePromises).then(dates => {
- dates.push(0);
- return new Date(Math.max.apply(Math, 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;
- if (!this._chromeUserDataFolder)
+ let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
+ if (!chromeUserDataPath)
return [];
let profiles = [];
try {
- let info_cache = ChromeMigrationUtils.getLocalState().profile.info_cache;
+ let localState = await ChromeMigrationUtils.getLocalState();
+ let info_cache = localState.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()) {
+ let defaultProfilePath = OS.Path.join(chromeUserDataPath, "Default");
+ if (await OS.File.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 = profiles.filter(function(profile) {
- let resources = this.getResources(profile);
+ this.__sourceProfiles = profileResources.filter(({resources}) => {
return resources && resources.length > 0;
- }, this);
+ }, this).map(({profile}) => profile);
return this.__sourceProfiles;
};
ChromeProfileMigrator.prototype.getSourceHomePageURL =
async function Chrome_getSourceHomePageURL() {
- 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);
+ let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
+ if (!chromeUserDataPath)
+ return "";
+ let prefsPath = OS.Path.join(chromeUserDataPath, "Preferences");
+ if (await OS.File.exists(prefsPath)) {
try {
- return JSON.parse(
- NetUtil.readInputStreamToString(fstream, fstream.available(),
- { charset: "UTF-8" })
- ).homepage;
+ let json = await OS.File.read(prefsPath, {encoding: "UTF-8"});
+ return JSON.parse(json).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())
+async function GetBookmarksResource(aProfileFolder) {
+ 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(bookmarksFile.path, {encoding: "UTF-8"});
+ let bookmarkJSON = await OS.File.read(bookmarksPath, {encoding: "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;
let bookmarks = convertBookmarks(roots.bookmark_bar.children, errorGatherer);
@@ -254,20 +263,19 @@ function GetBookmarksResource(aProfileFo
throw new Error("The migration included errors.");
}
})().then(() => aCallback(true),
() => aCallback(false));
},
};
}
-function GetHistoryResource(aProfileFolder) {
- let historyFile = aProfileFolder.clone();
- historyFile.append("History");
- if (!historyFile.exists())
+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");
@@ -278,17 +286,17 @@ function GetHistoryResource(aProfileFold
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 =
- await MigrationUtils.getRowsFromDBWithoutLocks(historyFile.path, "Chrome history", query);
+ await MigrationUtils.getRowsFromDBWithoutLocks(historyPath, "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;
@@ -326,28 +334,27 @@ function GetHistoryResource(aProfileFold
ex => {
Cu.reportError(ex);
aCallback(false);
});
},
};
}
-function GetCookiesResource(aProfileFolder) {
- let cookiesFile = aProfileFolder.clone();
- cookiesFile.append("Cookies");
- if (!cookiesFile.exists())
+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) {
// We don't support decrypting cookies yet so only import plaintext ones.
- let rows = await MigrationUtils.getRowsFromDBWithoutLocks(cookiesFile.path, "Chrome cookies",
+ let rows = await MigrationUtils.getRowsFromDBWithoutLocks(cookiesPath, "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.
@@ -378,27 +385,26 @@ function GetCookiesResource(aProfileFold
Cu.reportError(e);
}
}
aCallback(true);
},
};
}
-function GetWindowsPasswordsResource(aProfileFolder) {
- let loginFile = aProfileFolder.clone();
- loginFile.append("Login Data");
- if (!loginFile.exists())
+async function GetWindowsPasswordsResource(aProfileFolder) {
+ let loginPath = OS.Path.join(aProfileFolder, "Login Data");
+ if (!(await OS.File.exists(loginPath)))
return null;
return {
type: MigrationUtils.resourceTypes.PASSWORDS,
async migrate(aCallback) {
- let rows = await MigrationUtils.getRowsFromDBWithoutLocks(loginFile.path, "Chrome passwords",
+ 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.
@@ -463,36 +469,32 @@ ChromeProfileMigrator.prototype.classDes
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 path = ChromeMigrationUtils.getDataPath("Chromium");
- let chromiumUserDataFolder = new FileUtils.File(path);
- this._chromeUserDataFolder = chromiumUserDataFolder.exists() ? chromiumUserDataFolder : null;
+ this._chromeUserDataPathSuffix = "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 componentsArray = [ChromeProfileMigrator, ChromiumProfileMigrator];
/**
* Chrome Canary
* Not available on Linux
**/
function CanaryProfileMigrator() {
- let path = ChromeMigrationUtils.getDataPath("Canary");
- let chromeUserDataFolder = new FileUtils.File(path);
- this._chromeUserDataFolder = chromeUserDataFolder.exists() ? chromeUserDataFolder : null;
+ 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") {
componentsArray.push(CanaryProfileMigrator);