commit d629613da79e6aaf77a4e1c625f6f83f916e7e80 Author: Sukhbir Singh sukhbir@torproject.org 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@pearlcrescent.com +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@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, +@@ -1970,6 +1989,7 @@ GetOverrideStringBundle(nsIStringBundleService* aSBS, nsIStringBundle* *aResult) + } + + uriString.Append("profile.default/extensions/torbutton@torproject.org.xpi"); ++#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 +