tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 62562804a469f1393ba6984ff5cffd1031c2410d
parent 00a2b13143d217d9bbccef3964c85e70bed46547
Author: Pier Angelo Vendrame <pierov@torproject.org>
Date:   Fri, 27 Jan 2023 17:33:29 +0100

BB 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).

Diffstat:
Mbrowser/config/mozconfigs/base-browser | 2++
Mmoz.configure | 19+++++++++++++++++++
Mmozconfig-macos | 3+++
Mtoolkit/xre/LauncherRegistryInfo.cpp | 50+++++++++++++++++++++++++++++++++++++++++++++++---
Mtoolkit/xre/nsAppRunner.cpp | 14+++++++++-----
Mtoolkit/xre/nsIXREDirProvider.idl | 5+++++
Mtoolkit/xre/nsXREDirProvider.cpp | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/xre/nsXREDirProvider.h | 8++++++++
Mwidget/windows/WinTaskbar.cpp | 16+++++++++++++++-
Mxpcom/io/nsAppFileLocationProvider.cpp | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
10 files changed, 379 insertions(+), 18 deletions(-)

diff --git a/browser/config/mozconfigs/base-browser b/browser/config/mozconfigs/base-browser @@ -47,3 +47,5 @@ ac_add_options --disable-legacy-profile-creation 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 @@ -953,6 +953,25 @@ with only_when(cross_compiling): set_config("JS_BINARY", depends_if("JS_BINARY")(lambda value: value[0])) 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, help="Set the Base Browser version, e.g., 7.0a1", diff --git a/mozconfig-macos b/mozconfig-macos @@ -10,3 +10,6 @@ ac_add_options --disable-update-agent # Let's make sure no preference is enabling either Adobe's or Google's CDM. ac_add_options --disable-eme + +# For base-browser we do not enable portable mode on macOS. +ac_add_options --without-relative-data-dir diff --git a/toolkit/xre/LauncherRegistryInfo.cpp b/toolkit/xre/LauncherRegistryInfo.cpp @@ -16,6 +16,9 @@ #include <string> #include <type_traits> +// tor-browser#42163 +#include <filesystem> + // This is how long to wait after the last crash to try using the launcher // process again. (bug 1846830) 45 days in 100-nanosecond intervals (FILETIME // units) @@ -679,6 +682,45 @@ LauncherResult<bool> LauncherRegistryInfo::ClearLauncherCrashTimestamp() { LauncherResult<std::wstring> LauncherRegistryInfo::BuildDefaultBlocklistFilename() { + // tor-browser#42163: Make the DLL blocklist obey portable mode + { + std::filesystem::path appDir; + { + mozilla::UniquePtr<wchar_t[]> appDirStr = GetFullBinaryPath(); + if (!appDirStr) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + } + appDir = std::filesystem::path(appDirStr.get()).parent_path(); + } + std::error_code ec; + const bool isPortable = + !std::filesystem::exists(appDir / L"system-install", ec); + if (ec) { + // exists is supposed not to set an error when a file does not exist + // (whereas other functions such as is_regular_file sets it). + // The standard is quite opaque about the meaning of the numeric codes. + // Moreover, we use libcxx on Windows, and it seems they created a sort of + // POSIX compatibility layer (e.g., for stat), see + // libcxx/src/filesystem/posix_compat.h. + // std::error_code has a message function, but all the various macro are + // specific to handle Windows errors, so we have to use the generic error. + // At least, at the moment the error is dropped eventually. + return LAUNCHER_ERROR_GENERIC(); + } + if (isPortable) { + // RELATIVE_DATA_DIR must have forward slashes, but weakly_canonical + // already changes them to backslashes. + const std::filesystem::path blocklistPath = + std::filesystem::weakly_canonical( + appDir / L"" RELATIVE_DATA_DIR / L"blocklist", ec); + if (ec) { + return LAUNCHER_ERROR_GENERIC(); + } + return blocklistPath.wstring(); + } + // Normal installation, continue on Mozilla's path + } + // These flags are chosen to avoid I/O, see bug 1363398. const DWORD flags = KF_FLAG_SIMPLE_IDLIST | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_ALIAS; @@ -711,6 +753,8 @@ LauncherRegistryInfo::BuildDefaultBlocklistFilename() { } LauncherResult<std::wstring> LauncherRegistryInfo::GetBlocklistFileName() { + // tor-browser#42163: Make the DLL blocklist obey portable mode +#ifndef BASE_BROWSER_VERSION LauncherResult<Disposition> disposition = Open(); if (disposition.isErr()) { return disposition.propagateErr(); @@ -726,19 +770,19 @@ LauncherResult<std::wstring> LauncherRegistryInfo::GetBlocklistFileName() { UniquePtr<wchar_t[]> buf = readResult.unwrap(); return std::wstring(buf.get()); } - +#endif LauncherResult<std::wstring> defaultBlocklistPath = BuildDefaultBlocklistFilename(); if (defaultBlocklistPath.isErr()) { return defaultBlocklistPath.propagateErr(); } - +#ifndef BASE_BROWSER_VERSION LauncherVoidResult writeResult = WriteRegistryValueString( mRegKey, ResolveBlocklistValueName(), defaultBlocklistPath.inspect()); if (writeResult.isErr()) { return writeResult.propagateErr(); } - +#endif return defaultBlocklistPath; } diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp @@ -2768,6 +2768,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, @@ -2781,10 +2783,12 @@ static ReturnAbortOnError ProfileLockedDialog(nsIFile* aProfileDir, gStartupLock = nullptr; #endif - bool exists; - aProfileDir->Exists(&exists); - if (!exists) { - return ProfileMissingDialog(aNative); + if (aProfileDir) { + bool exists; + aProfileDir->Exists(&exists); + if (!exists) { + return ProfileMissingDialog(aNative); + } } ScopedXPCOMStartup xpcom; @@ -2794,7 +2798,7 @@ static ReturnAbortOnError ProfileLockedDialog(nsIFile* aProfileDir, #if defined(MOZ_TELEMETRY_REPORTING) // We cannot check if telemetry has been disabled by the user, yet. // So, rely on the build time settings, instead. - mozilla::Telemetry::WriteFailedProfileLock(aProfileDir); + if (aProfileDir) mozilla::Telemetry::WriteFailedProfileLock(aProfileDir); #endif rv = xpcom.SetWindowCreator(aNative); diff --git a/toolkit/xre/nsIXREDirProvider.idl b/toolkit/xre/nsIXREDirProvider.idl @@ -20,4 +20,9 @@ interface nsIXREDirProvider : nsISupports * Gets the hash for the current installation directory. */ AString getInstallHash(); + + /** + * Tells whether the browser has been started in portable mode. + */ + readonly attribute boolean isPortableMode; }; diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp @@ -107,6 +107,10 @@ nsXREDirProvider::legacyOrXDGHomeTelemetry gXdgTelemetry = nsXREDirProvider::legacyOrXDGHomeTelemetry::empty; #endif // defined(MOZ_WIDGET_GTK) +#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) @@ -1118,11 +1122,125 @@ nsresult nsXREDirProvider::RestoreUserDataProfileDirectoryFromGTest( 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.LowerCaseFindASCII("/applications/") == 0) { + aIsPortable = false; + gDataDirPortable.emplace(nullptr); + return NS_OK; + } +# endif + +# if defined(MOZ_WIDGET_GTK) + // On Linux, Firefox supports the is-packaged-app for the .deb distribution. + // We cannot use mozilla::widget::IsPackagedAppFileExists because it relies on + // this service to be initialized, but this function is called during the + // initialization. Therefore, we need to re-implement this check. + nsLiteralCString systemInstallNames[] = {"system-install"_ns, + "is-packaged-app"_ns}; +# else + nsLiteralCString systemInstallNames[] = {"system-install"_ns}; +# endif + for (const nsLiteralCString& fileName : systemInstallNames) { + nsCOMPtr<nsIFile> systemInstallFile; + rv = exeDir->Clone(getter_AddRefs(systemInstallFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = systemInstallFile->AppendNative(fileName); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists = false; + rv = systemInstallFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (exists) { + 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). + bool exists = false; + rv = localDir->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) { + rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700); + if (NS_FAILED(rv)) { + aIsPortable = false; + gDataDirPortable.emplace(nullptr); + return NS_OK; + } + } +# endif + + gDataDirPortable.emplace(localDir); + rv = (*gDataDirPortable)->Clone(aFile); + NS_ENSURE_SUCCESS(rv, rv); + aIsPortable = true; + return rv; +} +#endif + +NS_IMETHODIMP nsXREDirProvider::GetIsPortableMode(bool* aIsPortableMode) { +#ifdef RELATIVE_DATA_DIR + if (gDataDirPortable) { + *aIsPortableMode = *gDataDirPortable; + } else { + nsCOMPtr<nsIFile> dir; + GetPortableDataDir(getter_AddRefs(dir), *aIsPortableMode); + } +#else + *aIsPortableMode = false; +#endif + return NS_OK; +} + // Return the home directory that will contain user data nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal, bool aForceLegacy) { // Copied from nsAppFileLocationProvider (more or less) + NS_ENSURE_ARG_POINTER(aFile); nsCOMPtr<nsIFile> localDir; if (aLocal && gDataDirHomeLocal) { @@ -1132,6 +1250,27 @@ 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; + nsresult 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; @@ -1563,6 +1702,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 + // Similar to nsXREDirProvider::AppendFromAppData. // TODO: evaluate if refactoring might be required there in the future? diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h @@ -172,6 +172,14 @@ class nsXREDirProvider final : public nsIDirectoryServiceProvider2, void Append(nsIFile* aDirectory); +#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 + // On OSX, mGREDir points to .app/Contents/Resources nsCOMPtr<nsIFile> mGREDir; // On OSX, mGREBinDir points to .app/Contents/MacOS diff --git a/widget/windows/WinTaskbar.cpp b/widget/windows/WinTaskbar.cpp @@ -202,7 +202,21 @@ bool WinTaskbar::GenerateAppUserModelID(nsAString& aAppUserModelId, bool aPrivateBrowsing) { // If marked as such in prefs, use a hash of the profile path for the id // instead of the install path hash setup by the installer. - if (Preferences::GetBool("taskbar.grouping.useprofile", false)) { + bool useProfile = Preferences::GetBool("taskbar.grouping.useprofile", false); + + { + // For portable mode, force the AUMID to be based on the profile directory + // instead of reading it from the registry. + bool isPortable = true; + // Do not even check if taskbar.grouping.useprofile is already true. + if (!useProfile && + NS_SUCCEEDED(gDirServiceProvider->GetIsPortableMode(&isPortable)) && + isPortable) { + useProfile = true; + } + } + + if (useProfile) { nsCOMPtr<nsIFile> profileDir; NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profileDir)); diff --git a/xpcom/io/nsAppFileLocationProvider.cpp b/xpcom/io/nsAppFileLocationProvider.cpp @@ -164,14 +164,120 @@ 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.LowerCaseFindASCII("/applications/") == 0) { + aIsPortable = false; + return NS_OK; + } +# endif + +# if defined(MOZ_WIDGET_GTK) + // On Linux, Firefox supports the is-packaged-app for the .deb distribution. + nsLiteralCString systemInstallNames[] = {"system-install"_ns, + "is-packaged-app"_ns}; +# else + nsLiteralCString systemInstallNames[] = {"system-install"_ns}; +# endif + for (const nsLiteralCString& fileName : systemInstallNames) { + nsCOMPtr<nsIFile> systemInstallFile; + rv = exeDir->Clone(getter_AddRefs(systemInstallFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = systemInstallFile->AppendNative(fileName); + 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); + } + + bool exists = false; + 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/ or ${XDG_CONFIG_HOME:-~/.config}/mozilla -// if env var MOZ_LEGACY_HOME is set to 1, then ~/.mozilla/ is used -// 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 or ${XDG_CONFIG_HOME:-~/.config}/$MOZ_USER_DIR +// if env var MOZ_LEGACY_HOME is set to 1, then ~/.$MOZ_USER_DIR/ is +// used //---------------------------------------------------------------------------------------- nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, bool aLocal) { @@ -179,10 +285,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) nsCOMPtr<nsILocalFileMac> localDirMac; rv = NS_NewLocalFileWithCFURL( @@ -265,10 +381,10 @@ nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, // GetDefaultUserProfileRoot - Gets the directory which contains each user // profile dir // -// UNIX : ~/.mozilla/ or ${XDG_CONFIG_HOME:-~/.config}/mozilla -// if env var MOZ_LEGACY_HOME is set to 1, then ~/.mozilla/ is used -// 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) {