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/. */
6 #include "nsIAppStartup.h"
8 #include "nsIStringBundle.h"
9 #include "nsIToolkitProfile.h"
10 #include "nsIWindowWatcher.h"
12 #include "ProfileReset.h"
14 #include "nsDirectoryServiceDefs.h"
15 #include "nsDirectoryServiceUtils.h"
16 #include "nsPIDOMWindow.h"
18 #include "mozilla/Components.h"
19 #include "mozilla/XREAppData.h"
21 #include "mozilla/SpinEventLoopUntil.h"
22 #include "mozilla/Unused.h"
24 using namespace mozilla
;
26 extern const XREAppData
* gAppData
;
28 static const char kProfileProperties
[] =
29 "chrome://mozapps/locale/profile/profileSelection.properties";
32 * Spin up a thread to backup the old profile's main directory and delete the
33 * profile's local directory. Once complete have the profile service remove the
34 * old profile and if necessary make the new profile the default.
36 nsresult
ProfileResetCleanup(nsToolkitProfileService
* aService
,
37 nsIToolkitProfile
* aOldProfile
) {
39 nsCOMPtr
<nsIFile
> profileDir
;
40 rv
= aOldProfile
->GetRootDir(getter_AddRefs(profileDir
));
41 if (NS_FAILED(rv
)) return rv
;
43 nsCOMPtr
<nsIFile
> profileLocalDir
;
44 rv
= aOldProfile
->GetLocalDir(getter_AddRefs(profileLocalDir
));
45 if (NS_FAILED(rv
)) return rv
;
47 // Get the friendly name for the backup directory.
48 nsCOMPtr
<nsIStringBundleService
> sbs
=
49 mozilla::components::StringBundle::Service();
50 if (!sbs
) return NS_ERROR_FAILURE
;
52 nsCOMPtr
<nsIStringBundle
> sb
;
53 Unused
<< sbs
->CreateBundle(kProfileProperties
, getter_AddRefs(sb
));
54 if (!sb
) return NS_ERROR_FAILURE
;
56 NS_ConvertUTF8toUTF16
appName(gAppData
->name
);
57 AutoTArray
<nsString
, 2> params
= {appName
, appName
};
59 nsAutoString resetBackupDirectoryName
;
61 static const char* kResetBackupDirectory
= "resetBackupDirectory";
62 rv
= sb
->FormatStringFromName(kResetBackupDirectory
, params
,
63 resetBackupDirectoryName
);
64 if (NS_FAILED(rv
)) return rv
;
66 // Get info to copy the old root profile dir to the desktop as a backup.
67 nsCOMPtr
<nsIFile
> backupDest
, containerDest
, profileDest
;
68 rv
= NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR
, getter_AddRefs(backupDest
));
70 // Fall back to the home directory if the desktop is not available.
71 rv
= NS_GetSpecialDirectory(NS_OS_HOME_DIR
, getter_AddRefs(backupDest
));
72 if (NS_FAILED(rv
)) return rv
;
75 // Try to create a directory for all the backups
76 backupDest
->Clone(getter_AddRefs(containerDest
));
77 containerDest
->Append(resetBackupDirectoryName
);
78 rv
= containerDest
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
79 // It's OK if it already exists, if and only if it is a directory
80 if (rv
== NS_ERROR_FILE_ALREADY_EXISTS
) {
82 rv
= containerDest
->IsDirectory(&containerIsDir
);
83 if (NS_FAILED(rv
) || !containerIsDir
) {
86 } else if (NS_FAILED(rv
)) {
90 // Get the name of the profile
91 nsAutoString leafName
;
92 rv
= profileDir
->GetLeafName(leafName
);
93 if (NS_FAILED(rv
)) return rv
;
95 // Try to create a unique directory for the profile:
96 containerDest
->Clone(getter_AddRefs(profileDest
));
97 profileDest
->Append(leafName
);
98 rv
= profileDest
->CreateUnique(nsIFile::DIRECTORY_TYPE
, 0700);
99 if (NS_FAILED(rv
)) return rv
;
101 // Get the unique profile name
102 rv
= profileDest
->GetLeafName(leafName
);
103 if (NS_FAILED(rv
)) return rv
;
105 // Delete the empty directory that CreateUnique just created.
106 rv
= profileDest
->Remove(false);
107 if (NS_FAILED(rv
)) return rv
;
109 // Show a progress window while the cleanup happens since the disk I/O can
111 nsCOMPtr
<nsIWindowWatcher
> windowWatcher(
112 do_GetService(NS_WINDOWWATCHER_CONTRACTID
));
113 if (!windowWatcher
) return NS_ERROR_FAILURE
;
115 nsCOMPtr
<nsIAppStartup
> appStartup(components::AppStartup::Service());
116 if (!appStartup
) return NS_ERROR_FAILURE
;
118 nsCOMPtr
<mozIDOMWindowProxy
> progressWindow
;
119 rv
= windowWatcher
->OpenWindow(nullptr, nsDependentCString(kResetProgressURL
),
120 "_blank"_ns
, "centerscreen,chrome,titlebar"_ns
,
121 nullptr, getter_AddRefs(progressWindow
));
122 if (NS_FAILED(rv
)) return rv
;
124 // Create a new thread to do the bulk of profile cleanup to stay responsive.
125 nsCOMPtr
<nsIThread
> cleanupThread
;
126 rv
= NS_NewNamedThread("ResetCleanup", getter_AddRefs(cleanupThread
));
127 if (NS_SUCCEEDED(rv
)) {
128 nsCOMPtr
<nsIRunnable
> runnable
= new ProfileResetCleanupAsyncTask(
129 profileDir
, profileLocalDir
, containerDest
, leafName
);
130 cleanupThread
->Dispatch(runnable
, nsIThread::DISPATCH_NORMAL
);
131 // The result callback will shut down the worker thread.
133 // Wait for the cleanup thread to complete.
134 SpinEventLoopUntil("xre:ProfileResetCreateBackup"_ns
,
135 [&]() { return gProfileResetCleanupCompleted
; });
137 gProfileResetCleanupCompleted
= true;
138 NS_WARNING("Cleanup thread creation failed");
141 // Close the progress window now that the cleanup thread is done.
142 auto* piWindow
= nsPIDOMWindowOuter::From(progressWindow
);
145 return aService
->ApplyResetProfile(aOldProfile
);