[tor-commits] [tor-browser/tor-browser-38.7.1esr-6.0-1] Bug 13252 - Do not store data in the app bundle

gk at torproject.org gk at torproject.org
Sat Mar 19 20:21:55 UTC 2016


commit a9354c824b2435054c3d8d853606a2c7f8622f0b
Author: Kathy Brade <brade at pearlcrescent.com>
Date:   Fri Mar 18 14:20:02 2016 -0400

    Bug 13252 - Do not store data in the app bundle
    
    Add an --enable-tor-browser-data-outside-app-dir configure option.
    When this is enabled, all user data is stored in a directory named
    TorBrowser-Data which is located next to the application directory.
    
    The first time an updated browser is opened, migrate the existing
    browser profile, Tor data directory contents, and UpdateInfo to the
    TorBrowser-Data directory. If migration of the browser profile
    fails, an error alert is displayed and the browser is started
    using a new profile.
    
    Display an informative error messages if the TorBrowser-Data
    directory cannot be created due to an "access denied" or a
    "read only volume" error.
    
    Add support for installing "override" preferences within the user's
    browser profile. All .js files in distribution/preferences (on
    Mac OS, Contents/Resources/distribution/preferences) will be copied
    to the preferences directory within the user's browser profile when
    the profile is created and each time Tor Browser is updated. This
    mechanism will be used to install the extension-overrides.js file
    into the profile.
    
    On Mac OS, add support for the --invisible command line option which
    is used by the meek-http-helper to avoid showing an icon for the
    helper browser on the dock.
---
 .mozconfig-mac                                     |   1 +
 configure.in                                       |  11 +
 .../mozapps/profile/profileSelection.properties    |   1 +
 toolkit/mozapps/extensions/AddonManager.jsm        |  29 ++
 .../mozapps/extensions/internal/XPIProvider.jsm    |  59 ++++
 toolkit/xre/nsAppRunner.cpp                        | 303 ++++++++++++++++++++-
 toolkit/xre/nsXREDirProvider.cpp                   |  10 +
 xpcom/io/nsAppFileLocationProvider.cpp             |  14 +
 8 files changed, 417 insertions(+), 11 deletions(-)

diff --git a/.mozconfig-mac b/.mozconfig-mac
index 8ae44da..dc77c3b 100644
--- a/.mozconfig-mac
+++ b/.mozconfig-mac
@@ -23,6 +23,7 @@ ac_add_options --disable-debug
 
 ac_add_options --with-macos-private-frameworks="$CROSS_PRIVATE_FRAMEWORKS"
 
+ac_add_options --enable-tor-browser-data-outside-app-dir
 ac_add_options --enable-tor-browser-update
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
diff --git a/configure.in b/configure.in
index 132c72e..45dee7c 100644
--- a/configure.in
+++ b/configure.in
@@ -6518,9 +6518,20 @@ if test -n "$TOR_BROWSER_UPDATE"; then
     AC_DEFINE(TOR_BROWSER_UPDATE)
 fi
 
+MOZ_ARG_ENABLE_BOOL(tor-browser-data-outside-app-dir,
+[  --enable-tor-browser-data-outside-app-dir
+                          Enable Tor Browser data outside of app directory],
+    TOR_BROWSER_DATA_OUTSIDE_APP_DIR=1,
+    TOR_BROWSER_DATA_OUTSIDE_APP_DIR= )
+
+if test -n "$TOR_BROWSER_DATA_OUTSIDE_APP_DIR"; then
+    AC_DEFINE(TOR_BROWSER_DATA_OUTSIDE_APP_DIR)
+fi
+
 AC_DEFINE_UNQUOTED(TOR_BROWSER_VERSION,"$TOR_BROWSER_VERSION")
 AC_SUBST(TOR_BROWSER_VERSION)
 AC_SUBST(TOR_BROWSER_UPDATE)
+AC_SUBST(TOR_BROWSER_DATA_OUTSIDE_APP_DIR)
 
 dnl ========================================================
 dnl build the tests by default
diff --git a/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties b/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties
index 3cf48ff..f5cb77a 100644
--- a/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties
+++ b/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties
@@ -17,6 +17,7 @@ profileProblemTitle=%S Profile Problem
 profileReadOnly=You cannot run %S from a read-only file system.  Please copy %S to another location before trying to use it.
 profileReadOnlyMac=You cannot run %S from a read-only file system.  Please copy %S to your Desktop or Applications folder before trying to use it.
 profileAccessDenied=%S does not have permission to access the profile. Please adjust your file system permissions and try again.
+profileMigrationFailed=Migration of your existing %S profile failed.\nNew settings will be used.
 # Profile manager
 # LOCALIZATION NOTE (profileTooltip): First %S is the profile name, second %S is the path to the profile folder.
 profileTooltip=Profile: '%S' - Path: '%S'
diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm
index ed07718..52eebf2 100644
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -42,6 +42,11 @@ const PREF_MATCH_OS_LOCALE            = "intl.locale.matchOS";
 const PREF_SELECTED_LOCALE            = "general.useragent.locale";
 const UNKNOWN_XPCOM_ABI               = "unknownABI";
 
+#ifdef TOR_BROWSER_VERSION
+#expand const TOR_BROWSER_VERSION = __TOR_BROWSER_VERSION__;
+const PREF_EM_LAST_TORBROWSER_VERSION = "extensions.lastTorBrowserVersion";
+#endif
+
 const UPDATE_REQUEST_VERSION          = 2;
 const CATEGORY_UPDATE_PARAMS          = "extension-update-params";
 
@@ -866,6 +871,30 @@ var AddonManagerInternal = {
         this.validateBlocklist();
       }
 
+#ifdef TOR_BROWSER_VERSION
+      // To ensure that extension override prefs are reinstalled into the
+      // user's profile after each update, set appChanged = true if the
+      // Mozilla app version has not changed but the Tor Browser version
+      // has changed.
+      let tbChanged = undefined;
+      try {
+        tbChanged = TOR_BROWSER_VERSION !=
+                   Services.prefs.getCharPref(PREF_EM_LAST_TORBROWSER_VERSION);
+      }
+      catch (e) { }
+      if (tbChanged !== false) {
+        // Because PREF_EM_LAST_TORBROWSER_VERSION was not present in older
+        // versions of Tor Browser, an app change is indicated when tbChanged
+        // is undefined or true.
+        if (appChanged === false) {
+          appChanged = true;
+        }
+
+        Services.prefs.setCharPref(PREF_EM_LAST_TORBROWSER_VERSION,
+                                   TOR_BROWSER_VERSION);
+      }
+#endif
+
 #ifndef MOZ_COMPATIBILITY_NIGHTLY
       PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + "." +
                                     Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
index e43668f..0da7a93 100644
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -99,6 +99,7 @@ const URI_EXTENSION_STRINGS           = "chrome://mozapps/locale/extensions/exte
 const STRING_TYPE_NAME                = "type.%ID%.name";
 
 const DIR_EXTENSIONS                  = "extensions";
+const DIR_PREFERENCES                 = "preferences";
 const DIR_STAGE                       = "staged";
 const DIR_XPI_STAGE                   = "staged-xpis";
 const DIR_TRASH                       = "trash";
@@ -2820,6 +2821,58 @@ this.XPIProvider = {
   },
 
   /**
+   * Installs any preference files located in the preferences directory of the
+   * application's distribution specific directory into the profile.
+   *
+   * @return true if any preference files were installed
+   */
+  installDistributionPreferences: function XPI_installDistributionPreferences() {
+    let distroDir;
+    try {
+      distroDir = FileUtils.getDir(KEY_APP_DISTRIBUTION, [DIR_PREFERENCES]);
+    }
+    catch (e) {
+      return false;
+    }
+
+    if (!distroDir.exists() || !distroDir.isDirectory())
+      return false;
+
+    let changed = false;
+    let prefOverrideDir = Services.dirsvc.get("PrefDOverride", Ci.nsIFile);
+
+    let entries = distroDir.directoryEntries
+                           .QueryInterface(Ci.nsIDirectoryEnumerator);
+    let entry;
+    while ((entry = entries.nextFile)) {
+      let fileName = entry.leafName;
+      if (!entry.isFile() ||
+          fileName.substring(fileName.length - 3).toLowerCase() != ".js") {
+        logger.debug("Ignoring distribution preference that isn't a JS file: "
+                     + entry.path);
+        continue;
+      }
+
+      try {
+        if (!prefOverrideDir.exists()) {
+          prefOverrideDir.create(Ci.nsIFile.DIRECTORY_TYPE,
+                                 FileUtils.PERMS_DIRECTORY);
+        }
+
+        entry.copyTo(prefOverrideDir, null);
+        changed = true;
+      } catch (e) {
+        logger.debug("Unable to copy " + entry.path + " to " +
+                     prefOverrideDir.path);
+      }
+    }
+
+    entries.close();
+
+    return changed;
+  },
+
+  /**
    * Compares the add-ons that are currently installed to those that were
    * known to be installed when the application last ran and applies any
    * changes found to the database. Also sends "startupcache-invalidate" signal to
@@ -3549,6 +3602,12 @@ this.XPIProvider = {
       if (updated) {
         updateReasons.push("installDistributionAddons");
       }
+
+      // Also copy distribution preferences to the user's profile.
+      updated = this.installDistributionPreferences();
+      if (updated) {
+        updateReasons.push("installDistributionPreferences");
+      }
     }
 
     // Telemetry probe added around getInstallState() to check perf
diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp
index 0ffb495..4eb084b 100644
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1864,11 +1864,30 @@ GetOverrideStringBundle(nsIStringBundleService* aSBS, nsIStringBundle* *aResult)
 
   *aResult = nullptr;
 
-  // Build Torbutton file URI string by starting from the profiles directory.
   nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton();
   if (!dirProvider)
     return;
 
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+  // Build Torbutton file URI by starting from the distribution directory.
+  bool persistent = false; // ignored
+  nsCOMPtr<nsIFile> distribDir;
+  nsresult rv = dirProvider->GetFile(XRE_APP_DISTRIBUTION_DIR, &persistent,
+                                     getter_AddRefs(distribDir));
+  if (NS_FAILED(rv))
+    return;
+
+  // Create file URI, extract as string, and append Torbutton xpi relative path.
+  nsCOMPtr<nsIURI> uri;
+  nsAutoCString uriString;
+  if (NS_FAILED(NS_NewFileURI(getter_AddRefs(uri), distribDir)) ||
+      NS_FAILED(uri->GetSpec(uriString))) {
+    return;
+  }
+
+  uriString.Append("extensions/torbutton at torproject.org.xpi");
+#else
+  // Build Torbutton file URI string by starting from the profiles directory.
   bool persistent = false; // ignored
   nsCOMPtr<nsIFile> profilesDir;
   nsresult rv = dirProvider->GetFile(NS_APP_USER_PROFILES_ROOT_DIR, &persistent,
@@ -1885,6 +1904,7 @@ GetOverrideStringBundle(nsIStringBundleService* aSBS, nsIStringBundle* *aResult)
   }
 
   uriString.Append("profile.default/extensions/torbutton at torproject.org.xpi");
+#endif
 
   nsCString userAgentLocale;
   if (!NS_SUCCEEDED(Preferences::GetCString("general.useragent.locale",
@@ -1934,6 +1954,9 @@ enum ProfileStatus {
   PROFILE_STATUS_READ_ONLY,
   PROFILE_STATUS_IS_LOCKED,
   PROFILE_STATUS_OTHER_ERROR
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+  , PROFILE_STATUS_MIGRATION_FAILED
+#endif
 };
 
 static const char kProfileProperties[] =
@@ -1973,6 +1996,8 @@ private:
 
 } // anonymous namespace
 
+// If aUnlocker is NULL, it is also OK for the following arguments to be NULL:
+//   aProfileDir, aProfileLocalDir, aResult.
 static ReturnAbortOnError
 ProfileErrorDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,
                    ProfileStatus aStatus, nsIProfileUnlocker* aUnlocker,
@@ -1984,7 +2009,8 @@ ProfileErrorDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,
   rv = xpcom.Initialize();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozilla::Telemetry::WriteFailedProfileLock(aProfileDir);
+  if (aProfileDir)
+    mozilla::Telemetry::WriteFailedProfileLock(aProfileDir);
 
   rv = xpcom.SetWindowCreator(aNative);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
@@ -2015,21 +2041,27 @@ ProfileErrorDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,
     static const char16_t kReadOnly[] = MOZ_UTF16("profileReadOnlyMac");
 #endif
     static const char16_t kAccessDenied[] = MOZ_UTF16("profileAccessDenied");
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+    static const char16_t kMigrationFailed[] = MOZ_UTF16("profileMigrationFailed");
+#endif
  
     const char16_t* errorKey = aUnlocker ? kRestartUnlocker
                                          : kRestartNoUnlocker;
+    const char16_t* titleKey = MOZ_UTF16("profileProblemTitle");
     if (PROFILE_STATUS_READ_ONLY == aStatus)
       errorKey = kReadOnly;
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+    else if (PROFILE_STATUS_MIGRATION_FAILED == aStatus)
+      errorKey = kMigrationFailed;
+#endif
     else if (PROFILE_STATUS_ACCESS_DENIED == aStatus)
       errorKey = kAccessDenied;
+    else
+      titleKey = MOZ_UTF16("restartTitle");
+
     GetFormattedString(overrideSB, sb, errorKey, params, 2,
                        getter_Copies(killMessage));
 
-    const char16_t* titleKey = ((PROFILE_STATUS_READ_ONLY == aStatus) ||
-                                (PROFILE_STATUS_ACCESS_DENIED == aStatus))
-                                   ? MOZ_UTF16("profileProblemTitle")
-                                   : MOZ_UTF16("restartTitle");
-
     nsXPIDLString killTitle;
     GetFormattedString(overrideSB, sb, titleKey, params, 1,
                        getter_Copies(killTitle));
@@ -2073,7 +2105,8 @@ ProfileErrorDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,
       }
     } else {
 #ifdef MOZ_WIDGET_ANDROID
-      if (mozilla::widget::GeckoAppShell::UnlockProfile()) {
+      if (aProfileDir && aProfileLocalDir && aResult &&
+          mozilla::widget::GeckoAppShell::UnlockProfile()) {
         return NS_LockProfilePath(aProfileDir, aProfileLocalDir, 
                                   nullptr, aResult);
       }
@@ -2360,6 +2393,215 @@ static ProfileStatus CheckProfileWriteAccess(nsIToolkitProfile* aProfile)
   return CheckProfileWriteAccess(profileDir);
 }
 
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+// Obtain an nsIFile for the app root directory, e.g., TorBrowser.app on
+// Mac OS and the directory that contains Browser/ on Linux and Windows.
+static nsresult GetAppRootDir(nsIFile *aAppDir, nsIFile **aAppRootDir)
+{
+  NS_ENSURE_ARG_POINTER(aAppDir);
+
+#ifdef XP_MACOSX
+  nsCOMPtr<nsIFile> tmpDir;
+  nsresult rv = aAppDir->GetParent(getter_AddRefs(tmpDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  return tmpDir->GetParent(aAppRootDir);
+#else
+  return aAppDir->Clone(aAppRootDir);
+#endif
+}
+
+static ProfileStatus CheckTorBrowserDataWriteAccess(nsIFile *aAppDir)
+{
+  // Check whether we can write to the directory that will contain
+  // TorBrowser-Data, i.e., the directory that is above the application
+  // root directory.
+  nsCOMPtr<nsIFile> appRootDir;
+  nsresult rv = GetAppRootDir(aAppDir, getter_AddRefs(appRootDir));
+  NS_ENSURE_SUCCESS(rv, PROFILE_STATUS_OTHER_ERROR);
+  nsCOMPtr<nsIFile> tbDataDirParent;
+  rv = appRootDir->GetParent(getter_AddRefs(tbDataDirParent));
+  NS_ENSURE_SUCCESS(rv, PROFILE_STATUS_OTHER_ERROR);
+  return CheckProfileWriteAccess(tbDataDirParent);
+}
+
+// Move the directory defined by combining aSrcParentDir and aSrcRelativePath
+// to the location defined by combining aDestParentDir and aDestRelativePath.
+// If the source directory does not exist, no changes are made and NS_OK is
+// returned.
+// If the destination directory exists, its contents are removed after the
+// source directory has been moved (if the move fails for some reason, the
+// original contents of the destination directory are restored).
+static nsresult
+migrateOneTorBrowserDataDir(nsIFile *aSrcParentDir,
+                            const nsACString &aSrcRelativePath,
+                            nsIFile *aDestParentDir,
+                            const nsACString &aDestRelativePath)
+{
+  NS_ENSURE_ARG_POINTER(aSrcParentDir);
+  NS_ENSURE_ARG_POINTER(aDestParentDir);
+
+  nsCOMPtr<nsIFile> srcDir;
+  nsresult rv = aSrcParentDir->Clone(getter_AddRefs(srcDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!aSrcRelativePath.IsEmpty()) {
+    rv = srcDir->AppendRelativeNativePath(aSrcRelativePath);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  bool srcDirExists = false;
+  srcDir->Exists(&srcDirExists);
+  if (!srcDirExists)
+    return NS_OK;   // Old data does not exist; skip migration.
+
+  nsCOMPtr<nsIFile> destDir;
+  rv = aDestParentDir->Clone(getter_AddRefs(destDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!aDestRelativePath.IsEmpty()) {
+    rv = destDir->AppendRelativeNativePath(aDestRelativePath);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIFile> destParentDir;
+  rv = destDir->GetParent(getter_AddRefs(destParentDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString destLeafName;
+  rv = destDir->GetLeafName(destLeafName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool destDirExists = false;
+  destDir->Exists(&destDirExists);
+  nsCOMPtr<nsIFile> tmpDir;
+  if (destDirExists) {
+    // The destination directory exists. When we are migrating an old
+    // Tor Browser profile, we expect this to be the case because we first
+    // allow the standard Mozilla startup code to create a new profile as
+    // usual, and then later (here) we set aside that profile directory and
+    // replace it with the old Tor Browser profile that we need to migrate.
+    // For now, move the Mozilla profile directory aside and set tmpDir to
+    // point to its new, temporary location in case migration fails and we
+    // need to restore the profile that was created by the Mozilla code.
+    nsAutoString tmpName(NS_LITERAL_STRING("tmp"));
+    rv = destDir->RenameTo(nullptr, tmpName);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIFile> dir;
+    rv = destParentDir->Clone(getter_AddRefs(dir));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = dir->Append(tmpName);
+    NS_ENSURE_SUCCESS(rv, rv);
+    tmpDir = dir;
+  }
+
+  // Move the old directory to the new location using MoveTo() so that
+  // timestamps are preserved (MoveTo() is atomic as long as the source and
+  // destination are on the same volume).
+  rv = srcDir->MoveTo(destParentDir, destLeafName);
+  if (NS_FAILED(rv)) {
+    // The move failed. Restore the directory that we were trying to replace.
+    if (tmpDir)
+      tmpDir->RenameTo(nullptr, destLeafName);
+    return rv;
+  }
+
+  // Success. If we set aside a directory earlier by renaming it, remove it.
+  if (tmpDir)
+    tmpDir->Remove(true);
+
+  return NS_OK;
+}
+
+static nsresult
+deleteFile(nsIFile *aParentDir, const nsACString &aRelativePath)
+{
+  NS_ENSURE_ARG_POINTER(aParentDir);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aParentDir->Clone(getter_AddRefs(file));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!aRelativePath.IsEmpty()) {
+    rv = file->AppendRelativeNativePath(aRelativePath);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return file->Remove(false);
+}
+
+// When this function is called, aProfile is a brand new profile and
+// aAppDir is the directory that contains the firefox executable.
+// Our strategy is to check if an old "in application" profile exists at
+// <AppRootDir>/TorBrowser/Data/Browser/profile.default. If so, we set
+// aside the new profile directory and replace it with the old one.
+// We use a similar approach for the Tor data and UpdateInfo directories.
+static nsresult
+migrateInAppTorBrowserProfile(nsIToolkitProfile *aProfile, nsIFile *aAppDir)
+{
+  NS_ENSURE_ARG_POINTER(aProfile);
+  NS_ENSURE_ARG_POINTER(aAppDir);
+
+  nsCOMPtr<nsIFile> appRootDir;
+  nsresult rv = GetAppRootDir(aAppDir, getter_AddRefs(appRootDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Create an nsIFile for the old <AppRootDir>/TorBrowser directory.
+  nsCOMPtr<nsIFile> oldTorBrowserDir;
+  rv = appRootDir->Clone(getter_AddRefs(oldTorBrowserDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = oldTorBrowserDir->AppendRelativeNativePath(
+                                        NS_LITERAL_CSTRING("TorBrowser"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Create an nsIFile for the TorBrowser-Data directory (next to the app).
+  nsCOMPtr<nsIFile> newTBDataDir;
+  rv = appRootDir->GetParent(getter_AddRefs(newTBDataDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = newTBDataDir->AppendRelativeNativePath(
+                                      NS_LITERAL_CSTRING("TorBrowser-Data"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Try to migrate the browser profile. If this fails, we return an error
+  // code and we do not try to migrate any other data.
+  nsCOMPtr<nsIFile> newProfileDir;
+  rv = aProfile->GetRootDir(getter_AddRefs(newProfileDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsAutoCString path(NS_LITERAL_CSTRING("Data" XPCOM_FILE_PATH_SEPARATOR
+                    "Browser" XPCOM_FILE_PATH_SEPARATOR "profile.default"));
+  rv = migrateOneTorBrowserDataDir(oldTorBrowserDir, path,
+                                   newProfileDir, NS_LITERAL_CSTRING(""));
+  NS_ENSURE_SUCCESS(rv, rv);  // Return immediately upon failure.
+
+  // Try to migrate the Tor data directory but do not return upon failure.
+  nsAutoCString torDataDirPath(NS_LITERAL_CSTRING("Data"
+                                        XPCOM_FILE_PATH_SEPARATOR "Tor"));
+  rv = migrateOneTorBrowserDataDir(oldTorBrowserDir, torDataDirPath,
+                                   newTBDataDir, NS_LITERAL_CSTRING("Tor"));
+  if (NS_SUCCEEDED(rv)) {
+    // Make a "best effort" attempt to remove the Tor data files that should
+    // no longer be stored in the Tor user data directory (they have been
+    // relocated to a read-only Tor directory, e.g.,
+    // TorBrowser.app/Contents/Resources/TorBrowser/Tor.
+    deleteFile(newTBDataDir,
+         NS_LITERAL_CSTRING("Tor" XPCOM_FILE_PATH_SEPARATOR "geoip"));
+    deleteFile(newTBDataDir,
+         NS_LITERAL_CSTRING("Tor" XPCOM_FILE_PATH_SEPARATOR "geoip6"));
+    deleteFile(newTBDataDir,
+         NS_LITERAL_CSTRING("Tor" XPCOM_FILE_PATH_SEPARATOR "torrc-defaults"));
+  }
+
+  // Try to migrate the UpdateInfo directory.
+  nsAutoCString updateInfoPath(NS_LITERAL_CSTRING("UpdateInfo"));
+  nsresult rv2 = migrateOneTorBrowserDataDir(oldTorBrowserDir, updateInfoPath,
+                                             newTBDataDir, updateInfoPath);
+
+  // If all pieces of the migration succeeded, remove the old TorBrowser
+  // directory.
+  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2)) {
+    oldTorBrowserDir->Remove(true);
+  }
+
+  return NS_OK;
+}
+#endif
+
 static bool gDoMigration = false;
 static bool gDoProfileReset = false;
 
@@ -2372,7 +2614,8 @@ static bool gDoProfileReset = false;
 // 5) if there are *no* profiles, set up profile-migration
 // 6) display the profile-manager UI
 static nsresult
-SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative,
+SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc,
+              nsIFile *aAppDir, nsINativeAppSupport* aNative,
               bool* aStartOffline, nsACString* aProfileName)
 {
   StartupTimeline::Record(StartupTimeline::SELECT_PROFILE);
@@ -2653,6 +2896,20 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n
       aProfileSvc->SetDefaultProfile(profile);
 #endif
       aProfileSvc->Flush();
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+      // Handle migration from an older version of Tor Browser in which the
+      // user data was stored inside the application directory.
+      rv = migrateInAppTorBrowserProfile(profile, aAppDir);
+      if (!NS_SUCCEEDED(rv)) {
+        // Display an error alert and continue startup. Since XPCOM was
+        // initialized in a limited way inside ProfileErrorDialog() and
+        // because it cannot be reinitialized, use LaunchChild() to start
+        // the browser.
+        ProfileErrorDialog(profile, PROFILE_STATUS_MIGRATION_FAILED, nullptr,
+                           aNative, aResult);
+        return LaunchChild(aNative);
+      }
+#endif
       rv = profile->Lock(nullptr, aResult);
       if (NS_SUCCEEDED(rv)) {
         if (aProfileName)
@@ -3255,6 +3512,14 @@ XREMain::XRE_mainInit(bool* aExitFlag)
     NS_BREAK();
 #endif
 
+#if defined(XP_MACOSX) && defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR)
+  bool hideDockIcon = (CheckArg("invisible") == ARG_FOUND);
+  if (hideDockIcon) {
+    ProcessSerialNumber psn = { 0, kCurrentProcess };
+    TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
+  }
+#endif
+
 #ifdef USE_GLX_TEST
   // bug 639842 - it's very important to fire this process BEFORE we set up
   // error handling. indeed, this process is expected to be crashy, and we
@@ -4042,6 +4307,22 @@ XREMain::XRE_mainStartup(bool* aExitFlag)
 #endif
 
   rv = NS_NewToolkitProfileService(getter_AddRefs(mProfileSvc));
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+  if (NS_FAILED(rv)) {
+    // NS_NewToolkitProfileService() returns a generic NS_ERROR_FAILURE error
+    // if creation of the TorBrowser-Data directory fails due to access denied
+    // or because of a read-only disk volume. Do an extra check here to detect
+    // these errors so we can display an informative error message.
+    ProfileStatus status = CheckTorBrowserDataWriteAccess(exeDir);
+    if ((PROFILE_STATUS_ACCESS_DENIED == status) ||
+        (PROFILE_STATUS_READ_ONLY == status)) {
+      ProfileErrorDialog(nullptr, nullptr, status, nullptr, mNativeApp,
+                        nullptr);
+      return 1;
+    }
+  }
+#endif
+
   if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
     PR_fprintf(PR_STDERR, "Error: Access was denied while trying to open files in " \
                 "your profile directory.\n");
@@ -4052,8 +4333,8 @@ XREMain::XRE_mainStartup(bool* aExitFlag)
     return 1;
   }
 
-  rv = SelectProfile(getter_AddRefs(mProfileLock), mProfileSvc, mNativeApp, &mStartOffline,
-                      &mProfileName);
+  rv = SelectProfile(getter_AddRefs(mProfileLock), mProfileSvc, exeDir,
+                     mNativeApp, &mStartOffline, &mProfileName);
   if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ||
       rv == NS_ERROR_ABORT) {
     *aExitFlag = true;
diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp
index 1dce593..ab652a4 100644
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -1222,11 +1222,21 @@ nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal)
   NS_ENSURE_ARG_POINTER(aFile);
   nsCOMPtr<nsIFile> localDir;
 
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+  nsCOMPtr<nsIFile> appRootDir;
+  nsresult rv = GetAppRootDir(getter_AddRefs(appRootDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = appRootDir->GetParent(getter_AddRefs(localDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser-Data"
+                                     XPCOM_FILE_PATH_SEPARATOR "Browser"));
+#else
   nsresult rv = GetAppRootDir(getter_AddRefs(localDir));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser"
                                      XPCOM_FILE_PATH_SEPARATOR "Data"
                                      XPCOM_FILE_PATH_SEPARATOR "Browser"));
+#endif
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aLocal) {
diff --git a/xpcom/io/nsAppFileLocationProvider.cpp b/xpcom/io/nsAppFileLocationProvider.cpp
index f93e7f4..4948dbe 100644
--- a/xpcom/io/nsAppFileLocationProvider.cpp
+++ b/xpcom/io/nsAppFileLocationProvider.cpp
@@ -280,8 +280,13 @@ nsAppFileLocationProvider::CloneMozBinDirectory(nsIFile** aLocalFile)
 //----------------------------------------------------------------------------------------
 // GetProductDirectory - Gets the directory which contains the application data folder
 //
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+// UNIX and WIN   : <App Folder>/../TorBrowser-Data/Browser
+// Mac            : <App Folder>/../../../TorBrowser-Data/Browser
+#else
 // UNIX and WIN   : <App Folder>/TorBrowser/Data/Browser
 // Mac            : <App Folder>/../../TorBrowser/Data/Browser
+#endif
 //----------------------------------------------------------------------------------------
 NS_METHOD
 nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
@@ -298,7 +303,11 @@ nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
   rv = CloneMozBinDirectory(getter_AddRefs(localDir));
   NS_ENSURE_SUCCESS(rv, rv);
 
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+  int levelsToRemove = 2; // In FF21+, bin dir points to browser subdirectory.
+#else
   int levelsToRemove = 1; // In FF21+, bin dir points to browser subdirectory.
+#endif
 #if defined(XP_MACOSX)
   levelsToRemove += 2;
 #endif
@@ -324,9 +333,14 @@ nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
     return NS_ERROR_FAILURE;
   }
 
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+  rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser-Data"
+                                        XPCOM_FILE_PATH_SEPARATOR "Browser"));
+#else
   rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser"
                                         XPCOM_FILE_PATH_SEPARATOR "Data"
                                         XPCOM_FILE_PATH_SEPARATOR "Browser"));
+#endif
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aLocal) {





More information about the tor-commits mailing list