1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/xre/ProfileReset.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,174 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsIAppStartup.h" 1.10 +#include "nsIDOMWindow.h" 1.11 +#include "nsIFile.h" 1.12 +#include "nsIStringBundle.h" 1.13 +#include "nsIToolkitProfile.h" 1.14 +#include "nsIWindowWatcher.h" 1.15 + 1.16 +#include "ProfileReset.h" 1.17 + 1.18 +#include "nsDirectoryServiceDefs.h" 1.19 +#include "nsDirectoryServiceUtils.h" 1.20 +#include "nsPrintfCString.h" 1.21 +#include "nsToolkitCompsCID.h" 1.22 +#include "nsXPCOMCIDInternal.h" 1.23 +#include "nsXREAppData.h" 1.24 + 1.25 +#include "mozilla/Services.h" 1.26 +#include "prtime.h" 1.27 + 1.28 +extern const nsXREAppData* gAppData; 1.29 + 1.30 +static const char kProfileProperties[] = 1.31 + "chrome://mozapps/locale/profile/profileSelection.properties"; 1.32 + 1.33 +/** 1.34 + * Creates a new profile with a timestamp in the name to use for profile reset. 1.35 + */ 1.36 +nsresult 1.37 +CreateResetProfile(nsIToolkitProfileService* aProfileSvc, nsIToolkitProfile* *aNewProfile) 1.38 +{ 1.39 + NS_ABORT_IF_FALSE(aProfileSvc, "NULL profile service"); 1.40 + 1.41 + nsCOMPtr<nsIToolkitProfile> newProfile; 1.42 + // Make the new profile "default-" + the time in seconds since epoch for uniqueness. 1.43 + nsAutoCString newProfileName("default-"); 1.44 + newProfileName.Append(nsPrintfCString("%lld", PR_Now() / 1000)); 1.45 + nsresult rv = aProfileSvc->CreateProfile(nullptr, // choose a default dir for us 1.46 + newProfileName, 1.47 + getter_AddRefs(newProfile)); 1.48 + if (NS_FAILED(rv)) return rv; 1.49 + 1.50 + rv = aProfileSvc->Flush(); 1.51 + if (NS_FAILED(rv)) return rv; 1.52 + 1.53 + newProfile.swap(*aNewProfile); 1.54 + 1.55 + return NS_OK; 1.56 +} 1.57 + 1.58 +/** 1.59 + * Delete the profile directory being reset after a backup and delete the local profile directory. 1.60 + */ 1.61 +nsresult 1.62 +ProfileResetCleanup(nsIToolkitProfile* aOldProfile) 1.63 +{ 1.64 + nsresult rv; 1.65 + nsCOMPtr<nsIFile> profileDir; 1.66 + rv = aOldProfile->GetRootDir(getter_AddRefs(profileDir)); 1.67 + if (NS_FAILED(rv)) return rv; 1.68 + 1.69 + nsCOMPtr<nsIFile> profileLocalDir; 1.70 + rv = aOldProfile->GetLocalDir(getter_AddRefs(profileLocalDir)); 1.71 + if (NS_FAILED(rv)) return rv; 1.72 + 1.73 + // Get the friendly name for the backup directory. 1.74 + nsCOMPtr<nsIStringBundleService> sbs = mozilla::services::GetStringBundleService(); 1.75 + if (!sbs) return NS_ERROR_FAILURE; 1.76 + 1.77 + nsCOMPtr<nsIStringBundle> sb; 1.78 + rv = sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb)); 1.79 + if (!sb) return NS_ERROR_FAILURE; 1.80 + 1.81 + NS_ConvertUTF8toUTF16 appName(gAppData->name); 1.82 + const char16_t* params[] = {appName.get(), appName.get()}; 1.83 + 1.84 + nsXPIDLString resetBackupDirectoryName; 1.85 + 1.86 + static const char16_t* kResetBackupDirectory = MOZ_UTF16("resetBackupDirectory"); 1.87 + rv = sb->FormatStringFromName(kResetBackupDirectory, params, 2, 1.88 + getter_Copies(resetBackupDirectoryName)); 1.89 + 1.90 + // Get info to copy the old root profile dir to the desktop as a backup. 1.91 + nsCOMPtr<nsIFile> backupDest, containerDest, profileDest; 1.92 + rv = NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(backupDest)); 1.93 + if (NS_FAILED(rv)) { 1.94 + // Fall back to the home directory if the desktop is not available. 1.95 + rv = NS_GetSpecialDirectory(NS_OS_HOME_DIR, getter_AddRefs(backupDest)); 1.96 + if (NS_FAILED(rv)) return rv; 1.97 + } 1.98 + 1.99 + // Try to create a directory for all the backups 1.100 + backupDest->Clone(getter_AddRefs(containerDest)); 1.101 + containerDest->Append(resetBackupDirectoryName); 1.102 + rv = containerDest->Create(nsIFile::DIRECTORY_TYPE, 0700); 1.103 + // It's OK if it already exists, if and only if it is a directory 1.104 + if (rv == NS_ERROR_FILE_ALREADY_EXISTS) { 1.105 + bool containerIsDir; 1.106 + rv = containerDest->IsDirectory(&containerIsDir); 1.107 + if (NS_FAILED(rv) || !containerIsDir) { 1.108 + return rv; 1.109 + } 1.110 + } else if (NS_FAILED(rv)) { 1.111 + return rv; 1.112 + } 1.113 + 1.114 + // Get the name of the profile 1.115 + nsAutoString leafName; 1.116 + rv = profileDir->GetLeafName(leafName); 1.117 + if (NS_FAILED(rv)) return rv; 1.118 + 1.119 + // Try to create a unique directory for the profile: 1.120 + containerDest->Clone(getter_AddRefs(profileDest)); 1.121 + profileDest->Append(leafName); 1.122 + rv = profileDest->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700); 1.123 + if (NS_FAILED(rv)) return rv; 1.124 + 1.125 + // Get the unique profile name 1.126 + rv = profileDest->GetLeafName(leafName); 1.127 + if (NS_FAILED(rv)) return rv; 1.128 + 1.129 + // Delete the empty directory that CreateUnique just created. 1.130 + rv = profileDest->Remove(false); 1.131 + if (NS_FAILED(rv)) return rv; 1.132 + 1.133 + // Show a progress window while the cleanup happens since the disk I/O can take time. 1.134 + nsCOMPtr<nsIWindowWatcher> windowWatcher(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); 1.135 + if (!windowWatcher) return NS_ERROR_FAILURE; 1.136 + 1.137 + nsCOMPtr<nsIAppStartup> appStartup(do_GetService(NS_APPSTARTUP_CONTRACTID)); 1.138 + if (!appStartup) return NS_ERROR_FAILURE; 1.139 + 1.140 + nsCOMPtr<nsIDOMWindow> progressWindow; 1.141 + rv = windowWatcher->OpenWindow(nullptr, 1.142 + kResetProgressURL, 1.143 + "_blank", 1.144 + "centerscreen,chrome,titlebar", 1.145 + nullptr, 1.146 + getter_AddRefs(progressWindow)); 1.147 + if (NS_FAILED(rv)) return rv; 1.148 + 1.149 + // Create a new thread to do the bulk of profile cleanup to stay responsive. 1.150 + nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID); 1.151 + nsCOMPtr<nsIThread> cleanupThread; 1.152 + rv = tm->NewThread(0, 0, getter_AddRefs(cleanupThread)); 1.153 + if (NS_SUCCEEDED(rv)) { 1.154 + nsCOMPtr<nsIRunnable> runnable = new ProfileResetCleanupAsyncTask(profileDir, profileLocalDir, 1.155 + containerDest, leafName); 1.156 + cleanupThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL); 1.157 + // The result callback will shut down the worker thread. 1.158 + 1.159 + nsIThread *thread = NS_GetCurrentThread(); 1.160 + // Wait for the cleanup thread to complete. 1.161 + while(!gProfileResetCleanupCompleted) { 1.162 + NS_ProcessNextEvent(thread); 1.163 + } 1.164 + } else { 1.165 + gProfileResetCleanupCompleted = true; 1.166 + NS_WARNING("Cleanup thread creation failed"); 1.167 + return rv; 1.168 + } 1.169 + // Close the progress window now that the cleanup thread is done. 1.170 + progressWindow->Close(); 1.171 + 1.172 + // Delete the old profile from profiles.ini. The folder was already deleted by the thread above. 1.173 + rv = aOldProfile->Remove(false); 1.174 + if (NS_FAILED(rv)) NS_WARNING("Could not remove the profile"); 1.175 + 1.176 + return rv; 1.177 +}