From 0396544a8adc77afc5b7a13cef8a64788353a481 Mon Sep 17 00:00:00 2001
From: Pier Angelo Vendrame <pierov@torproject.org>
Date: Tue, 31 Jan 2023 11:59:21 +0100
Subject: [PATCH] Bug 13252: Customize profile management on macOS

On macOS we allow both portable mode and system installation.
However, in the latter case, we customize Firefox's directories to
match the hierarchy we use for the portable mode.

Also, display an informative error message if the TorBrowser-Data
directory cannot be created due to an "access denied" or a
"read only volume" error.
---
 toolkit/xre/nsXREDirProvider.cpp       | 49 +++++++++++++++++++++++---
 toolkit/xre/nsXREDirProvider.h         |  7 ++++
 xpcom/io/nsAppFileLocationProvider.cpp | 20 +++++++++++
 3 files changed, 71 insertions(+), 5 deletions(-)

diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp
index 8b3f9a8509045..924a38481e41f 100644
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -238,7 +238,9 @@ nsresult nsXREDirProvider::GetUserProfilesRootDir(nsIFile** aResult) {
   nsresult rv = GetUserDataDirectory(getter_AddRefs(file), false);
 
   if (NS_SUCCEEDED(rv)) {
-#if !defined(XP_UNIX) || defined(XP_MACOSX)
+    // For some reason, we have decided not to append "Profiles" in Tor Browser.
+    // So, let's keep removing it, or we should somehow migrate the profile.
+#if !defined(TOR_BROWSER) && (!defined(XP_UNIX) || defined(XP_MACOSX))
     rv = file->AppendNative("Profiles"_ns);
 #endif
     // We must create the profile directory here if it does not exist.
@@ -256,7 +258,7 @@ nsresult nsXREDirProvider::GetUserProfilesLocalDir(nsIFile** aResult) {
   nsresult rv = GetUserDataDirectory(getter_AddRefs(file), true);
 
   if (NS_SUCCEEDED(rv)) {
-#if !defined(XP_UNIX) || defined(XP_MACOSX)
+#if !defined(TOR_BROWSER) && (!defined(XP_UNIX) || defined(XP_MACOSX))
     rv = file->AppendNative("Profiles"_ns);
 #endif
     // We must create the profile directory here if it does not exist.
@@ -1482,16 +1484,20 @@ nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile,
 
 #if defined(XP_MACOSX)
   FSRef fsRef;
+#  if defined(TOR_BROWSER)
+  OSType folderType = kApplicationSupportFolderType;
+#  else
   OSType folderType;
   if (aLocal) {
     folderType = kCachedDataFolderType;
   } else {
-#  ifdef MOZ_THUNDERBIRD
+#    ifdef MOZ_THUNDERBIRD
     folderType = kDomainLibraryFolderType;
-#  else
+#    else
     folderType = kApplicationSupportFolderType;
-#  endif
+#    endif
   }
+#  endif
   OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef);
   NS_ENSURE_FALSE(err, NS_ERROR_FAILURE);
 
@@ -1504,6 +1510,17 @@ nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile,
   rv = dirFileMac->InitWithFSRef(&fsRef);
   NS_ENSURE_SUCCESS(rv, rv);
 
+#  if defined(TOR_BROWSER)
+  rv = dirFileMac->AppendNative("TorBrowser-Data"_ns);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = dirFileMac->AppendNative("Browser"_ns);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (aLocal) {
+    rv = dirFileMac->AppendNative("Caches"_ns);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+#  endif
+
   localDir = dirFileMac;
 #elif defined(XP_IOS)
   nsAutoCString userDir;
@@ -1693,6 +1710,9 @@ nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) {
   nsresult rv = NS_OK;
 
 #if defined(XP_MACOSX)
+#  ifndef TOR_BROWSER
+  // For Tor Browser we already prepare the data directory as we need it, even
+  // when we are running a system install.
   if (!profile.IsEmpty()) {
     rv = AppendProfileString(aFile, profile.get());
   } else {
@@ -1702,6 +1722,7 @@ nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) {
     rv = aFile->AppendNative(appName);
   }
   NS_ENSURE_SUCCESS(rv, rv);
+#  endif
 
 #elif defined(XP_WIN)
   if (!profile.IsEmpty()) {
@@ -1786,3 +1807,21 @@ nsresult nsXREDirProvider::AppendProfileString(nsIFile* aFile,
 
   return NS_OK;
 }
+
+nsresult nsXREDirProvider::GetTorBrowserUserDataDir(nsIFile** aFile) {
+#ifdef ANDROID
+  // We expect this function not to be called on Android.
+  // But, for sake of completeness, we handle also this case.
+  const char* homeDir = getenv("HOME");
+  if (!homeDir || !*homeDir) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_NewNativeLocalFile(nsDependentCString(homeDir), true, aFile);
+#endif
+
+  NS_ENSURE_ARG_POINTER(aFile);
+  nsCOMPtr<nsIFile> dataDir;
+  nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(dataDir), false);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return dataDir->GetParent(aFile);
+}
diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h
index 4b3eb00173e97..13f41c88e19ef 100644
--- a/toolkit/xre/nsXREDirProvider.h
+++ b/toolkit/xre/nsXREDirProvider.h
@@ -109,6 +109,13 @@ class nsXREDirProvider final : public nsIDirectoryServiceProvider2,
    */
   nsresult GetProfileDir(nsIFile** aResult);
 
+  /**
+   * Get the Tor Browser user data directory.
+   * We take for granted that for Tor Browser we can take the parent directory
+   * of the one returned by GetUserDataDirectoryHome (with aLocal = false).
+   */
+  nsresult GetTorBrowserUserDataDir(nsIFile** aFile);
+
  protected:
   nsresult GetFilesInternal(const char* aProperty,
                             nsISimpleEnumerator** aResult);
diff --git a/xpcom/io/nsAppFileLocationProvider.cpp b/xpcom/io/nsAppFileLocationProvider.cpp
index 72d161c561481..4caa57af1fbed 100644
--- a/xpcom/io/nsAppFileLocationProvider.cpp
+++ b/xpcom/io/nsAppFileLocationProvider.cpp
@@ -354,8 +354,12 @@ nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
 
 #if defined(MOZ_WIDGET_COCOA)
   FSRef fsRef;
+#  if defined(TOR_BROWSER)
+  OSType folderType = kApplicationSupportFolderType;
+#  else
   OSType folderType =
       aLocal ? (OSType)kCachedDataFolderType : (OSType)kDomainLibraryFolderType;
+#  endif
   OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef);
   if (err) {
     return NS_ERROR_FAILURE;
@@ -369,6 +373,17 @@ nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
   if (NS_FAILED(rv)) {
     return rv;
   }
+
+#  if defined(TOR_BROWSER)
+  rv = localDir->AppendNative("TorBrowser-Data"_ns);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = localDir->AppendNative("Browser"_ns);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (aLocal) {
+    rv = localDir->AppendNative("Caches"_ns);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+#  endif
 #elif defined(XP_WIN)
   nsCOMPtr<nsIProperties> directoryService =
       do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
@@ -391,10 +406,12 @@ nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
 #  error dont_know_how_to_get_product_dir_on_your_platform
 #endif
 
+#if !defined(TOR_BROWSER)
   rv = localDir->AppendRelativeNativePath(DEFAULT_PRODUCT_DIR);
   if (NS_FAILED(rv)) {
     return rv;
   }
+#endif
   rv = localDir->Exists(&exists);
 
   if (NS_SUCCEEDED(rv) && !exists) {
@@ -435,10 +452,13 @@ nsresult nsAppFileLocationProvider::GetDefaultUserProfileRoot(
 
 #if defined(MOZ_WIDGET_COCOA) || defined(XP_WIN)
   // These 3 platforms share this part of the path - do them as one
+#  ifndef TOR_BROWSER
+  // Legacy: we do not use "Profiles" on Tor Browser.
   rv = localDir->AppendRelativeNativePath("Profiles"_ns);
   if (NS_FAILED(rv)) {
     return rv;
   }
+#  endif
 
   bool exists;
   rv = localDir->Exists(&exists);
-- 
GitLab