toolkit/xre/ProfileReset.cpp

changeset 0
6474c204b198
     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 +}

mercurial