Bug 731047 - Clean up old profile after Firefox profile reset. r=bsmedberg, ui-r=limi
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Mon, 19 Mar 2012 17:01:49 -0700
changeset 95058 51eeffc5a31d1beaf1a7a0362f3678ca5da224b7
parent 95057 6261d5c12c7cc36652fcf5ee3859d6ddb393c61b
child 95059 133aa3a2ef0a1d87fb0de676b6eecf8617763e81
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbsmedberg, limi
bugs731047
milestone15.0a1
Bug 731047 - Clean up old profile after Firefox profile reset. r=bsmedberg, ui-r=limi
toolkit/content/jar.mn
toolkit/content/resetProfile.css
toolkit/content/resetProfileProgress.xul
toolkit/locales/en-US/chrome/global/resetProfile.dtd
toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties
toolkit/xre/Makefile.in
toolkit/xre/ProfileReset.cpp
toolkit/xre/ProfileReset.h
toolkit/xre/nsAppRunner.cpp
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -35,16 +35,17 @@ toolkit.jar:
 *+ content/global/findUtils.js                (findUtils.js)
    content/global/filepicker.properties       (filepicker.properties)
 *+ content/global/globalOverlay.js            (globalOverlay.js)
 +  content/global/mozilla.xhtml               (mozilla.xhtml)
 *+ content/global/nsDragAndDrop.js            (nsDragAndDrop.js)
    content/global/resetProfile.css            (resetProfile.css)
 *  content/global/resetProfile.js             (resetProfile.js)
 *  content/global/resetProfile.xul            (resetProfile.xul)
+*  content/global/resetProfileProgress.xul    (resetProfileProgress.xul)
 *  content/global/treeUtils.js                (treeUtils.js)
 *+ content/global/viewZoomOverlay.js          (viewZoomOverlay.js)
 *+ content/global/bindings/autocomplete.xml    (widgets/autocomplete.xml)
 *+ content/global/bindings/browser.xml         (widgets/browser.xml)
 *+ content/global/bindings/button.xml          (widgets/button.xml)
 *+ content/global/bindings/checkbox.xml        (widgets/checkbox.xml)
 *+ content/global/bindings/colorpicker.xml     (widgets/colorpicker.xml)
 *+ content/global/bindings/datetimepicker.xml  (widgets/datetimepicker.xml)
--- a/toolkit/content/resetProfile.css
+++ b/toolkit/content/resetProfile.css
@@ -4,8 +4,12 @@
 
 #migratedItems {
   -moz-margin-start: 1.5em;
 }
 
 #resetProfileFooter {
   font-weight: bold;
 }
+
+#resetProfileProgressDialog {
+  padding: 10px;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/content/resetProfileProgress.xul
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+# 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/.
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % resetProfileDTD SYSTEM "chrome://global/locale/resetProfile.dtd" >
+%resetProfileDTD;
+]>
+
+<?xml-stylesheet href="chrome://global/content/resetProfile.css"?>
+<?xml-stylesheet href="chrome://global/skin/"?>
+
+<window id="resetProfileProgressDialog"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="&resetProfile.dialog.title;">
+  <vbox>
+    <description>&resetProfile.cleaning.description;</description>
+    <progressmeter mode="undetermined"/>
+  </vbox>
+</window>
--- a/toolkit/locales/en-US/chrome/global/resetProfile.dtd
+++ b/toolkit/locales/en-US/chrome/global/resetProfile.dtd
@@ -6,8 +6,10 @@
 <!ENTITY resetProfile.dialog.description  "Are you sure you want to reset &brandShortName; to its initial state?">
 <!ENTITY resetProfile.dialog.items2.label "&brandShortName; will try to preserve your:">
 <!ENTITY resetProfile.dialog.footer.label "&brandShortName; will restart and everything else will be removed.">
 <!ENTITY resetProfile.dialog.button.label "Reset &brandShortName;">
 
 <!ENTITY resetProfile.title               "Reset &brandShortName; to its default state">
 <!ENTITY resetProfile.description         "If you're having major problems which you can't resolve, start fresh with only your essential information.">
 <!ENTITY resetProfile.button.label        "Reset &brandShortName;">
+
+<!ENTITY resetProfile.cleaning.description "Please wait while &brandShortName; cleans up your old data…">
--- a/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties
+++ b/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties
@@ -34,8 +34,10 @@ dontDeleteFiles=Don't Delete Files
 profileCreationFailed=Profile couldn't be created. Probably the chosen folder isn't writable.
 profileCreationFailedTitle=Profile Creation failed
 profileExists=A profile with this name already exists. Please choose another name.
 profileExistsTitle=Profile Exists
 profileFinishText=Click Finish to create this new profile.
 profileFinishTextMac=Click Done to create this new profile.
 profileMissing=Your %S profile cannot be loaded. It may be missing or inaccessible.
 profileMissingTitle=Profile Missing
+
+resetBackupDirectory=Old %S Data
--- a/toolkit/xre/Makefile.in
+++ b/toolkit/xre/Makefile.in
@@ -31,16 +31,17 @@ endif
 CPPSRCS = \
   nsAppRunner.cpp \
   nsConsoleWriter.cpp \
   nsXREDirProvider.cpp \
   nsNativeAppSupportBase.cpp \
   nsAppData.cpp \
   nsSigHandlers.cpp \
   nsEmbedFunctions.cpp \
+  ProfileReset.cpp \
   $(NULL)
 
 ifeq ($(MOZ_GL_DEFAULT_PROVIDER),GLX)
 DEFINES += -DUSE_GLX_TEST
 CPPSRCS += glxtest.cpp
 endif
 
 ifdef MOZ_INSTRUMENT_EVENT_LOOP
new file mode 100644
--- /dev/null
+++ b/toolkit/xre/ProfileReset.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsIAppStartup.h"
+#include "nsIDOMWindow.h"
+#include "nsILocalFile.h"
+#include "nsIStringBundle.h"
+#include "nsIToolkitProfile.h"
+#include "nsIWindowWatcher.h"
+
+#include "ProfileReset.h"
+
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsPrintfCString.h"
+#include "nsToolkitCompsCID.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsXREAppData.h"
+
+#include "mozilla/Services.h"
+
+extern const nsXREAppData* gAppData;
+
+static const char kProfileProperties[] =
+  "chrome://mozapps/locale/profile/profileSelection.properties";
+
+/**
+ * Creates a new profile with a timestamp in the name to use for profile reset.
+ */
+nsresult
+CreateResetProfile(nsIToolkitProfileService* aProfileSvc, nsIToolkitProfile* *aNewProfile)
+{
+  NS_ABORT_IF_FALSE(aProfileSvc, "NULL profile service");
+
+  nsCOMPtr<nsIToolkitProfile> newProfile;
+  // Make the new profile "default-" + the time in seconds since epoch for uniqueness.
+  nsCAutoString newProfileName("default-");
+  newProfileName.Append(nsPrintfCString("%lld", PR_Now() / 1000));
+  nsresult rv = aProfileSvc->CreateProfile(nsnull, // choose a default dir for us
+                                           nsnull, // choose a default dir for us
+                                           newProfileName,
+                                           getter_AddRefs(newProfile));
+  if (NS_FAILED(rv)) return rv;
+
+  rv = aProfileSvc->Flush();
+  if (NS_FAILED(rv)) return rv;
+
+  newProfile.swap(*aNewProfile);
+
+  return NS_OK;
+}
+
+/**
+ * Delete the profile directory being reset after a backup and delete the local profile directory.
+ */
+nsresult
+ProfileResetCleanup(nsIToolkitProfile* aOldProfile)
+{
+  nsresult rv;
+  nsCOMPtr<nsILocalFile> profileDir;
+  rv = aOldProfile->GetRootDir(getter_AddRefs(profileDir));
+  if (NS_FAILED(rv)) return rv;
+
+  nsCOMPtr<nsILocalFile> profileLocalDir;
+  rv = aOldProfile->GetLocalDir(getter_AddRefs(profileLocalDir));
+  if (NS_FAILED(rv)) return rv;
+
+  // Get the friendly name for the backup directory.
+  nsCOMPtr<nsIStringBundleService> sbs = mozilla::services::GetStringBundleService();
+  if (!sbs) return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIStringBundle> sb;
+  rv = sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
+  if (!sb) return NS_ERROR_FAILURE;
+
+  NS_ConvertUTF8toUTF16 appName(gAppData->name);
+  const PRUnichar* params[] = {appName.get(), appName.get()};
+
+  nsXPIDLString resetBackupDirectoryName;
+
+  static const PRUnichar* kResetBackupDirectory = NS_LITERAL_STRING("resetBackupDirectory").get();
+  rv = sb->FormatStringFromName(kResetBackupDirectory, params, 2,
+                                getter_Copies(resetBackupDirectoryName));
+
+  // Get info to copy the old root profile dir to the desktop as a backup.
+  nsCOMPtr<nsIFile> backupDest, uniqueDest;
+  rv = NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(backupDest));
+  if (NS_FAILED(rv)) {
+    // Fall back to the home directory if the desktop is not available.
+    rv = NS_GetSpecialDirectory(NS_OS_HOME_DIR, getter_AddRefs(backupDest));
+    if (NS_FAILED(rv)) return rv;
+  }
+
+  // Try to get a unique backup directory name.
+  backupDest->Clone(getter_AddRefs(uniqueDest));
+  uniqueDest->Append(resetBackupDirectoryName);
+  rv = uniqueDest->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
+  if (NS_FAILED(rv)) return rv;
+
+  nsAutoString leafName;
+  rv = uniqueDest->GetLeafName(leafName);
+  if (NS_FAILED(rv)) return rv;
+  // Delete the empty directory that CreateUnique just created.
+  rv = uniqueDest->Remove(false);
+  if (NS_FAILED(rv)) return rv;
+
+  // Show a progress window while the cleanup happens since the disk I/O can take time.
+  nsCOMPtr<nsIWindowWatcher> windowWatcher(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+  if (!windowWatcher) return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIAppStartup> appStartup(do_GetService(NS_APPSTARTUP_CONTRACTID));
+  if (!appStartup) return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIDOMWindow> progressWindow;
+  rv = windowWatcher->OpenWindow(nsnull,
+                                 kResetProgressURL,
+                                 "_blank",
+                                 "centerscreen,chrome,titlebar",
+                                 NULL,
+                                 getter_AddRefs(progressWindow));
+  if (NS_FAILED(rv)) return rv;
+
+  // Create a new thread to do the bulk of profile cleanup to stay responsive.
+  nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
+  nsCOMPtr<nsIThread> cleanupThread;
+  rv = tm->NewThread(0, 0, getter_AddRefs(cleanupThread));
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIRunnable> runnable = new ProfileResetCleanupAsyncTask(profileDir, profileLocalDir,
+                                                                      backupDest, leafName);
+    cleanupThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
+    // The result callback will shut down the worker thread.
+
+    nsIThread *thread = NS_GetCurrentThread();
+    // Wait for the cleanup thread to complete.
+    while(!gProfileResetCleanupCompleted) {
+      NS_ProcessNextEvent(thread);
+    }
+  } else {
+    gProfileResetCleanupCompleted = true;
+    NS_WARNING("Cleanup thread creation failed");
+    return rv;
+  }
+  // Close the progress window now that the cleanup thread is done.
+  progressWindow->Close();
+
+  // Delete the old profile from profiles.ini. The folder was already deleted above.
+  rv = aOldProfile->Remove(false);
+  if (NS_FAILED(rv)) NS_WARNING("Could not remove the profile");
+
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/xre/ProfileReset.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsIToolkitProfileService.h"
+
+#include "nsThreadUtils.h"
+
+static bool gProfileResetCleanupCompleted = false;
+static const char kResetProgressURL[] = "chrome://global/content/resetProfileProgress.xul";
+
+nsresult CreateResetProfile(nsIToolkitProfileService* aProfileSvc,
+                            nsIToolkitProfile* *aNewProfile);
+
+nsresult ProfileResetCleanup(nsIToolkitProfile* aOldProfile);
+
+class ProfileResetCleanupResultTask : public nsRunnable
+{
+public:
+  ProfileResetCleanupResultTask()
+    : mWorkerThread(do_GetCurrentThread())
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run() {
+    MOZ_ASSERT(NS_IsMainThread());
+    mWorkerThread->Shutdown();
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIThread> mWorkerThread;
+};
+
+class ProfileResetCleanupAsyncTask : public nsRunnable
+{
+public:
+  ProfileResetCleanupAsyncTask(nsILocalFile* aProfileDir, nsILocalFile* aProfileLocalDir,
+                               nsIFile* aDesktop, const nsAString &aLeafName)
+    : mProfileDir(aProfileDir)
+    , mProfileLocalDir(aProfileLocalDir)
+    , mDesktop(aDesktop)
+    , mLeafName(aLeafName)
+  { }
+
+/**
+ * Copy a root profile to a backup folder before deleting it.  Then delete the local profile dir.
+ */
+  NS_IMETHOD Run()
+  {
+    // Copy to the destination then delete the profile. A move doesn't follow links.
+    nsresult rv = mProfileDir->CopyToFollowingLinks(mDesktop, mLeafName);
+    if (NS_SUCCEEDED(rv))
+      rv = mProfileDir->Remove(true);
+    else
+      NS_WARNING("Could not backup the root profile directory");
+
+    // If we have a separate local cache profile directory, just delete it.
+    // Don't return an error if this fails so that reset can proceed if it can't be deleted.
+    bool sameDir;
+    nsresult rvLocal = mProfileDir->Equals(mProfileLocalDir, &sameDir);
+    if (NS_SUCCEEDED(rvLocal) && !sameDir) {
+      rvLocal = mProfileLocalDir->Remove(true);
+      if (NS_FAILED(rvLocal)) NS_WARNING("Could not remove the old local profile directory (cache)");
+    }
+    gProfileResetCleanupCompleted = true;
+
+    nsCOMPtr<nsIRunnable> resultRunnable = new ProfileResetCleanupResultTask();
+    NS_DispatchToMainThread(resultRunnable);
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsILocalFile> mProfileDir;
+  nsCOMPtr<nsILocalFile> mProfileLocalDir;
+  nsCOMPtr<nsIFile> mDesktop;
+  nsAutoString mLeafName;
+};
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -22,16 +22,17 @@
 
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 
 #include "mozilla/Util.h"
 
 #include "nsAppRunner.h"
 #include "nsUpdateDriver.h"
+#include "ProfileReset.h"
 
 #ifdef MOZ_INSTRUMENT_EVENT_LOOP
 #include "EventTracer.h"
 #endif
 
 #ifdef XP_MACOSX
 #include "nsVersionComparator.h"
 #include "MacLaunchHelper.h"
@@ -1735,16 +1736,17 @@ ProfileLockedDialog(nsILocalFile* aProfi
       rv = ps->Alert(nsnull, killTitle, killMessage);
       NS_ENSURE_SUCCESS_LOG(rv, rv);
     }
 
     return NS_ERROR_ABORT;
   }
 }
 
+
 static nsresult
 ProfileMissingDialog(nsINativeAppSupport* aNative)
 {
   nsresult rv;
 
   ScopedXPCOMStartup xpcom;
   rv = xpcom.Initialize();
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1781,16 +1783,38 @@ ProfileMissingDialog(nsINativeAppSupport
   
       ps->Alert(nsnull, missingTitle, missingMessage);
     }
 
     return NS_ERROR_ABORT;
   }
 }
 
+static nsresult
+ProfileLockedDialog(nsIToolkitProfile* aProfile, nsIProfileUnlocker* aUnlocker,
+                    nsINativeAppSupport* aNative, nsIProfileLock* *aResult)
+{
+  nsCOMPtr<nsILocalFile> profileDir;
+  nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir));
+  if (NS_FAILED(rv)) return rv;
+
+  nsCOMPtr<nsILocalFile> profileLocalDir;
+  rv = aProfile->GetLocalDir(getter_AddRefs(profileLocalDir));
+  if (NS_FAILED(rv)) return rv;
+
+  bool exists;
+  profileLocalDir->Exists(&exists);
+  if (!exists) {
+    return ProfileMissingDialog(aNative);
+  }
+
+  return ProfileLockedDialog(profileDir, profileLocalDir, aUnlocker, aNative,
+                             aResult);
+}
+
 static const char kProfileManagerURL[] =
   "chrome://mozapps/content/profile/profileSelection.xul";
 
 static nsresult
 ShowProfileManager(nsIToolkitProfileService* aProfileSvc,
                    nsINativeAppSupport* aNative)
 {
   nsresult rv;
@@ -1876,52 +1900,36 @@ ShowProfileManager(nsIToolkitProfileServ
   aProfileSvc->GetStartOffline(&offline);
   if (offline) {
     SaveToEnv("XRE_START_OFFLINE=1");
   }
 
   return LaunchChild(aNative);
 }
 
-// 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
-// 6) display the profile-manager UI
-
-static bool gDoMigration = false;
-static bool gDoProfileReset = false;
-
-/**
- * Creates a new profile with a timestamp in the name to use for profile reset.
- */
 static nsresult
-ResetProfile(nsIToolkitProfileService* aProfileSvc, nsIToolkitProfile* *aNewProfile)
+GetCurrentProfileIsDefault(nsIToolkitProfileService* aProfileSvc,
+                           nsILocalFile* aCurrentProfileRoot, bool *aResult)
 {
-  NS_ENSURE_ARG_POINTER(aProfileSvc);
-
-  nsCOMPtr<nsIToolkitProfile> newProfile;
-  // Make the new profile "default-" + the time in seconds since epoch for uniqueness.
-  nsCAutoString newProfileName("default-");
-  newProfileName.Append(nsPrintfCString("%lld", PR_Now() / 1000));
-  nsresult rv = aProfileSvc->CreateProfile(nsnull, // choose a default dir for us
-                                           nsnull, // choose a default dir for us
-                                           newProfileName,
-                                           getter_AddRefs(newProfile));
+  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<nsILocalFile> selectedProfileRoot;
+  rv = aProfileSvc->GetSelectedProfile(getter_AddRefs(selectedProfile));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = aProfileSvc->Flush();
+  rv = selectedProfile->GetRootDir(getter_AddRefs(selectedProfileRoot));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  NS_IF_ADDREF(*aNewProfile = newProfile);
-
-  return NS_OK;
+  bool currentIsSelected;
+  rv = aCurrentProfileRoot->Equals(selectedProfileRoot, &currentIsSelected);
+
+  *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.
@@ -1950,16 +1958,27 @@ SetCurrentProfileAsDefault(nsIToolkitPro
         rv = aProfileSvc->Flush();
       return rv;
     }
     rv = profiles->GetNext(getter_AddRefs(profile));
   }
   return rv;
 }
 
+static bool gDoMigration = false;
+static bool gDoProfileReset = false;
+
+// 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
+// 6) display the profile-manager UI
 static nsresult
 SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative,
               bool* aStartOffline, nsACString* aProfileName)
 {
   nsresult rv;
   ArgResult ar;
   const char* arg;
   *aResult = nsnull;
@@ -2011,19 +2030,28 @@ SelectProfile(nsIProfileLock* *aResult, 
 
     // 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;
+      GetCurrentProfileIsDefault(aProfileSvc, lf, &currentIsSelected);
+      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 = ResetProfile(aProfileSvc, getter_AddRefs(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);
 
         rv = newProfile->GetLocalDir(getter_AddRefs(localDir));
         NS_ENSURE_SUCCESS(rv, rv);
         SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", localDir);
@@ -2144,46 +2172,30 @@ SelectProfile(nsIProfileLock* *aResult, 
     if (ar == ARG_FOUND) {
       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 we're resetting a profile, create a new one and use it to startup.
       if (gDoProfileReset) {
         NS_WARNING("Profile reset is only supported for the default profile.");
         gDoProfileReset = false;
       }
 
       nsCOMPtr<nsIProfileUnlocker> unlocker;
       rv = profile->Lock(nsnull, aResult);
       if (NS_SUCCEEDED(rv)) {
         if (aProfileName)
           aProfileName->Assign(nsDependentCString(arg));
         return NS_OK;
       }
 
-      nsCOMPtr<nsILocalFile> profileDir;
-      rv = profile->GetRootDir(getter_AddRefs(profileDir));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsCOMPtr<nsILocalFile> profileLocalDir;
-      rv = profile->GetLocalDir(getter_AddRefs(profileLocalDir));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      bool exists;
-      profileLocalDir->Exists(&exists);
-      if (!exists) {
-        return ProfileMissingDialog(aNative);
-      }
-
-      return ProfileLockedDialog(profileDir, profileLocalDir, unlocker,
-                                 aNative, aResult);
+      return ProfileLockedDialog(profile, unlocker, aNative, aResult);
     }
 
     return ShowProfileManager(aProfileSvc, aNative);
   }
 
   ar = CheckArg("profilemanager", true);
   if (ar == ARG_BAD) {
     PR_fprintf(PR_STDERR, "Error: argument -profilemanager is invalid when argument -osint is specified\n");
@@ -2219,18 +2231,27 @@ SelectProfile(nsIProfileLock* *aResult, 
 
   if (useDefault) {
     nsCOMPtr<nsIToolkitProfile> profile;
     // GetSelectedProfile will auto-select the only profile if there's just one
     aProfileSvc->GetSelectedProfile(getter_AddRefs(profile));
     if (profile) {
       // If we're resetting a profile, create a new one and use it to startup.
       if (gDoProfileReset) {
+        {
+          // Check that the source profile is not in use by temporarily acquiring its lock.
+          nsIProfileLock* tempProfileLock;
+          nsCOMPtr<nsIProfileUnlocker> unlocker;
+          rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock);
+          if (NS_FAILED(rv))
+            return ProfileLockedDialog(profile, unlocker, aNative, &tempProfileLock);
+        }
+
         nsCOMPtr<nsIToolkitProfile> newProfile;
-        rv = ResetProfile(aProfileSvc, getter_AddRefs(newProfile));
+        rv = CreateResetProfile(aProfileSvc, getter_AddRefs(newProfile));
         if (NS_SUCCEEDED(rv))
           profile = newProfile;
         else
           gDoProfileReset = false;
       }
       nsCOMPtr<nsIProfileUnlocker> unlocker;
       rv = profile->Lock(getter_AddRefs(unlocker), aResult);
       if (NS_SUCCEEDED(rv)) {
@@ -2238,32 +2259,17 @@ SelectProfile(nsIProfileLock* *aResult, 
         if (aProfileName) {
           rv = profile->GetName(*aProfileName);
           if (NS_FAILED(rv))
             aProfileName->Truncate(0);
         }
         return NS_OK;
       }
 
-      nsCOMPtr<nsILocalFile> profileDir;
-      rv = profile->GetRootDir(getter_AddRefs(profileDir));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsCOMPtr<nsILocalFile> profileLocalDir;
-      rv = profile->GetRootDir(getter_AddRefs(profileLocalDir));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      bool exists;
-      profileLocalDir->Exists(&exists);
-      if (!exists) {
-        return ProfileMissingDialog(aNative);
-      }
-
-      return ProfileLockedDialog(profileDir, profileLocalDir, unlocker,
-                                 aNative, aResult);
+      return ProfileLockedDialog(profile, unlocker, aNative, aResult);
     }
   }
 
   return ShowProfileManager(aProfileSvc, aNative);
 }
 
 /** 
  * Checks the compatibility.ini file to see if we have updated our application
@@ -3612,35 +3618,50 @@ XREMain::XRE_mainRun()
       if (NS_SUCCEEDED(rv)) {
         if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') {
           gDoMigration = false;
         }
       }
     }
   }
 
-  // Profile Migration
-  if (mAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) {
-    gDoMigration = false;
-    nsCOMPtr<nsIProfileMigrator> pm
-      (do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID));
-    if (pm) {
-      nsCAutoString aKey;
-      if (gDoProfileReset) {
-        // Automatically migrate from the current application if we just
-        // reset the profile.
-        aKey = MOZ_APP_NAME;
-        pm->Migrate(&mDirProvider, aKey);
-        // Set the new profile as the default after migration.
-        rv = SetCurrentProfileAsDefault(mProfileSvc, mProfD);
-        if (NS_FAILED(rv)) NS_WARNING("Could not set current profile as the default");
-      } else {
+  {
+    nsCOMPtr<nsIToolkitProfile> selectedProfile;
+    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)) {
+        gDoProfileReset = false;
+        return 1;
+      }
+    }
+
+    // Profile Migration
+    if (mAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) {
+      gDoMigration = false;
+      nsCOMPtr<nsIProfileMigrator> pm(do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID));
+      if (pm) {
+        nsCAutoString aKey;
+        if (gDoProfileReset) {
+          // Automatically migrate from the current application if we just
+          // reset the profile.
+          aKey = MOZ_APP_NAME;
+        }
         pm->Migrate(&mDirProvider, aKey);
       }
     }
+
+    if (gDoProfileReset) {
+      nsresult backupCreated = ProfileResetCleanup(selectedProfile);
+      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");
+    }
   }
 
   NS_TIME_FUNCTION_MARK("Profile migration");
 
   mDirProvider.DoStartup();
 
   NS_TIME_FUNCTION_MARK("dirProvider.DoStartup() (profile-after-change)");