123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- /* -*- 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 "nsIFile.h"
- #include "nsIStringBundle.h"
- #include "nsIToolkitProfile.h"
- #include "nsIWindowWatcher.h"
- #include "ProfileReset.h"
- #include "nsDirectoryServiceDefs.h"
- #include "nsDirectoryServiceUtils.h"
- #include "nsPIDOMWindow.h"
- #include "nsPrintfCString.h"
- #include "nsToolkitCompsCID.h"
- #include "nsXPCOMCIDInternal.h"
- #include "nsXREAppData.h"
- #include "mozilla/Services.h"
- #include "prtime.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, const nsACString& aOldProfileName, nsIToolkitProfile* *aNewProfile)
- {
- MOZ_ASSERT(aProfileSvc, "NULL profile service");
- nsCOMPtr<nsIToolkitProfile> newProfile;
- // Make the new profile the old profile (or "default-") + the time in seconds since epoch for uniqueness.
- nsAutoCString newProfileName;
- if (!aOldProfileName.IsEmpty()) {
- newProfileName.Assign(aOldProfileName);
- newProfileName.Append("-");
- } else {
- newProfileName.Assign("default-");
- }
- newProfileName.Append(nsPrintfCString("%lld", PR_Now() / 1000));
- nsresult rv = aProfileSvc->CreateProfile(nullptr, // 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<nsIFile> profileDir;
- rv = aOldProfile->GetRootDir(getter_AddRefs(profileDir));
- if (NS_FAILED(rv)) return rv;
- nsCOMPtr<nsIFile> 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 char16_t* params[] = {appName.get(), appName.get()};
- nsXPIDLString resetBackupDirectoryName;
- static const char16_t* kResetBackupDirectory = u"resetBackupDirectory";
- 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, containerDest, profileDest;
- 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 create a directory for all the backups
- backupDest->Clone(getter_AddRefs(containerDest));
- containerDest->Append(resetBackupDirectoryName);
- rv = containerDest->Create(nsIFile::DIRECTORY_TYPE, 0700);
- // It's OK if it already exists, if and only if it is a directory
- if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
- bool containerIsDir;
- rv = containerDest->IsDirectory(&containerIsDir);
- if (NS_FAILED(rv) || !containerIsDir) {
- return rv;
- }
- } else if (NS_FAILED(rv)) {
- return rv;
- }
- // Get the name of the profile
- nsAutoString leafName;
- rv = profileDir->GetLeafName(leafName);
- if (NS_FAILED(rv)) return rv;
- // Try to create a unique directory for the profile:
- containerDest->Clone(getter_AddRefs(profileDest));
- profileDest->Append(leafName);
- rv = profileDest->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
- if (NS_FAILED(rv)) return rv;
- // Get the unique profile name
- rv = profileDest->GetLeafName(leafName);
- if (NS_FAILED(rv)) return rv;
- // Delete the empty directory that CreateUnique just created.
- rv = profileDest->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<mozIDOMWindowProxy> progressWindow;
- rv = windowWatcher->OpenWindow(nullptr,
- kResetProgressURL,
- "_blank",
- "centerscreen,chrome,titlebar",
- nullptr,
- 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,
- containerDest, 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.
- auto* piWindow = nsPIDOMWindowOuter::From(progressWindow);
- piWindow->Close();
- // Delete the old profile from profiles.ini. The folder was already deleted by the thread above.
- rv = aOldProfile->Remove(false);
- if (NS_FAILED(rv)) NS_WARNING("Could not remove the profile");
- return rv;
- }
|