[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [tor-messenger-build/master] ADd patch for #13252 (outside app directory)
commit d629613da79e6aaf77a4e1c625f6f83f916e7e80
Author: Sukhbir Singh <sukhbir@xxxxxxxxxxxxxx>
Date: Mon Jul 18 12:40:35 2016 -0400
ADd patch for #13252 (outside app directory)
---
.../Mac-outside-app-data-bug-13252.mozpatch | 1124 ++++++++++++++++++++
1 file changed, 1124 insertions(+)
diff --git a/projects/instantbird/Mac-outside-app-data-bug-13252.mozpatch b/projects/instantbird/Mac-outside-app-data-bug-13252.mozpatch
new file mode 100644
index 0000000..a68f446
--- /dev/null
+++ b/projects/instantbird/Mac-outside-app-data-bug-13252.mozpatch
@@ -0,0 +1,1124 @@
+From 4f8084edd80a3726e9a995ff3407331807401558 Mon Sep 17 00:00:00 2001
+From: Kathy Brade <brade@xxxxxxxxxxxxxxxxx>
+Date: Fri, 18 Mar 2016 14:20:02 -0400
+Subject: 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.
+
+diff --git a/configure.in b/configure.in
+index e9fb038..885ee84 100644
+--- a/configure.in
++++ b/configure.in
+@@ -6538,9 +6538,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 a453062..e2d4e44 100644
+--- a/toolkit/mozapps/extensions/AddonManager.jsm
++++ b/toolkit/mozapps/extensions/AddonManager.jsm
+@@ -45,6 +45,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 PREF_MIN_WEBEXT_PLATFORM_VERSION = "extensions.webExtensionsMinPlatformVersion";
+
+ const UPDATE_REQUEST_VERSION = 2;
+@@ -910,6 +915,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
++
+ if (!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 7a58a7d..51c51b8 100644
+--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
++++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+@@ -130,6 +130,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_SYSTEM_ADDONS = "features";
+ const DIR_STAGE = "staged";
+ const DIR_TRASH = "trash";
+@@ -3510,6 +3511,58 @@ this.XPIProvider = {
+ return changed;
+ },
+
++ /**
++ * 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;
++ },
++
+ /**
+ * Imports the xpinstall permissions from preferences into the permissions
+ * manager for the user to change later.
+@@ -3583,6 +3636,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/mozapps/extensions/moz.build b/toolkit/mozapps/extensions/moz.build
+index 8fbd96d..06091a5 100644
+--- a/toolkit/mozapps/extensions/moz.build
++++ b/toolkit/mozapps/extensions/moz.build
+@@ -30,12 +30,15 @@ EXTRA_PP_COMPONENTS += [
+ ]
+
+ EXTRA_JS_MODULES += [
+- 'AddonManager.jsm',
+ 'ChromeManifestParser.jsm',
+ 'DeferredSave.jsm',
+ 'LightweightThemeManager.jsm',
+ ]
+
++EXTRA_PP_JS_MODULES += [
++ 'AddonManager.jsm',
++]
++
+ JAR_MANIFESTS += ['jar.mn']
+
+ EXPORTS.mozilla += [
+diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp
+index a3a2857..20be296 100644
+--- a/toolkit/xre/nsAppRunner.cpp
++++ b/toolkit/xre/nsAppRunner.cpp
+@@ -1949,11 +1949,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@xxxxxxxxxxxxxxxxxx");
++#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,
+@@ -1970,6 +1989,7 @@ GetOverrideStringBundle(nsIStringBundleService* aSBS, nsIStringBundle* *aResult)
+ }
+
+ uriString.Append("profile.default/extensions/torbutton@xxxxxxxxxxxxxxxxxx");
++#endif
+
+ nsCString userAgentLocale;
+ if (!NS_SUCCEEDED(Preferences::GetCString("general.useragent.locale",
+@@ -2019,6 +2039,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[] =
+@@ -2058,6 +2081,8 @@ private:
+
+ } // 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,
+@@ -2069,7 +2094,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);
+@@ -2100,21 +2126,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));
+@@ -2158,7 +2190,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);
+ }
+@@ -2449,6 +2482,223 @@ 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.
++ nsCOMPtr<nsIFile> tbDataDir;
++ nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton();
++ if (!dirProvider)
++ return PROFILE_STATUS_OTHER_ERROR;
++ nsresult rv =
++ dirProvider->GetTorBrowserUserDataDir(getter_AddRefs(tbDataDir));
++ NS_ENSURE_SUCCESS(rv, PROFILE_STATUS_OTHER_ERROR);
++ nsCOMPtr<nsIFile> tbDataDirParent;
++ rv = tbDataDir->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);
++
++ // Get an nsIFile for the TorBrowser-Data directory.
++ nsCOMPtr<nsIFile> newTBDataDir;
++ nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton();
++ if (!dirProvider)
++ return NS_ERROR_UNEXPECTED;
++ rv = dirProvider->GetTorBrowserUserDataDir(getter_AddRefs(newTBDataDir));
++ 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.
++ nsCOMPtr<nsIFile> newUpdateInfoDir;
++ nsresult rv2 = dirProvider->GetUpdateRootDir(
++ getter_AddRefs(newUpdateInfoDir));
++ if (NS_SUCCEEDED(rv2)) {
++ nsAutoCString updateInfoPath(NS_LITERAL_CSTRING("UpdateInfo"));
++ rv2 = migrateOneTorBrowserDataDir(oldTorBrowserDir, updateInfoPath,
++ newUpdateInfoDir, NS_LITERAL_CSTRING(""));
++ }
++
++ // 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;
+
+@@ -2461,7 +2711,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);
+@@ -2742,6 +2993,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)
+@@ -3313,6 +3578,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
+@@ -4073,6 +4346,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");
+@@ -4083,8 +4372,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 403e820..e3c1449 100644
+--- a/toolkit/xre/nsXREDirProvider.cpp
++++ b/toolkit/xre/nsXREDirProvider.cpp
+@@ -38,6 +38,8 @@
+ #include "mozilla/Preferences.h"
+ #include "mozilla/Telemetry.h"
+
++#include "TorFileUtils.h"
++
+ #include <stdlib.h>
+
+ #ifdef XP_WIN
+@@ -1057,40 +1059,48 @@ nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult)
+ getter_AddRefs(updRoot));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+-#else
++#elif defined(TOR_BROWSER_UPDATE)
++ // For Tor Browser, we store update history, etc. within the UpdateInfo
++ // directory under the user data directory.
++ nsresult rv = GetTorBrowserUserDataDir(getter_AddRefs(updRoot));
++ NS_ENSURE_SUCCESS(rv, rv);
++ rv = updRoot->AppendNative(NS_LITERAL_CSTRING("UpdateInfo"));
++ NS_ENSURE_SUCCESS(rv, rv);
++#if defined(XP_MACOSX) && defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR)
++ // Since the TorBrowser-Data directory may be shared among different
++ // installations of the application, embed the app path in the update dir
++ // so that the update history is partitioned. This is much less likely to
++ // be an issue on Linux or Windows because the Tor Browser packages for
++ // those platforms include a "container" folder that provides partitioning
++ // by default, and we do not support use of a shared, OS-recommended area
++ // for user data on those platforms.
+ nsCOMPtr<nsIFile> appFile;
+ bool per = false;
+- nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
+- NS_ENSURE_SUCCESS(rv, rv);
+- rv = appFile->GetParent(getter_AddRefs(updRoot));
++ rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
+ NS_ENSURE_SUCCESS(rv, rv);
++ nsCOMPtr<nsIFile> appRootDirFile;
++ nsAutoString appDirPath;
++ if (NS_FAILED(appFile->GetParent(getter_AddRefs(appRootDirFile))) ||
++ NS_FAILED(appRootDirFile->GetPath(appDirPath))) {
++ return NS_ERROR_FAILURE;
++ }
+
+-#ifdef XP_MACOSX
+-#ifdef TOR_BROWSER_UPDATE
+-#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+- // For Tor Browser, we cannot store update history, etc. under the user's
+- // home directory. Instead, we place it under
+- // Tor Browser.app/../TorBrowser-Data/UpdateInfo/
+- nsCOMPtr<nsIFile> appRootDir;
+- rv = GetAppRootDir(getter_AddRefs(appRootDir));
+- NS_ENSURE_SUCCESS(rv, rv);
+- nsCOMPtr<nsIFile> localDir;
+- rv = appRootDir->GetParent(getter_AddRefs(localDir));
+- NS_ENSURE_SUCCESS(rv, rv);
+- rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser-Data"
+- XPCOM_FILE_PATH_SEPARATOR "UpdateInfo"));
+-#else
+- // For Tor Browser, we cannot store update history, etc. under the user's home directory.
+- // Instead, we place it under Tor Browser.app/TorBrowser/UpdateInfo/
+- nsCOMPtr<nsIFile> localDir;
+- rv = GetAppRootDir(getter_AddRefs(localDir));
+- NS_ENSURE_SUCCESS(rv, rv);
+- rv = localDir->AppendNative(NS_LITERAL_CSTRING("TorBrowser"));
++ int32_t dotIndex = appDirPath.RFind(".app");
++ if (dotIndex == kNotFound) {
++ dotIndex = appDirPath.Length();
++ }
++ appDirPath = Substring(appDirPath, 1, dotIndex - 1);
++ rv = updRoot->AppendRelativePath(appDirPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+- rv = localDir->AppendNative(NS_LITERAL_CSTRING("UpdateInfo"));
+ #endif
++#else // ! TOR_BROWSER_UPDATE
++ nsCOMPtr<nsIFile> appFile;
++ bool per = false;
++ nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+-#else
++ rv = appFile->GetParent(getter_AddRefs(updRoot));
++ NS_ENSURE_SUCCESS(rv, rv);
++#ifdef XP_MACOSX
+ nsCOMPtr<nsIFile> appRootDirFile;
+ nsCOMPtr<nsIFile> localDir;
+ nsAutoString appDirPath;
+@@ -1121,7 +1131,6 @@ nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult)
+ NS_FAILED(localDir->AppendRelativePath(appDirPath))) {
+ return NS_ERROR_FAILURE;
+ }
+-#endif
+
+ localDir.forget(aResult);
+ return NS_OK;
+@@ -1215,7 +1224,7 @@ nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult)
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ #endif // XP_WIN
+-#endif
++#endif // ! TOR_BROWSER_UPDATE
+ updRoot.forget(aResult);
+ return NS_OK;
+ }
+@@ -1268,12 +1277,15 @@ nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal)
+ // Copied from nsAppFileLocationProvider (more or less)
+ NS_ENSURE_ARG_POINTER(aFile);
+ nsCOMPtr<nsIFile> localDir;
+-
+- nsresult rv = GetAppRootDir(getter_AddRefs(localDir));
++ nsresult rv = GetTorBrowserUserDataDir(getter_AddRefs(localDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+- rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser"
+- XPCOM_FILE_PATH_SEPARATOR "Data"
++
++#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
++ rv = localDir->AppendNative(NS_LITERAL_CSTRING("Browser"));
++#else
++ rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("Data"
+ XPCOM_FILE_PATH_SEPARATOR "Browser"));
++#endif
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aLocal) {
+@@ -1377,43 +1389,16 @@ nsXREDirProvider::GetUserDataDirectory(nsIFile** aFile, bool aLocal,
+ }
+
+ nsresult
+-nsXREDirProvider::GetAppRootDir(nsIFile* *aFile)
++nsXREDirProvider::GetTorBrowserUserDataDir(nsIFile* *aFile)
+ {
+ NS_ENSURE_ARG_POINTER(aFile);
+- nsCOMPtr<nsIFile> appRootDir;
+-
+- nsresult rv = GetAppDir()->Clone(getter_AddRefs(appRootDir));
++ nsCOMPtr<nsIFile> exeFile;
++ bool per = false;
++ nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(exeFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+-
+- int levelsToRemove = 0; // In FF21+, appDir points to browser subdirectory.
+-#if defined(XP_MACOSX)
+- levelsToRemove += 1;
+-#endif
+- while (appRootDir && (levelsToRemove > 0)) {
+- // When crawling up the hierarchy, components named "." do not count.
+- nsAutoCString removedName;
+- rv = appRootDir->GetNativeLeafName(removedName);
+- NS_ENSURE_SUCCESS(rv, rv);
+- bool didRemove = !removedName.Equals(".");
+-
+- // Remove a directory component.
+- nsCOMPtr<nsIFile> parentDir;
+- rv = appRootDir->GetParent(getter_AddRefs(parentDir));
+- NS_ENSURE_SUCCESS(rv, rv);
+- appRootDir = parentDir;
+-
+- if (didRemove)
+- --levelsToRemove;
+- }
+-
+- if (!appRootDir)
+- return NS_ERROR_FAILURE;
+-
+- appRootDir.forget(aFile);
+- return NS_OK;
++ return TorBrowser_GetUserDataDir(exeFile, aFile);
+ }
+
+-
+ nsresult
+ nsXREDirProvider::EnsureDirectoryExists(nsIFile* aDirectory)
+ {
+diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h
+index b86cc68..e8190e3 100644
+--- a/toolkit/xre/nsXREDirProvider.h
++++ b/toolkit/xre/nsXREDirProvider.h
+@@ -100,6 +100,12 @@ public:
+ */
+ nsresult GetProfileDir(nsIFile* *aResult);
+
++ /**
++ * Get the TorBrowser user data directory by calling the
++ * TorBrowser_GetUserDataDir() utility function.
++ */
++ nsresult GetTorBrowserUserDataDir(nsIFile* *aFile);
++
+ protected:
+ nsresult GetFilesInternal(const char* aProperty, nsISimpleEnumerator** aResult);
+ nsresult GetUserDataDirectoryHome(nsIFile* *aFile, bool aLocal);
+@@ -107,7 +113,6 @@ protected:
+ #if defined(XP_UNIX) || defined(XP_MACOSX)
+ static nsresult GetSystemExtensionsDirectory(nsIFile** aFile);
+ #endif
+- nsresult GetAppRootDir(nsIFile* *aFile);
+ static nsresult EnsureDirectoryExists(nsIFile* aDirectory);
+ void EnsureProfileFileExists(nsIFile* aFile);
+
+diff --git a/xpcom/io/TorFileUtils.cpp b/xpcom/io/TorFileUtils.cpp
+new file mode 100644
+index 0000000..2b0b100
+--- /dev/null
++++ b/xpcom/io/TorFileUtils.cpp
+@@ -0,0 +1,130 @@
++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
++/* vim: set ts=8 sts=2 et sw=2 tw=80: */
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#include "TorFileUtils.h"
++
++static nsresult GetAppRootDir(nsIFile *aExeFile, nsIFile** aFile);
++
++//-----------------------------------------------------------------------------
++NS_METHOD
++TorBrowser_GetUserDataDir(nsIFile *aExeFile, nsIFile** aFile)
++{
++ NS_ENSURE_ARG_POINTER(aFile);
++ nsCOMPtr<nsIFile> tbDataDir;
++
++#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
++ nsAutoCString tbDataLeafName(NS_LITERAL_CSTRING("TorBrowser-Data"));
++ nsCOMPtr<nsIFile> appRootDir;
++ nsresult rv = GetAppRootDir(aExeFile, getter_AddRefs(appRootDir));
++ NS_ENSURE_SUCCESS(rv, rv);
++#ifndef XP_MACOSX
++ // On all platforms except Mac OS, we always operate in a "portable" mode
++ // where the TorBrowser-Data directory is located next to the application.
++ rv = appRootDir->GetParent(getter_AddRefs(tbDataDir));
++ NS_ENSURE_SUCCESS(rv, rv);
++ rv = tbDataDir->AppendNative(tbDataLeafName);
++ NS_ENSURE_SUCCESS(rv, rv);
++#else
++ // For Mac OS, determine whether we should store user data in the OS's
++ // standard location (i.e., under ~/Library/Application Support). We use
++ // the OS location if (1) the application is installed in a directory whose
++ // path contains "/Applications" or (2) the TorBrowser-Data directory does
++ // not exist and cannot be created (which probably means we lack write
++ // permission to the directory that contains the application).
++ nsAutoString appRootPath;
++ rv = appRootDir->GetPath(appRootPath);
++ NS_ENSURE_SUCCESS(rv, rv);
++ bool useOSLocation = (appRootPath.Find("/Applications",
++ true /* ignore case */) >= 0);
++ if (!useOSLocation) {
++ // We hope to use the portable (aka side-by-side) approach, but before we
++ // commit to that, let's ensure that we can create the TorBrowser-Data
++ // directory. If it already exists, we will try to use it; if not and we
++ // fail to create it, we will switch to ~/Library/Application Support.
++ rv = appRootDir->GetParent(getter_AddRefs(tbDataDir));
++ NS_ENSURE_SUCCESS(rv, rv);
++ rv = tbDataDir->AppendNative(tbDataLeafName);
++ NS_ENSURE_SUCCESS(rv, rv);
++ bool exists = false;
++ rv = tbDataDir->Exists(&exists);
++ if (NS_SUCCEEDED(rv) && !exists)
++ rv = tbDataDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
++ useOSLocation = NS_FAILED(rv);
++ }
++
++ if (useOSLocation) {
++ // We are using ~/Library/Application Support/TorBrowser-Data. We do not
++ // need to create that directory here because the code in nsXREDirProvider
++ // will do so (and the user should always have write permission for
++ // ~/Library/Application Support; if they do not we have no more options).
++ FSRef fsRef;
++ OSErr err = ::FSFindFolder(kUserDomain, kApplicationSupportFolderType,
++ kCreateFolder, &fsRef);
++ NS_ENSURE_FALSE(err, NS_ERROR_FAILURE);
++ // To convert the FSRef returned by FSFindFolder() into an nsIFile that
++ // points to ~/Library/Application Support, we first create an empty
++ // nsIFile object (no path) and then use InitWithFSRef() to set the
++ // path.
++ rv = NS_NewNativeLocalFile(EmptyCString(), true,
++ getter_AddRefs(tbDataDir));
++ NS_ENSURE_SUCCESS(rv, rv);
++ nsCOMPtr<nsILocalFileMac> dirFileMac = do_QueryInterface(tbDataDir);
++ if (!dirFileMac)
++ return NS_ERROR_UNEXPECTED;
++ rv = dirFileMac->InitWithFSRef(&fsRef);
++ NS_ENSURE_SUCCESS(rv, rv);
++ rv = tbDataDir->AppendNative(tbDataLeafName);
++ NS_ENSURE_SUCCESS(rv, rv);
++ }
++#endif
++
++#else
++ // User data is embedded within the application directory (i.e.,
++ // TOR_BROWSER_DATA_OUTSIDE_APP_DIR is not defined).
++ nsresult rv = GetAppRootDir(aExeFile, getter_AddRefs(tbDataDir));
++ NS_ENSURE_SUCCESS(rv, rv);
++ rv = tbDataDir->AppendNative(NS_LITERAL_CSTRING("TorBrowser"));
++ NS_ENSURE_SUCCESS(rv, rv);
++#endif
++
++ tbDataDir.forget(aFile);
++ return NS_OK;
++}
++
++static nsresult
++GetAppRootDir(nsIFile *aExeFile, nsIFile** aFile)
++{
++ NS_ENSURE_ARG_POINTER(aExeFile);
++ NS_ENSURE_ARG_POINTER(aFile);
++ nsCOMPtr<nsIFile> appRootDir = aExeFile;
++
++ int levelsToRemove = 0; // Remove firefox (the executable file).
++#if defined(XP_MACOSX)
++ levelsToRemove += 1; // On Mac OS, we must also remove Contents/MacOS.
++#endif
++ while (appRootDir && (levelsToRemove > 0)) {
++ // When crawling up the hierarchy, components named "." do not count.
++ nsAutoCString removedName;
++ nsresult rv = appRootDir->GetNativeLeafName(removedName);
++ NS_ENSURE_SUCCESS(rv, rv);
++ bool didRemove = !removedName.Equals(".");
++
++ // Remove a directory component.
++ nsCOMPtr<nsIFile> parentDir;
++ rv = appRootDir->GetParent(getter_AddRefs(parentDir));
++ NS_ENSURE_SUCCESS(rv, rv);
++ appRootDir = parentDir;
++
++ if (didRemove)
++ --levelsToRemove;
++ }
++
++ if (!appRootDir)
++ return NS_ERROR_FAILURE;
++
++ appRootDir.forget(aFile);
++ return NS_OK;
++}
+diff --git a/xpcom/io/TorFileUtils.h b/xpcom/io/TorFileUtils.h
+new file mode 100644
+index 0000000..293ed04
+--- /dev/null
++++ b/xpcom/io/TorFileUtils.h
+@@ -0,0 +1,35 @@
++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
++/* vim: set ts=8 sts=2 et sw=2 tw=80: */
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#ifndef TorFileUtils_h__
++#define TorFileUtils_h__
++
++#include "nsIFile.h"
++
++class nsIFile;
++
++/**
++ * TorBrowser_GetUserDataDir
++ *
++ * Retrieve the Tor Browser user data directory.
++ * When built with --enable-tor-browser-data-outside-app-dir, the directory
++ * is next to the application directory, except on Mac OS where it may be
++ * there or it may be at ~/Library/Application Support/TorBrowser-Data (the
++ * latter location is used if the .app bundle is in a directory whose path
++ * contains /Applications or if we lack write access to the directory that
++ * contains the .app).
++ * When built without --enable-tor-browser-data-outside-app-dir, this
++ * directory is TorBrowser.app/TorBrowser.
++ *
++ * @param aExeFile The firefox executable.
++ * @param aFile Out parameter that is set to the Tor Browser user data
++ * directory.
++ * @return NS_OK on success. Error otherwise.
++ */
++extern NS_METHOD
++TorBrowser_GetUserDataDir(nsIFile *aExeFile, nsIFile** aFile);
++
++#endif // !TorFileUtils_h__
+diff --git a/xpcom/io/moz.build b/xpcom/io/moz.build
+index bc62f71..c90f33c 100644
+--- a/xpcom/io/moz.build
++++ b/xpcom/io/moz.build
+@@ -80,6 +80,7 @@ EXPORTS += [
+ 'nsUnicharInputStream.h',
+ 'nsWildCard.h',
+ 'SpecialSystemDirectory.h',
++ 'TorFileUtils.h',
+ ]
+
+ EXPORTS.mozilla += [
+@@ -116,6 +117,7 @@ UNIFIED_SOURCES += [
+ 'SnappyFrameUtils.cpp',
+ 'SnappyUncompressInputStream.cpp',
+ 'SpecialSystemDirectory.cpp',
++ 'TorFileUtils.cpp',
+ ]
+
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+diff --git a/xpcom/io/nsAppFileLocationProvider.cpp b/xpcom/io/nsAppFileLocationProvider.cpp
+index 039a89e..da66b90 100644
+--- a/xpcom/io/nsAppFileLocationProvider.cpp
++++ b/xpcom/io/nsAppFileLocationProvider.cpp
+@@ -29,6 +29,7 @@
+ #include <sys/param.h>
+ #endif
+
++#include "TorFileUtils.h"
+
+ // WARNING: These hard coded names need to go away. They need to
+ // come from localizable resources
+@@ -282,8 +283,14 @@ 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 OR
++// ~/Library/Application Support/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,
+@@ -293,42 +300,25 @@ nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
+ return NS_ERROR_INVALID_ARG;
+ }
+
+- nsresult rv;
++ nsresult rv = NS_ERROR_UNEXPECTED;
+ bool exists;
+- nsCOMPtr<nsIFile> localDir;
++ nsCOMPtr<nsIFile> localDir, exeFile;
+
+- rv = CloneMozBinDirectory(getter_AddRefs(localDir));
++ nsCOMPtr<nsIProperties> directoryService(
++ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
++ NS_ENSURE_SUCCESS(rv, rv);
++ rv = directoryService->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
++ getter_AddRefs(exeFile));
++ NS_ENSURE_SUCCESS(rv, rv);
++ rv = TorBrowser_GetUserDataDir(exeFile, getter_AddRefs(localDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+- int levelsToRemove = 0; // In FF21+, bin dir points to browser subdirectory.
+-#if defined(XP_MACOSX)
+- levelsToRemove += 1;
+-#endif
+- while (localDir && (levelsToRemove > 0)) {
+- // When crawling up the hierarchy, components named "." do not count.
+- nsAutoCString removedName;
+- rv = localDir->GetNativeLeafName(removedName);
+- NS_ENSURE_SUCCESS(rv, rv);
+- bool didRemove = !removedName.Equals(".");
+-
+- // Remove a directory component.
+- nsCOMPtr<nsIFile> parentDir;
+- rv = localDir->GetParent(getter_AddRefs(parentDir));
+- NS_ENSURE_SUCCESS(rv, rv);
+- localDir = parentDir;
+-
+- if (didRemove) {
+- --levelsToRemove;
+- }
+- }
+-
+- if (!localDir) {
+- return NS_ERROR_FAILURE;
+- }
+-
+- rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser"
+- XPCOM_FILE_PATH_SEPARATOR "Data"
++#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
++ rv = localDir->AppendNative(NS_LITERAL_CSTRING("Browser"));
++#else
++ rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("Data"
+ XPCOM_FILE_PATH_SEPARATOR "Browser"));
++#endif
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aLocal) {
+--
+cgit v0.10.2
+
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits