Bug 1265368 - enable resetting non-default profiles, r?MattN
MozReview-Commit-ID: KpW9JgghFkn
--- a/browser/components/migration/FirefoxProfileMigrator.js
+++ b/browser/components/migration/FirefoxProfileMigrator.js
@@ -127,17 +127,17 @@ FirefoxProfileMigrator.prototype._getRes
let bookmarksBackups = getFileResource(types.OTHERDATA,
[PlacesBackups.profileRelativeFolderPath]);
let dictionary = getFileResource(types.OTHERDATA, ["persdict.dat"]);
let sessionCheckpoints = this._getFileObject(sourceProfileDir, "sessionCheckpoints.json");
let sessionFile = this._getFileObject(sourceProfileDir, "sessionstore.js");
let session;
if (sessionFile) {
- session = aProfile ? getFileResource(types.SESSION, ["sessionstore.js"]) : {
+ session = {
type: types.SESSION,
migrate: function(aCallback) {
sessionCheckpoints.copyTo(currentProfileDir, "sessionCheckpoints.json");
let newSessionFile = currentProfileDir.clone();
newSessionFile.append("sessionstore.js");
let migrationPromise = SessionMigration.migrate(sessionFile.path, newSessionFile.path);
migrationPromise.then(function() {
let buildID = Services.appinfo.platformBuildID;
@@ -153,17 +153,17 @@ FirefoxProfileMigrator.prototype._getRes
let newPrefsFile = currentProfileDir.clone();
newPrefsFile.append("prefs.js");
Services.prefs.savePrefFile(newPrefsFile);
aCallback(true);
}, function() {
aCallback(false);
});
}
- }
+ };
}
// Telemetry related migrations.
let times = {
name: "times", // name is used only by tests.
type: types.OTHERDATA,
migrate: aCallback => {
let file = this._getFileObject(sourceProfileDir, "times.json");
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -547,16 +547,17 @@ this.MigrationUtils = Object.freeze({
* This is passed as-is for the params argument of
* nsIWindowWatcher.openWindow. The array elements we expect are, in
* order:
* - {Number} migration entry point constant (see below)
* - {String} source browser identifier
* - {nsIBrowserProfileMigrator} actual migrator object
* - {Boolean} whether this is a startup migration
* - {Boolean} whether to skip the 'source' page
+ * - {String} an identifier for the profile to use when migrating
* NB: If you add new consumers, please add a migration entry point
* constant below, and specify at least the first element of the array
* (the migration entry point for purposes of telemetry).
*/
showMigrationWizard:
function MU_showMigrationWizard(aOpener, aParams) {
let features = "chrome,dialog,modal,centerscreen,titlebar,resizable=no";
if (AppConstants.platform == "macosx" && !this.isStartupMigration) {
@@ -629,21 +630,23 @@ this.MigrationUtils = Object.freeze({
* @param [optional] aMigratorKey
* If set, the migration wizard will import from the corresponding
* migrator, bypassing the source-selection page. Otherwise, the
* source-selection page will be displayed, either with the default
* browser selected, if it could be detected and if there is a
* migrator for it, or with the first option selected as a fallback
* (The first option is hardcoded to be the most common browser for
* the OS we run on. See migration.xul).
+ * @param [optional] aProfileToMigrate
+ * If set, the migration wizard will import from the profile indicated.
* @throws if aMigratorKey is invalid or if it points to a non-existent
* source.
*/
startupMigration:
- function MU_startupMigrator(aProfileStartup, aMigratorKey) {
+ function MU_startupMigrator(aProfileStartup, aMigratorKey, aProfileToMigrate) {
if (!aProfileStartup) {
throw new Error("an profile-startup instance is required for startup-migration");
}
gProfileStartup = aProfileStartup;
let skipSourcePage = false, migrator = null, migratorKey = "";
if (aMigratorKey) {
migrator = this.getMigrator(aMigratorKey);
@@ -683,17 +686,18 @@ this.MigrationUtils = Object.freeze({
migrationEntryPoint = this.MIGRATION_ENTRYPOINT_FXREFRESH;
}
let params = [
migrationEntryPoint,
migratorKey,
migrator,
aProfileStartup,
- skipSourcePage
+ skipSourcePage,
+ aProfileToMigrate,
];
this.showMigrationWizard(null, params);
},
/**
* Cleans up references to migrators and nsIProfileInstance instances.
*/
finishMigration: function MU_finishMigration() {
--- a/browser/components/migration/content/migration.js
+++ b/browser/components/migration/content/migration.js
@@ -35,16 +35,20 @@ var MigrationWizard = {
let entryPointId = args[0] || MigrationUtils.MIGRATION_ENTRYPOINT_UNKNOWN;
Services.telemetry.getHistogramById("FX_MIGRATION_ENTRY_POINT").add(entryPointId);
if (args.length > 1) {
this._source = args[1];
this._migrator = args[2] instanceof kIMig ? args[2] : null;
this._autoMigrate = args[3].QueryInterface(kIPStartup);
this._skipImportSourcePage = args[4];
+ if (this._migrator && args[5]) {
+ let sourceProfiles = this._migrator.sourceProfiles;
+ this._selectedProfile = sourceProfiles.find(profile => profile.id == args[5]);
+ }
if (this._autoMigrate) {
// Show the "nothing" option in the automigrate case to provide an
// easily identifiable way to avoid migration and create a new profile.
document.getElementById("nothing").hidden = false;
}
}
--- a/toolkit/modules/ResetProfile.jsm
+++ b/toolkit/modules/ResetProfile.jsm
@@ -16,27 +16,32 @@ const MOZ_BUILD_APP = AppConstants.MOZ_B
this.ResetProfile = {
/**
* Check if reset is supported for the currently running profile.
*
* @return boolean whether reset is supported.
*/
resetSupported: function() {
+ // Reset is only supported if the self-migrator used for reset exists.
+ let migrator = "@mozilla.org/profile/migrator;1?app=" + MOZ_BUILD_APP +
+ "&type=" + MOZ_APP_NAME;
+ if (!(migrator in Cc)) {
+ return false;
+ }
+ // We also need to be using a profile the profile manager knows about.
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].
getService(Ci.nsIToolkitProfileService);
let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-
- // Reset is only supported for the default profile if the self-migrator used for reset exists.
- try {
- return currentProfileDir.equals(profileService.selectedProfile.rootDir) &&
- ("@mozilla.org/profile/migrator;1?app=" + MOZ_BUILD_APP + "&type=" + MOZ_APP_NAME in Cc);
- } catch (e) {
- // Catch exception when there is no selected profile.
- Cu.reportError(e);
+ let profileEnumerator = profileService.profiles;
+ while (profileEnumerator.hasMoreElements()) {
+ let profile = profileEnumerator.getNext().QueryInterface(Ci.nsIToolkitProfile);
+ if (profile.rootDir && profile.rootDir.equals(currentProfileDir)) {
+ return true;
+ }
}
return false;
},
/**
* Ask the user if they wish to restart the application to reset the profile.
*/
openConfirmationDialog: function(window) {
--- a/toolkit/profile/nsIProfileMigrator.idl
+++ b/toolkit/profile/nsIProfileMigrator.idl
@@ -51,17 +51,19 @@ interface nsIProfileMigrator : nsISuppor
* profile. To figure out the directory of the "current" profile, use
* aStartup.directory.
*
* If your migrator needs to access services that use the profile (to
* set profile prefs or bookmarks, for example), use aStartup.doStartup.
*
* @param aStartup nsIProfileStartup object to use during migration.
* @param aKey optional key of a migrator to use to skip source selection.
+ * @param aProfileName optional name of the profile to use for migration.
*
* @note The startup code ignores COM exceptions thrown from this method.
*/
- void migrate(in nsIProfileStartup aStartup, in ACString aKey);
+ void migrate(in nsIProfileStartup aStartup, in ACString aKey,
+ [optional] in ACString aProfileName);
};
%{C++
#define NS_PROFILEMIGRATOR_CONTRACTID "@mozilla.org/toolkit/profile-migrator;1"
%}
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -2205,48 +2205,26 @@ ShowProfileManager(nsIToolkitProfileServ
aProfileSvc->GetStartOffline(&offline);
if (offline) {
SaveToEnv("XRE_START_OFFLINE=1");
}
return LaunchChild(aNative);
}
-static nsresult
-GetCurrentProfileIsDefault(nsIToolkitProfileService* aProfileSvc,
- nsIFile* aCurrentProfileRoot, bool *aResult)
-{
- nsresult rv;
- // Check that the profile to reset is the default since reset and the associated migration are
- // only supported in that case.
- nsCOMPtr<nsIToolkitProfile> selectedProfile;
- nsCOMPtr<nsIFile> selectedProfileRoot;
- rv = aProfileSvc->GetSelectedProfile(getter_AddRefs(selectedProfile));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = selectedProfile->GetRootDir(getter_AddRefs(selectedProfileRoot));
- NS_ENSURE_SUCCESS(rv, rv);
-
- bool currentIsSelected;
- rv = aCurrentProfileRoot->Equals(selectedProfileRoot, ¤tIsSelected);
-
- *aResult = currentIsSelected;
- return rv;
-}
-
/**
* Set the currently running profile as the default/selected one.
*
* @param aCurrentProfileRoot The root directory of the current profile.
* @return an error if aCurrentProfileRoot is not found or the profile could not
* be set as the default.
*/
static nsresult
-SetCurrentProfileAsDefault(nsIToolkitProfileService* aProfileSvc,
- nsIFile* aCurrentProfileRoot)
+SetCurrentProfileAsSelected(nsIToolkitProfileService* aProfileSvc,
+ nsIFile* aCurrentProfileRoot)
{
NS_ENSURE_ARG_POINTER(aProfileSvc);
nsCOMPtr<nsISimpleEnumerator> profiles;
nsresult rv = aProfileSvc->GetProfiles(getter_AddRefs(profiles));
if (NS_FAILED(rv))
return rv;
@@ -2266,16 +2244,17 @@ SetCurrentProfileAsDefault(nsIToolkitPro
}
rv = profiles->GetNext(getter_AddRefs(supports));
}
return rv;
}
static bool gDoMigration = false;
static bool gDoProfileReset = false;
+static nsAutoCString gResetOldProfileName;
// Pick a profile. We need to end up with a profile lock.
//
// 1) check for --profile <path>
// 2) check for -P <name>
// 3) check for --ProfileManager
// 4) use the default profile, if there is one
// 5) if there are *no* profiles, set up profile-migration
@@ -2328,37 +2307,30 @@ SelectProfile(nsIProfileLock* *aResult,
if (lf) {
nsCOMPtr<nsIFile> localDir =
GetFileFromEnv("XRE_PROFILE_LOCAL_PATH");
if (!localDir) {
localDir = lf;
}
arg = PR_GetEnv("XRE_PROFILE_NAME");
- if (arg && *arg && aProfileName)
+ if (arg && *arg && aProfileName) {
aProfileName->Assign(nsDependentCString(arg));
+ if (gDoProfileReset) {
+ gResetOldProfileName.Assign(*aProfileName);
+ }
+ }
// Clear out flags that we handled (or should have handled!) last startup.
const char *dummy;
CheckArg("p", false, &dummy);
CheckArg("profile", false, &dummy);
CheckArg("profilemanager");
if (gDoProfileReset) {
- // Check that the profile to reset is the default since reset and migration are only
- // supported in that case.
- bool currentIsSelected;
- rv = GetCurrentProfileIsDefault(aProfileSvc, lf, ¤tIsSelected);
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (!currentIsSelected) {
- NS_WARNING("Profile reset is only supported for the default profile.");
- gDoProfileReset = gDoMigration = false;
- }
-
// If we're resetting a profile, create a new one and use it to startup.
nsCOMPtr<nsIToolkitProfile> newProfile;
rv = CreateResetProfile(aProfileSvc, getter_AddRefs(newProfile));
if (NS_SUCCEEDED(rv)) {
rv = newProfile->GetRootDir(getter_AddRefs(lf));
NS_ENSURE_SUCCESS(rv, rv);
SaveFileToEnv("XRE_PROFILE_PATH", lf);
@@ -2381,17 +2353,17 @@ SelectProfile(nsIProfileLock* *aResult,
ar = CheckArg("profile", true, &arg);
if (ar == ARG_BAD) {
PR_fprintf(PR_STDERR, "Error: argument --profile requires a path\n");
return NS_ERROR_FAILURE;
}
if (ar) {
if (gDoProfileReset) {
- NS_WARNING("Profile reset is only supported for the default profile.");
+ NS_WARNING("Profile reset is not supported in conjunction with --profile.");
gDoProfileReset = false;
}
nsCOMPtr<nsIFile> lf;
rv = XRE_GetFileFromPath(arg, getter_AddRefs(lf));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIProfileUnlocker> unlocker;
@@ -2486,17 +2458,17 @@ SelectProfile(nsIProfileLock* *aResult,
PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument --osint is specified\n");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIToolkitProfile> profile;
rv = aProfileSvc->GetProfileByName(nsDependentCString(arg),
getter_AddRefs(profile));
if (NS_SUCCEEDED(rv)) {
if (gDoProfileReset) {
- NS_WARNING("Profile reset is only supported for the default profile.");
+ NS_WARNING("Profile reset is not supported in conjunction with -p .");
gDoProfileReset = false;
}
nsCOMPtr<nsIProfileUnlocker> unlocker;
rv = profile->Lock(getter_AddRefs(unlocker), aResult);
if (NS_SUCCEEDED(rv)) {
if (aProfileName)
aProfileName->Assign(nsDependentCString(arg));
@@ -2583,20 +2555,28 @@ SelectProfile(nsIProfileLock* *aResult,
nsCOMPtr<nsIProfileUnlocker> unlocker;
rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock);
if (NS_FAILED(rv))
return ProfileLockedDialog(profile, unlocker, aNative, &tempProfileLock);
}
nsCOMPtr<nsIToolkitProfile> newProfile;
rv = CreateResetProfile(aProfileSvc, getter_AddRefs(newProfile));
- if (NS_SUCCEEDED(rv))
- profile = newProfile;
- else
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to create a profile to reset to.");
gDoProfileReset = false;
+ } else {
+ nsresult gotName = profile->GetName(gResetOldProfileName);
+ if (NS_SUCCEEDED(gotName)) {
+ profile = newProfile;
+ } else {
+ gResetOldProfileName.Truncate(0);
+ gDoProfileReset = false;
+ }
+ }
}
// If you close Firefox and very quickly reopen it, the old Firefox may
// still be closing down. Rather than immediately showing the
// "Firefox is running but is not responding" message, we spend a few
// seconds retrying first.
static const int kLockRetrySeconds = 5;
@@ -4172,48 +4152,66 @@ XREMain::XRE_mainRun()
if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') {
gDoMigration = false;
}
}
}
}
{
- nsCOMPtr<nsIToolkitProfile> selectedProfile;
+ nsCOMPtr<nsIToolkitProfile> profileBeingReset;
+ bool profileWasSelected = false;
if (gDoProfileReset) {
- // At this point we can be sure that profile reset is happening on the default profile.
- rv = mProfileSvc->GetSelectedProfile(getter_AddRefs(selectedProfile));
- if (NS_FAILED(rv)) {
+ if (gResetOldProfileName.IsEmpty()) {
+ NS_WARNING("Not resetting profile as the profile has no name.");
gDoProfileReset = false;
- return NS_ERROR_FAILURE;
+ } else {
+ rv = mProfileSvc->GetProfileByName(gResetOldProfileName,
+ getter_AddRefs(profileBeingReset));
+ if (NS_FAILED(rv)) {
+ gDoProfileReset = false;
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIToolkitProfile> defaultProfile;
+ // This can fail if there is no default profile.
+ // That shouldn't stop reset from proceeding.
+ nsresult gotSelected = mProfileSvc->GetSelectedProfile(getter_AddRefs(defaultProfile));
+ if (NS_SUCCEEDED(gotSelected)) {
+ profileWasSelected = defaultProfile == profileBeingReset;
+ }
}
}
// Profile Migration
if (mAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) {
gDoMigration = false;
nsCOMPtr<nsIProfileMigrator> pm(do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID));
if (pm) {
nsAutoCString aKey;
if (gDoProfileReset) {
// Automatically migrate from the current application if we just
// reset the profile.
aKey = MOZ_APP_NAME;
}
- pm->Migrate(&mDirProvider, aKey);
+ pm->Migrate(&mDirProvider, aKey, gResetOldProfileName);
}
}
if (gDoProfileReset) {
- nsresult backupCreated = ProfileResetCleanup(selectedProfile);
+ nsresult backupCreated = ProfileResetCleanup(profileBeingReset);
if (NS_FAILED(backupCreated)) NS_WARNING("Could not cleanup the profile that was reset");
- // Set the new profile as the default after we're done cleaning up the old default.
- rv = SetCurrentProfileAsDefault(mProfileSvc, mProfD);
- if (NS_FAILED(rv)) NS_WARNING("Could not set current profile as the default");
+ // Set the new profile as the default after we're done cleaning up the old profile,
+ // iff that profile was already the default
+ if (profileWasSelected) {
+ //XXXgijs this is actually "broken" - see bug 1270615
+ rv = SetCurrentProfileAsSelected(mProfileSvc, mProfD);
+ if (NS_FAILED(rv)) NS_WARNING("Could not set current profile as the default");
+ }
}
}
mDirProvider.DoStartup();
OverrideDefaultLocaleIfNeeded();
#ifdef MOZ_CRASHREPORTER