tor-browser

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

commit dea79b8a60f1552030dc00825e5dfc75975d29ba
parent 98cb1d5411935ac5a629b7691558925d6ea78e05
Author: Alexandre Lissy <lissyx+mozillians@lissyx.dyndns.org>
Date:   Wed, 19 Nov 2025 17:46:17 +0000

Bug 259356 - Add support for the XDG Base Directory Specification r=stransky,glandium,gcp

Differential Revision: https://phabricator.services.mozilla.com/D6995

Diffstat:
Mbrowser/app/profile/firefox.js | 7+++++++
Mtoolkit/moz.configure | 9++++++++-
Mtoolkit/xre/nsXREDirProvider.cpp | 258++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mtoolkit/xre/nsXREDirProvider.h | 27++++++++++++++++++++++++++-
Mtoolkit/xre/test/browser.toml | 3+++
Atoolkit/xre/test/browser_xdg_pref.js | 11+++++++++++
Mtools/lint/dot-mozilla-reference.yml | 1+
Mxpcom/io/moz.build | 1+
Mxpcom/io/nsAppFileLocationProvider.cpp | 41++++++++++++++++++++++++++++++++++++-----
9 files changed, 330 insertions(+), 28 deletions(-)

diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js @@ -3536,3 +3536,10 @@ pref("toolkit.rust-components.logging.internal-level", "Warn"); // Settings Redesign 2025 prefs pref("browser.settings-redesign.enabled", false); + +// A preference that will be locked to reflect whether this build has support +// for XDG Config Home handling. Mostly used to be able to keep tests around +// in case of a backout of the feature +#if defined(MOZ_WIDGET_GTK) +pref("widget.support-xdg-config", true, locked); +#endif diff --git a/toolkit/moz.configure b/toolkit/moz.configure @@ -3901,7 +3901,9 @@ with only_when(compile_environment): def default_user_appdir(target): if target.kernel in ("WINNT", "Darwin"): return "Mozilla" - return ".mozilla" + elif target.os == "Android": + return ".mozilla" + return "mozilla" option( "--with-user-appdir", @@ -3918,7 +3920,12 @@ with only_when(compile_environment): die("--with-user-appdir must be a single relative path.") return '"{}"'.format(appdir[0]) + @depends(depends("--with-user-appdir")(lambda x: x), when=toolkit_gtk) + def forced_or_dotted_user_appdir(value): + return value.origin != "default" or value[0].startswith(".") + set_define("MOZ_USER_DIR", user_appdir) + set_define("MOZ_LEGACY_HOME", True, when=forced_or_dotted_user_appdir) # Check for sin_len and sin6_len - used by SCTP; only appears in Mac/*BSD generally diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp @@ -12,6 +12,7 @@ #include "jsapi.h" #include "xpcpublic.h" #include "prprf.h" +#include "prenv.h" #include "nsIAppStartup.h" #include "nsIFile.h" @@ -45,6 +46,7 @@ #endif #include "mozilla/CmdLineAndEnvUtils.h" #include "mozilla/Components.h" +#include "mozilla/DebugOnly.h" #include "mozilla/Services.h" #include "mozilla/Omnijar.h" #include "mozilla/Preferences.h" @@ -365,7 +367,9 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent, else if (!strcmp(aProperty, XRE_SYS_NATIVE_MANIFESTS)) { rv = ::GetSystemParentDirectory(getter_AddRefs(file)); } else if (!strcmp(aProperty, XRE_USER_NATIVE_MANIFESTS)) { - rv = GetUserDataDirectoryHome(getter_AddRefs(file), false); + // Keep forcing the legacy path for compatibility + rv = GetUserDataDirectoryHome(getter_AddRefs(file), /* aLocal */ false, + /* aForceLegacy */ true); NS_ENSURE_SUCCESS(rv, rv); # if defined(XP_MACOSX) rv = file->AppendNative("Mozilla"_ns); @@ -918,7 +922,8 @@ nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult, nsAutoString appDirPath; if (NS_FAILED(appFile->GetParent(getter_AddRefs(appRootDirFile))) || NS_FAILED(appRootDirFile->GetPath(appDirPath)) || - NS_FAILED(GetUserDataDirectoryHome(getter_AddRefs(localDir), true))) { + NS_FAILED(GetUserDataDirectoryHome(getter_AddRefs(localDir), + /* aLocal */ true))) { return NS_ERROR_FAILURE; } @@ -1055,7 +1060,8 @@ nsresult nsXREDirProvider::RestoreUserDataProfileDirectoryFromGTest( // Return the home directory that will contain user data nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, - bool aLocal) { + bool aLocal, + bool aForceLegacy) { // Copied from nsAppFileLocationProvider (more or less) nsCOMPtr<nsIFile> localDir; @@ -1110,28 +1116,23 @@ nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, MOZ_TRY(NS_NewLocalFile(path, getter_AddRefs(localDir))); #elif defined(XP_UNIX) - const char* homeDir = getenv("HOME"); + const char* homeDir = PR_GetEnv("HOME"); if (!homeDir || !*homeDir) return NS_ERROR_FAILURE; # ifdef ANDROID /* We want (ProfD == ProfLD) on Android. */ - aLocal = false; -# endif - + MOZ_TRY(NS_NewNativeLocalFile(nsDependentCString(homeDir), + getter_AddRefs(localDir))); +# else if (aLocal) { - // If $XDG_CACHE_HOME is defined use it, otherwise use $HOME/.cache. - const char* cacheHome = getenv("XDG_CACHE_HOME"); - if (cacheHome && *cacheHome) { - MOZ_TRY(NS_NewNativeLocalFile(nsDependentCString(cacheHome), - getter_AddRefs(localDir))); - } else { - MOZ_TRY(NS_NewNativeLocalFile(nsDependentCString(homeDir), - getter_AddRefs(localDir))); - MOZ_TRY(localDir->AppendNative(".cache"_ns)); - } + // Not forcing legacy because cache can be lost without consequences, so + // there is no real requirement to keep compatibility here + MOZ_TRY(nsXREDirProvider::GetLegacyOrXDGCachePath( + homeDir, getter_AddRefs(localDir))); } else { - MOZ_TRY(NS_NewNativeLocalFile(nsDependentCString(homeDir), - getter_AddRefs(localDir))); + MOZ_TRY(nsXREDirProvider::GetLegacyOrXDGHomePath( + homeDir, getter_AddRefs(localDir), aForceLegacy)); } +# endif // ANDROID #else # error "Don't know how to get product dir on your platform" #endif @@ -1142,7 +1143,8 @@ nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, nsresult nsXREDirProvider::GetSysUserExtensionsDirectory(nsIFile** aFile) { nsCOMPtr<nsIFile> localDir; - nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false); + nsresult rv = GetUserDataDirectoryHome( + getter_AddRefs(localDir), /* aLocal */ false, /* aForceLegacy */ true); NS_ENSURE_SUCCESS(rv, rv); rv = AppendSysUserExtensionPath(localDir); @@ -1254,6 +1256,211 @@ nsresult nsXREDirProvider::AppendSysUserExtensionPath(nsIFile* aFile) { return NS_OK; } +#if defined(MOZ_WIDGET_GTK) +/* + * Return whether MOZ_LEGACY_HOME == 1, via environment or at build time + */ +bool nsXREDirProvider::IsForceLegacyHome() { +# if !defined(MOZ_LEGACY_HOME) + const char* legacyhomedir = PR_GetEnv("MOZ_LEGACY_HOME"); + return legacyhomedir && legacyhomedir[0] == '1'; +# else + return true; +# endif +} + +/* static */ +nsresult nsXREDirProvider::AppendFromAppData(nsIFile* aFile, bool aIsDotted) { + // This might happen in xpcshell so assert that it is indeed in a xpcshell + // test. This assumes the xpcshell are ran through test harness. + if (!gAppData) { + mozilla::DebugOnly<const char*> xpcshell = + PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR"); + MOZ_ASSERT(xpcshell, "gAppData can only be nullptr in xpcshell tests"); + return NS_OK; + } + + // Similar to nsXREDirProvider::AppendProfilePath. + // TODO: Bug 1990407 - Evaluate if refactoring might be required there in the + // future? + if (gAppData->profile) { + nsAutoCString profile; + profile = gAppData->profile; + MOZ_TRY(aFile->AppendRelativeNativePath(profile)); + } else { + nsAutoCString vendor; + nsAutoCString appName; + vendor = gAppData->vendor; + appName = gAppData->name; + ToLowerCase(vendor); + ToLowerCase(appName); + + MOZ_TRY(aFile->AppendRelativeNativePath(aIsDotted ? ("."_ns + vendor) + : vendor)); + MOZ_TRY(aFile->AppendRelativeNativePath(appName)); + } + + return NS_OK; +} + +/* + * Check if legacy directory exists, which can be: + * (1) $HOME/.<gAppData->vendor>/<gAppData->appName> + * (2) $HOME/<gAppData->profile> + * (3) $HOME/<MOZ_USER_DIR> + * + * The MOZ_USER_DIR will also be defined in case (1), so first check the deeper + * directory. + */ +bool nsXREDirProvider::LegacyHomeExists(nsIFile** aFile) { + bool exists; + nsDependentCString homeDir(PR_GetEnv("HOME")); + nsCOMPtr<nsIFile> localDir; + nsCOMPtr<nsIFile> parentDir; + + // check old config ~/.mozilla + nsresult rv = NS_NewNativeLocalFile(homeDir, getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, false); + + rv = localDir->Clone(getter_AddRefs(parentDir)); + NS_ENSURE_SUCCESS(rv, false); + + // Handle (1) and (2) + rv = AppendFromAppData(localDir, true); + NS_ENSURE_SUCCESS(rv, false); + + rv = localDir->Exists(&exists); + NS_ENSURE_SUCCESS(rv, false); + + // Give a chance to (3) + if (!exists) { + nsCOMPtr<nsIFile> userDir; + rv = parentDir->Clone(getter_AddRefs(userDir)); + NS_ENSURE_SUCCESS(rv, false); + + nsAutoCString mozUserDir; + mozUserDir = nsLiteralCString(MOZ_USER_DIR); + + rv = userDir->AppendRelativeNativePath(mozUserDir); + NS_ENSURE_SUCCESS(rv, false); + + rv = userDir->Exists(&exists); + NS_ENSURE_SUCCESS(rv, false); + } + + // If required, return the parent dir that may exist. + if (aFile) { + parentDir.forget(aFile); + } + + return exists; +} + +/* static */ +nsresult nsXREDirProvider::GetLegacyOrXDGEnvValue(const char* aHomeDir, + const char* aEnvName, + nsCString aSubdir, + nsIFile** aFile) { + nsCOMPtr<nsIFile> localDir; + nsresult rv = NS_OK; + + const char* envValue = PR_GetEnv(aEnvName); + if (envValue && *envValue) { + rv = NS_NewNativeLocalFile(nsDependentCString(envValue), + getter_AddRefs(localDir)); + } + + // Explicitly check for rv failure because in case we get passed an env + // value that is an invalid dir by the XDG specification level, it should + // be ignored. Per + // https://specifications.freedesktop.org/basedir-spec/0.8/: + // "If an implementation encounters a relative path in any of + // these variables it should consider the path invalid and ignore it." + if (NS_FAILED(rv) || !envValue || !*envValue) { + MOZ_TRY(NS_NewNativeLocalFile(nsDependentCString(aHomeDir), + getter_AddRefs(localDir))); + MOZ_TRY(localDir->AppendNative(aSubdir)); + } + + localDir.forget(aFile); + return NS_OK; +} + +/* static */ +nsresult nsXREDirProvider::GetLegacyOrXDGCachePath(const char* aHomeDir, + nsIFile** aFile) { + return GetLegacyOrXDGEnvValue(aHomeDir, "XDG_CACHE_HOME", ".cache"_ns, aFile); +} + +/* + * Check if XDG_CONFIG_HOME is here and use it or default to ${aHomeDir}/.config + */ +/* static */ +nsresult nsXREDirProvider::GetLegacyOrXDGConfigHome(const char* aHomeDir, + nsIFile** aFile) { + return GetLegacyOrXDGEnvValue(aHomeDir, "XDG_CONFIG_HOME", ".config"_ns, + aFile); +} + +// Attempt to construct the HOME path depending on XDG or legacy status. +nsresult nsXREDirProvider::GetLegacyOrXDGHomePath(const char* aHomeDir, + nsIFile** aFile, + bool aForceLegacy) { + nsCOMPtr<nsIFile> parentDir; + nsDependentCString homeDir(aHomeDir); + + bool exists = LegacyHomeExists(getter_AddRefs(parentDir)); + if (exists || IsForceLegacyHome() || aForceLegacy) { + parentDir.forget(aFile); + return NS_OK; + } + + nsCOMPtr<nsIFile> localDir; + + // If the build was made with --with-user-appdir=.fooProfile it needs to be + // applied and considered as a legacy path. + nsAutoCString mozUserDir; + mozUserDir = nsLiteralCString(MOZ_USER_DIR); + if (mozUserDir.get()[0] == '.') { + MOZ_TRY(NS_NewNativeLocalFile(nsDependentCString(aHomeDir), + getter_AddRefs(localDir))); + MOZ_TRY(localDir->AppendRelativeNativePath(mozUserDir)); + } else { + // This might happen in xpcshell so assert that it is indeed in a xpcshell + // test + if (!gAppData) { + mozilla::DebugOnly<const char*> xpcshell = + PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR"); + MOZ_ASSERT(xpcshell, "gAppData can only be nullptr in xpcshell tests"); + return NS_OK; + } + + // If the build was made against a specific profile name, MOZ_APP_PROFILE= + // then make sure we respect this and dont move to XDG directory + if (gAppData->profile) { + MOZ_TRY(NS_NewNativeLocalFile(nsDependentCString(aHomeDir), + getter_AddRefs(localDir))); + } else { + MOZ_TRY(GetLegacyOrXDGConfigHome(aHomeDir, getter_AddRefs(localDir))); + MOZ_TRY(localDir->Clone(getter_AddRefs(parentDir))); + } + + MOZ_TRY(AppendFromAppData(localDir, false)); + } + + // If required return the parent directory that matches the profile root + // directory. + if (aFile) { + parentDir.forget(aFile); + } + + // The profile root directory needs to exists at that point. + MOZ_TRY(EnsureDirectoryExists(localDir)); + + return NS_OK; +} +#endif // defined(MOZ_WIDGET_GTK) + nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) { NS_ASSERTION(aFile, "Null pointer!"); @@ -1264,6 +1471,9 @@ nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) { return NS_OK; } + // Similar to nsXREDirProvider::AppendFromAppData. + // TODO: evaluate if refactoring might be required there in the future? + nsAutoCString profile; nsAutoCString appName; nsAutoCString vendor; @@ -1310,7 +1520,13 @@ nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) { nsAutoCString folder; // Make it hidden (by starting with "."), except when local (the // profile is already under ~/.cache or XDG_CACHE_HOME). - if (!aLocal) folder.Assign('.'); + if (!aLocal +# if defined(MOZ_WIDGET_GTK) + && (IsForceLegacyHome() || LegacyHomeExists(nullptr)) +# endif + ) { + folder.Assign('.'); + } if (!profile.IsEmpty()) { // Skip any leading path characters diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h @@ -69,6 +69,25 @@ class nsXREDirProvider final : public nsIDirectoryServiceProvider2, static nsresult GetUserDataDirectory(nsIFile** aFile, bool aLocal); +#if defined(MOZ_WIDGET_GTK) + static nsresult GetLegacyOrXDGEnvValue(const char* aHomeDir, + const char* aEnvName, + nsCString aSubdir, nsIFile** aFile); + static nsresult GetLegacyOrXDGCachePath(const char* aHomeDir, + nsIFile** aFile); + static nsresult GetLegacyOrXDGHomePath(const char* aHomeDir, nsIFile** aFile, + bool aForceLegacy = false); + static nsresult AppendFromAppData(nsIFile* aFile, bool aIsDotted); + + static bool IsForceLegacyHome(); + + static bool LegacyHomeExists(nsIFile** aFile); + + static nsresult GetLegacyOrXDGConfigHome(const char* aHomeDir, + nsIFile** aFile); + +#endif // defined(MOZ_WIDGET_GTK) + /* make sure you clone it, if you need to do stuff to it */ nsIFile* GetGREDir() { return mGREDir; } nsIFile* GetGREBinDir() { return mGREBinDir; } @@ -117,7 +136,13 @@ class nsXREDirProvider final : public nsIDirectoryServiceProvider2, private: nsresult GetFilesInternal(const char* aProperty, nsISimpleEnumerator** aResult); - static nsresult GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal); + + // aForceLegacy will only act on !aLocal and make sure the path returned + // is directly under $HOME. Useful for UserNativeManifests and + // SysUserExtensionDir to keep legacy behavior with XDG support active. + static nsresult GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal, + bool aForceLegacy = false); + static nsresult GetNativeUserManifestsDirectory(nsIFile** aFile); static nsresult GetSysUserExtensionsDirectory(nsIFile** aFile); #if defined(XP_UNIX) || defined(XP_MACOSX) static nsresult GetSystemExtensionsDirectory(nsIFile** aFile); diff --git a/toolkit/xre/test/browser.toml b/toolkit/xre/test/browser.toml @@ -4,3 +4,6 @@ tags = "os_integration" ["browser_checkdllblockliststate.js"] run-if = ["os == 'win'"] skip-if = ["ccov"] # Bug 1531789 + +["browser_xdg_pref.js"] +run-if = ["os == 'linux'"] diff --git a/toolkit/xre/test/browser_xdg_pref.js b/toolkit/xre/test/browser_xdg_pref.js @@ -0,0 +1,11 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ + +add_task(async function test_pref_is_true() { + Assert.equal(true, Services.prefs.getBoolPref("widget.support-xdg-config")); +}); + +add_task(async function test_pref_is_locked() { + Services.prefs.setBoolPref("widget.support-xdg-config", false); + Assert.equal(true, Services.prefs.getBoolPref("widget.support-xdg-config")); +}); diff --git a/tools/lint/dot-mozilla-reference.yml b/tools/lint/dot-mozilla-reference.yml @@ -40,3 +40,4 @@ avoid-dot-mozilla-without-xdg: - toolkit/moz.configure - toolkit/xre/nsXREDirProvider.cpp - toolkit/tests/gtest/TestXREAppDir.cpp + - xpcom/io/nsAppFileLocationProvider.cpp diff --git a/xpcom/io/moz.build b/xpcom/io/moz.build @@ -165,4 +165,5 @@ if CONFIG["OS_ARCH"] == "Linux" and "lib64" in CONFIG["libdir"]: LOCAL_INCLUDES += [ "!..", "../build", + "/toolkit/xre", ] diff --git a/xpcom/io/nsAppFileLocationProvider.cpp b/xpcom/io/nsAppFileLocationProvider.cpp @@ -13,6 +13,7 @@ #include "nsIFile.h" #include "nsString.h" #include "nsSimpleEnumerator.h" +#include "nsXREDirProvider.h" #include "prenv.h" #include "nsCRT.h" #if defined(MOZ_WIDGET_COCOA) @@ -168,7 +169,8 @@ nsresult nsAppFileLocationProvider::CloneMozBinDirectory(nsIFile** aLocalFile) { // GetProductDirectory - Gets the directory which contains the application data // folder // -// UNIX : ~/.mozilla/ +// 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: //---------------------------------------------------------------------------------------- @@ -204,21 +206,49 @@ nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, return rv; } #elif defined(XP_UNIX) - rv = NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")), + const char* homeDir = PR_GetEnv("HOME"); + rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), getter_AddRefs(localDir)); if (NS_FAILED(rv)) { return rv; } + +# if defined(MOZ_WIDGET_GTK) + rv = nsXREDirProvider::GetLegacyOrXDGHomePath(homeDir, + getter_AddRefs(localDir)); + if (NS_FAILED(rv)) { + return rv; + } +# endif + #else # error dont_know_how_to_get_product_dir_on_your_platform #endif - rv = localDir->AppendRelativeNativePath(DEFAULT_PRODUCT_DIR); +#if defined(MOZ_WIDGET_GTK) + bool legacyExists = nsXREDirProvider::LegacyHomeExists(nullptr); + if (legacyExists || nsXREDirProvider::IsForceLegacyHome()) { + nsAutoCString productDir; + nsAutoCString mozUserDir; + mozUserDir = nsLiteralCString(MOZ_USER_DIR); + if (mozUserDir.get()[0] != '.') { + productDir = "."_ns + DEFAULT_PRODUCT_DIR; + } else { + productDir = DEFAULT_PRODUCT_DIR; + } + rv = localDir->AppendNative(productDir); + } else { + rv = localDir->AppendNative(DEFAULT_PRODUCT_DIR); + } +#else + rv = localDir->AppendNative(DEFAULT_PRODUCT_DIR); +#endif + if (NS_FAILED(rv)) { return rv; } - rv = localDir->Exists(&exists); + rv = localDir->Exists(&exists); if (NS_SUCCEEDED(rv) && !exists) { rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700); } @@ -236,7 +266,8 @@ nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, // GetDefaultUserProfileRoot - Gets the directory which contains each user // profile dir // -// UNIX : ~/.mozilla/ +// 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: //----------------------------------------------------------------------------------------