From 02a4f59cddba7eddfdf7dc3f45785ca98e57be8b Mon Sep 17 00:00:00 2001 From: Pier Angelo Vendrame <pierov@torproject.org> Date: Fri, 27 Jan 2023 17:33:29 +0100 Subject: [PATCH] Bug 9173: Change the default Firefox profile directory to be relative. This commit makes Firefox look for the default profile directory in a directory relative to the binary path. The directory can be specified through the --with-relative-data-dir. This is relative to the same directory as the firefox main binary for Linux and Windows. On macOS, we remove Contents/MacOS from it. Or, in other words, the directory is relative to the application bundle. This behavior can be overriden at runtime, by placing a file called system-install adjacent to the firefox main binary (also on macOS). --- browser/config/mozconfigs/base-browser | 2 + moz.configure | 19 ++++ mozconfig-macos | 3 + toolkit/xre/nsAppRunner.cpp | 16 ++-- toolkit/xre/nsXREDirProvider.cpp | 115 ++++++++++++++++++++++++ toolkit/xre/nsXREDirProvider.h | 8 ++ xpcom/io/nsAppFileLocationProvider.cpp | 120 +++++++++++++++++++++++-- 7 files changed, 271 insertions(+), 12 deletions(-) diff --git a/browser/config/mozconfigs/base-browser b/browser/config/mozconfigs/base-browser index 18bf4f60b2658..14eb0dfb2b27a 100644 --- a/browser/config/mozconfigs/base-browser +++ b/browser/config/mozconfigs/base-browser @@ -45,3 +45,5 @@ ac_add_options MOZ_TELEMETRY_REPORTING= if test -z "$WASI_SYSROOT"; then ac_add_options --without-wasm-sandboxed-libraries fi + +ac_add_options --with-relative-data-dir=BaseBrowser/Data/Browser diff --git a/moz.configure b/moz.configure index 6a44e9dcd904d..815cddc2f2441 100755 --- a/moz.configure +++ b/moz.configure @@ -1017,6 +1017,25 @@ set_config("ZLIB_IN_MOZGLUE", zlib_in_mozglue) set_define("ZLIB_IN_MOZGLUE", zlib_in_mozglue) +option( + "--with-relative-data-dir", + nargs=1, + help="Sets the data directories to be relative to the application directory" +) + + +@depends("--with-relative-data-dir", target) +@imports("json") +def relative_data_dir(value, target): + if value and target.os == "Android": + die("--with-relative-data-dir is not supported on Android") + if value: + return json.dumps(value[0]) + + +set_define("RELATIVE_DATA_DIR", relative_data_dir) + + option( "--with-base-browser-version", nargs=1, diff --git a/mozconfig-macos b/mozconfig-macos index 64f67c01ea150..074da9a4d7fe6 100644 --- a/mozconfig-macos +++ b/mozconfig-macos @@ -4,3 +4,6 @@ ac_add_options --enable-strip # See bug #41131 ac_add_options --disable-update-agent + +# For base-browser we do not enable portable mode on macOS. +ac_add_options --without-relative-data-dir diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 79baf84160830..44ce6b8db2032 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -2777,6 +2777,8 @@ static nsresult ProfileMissingDialog(nsINativeAppSupport* aNative) { #endif // MOZ_WIDGET_ANDROID } +// If aUnlocker is NULL, it is also OK for the following arguments to be NULL: +// aProfileDir, aProfileLocalDir, aResult. static ReturnAbortOnError ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, nsIProfileUnlocker* aUnlocker, @@ -2784,17 +2786,21 @@ static ReturnAbortOnError ProfileLockedDialog(nsIFile* aProfileDir, nsIProfileLock** aResult) { nsresult rv; - bool exists; - aProfileDir->Exists(&exists); - if (!exists) { - return ProfileMissingDialog(aNative); + if (aProfileDir) { + bool exists; + aProfileDir->Exists(&exists); + if (!exists) { + return ProfileMissingDialog(aNative); + } } ScopedXPCOMStartup xpcom; rv = xpcom.Initialize(); NS_ENSURE_SUCCESS(rv, rv); - mozilla::Telemetry::WriteFailedProfileLock(aProfileDir); +#if defined(MOZ_TELEMETRY_REPORTING) + if (aProfileDir) mozilla::Telemetry::WriteFailedProfileLock(aProfileDir); +#endif rv = xpcom.SetWindowCreator(aNative); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp index 5536f4423f4af..963e1aa8bdae8 100644 --- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -111,6 +111,10 @@ nsIFile* gDataDirHome = nullptr; nsCOMPtr<nsIFile> gDataDirProfileLocal = nullptr; nsCOMPtr<nsIFile> gDataDirProfile = nullptr; +#if defined(RELATIVE_DATA_DIR) +mozilla::Maybe<nsCOMPtr<nsIFile>> gDataDirPortable; +#endif + // These are required to allow nsXREDirProvider to be usable in xpcshell tests. // where gAppData is null. #if defined(XP_MACOSX) || defined(XP_UNIX) @@ -1324,10 +1328,96 @@ nsresult nsXREDirProvider::SetUserDataProfileDirectory(nsCOMPtr<nsIFile>& aFile, return NS_OK; } +#if defined(RELATIVE_DATA_DIR) +nsresult nsXREDirProvider::GetPortableDataDir(nsIFile** aFile, + bool& aIsPortable) { + if (gDataDirPortable) { + if (*gDataDirPortable) { + nsresult rv = (*gDataDirPortable)->Clone(aFile); + NS_ENSURE_SUCCESS(rv, rv); + aIsPortable = true; + } else { + aIsPortable = false; + } + return NS_OK; + } + + nsCOMPtr<nsIFile> exeFile, exeDir; + bool persistent = false; + nsresult rv = + GetFile(XRE_EXECUTABLE_FILE, &persistent, getter_AddRefs(exeFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = exeFile->Normalize(); + NS_ENSURE_SUCCESS(rv, rv); + rv = exeFile->GetParent(getter_AddRefs(exeDir)); + NS_ENSURE_SUCCESS(rv, rv); + +# if defined(XP_MACOSX) + nsAutoString exeDirPath; + rv = exeDir->GetPath(exeDirPath); + NS_ENSURE_SUCCESS(rv, rv); + // When the browser is installed in /Applications, we never run in portable + // mode. + if (exeDirPath.Find("/Applications/", true /* ignore case */) == 0) { + aIsPortable = false; + return NS_OK; + } +# endif + + nsCOMPtr<nsIFile> systemInstallFile; + rv = exeDir->Clone(getter_AddRefs(systemInstallFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = systemInstallFile->AppendNative("system-install"_ns); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists = false; + rv = systemInstallFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (exists) { + aIsPortable = false; + gDataDirPortable.emplace(nullptr); + return NS_OK; + } + + nsCOMPtr<nsIFile> localDir = exeDir; +# if defined(XP_MACOSX) + rv = exeDir->GetParent(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); + exeDir = localDir; + rv = exeDir->GetParent(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); +# endif + rv = localDir->SetRelativePath(localDir.get(), + nsLiteralCString(RELATIVE_DATA_DIR)); + NS_ENSURE_SUCCESS(rv, rv); + +# if defined(XP_MACOSX) + // On macOS we try to create the directory immediately to switch to + // system-install mode if needed (e.g., when running from the DMG). + rv = localDir->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) { + rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700); + if (NS_FAILED(rv)) { + aIsPortable = false; + return NS_OK; + } + } +# endif + + gDataDirPortable.emplace(localDir); + rv = (*gDataDirPortable)->Clone(aFile); + NS_ENSURE_SUCCESS(rv, rv); + aIsPortable = true; + return rv; +} +#endif + nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal) { // Copied from nsAppFileLocationProvider (more or less) nsresult rv; + NS_ENSURE_ARG_POINTER(aFile); nsCOMPtr<nsIFile> localDir; if (aLocal && gDataDirHomeLocal) { @@ -1337,6 +1427,24 @@ nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, return gDataDirHome->Clone(aFile); } +#if defined(RELATIVE_DATA_DIR) + RefPtr<nsXREDirProvider> singleton = GetSingleton(); + if (!singleton) { + return NS_ERROR_OUT_OF_MEMORY; + } + bool isPortable = false; + rv = singleton->GetPortableDataDir(getter_AddRefs(localDir), isPortable); + NS_ENSURE_SUCCESS(rv, rv); + if (isPortable) { + if (aLocal) { + rv = localDir->AppendNative("Caches"_ns); + NS_ENSURE_SUCCESS(rv, rv); + } + NS_IF_ADDREF(*aFile = localDir); + return rv; + } +#endif + #if defined(XP_MACOSX) FSRef fsRef; OSType folderType; @@ -1530,6 +1638,13 @@ nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) { return NS_OK; } +#if defined(RELATIVE_DATA_DIR) + if (gDataDirPortable && *gDataDirPortable) { + // Do nothing in portable mode + return NS_OK; + } +#endif + nsAutoCString profile; nsAutoCString appName; nsAutoCString vendor; diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h index 40cd9c8eb06a4..4b3eb00173e97 100644 --- a/toolkit/xre/nsXREDirProvider.h +++ b/toolkit/xre/nsXREDirProvider.h @@ -155,6 +155,14 @@ class nsXREDirProvider final : public nsIDirectoryServiceProvider2, private: static nsresult SetUserDataProfileDirectory(nsCOMPtr<nsIFile>& aFile, bool aLocal); + +#if defined(RELATIVE_DATA_DIR) + /** + * Get the path to the portable data dir, if the application is running in + * portable mode. + */ + nsresult GetPortableDataDir(nsIFile** aFile, bool& aIsPortable); +#endif }; #endif diff --git a/xpcom/io/nsAppFileLocationProvider.cpp b/xpcom/io/nsAppFileLocationProvider.cpp index ef974f99048f8..72d161c561481 100644 --- a/xpcom/io/nsAppFileLocationProvider.cpp +++ b/xpcom/io/nsAppFileLocationProvider.cpp @@ -229,13 +229,108 @@ nsresult nsAppFileLocationProvider::CloneMozBinDirectory(nsIFile** aLocalFile) { return NS_OK; } +#ifdef RELATIVE_DATA_DIR +static nsresult SetupPortableMode(nsIFile** aDirectory, bool aLocal, + bool& aIsPortable) { + // This is almost the same as nsXREDirProvider::GetPortableDataDir. + // However, it seems that this is never called, at least during simple usage + // of the browser. + + nsresult rv = NS_ERROR_UNEXPECTED; + nsCOMPtr<nsIProperties> directoryService( + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIFile> exeFile, exeDir; + rv = directoryService->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile), + getter_AddRefs(exeFile)); + rv = exeFile->Normalize(); + NS_ENSURE_SUCCESS(rv, rv); + rv = exeFile->GetParent(getter_AddRefs(exeDir)); + NS_ENSURE_SUCCESS(rv, rv); + +# if defined(XP_MACOSX) + nsAutoString exeDirPath; + rv = exeDir->GetPath(exeDirPath); + NS_ENSURE_SUCCESS(rv, rv); + // When the browser is installed in /Applications, we never run in portable + // mode. + if (exeDirPath.Find("/Applications/", true /* ignore case */) == 0) { + aIsPortable = false; + return NS_OK; + } +# endif + + nsCOMPtr<nsIFile> systemInstallFile; + rv = exeDir->Clone(getter_AddRefs(systemInstallFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = systemInstallFile->AppendNative("system-install"_ns); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists = false; + rv = systemInstallFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (exists) { + aIsPortable = false; + return NS_OK; + } + + nsCOMPtr<nsIFile> localDir = exeDir; +# if defined(XP_MACOSX) + rv = exeDir->GetParent(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); + exeDir = localDir; + rv = exeDir->GetParent(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); +# endif + + rv = localDir->SetRelativePath(localDir.get(), + nsLiteralCString(RELATIVE_DATA_DIR)); + NS_ENSURE_SUCCESS(rv, rv); + if (aLocal) { + rv = localDir->AppendNative("Caches"_ns); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = localDir->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) { + rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700); +# if defined(XP_MACOSX) + if (NS_FAILED(rv)) { + // On macOS, we forgive this failure to allow running from the DMG. + aIsPortable = false; + return NS_OK; + } +# else + NS_ENSURE_SUCCESS(rv, rv); +# endif + } + + localDir.forget(aDirectory); + aIsPortable = true; + return rv; +} +#endif + //---------------------------------------------------------------------------------------- // GetProductDirectory - Gets the directory which contains the application data // folder // -// UNIX : ~/.mozilla/ -// WIN : <Application Data folder on user's machine>\Mozilla -// Mac : :Documents:Mozilla: +// If portable mode is enabled: +// - aLocal == false: $APP_ROOT/$RELATIVE_DATA_DIR +// - aLocal == true: $APP_ROOT/$RELATIVE_DATA_DIR/Caches +// where $APP_ROOT is: +// - the parent directory of the executable on Windows and Linux +// - the root of the app bundle on macOS +// +// Otherwise: +// - Windows: +// - aLocal == false: %APPDATA%/$MOZ_USER_DIR +// - aLocal == true: %LOCALAPPDATA%/$MOZ_USER_DIR +// - macOS: +// - aLocal == false: kDomainLibraryFolderType/$MOZ_USER_DIR +// - aLocal == true: kCachedDataFolderType/$MOZ_USER_DIR +// - Unix: ~/$MOZ_USER_DIR //---------------------------------------------------------------------------------------- nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, bool aLocal) { @@ -243,10 +338,20 @@ nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, return NS_ERROR_INVALID_ARG; } - nsresult rv; + nsresult rv = NS_ERROR_UNEXPECTED; bool exists; nsCOMPtr<nsIFile> localDir; +#if defined(RELATIVE_DATA_DIR) + bool isPortable = false; + rv = SetupPortableMode(aLocalFile, aLocal, isPortable); + // If portable mode is enabled, we absolutely want it (e.g., to be sure there + // will not be disk leaks), so a failure is to be propagated. + if (NS_FAILED(rv) || isPortable) { + return rv; + } +#endif + #if defined(MOZ_WIDGET_COCOA) FSRef fsRef; OSType folderType = @@ -309,9 +414,10 @@ nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, // GetDefaultUserProfileRoot - Gets the directory which contains each user // profile dir // -// UNIX : ~/.mozilla/ -// WIN : <Application Data folder on user's machine>\Mozilla\Profiles -// Mac : :Documents:Mozilla:Profiles: +// - Windows and macOS: $PRODUCT_DIRECTORY/Profiles +// - Unix: $PRODUCT_DIRECTORY +// See also GetProductDirectory for instructions on how $PRODUCT_DIRECTORY is +// generated. //---------------------------------------------------------------------------------------- nsresult nsAppFileLocationProvider::GetDefaultUserProfileRoot( nsIFile** aLocalFile, bool aLocal) { -- GitLab