ProfileReset.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  4. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsIAppStartup.h"
  6. #include "nsIFile.h"
  7. #include "nsIStringBundle.h"
  8. #include "nsIToolkitProfile.h"
  9. #include "nsIWindowWatcher.h"
  10. #include "ProfileReset.h"
  11. #include "nsDirectoryServiceDefs.h"
  12. #include "nsDirectoryServiceUtils.h"
  13. #include "nsPIDOMWindow.h"
  14. #include "nsPrintfCString.h"
  15. #include "nsToolkitCompsCID.h"
  16. #include "nsXPCOMCIDInternal.h"
  17. #include "nsXREAppData.h"
  18. #include "mozilla/Services.h"
  19. #include "prtime.h"
  20. extern const nsXREAppData* gAppData;
  21. static const char kProfileProperties[] =
  22. "chrome://mozapps/locale/profile/profileSelection.properties";
  23. /**
  24. * Creates a new profile with a timestamp in the name to use for profile reset.
  25. */
  26. nsresult
  27. CreateResetProfile(nsIToolkitProfileService* aProfileSvc, const nsACString& aOldProfileName, nsIToolkitProfile* *aNewProfile)
  28. {
  29. MOZ_ASSERT(aProfileSvc, "NULL profile service");
  30. nsCOMPtr<nsIToolkitProfile> newProfile;
  31. // Make the new profile the old profile (or "default-") + the time in seconds since epoch for uniqueness.
  32. nsAutoCString newProfileName;
  33. if (!aOldProfileName.IsEmpty()) {
  34. newProfileName.Assign(aOldProfileName);
  35. newProfileName.Append("-");
  36. } else {
  37. newProfileName.Assign("default-");
  38. }
  39. newProfileName.Append(nsPrintfCString("%lld", PR_Now() / 1000));
  40. nsresult rv = aProfileSvc->CreateProfile(nullptr, // choose a default dir for us
  41. newProfileName,
  42. getter_AddRefs(newProfile));
  43. if (NS_FAILED(rv)) return rv;
  44. rv = aProfileSvc->Flush();
  45. if (NS_FAILED(rv)) return rv;
  46. newProfile.swap(*aNewProfile);
  47. return NS_OK;
  48. }
  49. /**
  50. * Delete the profile directory being reset after a backup and delete the local profile directory.
  51. */
  52. nsresult
  53. ProfileResetCleanup(nsIToolkitProfile* aOldProfile)
  54. {
  55. nsresult rv;
  56. nsCOMPtr<nsIFile> profileDir;
  57. rv = aOldProfile->GetRootDir(getter_AddRefs(profileDir));
  58. if (NS_FAILED(rv)) return rv;
  59. nsCOMPtr<nsIFile> profileLocalDir;
  60. rv = aOldProfile->GetLocalDir(getter_AddRefs(profileLocalDir));
  61. if (NS_FAILED(rv)) return rv;
  62. // Get the friendly name for the backup directory.
  63. nsCOMPtr<nsIStringBundleService> sbs = mozilla::services::GetStringBundleService();
  64. if (!sbs) return NS_ERROR_FAILURE;
  65. nsCOMPtr<nsIStringBundle> sb;
  66. rv = sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
  67. if (!sb) return NS_ERROR_FAILURE;
  68. NS_ConvertUTF8toUTF16 appName(gAppData->name);
  69. const char16_t* params[] = {appName.get(), appName.get()};
  70. nsXPIDLString resetBackupDirectoryName;
  71. static const char16_t* kResetBackupDirectory = u"resetBackupDirectory";
  72. rv = sb->FormatStringFromName(kResetBackupDirectory, params, 2,
  73. getter_Copies(resetBackupDirectoryName));
  74. // Get info to copy the old root profile dir to the desktop as a backup.
  75. nsCOMPtr<nsIFile> backupDest, containerDest, profileDest;
  76. rv = NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(backupDest));
  77. if (NS_FAILED(rv)) {
  78. // Fall back to the home directory if the desktop is not available.
  79. rv = NS_GetSpecialDirectory(NS_OS_HOME_DIR, getter_AddRefs(backupDest));
  80. if (NS_FAILED(rv)) return rv;
  81. }
  82. // Try to create a directory for all the backups
  83. backupDest->Clone(getter_AddRefs(containerDest));
  84. containerDest->Append(resetBackupDirectoryName);
  85. rv = containerDest->Create(nsIFile::DIRECTORY_TYPE, 0700);
  86. // It's OK if it already exists, if and only if it is a directory
  87. if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
  88. bool containerIsDir;
  89. rv = containerDest->IsDirectory(&containerIsDir);
  90. if (NS_FAILED(rv) || !containerIsDir) {
  91. return rv;
  92. }
  93. } else if (NS_FAILED(rv)) {
  94. return rv;
  95. }
  96. // Get the name of the profile
  97. nsAutoString leafName;
  98. rv = profileDir->GetLeafName(leafName);
  99. if (NS_FAILED(rv)) return rv;
  100. // Try to create a unique directory for the profile:
  101. containerDest->Clone(getter_AddRefs(profileDest));
  102. profileDest->Append(leafName);
  103. rv = profileDest->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
  104. if (NS_FAILED(rv)) return rv;
  105. // Get the unique profile name
  106. rv = profileDest->GetLeafName(leafName);
  107. if (NS_FAILED(rv)) return rv;
  108. // Delete the empty directory that CreateUnique just created.
  109. rv = profileDest->Remove(false);
  110. if (NS_FAILED(rv)) return rv;
  111. // Show a progress window while the cleanup happens since the disk I/O can take time.
  112. nsCOMPtr<nsIWindowWatcher> windowWatcher(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
  113. if (!windowWatcher) return NS_ERROR_FAILURE;
  114. nsCOMPtr<nsIAppStartup> appStartup(do_GetService(NS_APPSTARTUP_CONTRACTID));
  115. if (!appStartup) return NS_ERROR_FAILURE;
  116. nsCOMPtr<mozIDOMWindowProxy> progressWindow;
  117. rv = windowWatcher->OpenWindow(nullptr,
  118. kResetProgressURL,
  119. "_blank",
  120. "centerscreen,chrome,titlebar",
  121. nullptr,
  122. getter_AddRefs(progressWindow));
  123. if (NS_FAILED(rv)) return rv;
  124. // Create a new thread to do the bulk of profile cleanup to stay responsive.
  125. nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
  126. nsCOMPtr<nsIThread> cleanupThread;
  127. rv = tm->NewThread(0, 0, getter_AddRefs(cleanupThread));
  128. if (NS_SUCCEEDED(rv)) {
  129. nsCOMPtr<nsIRunnable> runnable = new ProfileResetCleanupAsyncTask(profileDir, profileLocalDir,
  130. containerDest, leafName);
  131. cleanupThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
  132. // The result callback will shut down the worker thread.
  133. nsIThread *thread = NS_GetCurrentThread();
  134. // Wait for the cleanup thread to complete.
  135. while(!gProfileResetCleanupCompleted) {
  136. NS_ProcessNextEvent(thread);
  137. }
  138. } else {
  139. gProfileResetCleanupCompleted = true;
  140. NS_WARNING("Cleanup thread creation failed");
  141. return rv;
  142. }
  143. // Close the progress window now that the cleanup thread is done.
  144. auto* piWindow = nsPIDOMWindowOuter::From(progressWindow);
  145. piWindow->Close();
  146. // Delete the old profile from profiles.ini. The folder was already deleted by the thread above.
  147. rv = aOldProfile->Remove(false);
  148. if (NS_FAILED(rv)) NS_WARNING("Could not remove the profile");
  149. return rv;
  150. }