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