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 id9929
push usermozilla@noorenberghe.ca
push dateSat, 26 May 2012 22:48:59 +0000
treeherdermozilla-inbound@51eeffc5a31d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, limi
bugs731047
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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)");