Bug 718608 - Migration code shrink and cleanup: unified code for notifications and error handling, prepare for deCOM. r=mak. sr=gavin on the interface change. browser/components/build part was done by mak (r=bsmedberg.

This commit is contained in:
Asaf Romano 2012-03-21 21:20:47 +02:00
parent c6838a7ad3
commit bdcf891941
11 changed files with 733 additions and 630 deletions

View File

@ -30,7 +30,6 @@ endif
LOCAL_INCLUDES = \
-I$(srcdir)/../shell/src \
-I$(srcdir)/../feeds/src \
-I$(srcdir)/../places/src \
-I$(srcdir)/../privatebrowsing/src \
-I$(srcdir)/../about \
-I$(srcdir)/../dirprovider \
@ -53,10 +52,14 @@ endif
EXTRA_DSO_LDOPTS += \
$(call EXPAND_LIBNAME_PATH,unicharutil_external_s,$(LIBXUL_DIST)/lib) \
$(LIBXUL_DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
$(XPCOM_GLUE_LDOPTS) \
$(MOZ_COMPONENT_LIBS) \
$(NULL)
ifdef JS_SHARED_LIBRARY
EXTRA_DSO_LDOPTS += $(MOZ_JS_LIBS)
endif
LOCAL_INCLUDES += -I$(srcdir)/../migration/src
SHARED_LIBRARY_LIBS += ../migration/src/$(LIB_PREFIX)migration_s.$(LIB_SUFFIX)

View File

@ -1,44 +1,15 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is The Browser Profile Migrator.
#
# The Initial Developer of the Original Code is Ben Goodger.
# Portions created by the Initial Developer are Copyright (C) 2004
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <ben@bengoodger.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const kIMig = Components.interfaces.nsIBrowserProfileMigrator;
const kIPStartup = Components.interfaces.nsIProfileStartup;
const kProfileMigratorContractIDPrefix = "@mozilla.org/profile/migrator;1?app=browser&type=";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const kIMig = Ci.nsIBrowserProfileMigrator;
const kIPStartup = Ci.nsIProfileStartup;
Cu.import("resource://gre/modules/MigrationUtils.jsm");
var MigrationWizard = {
_source: "", // Source Profile Migrator ContractID suffix
@ -87,6 +58,7 @@ var MigrationWizard = {
os.removeObserver(this, "Migration:ItemAfterMigrate");
os.removeObserver(this, "Migration:ItemError");
os.removeObserver(this, "Migration:Ended");
MigrationUtils.finishMigration();
},
// 1 - Import Source
@ -118,21 +90,13 @@ var MigrationWizard = {
// Figure out what source apps are are available to import from:
var group = document.getElementById("importSourceGroup");
for (var i = 0; i < group.childNodes.length; ++i) {
var suffix = group.childNodes[i].id;
if (suffix != "nothing" && suffix != "fromfile") {
var contractID = kProfileMigratorContractIDPrefix + suffix;
try {
var migrator = Components.classes[contractID].createInstance(kIMig);
}
catch (e) {
dump("*** invalid contractID =" + contractID + "\n");
return;
}
if (migrator.sourceExists) {
var migratorKey = group.childNodes[i].id;
if (migratorKey != "nothing" && migratorKey != "fromfile") {
var migrator = MigrationUtils.getMigrator(migratorKey);
if (migrator) {
// Save this as the first selectable item, if we don't already have
// one, or if it is the migrator that was passed to us.
if (!selectedMigrator || this._source == suffix)
if (!selectedMigrator || this._source == migratorKey)
selectedMigrator = group.childNodes[i];
} else {
// Hide this option
@ -173,8 +137,7 @@ var MigrationWizard = {
if (!this._migrator || (newSource != this._source)) {
// Create the migrator for the selected source.
var contractID = kProfileMigratorContractIDPrefix + newSource;
this._migrator = Components.classes[contractID].createInstance(kIMig);
this._migrator = MigrationUtils.getMigrator(newSource);
this._itemsFlags = kIMig.ALL;
this._selectedProfile = null;
@ -182,8 +145,10 @@ var MigrationWizard = {
this._source = newSource;
// check for more than one source profile
if (this._migrator.sourceHasMultipleProfiles)
var sourceProfiles = this._migrator.sourceProfiles;
if (sourceProfiles && sourceProfiles.length > 1) {
this._wiz.currentPage.next = "selectProfile";
}
else {
if (this._autoMigrate)
this._wiz.currentPage.next = "homePageImport";
@ -192,11 +157,8 @@ var MigrationWizard = {
else
this._wiz.currentPage.next = "importItems";
var sourceProfiles = this._migrator.sourceProfiles;
if (sourceProfiles && sourceProfiles.length == 1) {
var profileName = sourceProfiles.queryElementAt(0, Ci.nsISupportsString);
this._selectedProfile = profileName.data;
}
if (sourceProfiles && sourceProfiles.length == 1)
this._selectedProfile = sourceProfiles[0];
else
this._selectedProfile = "";
}
@ -220,9 +182,8 @@ var MigrationWizard = {
var sourceProfiles = this._migrator.sourceProfiles;
for (var i = 0; i < sourceProfiles.length; ++i) {
var item = document.createElement("radio");
var str = sourceProfiles.queryElementAt(i, Ci.nsISupportsString);
item.id = str.data;
item.setAttribute("label", str.data);
item.id = sourceProfiles[i];
item.setAttribute("label", sourceProfiles[i]);
profiles.appendChild(item);
}
}

View File

@ -39,7 +39,7 @@
interface nsIArray;
interface nsIProfileStartup;
[scriptable, uuid(5f445759-86a8-4dd3-ab84-4fc5341d9d9d)]
[scriptable, uuid(44993E0E-74E8-4BEC-9D66-AD8156E0A274)]
interface nsIBrowserProfileMigrator : nsISupports
{
/**
@ -80,17 +80,12 @@ interface nsIBrowserProfileMigrator : nsISupports
*/
readonly attribute boolean sourceExists;
/**
* Whether or not the import source implementing this interface
* has multiple user profiles configured.
*/
readonly attribute boolean sourceHasMultipleProfiles;
/**
* An enumeration of available profiles. If the import source does
* not support profiles, this attribute is null.
*/
readonly attribute nsIArray sourceProfiles;
readonly attribute jsval sourceProfiles;
/**
* The import source homepage. Returns null if not present/available

View File

@ -1,69 +1,29 @@
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The Original Code is the Browser Profile Migrator.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Makoto Kato <m_kato@ga2.so-net.ne.jp> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
* This Source Code is subject to the terms of the Mozilla Public License
* version 2.0 (the "License"). You can obtain a copy of the License at
* http://mozilla.org/MPL/2.0/. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
const MIGRATOR = Ci.nsIBrowserProfileMigrator;
const LOCAL_FILE_CID = "@mozilla.org/file/local;1";
const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
const BUNDLE_MIGRATION = "chrome://browser/locale/migration/migration.properties";
const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
const S100NS_PER_MS = 10;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
Components.utils.import("resource://gre/modules/NetUtil.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/MigrationUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "bookmarksSubfolderTitle", function () {
// get "import from google chrome" string for folder
let strbundle =
Services.strings.createBundle(BUNDLE_MIGRATION);
let sourceNameChrome = strbundle.GetStringFromName("sourceNameChrome");
return strbundle.formatStringFromName("importedBookmarksFolder",
[sourceNameChrome],
1);
});
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
/**
* Convert Chrome time format to Date object
@ -112,223 +72,230 @@ function insertBookmarkItems(aFolderId, aItems)
}
}
function ChromeProfileMigrator()
{
function ChromeProfileMigrator() {
let chromeUserDataFolder = FileUtils.getDir(
#ifdef XP_WIN
"LocalAppData", ["Google", "Chrome", "User Data"]
#elifdef XP_MACOSX
"ULibDir", ["Application Support", "Google", "Chrome"]
#else
"Home", [".config", "google-chrome"]
#endif
, false);
this._chromeUserDataFolder = chromeUserDataFolder.exists() ?
chromeUserDataFolder : null;
}
ChromeProfileMigrator.prototype = {
_paths: {
bookmarks : null,
cookies : null,
history : null,
prefs : null,
userData : null,
},
ChromeProfileMigrator.prototype = Object.create(MigratorPrototype);
_homepageURL : null,
_replaceBookmarks : false,
_sourceProfile: null,
_profilesCache: null,
/**
* Notify to observers to start migration
*
* @param aType
* notification type such as MIGRATOR.BOOKMARKS
*/
_notifyStart : function Chrome_notifyStart(aType)
{
Services.obs.notifyObservers(null, "Migration:ItemBeforeMigrate", aType);
this._pendingCount++;
},
/**
* Notify observers that a migration error occured with an item
*
* @param aType
* notification type such as MIGRATOR.BOOKMARKS
*/
_notifyError : function Chrome_notifyError(aType)
{
Services.obs.notifyObservers(null, "Migration:ItemError", aType);
},
/**
* Notify to observers to finish migration for item
* If all items are finished, it sends migration end notification.
*
* @param aType
* notification type such as MIGRATOR.BOOKMARKS
*/
_notifyCompleted : function Chrome_notifyIfCompleted(aType)
{
Services.obs.notifyObservers(null, "Migration:ItemAfterMigrate", aType);
if (--this._pendingCount == 0) {
// All items are migrated, so we have to send end notification.
Services.obs.notifyObservers(null, "Migration:Ended", null);
ChromeProfileMigrator.prototype.getResources =
function Chrome_getResources(aProfile) {
if (this._chromeUserDataFolder) {
let profileFolder = this._chromeUserDataFolder.clone();
profileFolder.append(aProfile);
if (profileFolder.exists()) {
let possibleResources = [GetBookmarksResource(profileFolder),
GetHistoryResource(profileFolder),
GetCookiesResource(profileFolder)];
return [r for each (r in possibleResources) if (r != null)];
}
}
},
return [];
};
/**
* Migrating bookmark items
*/
_migrateBookmarks : function Chrome_migrateBookmarks()
{
this._notifyStart(MIGRATOR.BOOKMARKS);
Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", {
get: function Chrome_sourceProfiles() {
if ("__sourceProfiles" in this)
return this.__sourceProfiles;
if (!this._chromeUserDataFolder)
return [];
let profiles;
try {
PlacesUtils.bookmarks.runInBatchMode({
_self : this,
runBatched : function (aUserData) {
let migrator = this._self;
let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
file.initWithPath(migrator._paths.bookmarks);
// 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.");
NetUtil.asyncFetch(file, function(aInputStream, aResultCode) {
if (!Components.isSuccessCode(aResultCode)) {
migrator._notifyCompleted(MIGRATOR.BOOKMARKS);
return;
}
// Parse Chrome bookmark file that is JSON format
let bookmarkJSON = NetUtil.readInputStreamToString(aInputStream,
aInputStream.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 parentId = PlacesUtils.toolbarFolderId;
if (!migrator._replaceBookmarks) {
parentId =
PlacesUtils.bookmarks.createFolder(parentId,
bookmarksSubfolderTitle,
PlacesUtils.bookmarks.DEFAULT_INDEX);
}
insertBookmarkItems(parentId, roots.bookmark_bar.children);
}
// Importing bookmark menu items
if (roots.other.children &&
roots.other.children.length > 0) {
// Bookmark menu
let parentId = PlacesUtils.bookmarksMenuFolderId;
if (!migrator._replaceBookmarks) {
parentId =
PlacesUtils.bookmarks.createFolder(parentId,
bookmarksSubfolderTitle,
PlacesUtils.bookmarks.DEFAULT_INDEX);
}
insertBookmarkItems(parentId, roots.other.children);
}
migrator._notifyCompleted(MIGRATOR.BOOKMARKS);
});
}
}, null);
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;
if (info_cache)
profiles = Object.keys(info_cache);
} catch (e) {
Cu.reportError(e);
this._notifyError(MIGRATOR.BOOKMARKS);
this._notifyCompleted(MIGRATOR.BOOKMARKS);
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 = ["Default"];
}
},
/**
* Migrating history
*/
_migrateHistory : function Chrome_migrateHistory()
{
this._notifyStart(MIGRATOR.HISTORY);
// Only list profiles from which any data can be imported
return this.__sourceProfiles = profiles.filter(function(profileName) {
let resources = this.getResources(profileName);
return resources && resources.length > 0;
}, this);
}
});
try {
PlacesUtils.history.runInBatchMode({
_self : this,
runBatched : function (aUserData) {
// access sqlite3 database of Chrome's history
let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
file.initWithPath(this._self._paths.history);
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 "";
}
});
let dbConn = Services.storage.openUnsharedDatabase(file);
let stmt = dbConn.createAsyncStatement(
"SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0");
function GetBookmarksResource(aProfileFolder) {
let bookmarksFile = aProfileFolder.clone();
bookmarksFile.append("Bookmarks");
if (!bookmarksFile.exists())
return null;
stmt.executeAsync({
_asyncHistory : Cc["@mozilla.org/browser/history;1"]
.getService(Ci.mozIAsyncHistory),
_db : dbConn,
_self : this._self,
handleResult : function(aResults) {
let places = [];
for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
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;
return {
type: Ci.nsIBrowserProfileMigrator.BOOKMARKS,
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);
migrate: function(aCallback) {
NetUtil.asyncFetch(bookmarksFile, MigrationUtils.wrapMigrateFunction(
function(aInputStream, aResultCode) {
if (!Components.isSuccessCode(aResultCode))
throw new Error("Could not read Bookmarks file");
// Parse Chrome bookmark file that is JSON format
let bookmarkJSON = NetUtil.readInputStreamToString(
aInputStream, aInputStream.available(), { charset : "UTF-8" });
let roots = JSON.parse(bookmarkJSON).roots;
PlacesUtils.bookmarks.runInBatchMode({
runBatched: function() {
// Importing bookmark bar items
if (roots.bookmark_bar.children &&
roots.bookmark_bar.children.length > 0) {
// Toolbar
let parentId = PlacesUtils.toolbarFolderId;
if (!MigrationUtils.isStartupMigration) {
parentId = MigrationUtils.createImportedBookmarksFolder(
"Chrome", parentId);
}
insertBookmarkItems(parentId, roots.bookmark_bar.children);
}
try {
this._asyncHistory.updatePlaces(places);
} catch (e) {
Cu.reportError(e);
// Importing bookmark menu items
if (roots.other.children &&
roots.other.children.length > 0) {
// Bookmark menu
let parentId = PlacesUtils.bookmarksMenuFolderId;
if (!MigrationUtils.isStartupMigration) {
parentId = MigrationUtils.createImportedBookmarksFolder(
"Chrome", parentId);
}
insertBookmarkItems(parentId, roots.other.children);
}
},
handleError : function(aError) {
Cu.reportError("Async statement execution returned with '" +
aError.result + "', '" + aError.message + "'");
},
handleCompletion : function(aReason) {
this._db.asyncClose();
this._self._notifyCompleted(MIGRATOR.HISTORY);
}
});
stmt.finalize();
}
}, null);
} catch (e) {
Cu.reportError(e);
this._notifyError(MIGRATOR.HISTORY);
this._notifyCompleted(MIGRATOR.HISTORY);
}, null);
}, aCallback));
}
},
};
}
/**
* Migrating cookies
*/
_migrateCookies : function Chrome_migrateCookies()
{
this._notifyStart(MIGRATOR.COOKIES);
function GetHistoryResource(aProfileFolder) {
let historyFile = aProfileFolder.clone();
historyFile.append("History");
if (!historyFile.exists())
return null;
try {
// Access sqlite3 database of Chrome's cookie
let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
file.initWithPath(this._paths.cookies);
return {
type: Ci.nsIBrowserProfileMigrator.HISTORY,
let dbConn = Services.storage.openUnsharedDatabase(file);
migrate: function(aCallback) {
let dbConn = Services.storage.openUnsharedDatabase(historyFile);
let stmt = dbConn.createAsyncStatement(
"SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0");
stmt.executeAsync({
handleResult : function(aResults) {
let places = [];
for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
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);
}
}
try {
PlacesUtils.asyncHistory.updatePlaces(places);
} 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 GetCookiesResource(aProfileFolder) {
let cookiesFile = aProfileFolder.clone();
cookiesFile.append("Cookies");
if (!cookiesFile.exists())
return null;
return {
type: Ci.nsIBrowserProfileMigrator.COOKIES,
migrate: function(aCallback) {
let dbConn = Services.storage.openUnsharedDatabase(cookiesFile);
let stmt = dbConn.createAsyncStatement(
"SELECT host_key, path, name, value, secure, httponly, expires_utc FROM cookies");
stmt.executeAsync({
_db : dbConn,
_self : this,
handleResult : function(aResults) {
for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
let host_key = row.getResultByName("host_key");
@ -360,246 +327,17 @@ ChromeProfileMigrator.prototype = {
},
handleCompletion : function(aReason) {
this._db.asyncClose();
this._self._notifyCompleted(MIGRATOR.COOKIES);
dbConn.asyncClose();
aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
},
});
stmt.finalize();
} catch (e) {
Cu.reportError(e);
this._notifyError(MIGRATOR.COOKIES);
this._notifyCompleted(MIGRATOR.COOKIES);
}
},
}
}
/**
* nsIBrowserProfileMigrator interface implementation
*/
/**
* Let's migrate all items
*
* @param aItems
* list of data items to migrate.
* @param aStartup
* non-null if called during startup.
* @param aProfile
* profile directory name to migrate
*/
migrate : function Chrome_migrate(aItems, aStartup, aProfile)
{
if (aStartup) {
aStartup.doStartup();
this._replaceBookmarks = true;
}
this._sourceProfile = aProfile;
Services.obs.notifyObservers(null, "Migration:Started", null);
// Reset panding count. If this count becomes 0, "Migration:Ended"
// notification is sent
this._pendingCount = 1;
if (aItems & MIGRATOR.HISTORY)
this._migrateHistory();
if (aItems & MIGRATOR.COOKIES)
this._migrateCookies();
if (aItems & MIGRATOR.BOOKMARKS)
this._migrateBookmarks();
if (--this._pendingCount == 0) {
// When async imports are immeditelly completed unfortunately,
// this will be called.
// Usually, this notification is sent by _notifyCompleted()
Services.obs.notifyObservers(null, "Migration:Ended", null);
}
},
/**
* return supported migration types
*
* @param aProfile
* directory name of the profile
* @param aDoingStartup
* non-null if called during startup.
* @return supported migration types
*/
getMigrateData: function Chrome_getMigrateData(aProfile, aDoingStartup)
{
this._sourceProfile = aProfile;
let chromeProfileDir = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
chromeProfileDir.initWithPath(this._paths.userData + aProfile);
let result = 0;
if (!chromeProfileDir.exists() || !chromeProfileDir.isReadable())
return result;
// bookmark and preference are JSON format
try {
let file = chromeProfileDir.clone();
file.append("Bookmarks");
if (file.exists()) {
this._paths.bookmarks = file.path;
result += MIGRATOR.BOOKMARKS;
}
} catch (e) {
Cu.reportError(e);
}
if (!this._paths.prefs) {
let file = chromeProfileDir.clone();
file.append("Preferences");
this._paths.prefs = file.path;
}
// history and cookies are SQLite database
try {
let file = chromeProfileDir.clone();
file.append("History");
if (file.exists()) {
this._paths.history = file.path;
result += MIGRATOR.HISTORY;
}
} catch (e) {
Cu.reportError(e);
}
try {
let file = chromeProfileDir.clone();
file.append("Cookies");
if (file.exists()) {
this._paths.cookies = file.path;
result += MIGRATOR.COOKIES;
}
} catch (e) {
Cu.reportError(e);
}
return result;
},
/**
* Whether we support migration of Chrome
*
* @return true if supported
*/
get sourceExists()
{
#ifdef XP_WIN
this._paths.userData = Services.dirsvc.get("LocalAppData", Ci.nsIFile).path +
"\\Google\\Chrome\\User Data\\";
#elifdef XP_MACOSX
this._paths.userData = Services.dirsvc.get("Home", Ci.nsIFile).path +
"/Library/Application Support/Google/Chrome/";
#else
this._paths.userData = Services.dirsvc.get("Home", Ci.nsIFile).path +
"/.config/google-chrome/";
#endif
let result = 0;
try {
let userDataDir = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
userDataDir.initWithPath(this._paths.userData);
if (!userDataDir.exists() || !userDataDir.isReadable())
return false;
let profiles = this.sourceProfiles;
if (profiles.length < 1)
return false;
// check that we can actually get data from the first profile
result = this.getMigrateData(profiles.queryElementAt(0, Ci.nsISupportsString), false);
} catch (e) {
Cu.reportError(e);
}
return result > 0;
},
get sourceHasMultipleProfiles()
{
return this.sourceProfiles.length > 1;
},
get sourceProfiles()
{
let profiles = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
try {
if (!this._profilesCache) {
let localState = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
// Local State is a JSON file that contains profile info.
localState.initWithPath(this._paths.userData + "Local State");
if (!localState.exists())
throw new Components.Exception("Chrome's 'Local State' file does not exist.",
Cr.NS_ERROR_FILE_NOT_FOUND);
if (!localState.isReadable())
throw new Components.Exception("Chrome's 'Local State' file could not be read.",
Cr.NS_ERROR_FILE_ACCESS_DENIED);
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" });
this._profilesCache = JSON.parse(inputStream).profile.info_cache;
}
for (let index in this._profilesCache) {
let str = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
str.data = index;
profiles.appendElement(str, false);
}
} catch (e) {
Cu.reportError("Error detecting Chrome profiles: " + e);
// if we weren't able to detect any profiles above, fallback to the Default profile.
if (profiles.length < 1) {
let str = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
// the default profile name is "Default"
str.data = "Default";
profiles.appendElement(str, false);
}
}
return profiles;
},
/**
* Return home page URL
*
* @return home page URL
*/
get sourceHomePageURL()
{
try {
if (this._homepageURL)
return this._homepageURL;
if (!this._paths.prefs)
this.getMigrateData(this._sourceProfile, false);
// XXX reading and parsing JSON is synchronous.
let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
file.initWithPath(this._paths.prefs);
let fstream = Cc[FILE_INPUT_STREAM_CID].
createInstance(Ci.nsIFileInputStream);
fstream.init(file, -1, 0, 0);
this._homepageURL = JSON.parse(
NetUtil.readInputStreamToString(fstream, fstream.available(),
{ charset: "UTF-8" })).homepage;
return this._homepageURL;
} catch (e) {
Cu.reportError(e);
}
return "";
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIBrowserProfileMigrator
]),
classDescription: "Chrome Profile Migrator",
contractID: "@mozilla.org/profile/migrator;1?app=browser&type=chrome",
classID: Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}")
};
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}");
const NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeProfileMigrator]);

View File

@ -59,7 +59,7 @@ ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += nsSafariProfileMigrator.cpp \
nsBrowserProfileMigratorUtils.cpp \
$(NULL)
endif
endif
EXTRA_PP_COMPONENTS = \
ProfileMigrator.js \
@ -71,5 +71,9 @@ EXTRA_COMPONENTS = \
BrowserProfileMigrators.manifest \
$(NULL)
EXTRA_PP_JS_MODULES = \
MigrationUtils.jsm \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,453 @@
/* This Source Code is subject to the terms of the Mozilla Public License
* version 2.0 (the "License"). You can obtain a copy of the License at
* http://mozilla.org/MPL/2.0/. */
"use strict";
let EXPORTED_SYMBOLS = ["MigrationUtils", "MigratorPrototype"];
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cc = Components.classes;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Dict",
"resource://gre/modules/Dict.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
let gMigrators = null;
let gProfileStartup = null;
/**
* Shared prototype for migrators, implementing nsIBrowserProfileMigrator.
*
* To implement a migrator:
* 1. Import this module.
* 2. Create the prototype for the migrator, extending MigratorPrototype.
* Namely: MosaicMigrator.prototype = Object.create(MigratorPrototype);
* 3. Set classDescription, contractID and classID for your migrator, and set
* NSGetFactory appropriately.
* 4. If the migrator supports multiple profiles, override the sourceProfiles
* Here we default for single-profile migrator.
* 5. Implement getResources(aProfile) (see below).
* 6. If the migrator supports reading the home page of the source browser,
* override |sourceHomePageURL| getter.
* 7. For startup-only migrators, override |startupOnlyMigrator|.
*/
let MigratorPrototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserProfileMigrator]),
/**
* OVERRIDE IF AND ONLY IF the source supports multiple profiles.
*
* Returns array of profiles (by names) from which data may be imported.
*
* Only profiles from which data can be imported should be listed. Otherwise
* the behavior of the migration wizard isn't well-defined.
*
* For a single-profile source (e.g. safari, ie), this returns null,
* and not an empty array. That is the default implementation.
*/
get sourceProfiles() null,
/**
* MUST BE OVERRIDDEN.
*
* Returns an array of "migration resources" objects for the given profile,
* or for the "default" profile, if the migrator does not support multiple
* profiles.
*
* Each migration resource should provide:
* - a |type| getter, retunring any of the migration types (see
* nsIBrowserProfileMigrator).
*
* - a |migrate| method, taking a single argument, aCallback(bool success),
* for migrating the data for this resource. It may do its job
* synchronously or asynchronously. Either way, it must call
* aCallback(bool aSuccess) when it's done. In the case of an exception
* thrown from |migrate|, it's taken as if aCallback(false) is called.
*
* Note: In the case of a simple asynchronous implementation, you may find
* MigrationUtils.wrapMigrateFunction handy for handling aCallback easily.
*
* For each migration type listed in nsIBrowserProfileMigrator, multiple
* migration resources may be provided. This practice is useful when the
* data for a certain migration type is independently stored in few
* locations. For example, the mac version of Safari stores its "reading list"
* bookmarks in a separate property list.
*
* Note that the importation of a particular migration type is reported as
* successful if _any_ of its resources succeeded to import (that is, called,
* |aCallback(true)|). However, completion-status for a particular migration
* type is reported to the UI only once all of its migrators have called
* aCallback.
*
* @note The returned array should only include resources from which data
* can be imported. So, for example, before adding a resource for the
* BOOKMARKS migration type, you should check if you should check that the
* bookmarks file exists.
*
* @param aProfile
* The profile from which data may be imported, or an empty string
* in the case of a single-profile migrator.
* In the case of multiple-profiles migrator, it is guaranteed that
* aProfile is a value returned by the sourceProfiles getter (see
* above).
*/
getResources: function MP_getResources(aProfile) {
throw new Error("getResources must be overridden");
},
/**
* OVERRIDE IF AND ONLY IF the migrator is a startup-only migrator (For now,
* that is just the Firefox migrator, see bug 737381). Default: false.
*
* Startup-only migrators are different in two ways:
* - they may only be used during startup.
* - the user-profile is half baked during migration. The folder exists,
* but it's only accessible through MigrationUtils.profileStartup.
* The migrator can call MigrationUtils.profileStartup.doStartup
* at any point in order to initialize the profile.
*/
get startupOnlyMigrator() false,
/**
* OVERRIDE IF AND ONLY IF your migrator supports importing the homepage.
* @see nsIBrowserProfileMigrator
*/
get sourceHomePageURL() "",
/**
* DO NOT OVERRIDE - After deCOMing migration, the UI will just call
* getResources.
*
* @see nsIBrowserProfileMigrator
*/
getMigrateData: function MP_getMigrateData(aProfile) {
let types = [r.type for each (r in this._getMaybeCachedResources(aProfile))];
return types.reduce(function(a, b) a |= b, 0);
},
/**
* DO NOT OVERRIDE - After deCOMing migration, the UI will just call
* migrate for each resource.
*
* @see nsIBrowserProfileMigrator
*/
migrate: function MP_migrate(aItems, aStartup, aProfile) {
// Not using aStartup because it's going away soon.
if (MigrationUtils.isStartupMigration && !this.startupOnlyMigrator)
MigrationUtils.profileStartup.doStartup();
let resources = this._getMaybeCachedResources(aProfile);
if (resources.length == 0)
throw new Error("migrate called for a non-existent source");
if (aItems != Ci.nsIBrowserProfileMigrator.ALL)
resources = [r for each (r in resources) if (aItems & r.type)];
// TODO: use Map (for the items) and Set (for the resources)
// once they are iterable.
let resourcesGroupedByItems = new Dict();
resources.forEach(function(resource) {
if (resourcesGroupedByItems.has(resource.type))
resourcesGroupedByItems.get(resource.type).push(resource);
else
resourcesGroupedByItems.set(resource.type, [resource]);
});
if (resourcesGroupedByItems.count == 0)
throw new Error("No items to import");
let notify = function(aMsg, aItemType) {
Services.obs.notifyObservers(null, aMsg, aItemType);
}
notify("Migration:Started");
resourcesGroupedByItems.listkeys().forEach(function(migrationType) {
let migrationTypeA = migrationType;
let itemResources = resourcesGroupedByItems.get(migrationType);
notify("Migration:ItemBeforeMigrate", migrationType);
let itemSuccess = false;
itemResources.forEach(function(resource) {
let resourceDone = function(aSuccess) {
let resourceIndex = itemResources.indexOf(resource);
if (resourceIndex != -1) {
itemResources.splice(resourceIndex, 1);
itemSuccess |= aSuccess;
if (itemResources.length == 0) {
resourcesGroupedByItems.del(migrationType);
notify(itemSuccess ?
"Migration:ItemAfterMigrate" : "Migration:ItemError",
migrationType);
if (resourcesGroupedByItems.count == 0)
notify("Migration:Ended");
}
}
};
Services.tm.mainThread.dispatch(function() {
// If migrate throws, an error occurred, and the callback
// (itemMayBeDone) might haven't been called.
try {
resource.migrate(resourceDone);
}
catch(ex) {
Cu.reportError(ex);
resourceDone(false);
}
}, Ci.nsIThread.DISPATCH_NORMAL);
});
});
},
/**
* DO NOT OVERRIDE - After deCOMing migration, this code
* won't be part of the migrator itself.
*
* @see nsIBrowserProfileMigrator
*/
get sourceExists() {
if (this.startupOnlyMigrator && !MigrationUtils.isStartupMigration)
return false;
// For a single-profile source, check if any data is available.
// For multiple-profiles source, make sure that at least one
// profile is available.
let profiles = this.sourceProfiles;
return (!profiles && this.getResources("")) ||
(profiles && profiles.length > 0);
},
/*** PRIVATE STUFF - DO NOT OVERRIDE ***/
_getMaybeCachedResources: function PMB__getMaybeCachedResources(aProfile) {
if (this._resourcesByProfile) {
if (aProfile in this._resourcesByProfile)
return this._resourcesByProfile[aProfile];
}
else {
this._resourcesByProfile = { };
}
return this._resourcesByProfile[aProfile] = this.getResources(aProfile);
}
};
let MigrationUtils = Object.freeze({
/**
* Helper for implementing simple asynchronous cases of migration resources'
* |migrate(aCallback)| (see MigratorPrototype). If your |migrate| method
* just waits for some file to be read, for example, and then migrates
* everything right away, you can wrap the async-function with this helper
* and not worry about notifying the callback.
*
* For example, instead of writing:
* setTimeout(function() {
* try {
* ....
* aCallback(true);
* }
* catch() {
* aCallback(false);
* }
* }, 0);
*
* You may write:
* setTimeout(MigrationUtils.wrapMigrateFunction(function() {
* if (importingFromMosaic)
* throw Cr.NS_ERROR_UNEXPECTED;
* }, aCallback), 0);
*
* ... and aCallback will be called with aSuccess=false when importing
* from Mosaic, or with aSuccess=true otherwise.
*
* @param aFunction
* the function that will be called sometime later. If aFunction
* throws when it's called, aCallback(false) is called, otherwise
* aCallback(true) is called.
* @param aCallback
* the callback function passed to |migrate|.
* @return the wrapped function.
*/
wrapMigrateFunction: function MU_wrapMigrateFunction(aFunction, aCallback) {
return function() {
let success = false;
try {
aFunction.apply(null, arguments);
success = true;
}
catch(ex) {
Cu.reportError(ex);
}
// Do not change this to call aCallback directly in try try & catch
// blocks, because if aCallback throws, we may end up calling aCallback
// twice.
aCallback(success);
}
},
/**
* Helper for creating a folder for imported bookmarks from a particular
* migration source. The folder is created at the end of the given folder.
*
* @param aSourceNameStr
* the source name (first letter capitalized). This is used
* for reading the localized source name from the migration
* bundle (e.g. if aSourceNameStr is Mosaic, this will try to read
* sourceNameMosaic from the migration bundle).
* @param aParentId
* the item-id of the folder in which the new folder should be
* created.
* @return the item-id of the new folder.
*/
createImportedBookmarksFolder:
function MU_createImportedBookmarksFolder(aSourceNameStr, aParentId) {
let bundle = Services.strings.createBundle(
"chrome://browser/locale/migration/migration.properties");
let sourceName = bundle.GetStringFromName("sourceName" + aSourceNameStr);
let folderName = bundle.formatStringFromName("importedBookmarksFolder",
[sourceName], 1);
return PlacesUtils.bookmarks.createFolder(
aParentId, folderName, PlacesUtils.bookmarks.DEFAULT_INDEX);
},
get _migrators() gMigrators ? gMigrators : gMigrators = new Dict(),
/*
* Returns the migrator for the given source, if any data is available
* for this source, or null otherwise.
*
* @param aKey internal name of the migration source.
* Supported values: ie (windows),
* safari (mac/windows),
* chrome (mac/windows/linux).
*
* If null is returned, either no data can be imported
* for the given migrator, or aMigratorKey is invalid (e.g. ie on mac,
* or mosaic everywhere). This method should be used rather than direct
* getService for future compatibility (see bug 718280).
*
* @return profile migrator implementing nsIBrowserProfileMigrator, if it can
* import any data, null otherwise.
*/
getMigrator: function MU_getMigrator(aKey) {
let migrator = null;
if (this._migrators.has(aKey)) {
migrator = this._migrators.get(aKey);
}
else {
try {
migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=" +
aKey].createInstance(Ci.nsIBrowserProfileMigrator);
}
catch(ex) {
Cu.reportError("Could not get migrator '" + aKey + "' (" + ex + ")");
}
this._migrators.set(aKey, migrator);
}
return migrator && migrator.sourceExists ? migrator : null;
},
// Whether or not we're in the process of startup migration
get isStartupMigration() gProfileStartup != null,
/**
* In the case of startup migration, this is set to the nsIProfileStartup
* instance passed to ProfileMigrator's migrate.
*
* @see showMigrationWizard
*/
get profileStartup() gProfileStartup,
/**
* Start the migration wizard.
*
* Supplying a migrator will result in automatic migration. You should
* make sure that the migrator for this key exists before passing
* it (use getMigrator).
*
* @param [optional] aOpener
* the window to which the wizard window is associated.
* @param [optional] aProfileStartup
* @see nsIProfileMigrator and nsIProfileStartup. This is used
* for initializing the profile during migration and for indicating
* startup-migration
* @param [optional] aKey
* A migration-source internal name (@see getMigrator) for an existent
* source. This is ignored if aProfileStartup is not set, and required
* if it is.
* @param [optional] aSkipImportSourcePage
* Whether or not to skip the migration-source selection page in the
* wizard (ignored if aProfileStartup is not set). Default: false.
*
* @throws if aKey is set to an invalid or non-existent migration source.
* @note aParentWindow is ignored on OS X, because wizards are not modal
* on this platform.
*/
showMigrationWizard:
function MU_showMigrationWizard(aOpener, aProfileStartup, aMigratorKey,
aSkipImportSourcePage) {
let features = "chrome,dialog,modal,centerscreen,titlebar";
let params = null;
if (!aProfileStartup) {
#ifdef XP_MACOSX
let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard");
if (win) {
win.focus();
return;
}
features = "centerscreen,chrome,resizable=no";
#endif
}
else {
if (!aMigratorKey)
throw new Error("aMigratorKey must be set for startup migration");
let migrator = this.getMigrator(aMigratorKey);
if (!migrator) {
throw new Error("startMigration was asked to open auto-migrate from a non-existent source: " +
aMigratorKey);
}
else {
gProfileStartup = aProfileStartup;
}
// By opening the wizard with a supplied migrator, it will
// automatically migrate from it.
params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
let keyCSTR = Cc["@mozilla.org/supports-cstring;1"].
createInstance(Ci.nsISupportsCString);
keyCSTR.data = aMigratorKey;
let skipImportSourcePageBool = Cc["@mozilla.org/supports-PRBool;1"].
createInstance(Ci.nsISupportsPRBool);
params.appendElement(keyCSTR, false);
params.appendElement(migrator, false);
params.appendElement(aProfileStartup, false);
if (aSkipImportSourcePage === true) {
let wrappedBool = Cc["@mozilla.org/supports-PRBool;1"].
createInstance(Ci.nsISupportsPRBool);
wrappedBool.data = true;
params.appendElement(wrappedBool);
}
}
Services.ww.openWindow(null,
"chrome://browser/content/migration/migration.xul",
"_blank",
features,
params);
},
/**
* Cleans up references to migrators and nsIProfileInstance instances.
*/
finishMigration: function MU_finishMigration() {
gMigrators = null;
gProfileStartup = null;
}
});

View File

@ -9,65 +9,34 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/MigrationUtils.jsm");
function ProfileMigrator() {
}
ProfileMigrator.prototype = {
migrate: function PM_migrate(aStartup, aKey) {
// By opening the wizard with a supplied migrator, it will automatically
// migrate from it.
let key = null, migrator = null;
let skipImportSourcePage = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
if (aKey) {
key = aKey;
migrator = this._getMigratorIfSourceExists(key);
if (!migrator) {
let key = aKey;
let skipSourcePage = false;
if (key.length > 0) {
if (!MigrationUtils.getMigrator(key)) {
Cu.reportError("Invalid migrator key specified or source does not exist.");
return;
return;
}
// If the migrator was passed to us from the caller, use that migrator
// and skip the import source page.
skipImportSourcePage.data = true;
} else {
[key, migrator] = this._getDefaultMigrator();
skipSourcePage = true;
}
else {
key = this._getDefaultMigrator();
}
if (!key)
return;
return;
let params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
params.appendElement(this._toCString(key), false);
params.appendElement(migrator, false);
params.appendElement(aStartup, false);
params.appendElement(skipImportSourcePage, false);
Services.ww.openWindow(null,
"chrome://browser/content/migration/migration.xul",
"_blank",
"chrome,dialog,modal,centerscreen,titlebar",
params);
},
_toCString: function PM__toCString(aStr) {
let cstr = Cc["@mozilla.org/supports-cstring;1"].
createInstance(Ci.nsISupportsCString);
cstr.data = aStr;
return cstr;
},
_getMigratorIfSourceExists: function PM__getMigratorIfSourceExists(aKey) {
try {
let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey;
let migrator = Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator);
if (migrator.sourceExists)
return migrator;
} catch (ex) {
Cu.reportError("Could not get migrator: " + ex);
}
return null;
MigrationUtils.showMigrationWizard(null, aStartup, key, skipSourcePage);
},
// We don't yet support checking for the default browser on all platforms,
@ -144,12 +113,12 @@ ProfileMigrator.prototype = {
migratorsOrdered.sort(function(a, b) b == defaultBrowser ? 1 : 0);
for (let i = 0; i < migratorsOrdered.length; i++) {
let migrator = this._getMigratorIfSourceExists(migratorsOrdered[i]);
let migrator = MigrationUtils.getMigrator(migratorsOrdered[i]);
if (migrator)
return [migratorsOrdered[i], migrator];
return migratorsOrdered[i];
}
return ["", null];
return "";
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProfileMigrator]),

View File

@ -98,6 +98,7 @@
#include "nsUnicharUtils.h"
#include "nsIWindowsRegKey.h"
#include "nsISupportsPrimitives.h"
#include "jsapi.h"
#define kNotFound -1
@ -480,16 +481,9 @@ nsIEProfileMigrator::GetSourceExists(bool* aResult)
}
NS_IMETHODIMP
nsIEProfileMigrator::GetSourceHasMultipleProfiles(bool* aResult)
nsIEProfileMigrator::GetSourceProfiles(JS::Value* aResult)
{
*aResult = false;
return NS_OK;
}
NS_IMETHODIMP
nsIEProfileMigrator::GetSourceProfiles(nsIArray** aResult)
{
*aResult = nsnull;
*aResult = JSVAL_NULL;
return NS_OK;
}

View File

@ -64,6 +64,7 @@
#include "nsToolkitCompsCID.h"
#include "nsNetUtil.h"
#include "nsTArray.h"
#include "jsapi.h"
#include "mozilla/Util.h"
@ -197,17 +198,9 @@ nsSafariProfileMigrator::GetSourceExists(bool* aResult)
}
NS_IMETHODIMP
nsSafariProfileMigrator::GetSourceHasMultipleProfiles(bool* aResult)
nsSafariProfileMigrator::GetSourceProfiles(JS::Value* aResult)
{
// Safari only has one profile per-user.
*aResult = false;
return NS_OK;
}
NS_IMETHODIMP
nsSafariProfileMigrator::GetSourceProfiles(nsIArray** aResult)
{
*aResult = nsnull;
*aResult = JSVAL_NULL;
return NS_OK;
}

View File

@ -40,6 +40,8 @@
*
* ***** END LICENSE BLOCK ***** */
Components.utils.import("resource://gre/modules/MigrationUtils.jsm");
var PlacesOrganizer = {
_places: null,
_content: null,
@ -385,20 +387,7 @@ var PlacesOrganizer = {
* cookies, history, preferences, and bookmarks.
*/
importFromBrowser: function PO_importFromBrowser() {
#ifdef XP_MACOSX
// On Mac, the window is not modal
let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard");
if (win) {
win.focus();
return;
}
let features = "centerscreen,chrome,resizable=no";
#else
let features = "modal,centerscreen,chrome,resizable=no";
#endif
window.openDialog("chrome://browser/content/migration/migration.xul",
"migration", features);
MigrationUtils.showMigrationWizard(window);
},
/**

View File

@ -2206,6 +2206,10 @@ XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "history",
"@mozilla.org/browser/nav-history-service;1",
"nsINavHistoryService");
XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "asyncHistory",
"@mozilla.org/browser/history;1",
"mozIAsyncHistory");
XPCOMUtils.defineLazyGetter(PlacesUtils, "bhistory", function() {
return PlacesUtils.history.QueryInterface(Ci.nsIBrowserHistory);
});