Coverage Report

Created: 2018-09-25 14:53

/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
}