Bug 1888033 - [Menu Redesign] Add a secret setting and feature flag for the menu...
[gecko.git] / toolkit / xre / nsXREDirProvider.cpp
blobdac063c8a17fc6493d7b01d2295e1bc957242d55
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsAppRunner.h"
7 #include "nsXREDirProvider.h"
8 #ifndef ANDROID
9 # include "commonupdatedir.h"
10 #endif
12 #include "jsapi.h"
13 #include "xpcpublic.h"
14 #include "prprf.h"
16 #include "nsIAppStartup.h"
17 #include "nsIFile.h"
18 #include "nsIObserver.h"
19 #include "nsIObserverService.h"
20 #include "nsISimpleEnumerator.h"
21 #include "nsIToolkitProfileService.h"
22 #include "nsIXULRuntime.h"
23 #include "commonupdatedir.h"
25 #include "nsAppDirectoryServiceDefs.h"
26 #include "nsDirectoryServiceDefs.h"
27 #include "nsDirectoryServiceUtils.h"
28 #include "nsXULAppAPI.h"
29 #include "nsCategoryManagerUtils.h"
31 #include "nsDependentString.h"
32 #include "nsCOMArray.h"
33 #include "nsArrayEnumerator.h"
34 #include "nsEnumeratorUtils.h"
35 #include "nsReadableUtils.h"
37 #include "SpecialSystemDirectory.h"
39 #include "mozilla/dom/ScriptSettings.h"
41 #include "mozilla/AppShutdown.h"
42 #include "mozilla/AutoRestore.h"
43 #ifdef MOZ_BACKGROUNDTASKS
44 # include "mozilla/BackgroundTasks.h"
45 #endif
46 #include "mozilla/Components.h"
47 #include "mozilla/Services.h"
48 #include "mozilla/Omnijar.h"
49 #include "mozilla/Preferences.h"
50 #include "mozilla/ProfilerLabels.h"
51 #include "mozilla/Telemetry.h"
52 #include "mozilla/XREAppData.h"
53 #include "nsPrintfCString.h"
55 #ifdef MOZ_THUNDERBIRD
56 # include "nsIPK11TokenDB.h"
57 # include "nsIPK11Token.h"
58 #endif
60 #include <stdlib.h>
62 #ifdef XP_WIN
63 # include <windows.h>
64 # include <shlobj.h>
65 # include "WinUtils.h"
66 #endif
67 #ifdef XP_MACOSX
68 # include "nsILocalFileMac.h"
69 // for chflags()
70 # include <sys/stat.h>
71 # include <unistd.h>
72 #endif
73 #ifdef XP_UNIX
74 # include <ctype.h>
75 #endif
76 #ifdef XP_IOS
77 # include "UIKitDirProvider.h"
78 #endif
80 #if defined(MOZ_CONTENT_TEMP_DIR)
81 # include "mozilla/SandboxSettings.h"
82 # include "nsID.h"
83 # include "mozilla/Unused.h"
84 #endif
86 #if defined(XP_MACOSX)
87 # define APP_REGISTRY_NAME "Application Registry"
88 #elif defined(XP_WIN)
89 # define APP_REGISTRY_NAME "registry.dat"
90 #else
91 # define APP_REGISTRY_NAME "appreg"
92 #endif
94 #define PREF_OVERRIDE_DIRNAME "preferences"
96 #if defined(MOZ_CONTENT_TEMP_DIR)
97 static already_AddRefed<nsIFile> GetProcessSandboxTempDir(
98 GeckoProcessType type);
99 static nsresult DeleteDirIfExists(nsIFile* dir);
100 static bool IsContentSandboxDisabled();
101 static const char* GetProcessTempBaseDirKey();
102 static already_AddRefed<nsIFile> CreateProcessSandboxTempDir(
103 GeckoProcessType procType);
104 #endif
106 nsXREDirProvider* gDirServiceProvider = nullptr;
107 nsIFile* gDataDirHomeLocal = nullptr;
108 nsIFile* gDataDirHome = nullptr;
109 nsCOMPtr<nsIFile> gDataDirProfileLocal = nullptr;
110 nsCOMPtr<nsIFile> gDataDirProfile = nullptr;
112 // These are required to allow nsXREDirProvider to be usable in xpcshell tests.
113 // where gAppData is null.
114 #if defined(XP_MACOSX) || defined(XP_UNIX)
115 static const char* GetAppName() {
116 if (gAppData) {
117 return gAppData->name;
119 return nullptr;
121 #endif
123 #ifdef XP_MACOSX
124 static const char* GetAppVendor() {
125 if (gAppData) {
126 return gAppData->vendor;
128 return nullptr;
130 #endif
132 nsXREDirProvider::nsXREDirProvider() { gDirServiceProvider = this; }
134 nsXREDirProvider::~nsXREDirProvider() {
135 gDirServiceProvider = nullptr;
136 gDataDirHomeLocal = nullptr;
137 gDataDirHome = nullptr;
140 already_AddRefed<nsXREDirProvider> nsXREDirProvider::GetSingleton() {
141 if (!gDirServiceProvider) {
142 new nsXREDirProvider(); // This sets gDirServiceProvider
144 return do_AddRef(gDirServiceProvider);
147 nsresult nsXREDirProvider::Initialize(nsIFile* aXULAppDir, nsIFile* aGREDir) {
148 NS_ENSURE_ARG(aXULAppDir);
149 NS_ENSURE_ARG(aGREDir);
151 mXULAppDir = aXULAppDir;
152 mGREDir = aGREDir;
153 nsCOMPtr<nsIFile> binaryPath;
154 nsresult rv = XRE_GetBinaryPath(getter_AddRefs(binaryPath));
155 NS_ENSURE_SUCCESS(rv, rv);
156 return binaryPath->GetParent(getter_AddRefs(mGREBinDir));
159 nsresult nsXREDirProvider::SetProfile(nsIFile* aDir, nsIFile* aLocalDir) {
160 MOZ_ASSERT(aDir && aLocalDir, "We don't support no-profile apps!");
161 MOZ_ASSERT(!mProfileDir && !mProfileLocalDir,
162 "You may only set the profile directories once");
164 nsresult rv = EnsureDirectoryExists(aDir);
165 NS_ENSURE_SUCCESS(rv, rv);
167 rv = EnsureDirectoryExists(aLocalDir);
168 NS_ENSURE_SUCCESS(rv, rv);
170 #ifndef XP_WIN
171 nsAutoCString profilePath;
172 rv = aDir->GetNativePath(profilePath);
173 NS_ENSURE_SUCCESS(rv, rv);
175 nsAutoCString localProfilePath;
176 rv = aLocalDir->GetNativePath(localProfilePath);
177 NS_ENSURE_SUCCESS(rv, rv);
179 if (!mozilla::IsUtf8(profilePath) || !mozilla::IsUtf8(localProfilePath)) {
180 PR_fprintf(
181 PR_STDERR,
182 "Error: The profile path is not valid UTF-8. Unable to continue.\n");
183 return NS_ERROR_FAILURE;
185 #endif
187 #ifdef XP_MACOSX
188 bool same;
189 if (NS_SUCCEEDED(aDir->Equals(aLocalDir, &same)) && !same) {
190 // Ensure that the cache directory is not indexed by Spotlight
191 // (bug 718910). At least on OS X, the cache directory (under
192 // ~/Library/Caches/) is always the "local" user profile
193 // directory. This is confusing, since *both* user profile
194 // directories are "local" (they both exist under the user's
195 // home directory). But this usage dates back at least as far
196 // as the patch for bug 291033, where "local" seems to mean
197 // "suitable for temporary storage". Don't hide the cache
198 // directory if by some chance it and the "non-local" profile
199 // directory are the same -- there are bad side effects from
200 // hiding a profile directory under /Library/Application Support/
201 // (see bug 801883).
202 nsAutoCString cacheDir;
203 if (NS_SUCCEEDED(aLocalDir->GetNativePath(cacheDir))) {
204 if (chflags(cacheDir.get(), UF_HIDDEN)) {
205 NS_WARNING("Failed to set Cache directory to HIDDEN.");
209 #endif
211 mProfileDir = aDir;
212 mProfileLocalDir = aLocalDir;
213 return NS_OK;
216 NS_IMPL_QUERY_INTERFACE(nsXREDirProvider, nsIDirectoryServiceProvider,
217 nsIDirectoryServiceProvider2, nsIXREDirProvider,
218 nsIProfileStartup)
220 NS_IMETHODIMP_(MozExternalRefCountType)
221 nsXREDirProvider::AddRef() { return 1; }
223 NS_IMETHODIMP_(MozExternalRefCountType)
224 nsXREDirProvider::Release() { return 0; }
226 nsresult nsXREDirProvider::GetUserProfilesRootDir(nsIFile** aResult) {
227 nsCOMPtr<nsIFile> file;
228 nsresult rv = GetUserDataDirectory(getter_AddRefs(file), false);
230 if (NS_SUCCEEDED(rv)) {
231 #if !defined(XP_UNIX) || defined(XP_MACOSX)
232 rv = file->AppendNative("Profiles"_ns);
233 #endif
234 // We must create the profile directory here if it does not exist.
235 nsresult tmp = EnsureDirectoryExists(file);
236 if (NS_FAILED(tmp)) {
237 rv = tmp;
240 file.swap(*aResult);
241 return rv;
244 nsresult nsXREDirProvider::GetUserProfilesLocalDir(nsIFile** aResult) {
245 nsCOMPtr<nsIFile> file;
246 nsresult rv = GetUserDataDirectory(getter_AddRefs(file), true);
248 if (NS_SUCCEEDED(rv)) {
249 #if !defined(XP_UNIX) || defined(XP_MACOSX)
250 rv = file->AppendNative("Profiles"_ns);
251 #endif
252 // We must create the profile directory here if it does not exist.
253 nsresult tmp = EnsureDirectoryExists(file);
254 if (NS_FAILED(tmp)) {
255 rv = tmp;
258 file.swap(*aResult);
259 return NS_OK;
262 #ifdef MOZ_BACKGROUNDTASKS
263 nsresult nsXREDirProvider::GetBackgroundTasksProfilesRootDir(
264 nsIFile** aResult) {
265 nsCOMPtr<nsIFile> file;
266 nsresult rv = GetUserDataDirectory(getter_AddRefs(file), false);
268 if (NS_SUCCEEDED(rv)) {
269 # if !defined(XP_UNIX) || defined(XP_MACOSX)
270 // Sibling to regular user "Profiles" directory.
271 rv = file->AppendNative("Background Tasks Profiles"_ns);
272 # endif
273 // We must create the directory here if it does not exist.
274 nsresult tmp = EnsureDirectoryExists(file);
275 if (NS_FAILED(tmp)) {
276 rv = tmp;
279 file.swap(*aResult);
280 return rv;
282 #endif
284 #if defined(XP_UNIX) || defined(XP_MACOSX)
286 * Get the directory that is the parent of the system-wide directories
287 * for extensions and native manifests.
289 * On OSX this is /Library/Application Support/Mozilla
290 * On Linux this is /usr/{lib,lib64}/mozilla
291 * (for 32- and 64-bit systems respsectively)
293 static nsresult GetSystemParentDirectory(nsIFile** aFile) {
294 nsresult rv;
295 nsCOMPtr<nsIFile> localDir;
296 # if defined(XP_MACOSX)
297 rv = GetOSXFolderType(kOnSystemDisk, kApplicationSupportFolderType,
298 getter_AddRefs(localDir));
299 if (NS_SUCCEEDED(rv)) {
300 rv = localDir->AppendNative("Mozilla"_ns);
302 # else
303 constexpr auto dirname =
304 # ifdef HAVE_USR_LIB64_DIR
305 "/usr/lib64/mozilla"_ns
306 # elif defined(__OpenBSD__) || defined(__FreeBSD__)
307 "/usr/local/lib/mozilla"_ns
308 # else
309 "/usr/lib/mozilla"_ns
310 # endif
312 rv = NS_NewNativeLocalFile(dirname, false, getter_AddRefs(localDir));
313 # endif
315 if (NS_SUCCEEDED(rv)) {
316 localDir.forget(aFile);
318 return rv;
320 #endif
322 NS_IMETHODIMP
323 nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
324 nsIFile** aFile) {
325 *aPersistent = true;
326 nsresult rv = NS_ERROR_FAILURE;
328 nsCOMPtr<nsIFile> file;
330 if (!strcmp(aProperty, NS_APP_USER_PROFILE_LOCAL_50_DIR) ||
331 !strcmp(aProperty, NS_APP_PROFILE_LOCAL_DIR_STARTUP)) {
332 if (mProfileLocalDir) {
333 rv = mProfileLocalDir->Clone(getter_AddRefs(file));
334 } else {
335 // Profile directories are only set up in the parent process.
336 // We don't expect every caller to check if they are in the right process,
337 // so fail immediately to avoid warning spam.
338 NS_WARNING_ASSERTION(!XRE_IsParentProcess(),
339 "tried to get profile in parent too early");
340 return NS_ERROR_FAILURE;
342 } else if (!strcmp(aProperty, NS_APP_USER_PROFILE_50_DIR) ||
343 !strcmp(aProperty, NS_APP_PROFILE_DIR_STARTUP)) {
344 rv = GetProfileStartupDir(getter_AddRefs(file));
345 if (NS_FAILED(rv)) {
346 return rv;
348 } else if (!strcmp(aProperty, NS_GRE_DIR)) {
349 // On Android, internal files are inside the APK, a zip file, so this
350 // folder doesn't really make sense.
351 #if !defined(MOZ_WIDGET_ANDROID)
352 rv = mGREDir->Clone(getter_AddRefs(file));
353 #endif // !defined(MOZ_WIDGET_ANDROID)
354 } else if (!strcmp(aProperty, NS_GRE_BIN_DIR)) {
355 rv = mGREBinDir->Clone(getter_AddRefs(file));
356 } else if (!strcmp(aProperty, NS_OS_CURRENT_PROCESS_DIR) ||
357 !strcmp(aProperty, NS_APP_INSTALL_CLEANUP_DIR)) {
358 rv = GetAppDir()->Clone(getter_AddRefs(file));
359 } else if (!strcmp(aProperty, NS_APP_PREF_DEFAULTS_50_DIR)) {
360 // Same as NS_GRE_DIR
361 #if !defined(MOZ_WIDGET_ANDROID)
362 // return the GRE default prefs directory here, and the app default prefs
363 // directory (if applicable) in NS_APP_PREFS_DEFAULTS_DIR_LIST.
364 rv = mGREDir->Clone(getter_AddRefs(file));
365 NS_ENSURE_SUCCESS(rv, rv);
366 rv = file->AppendNative("defaults"_ns);
367 NS_ENSURE_SUCCESS(rv, rv);
368 rv = file->AppendNative("pref"_ns);
369 #endif // !defined(MOZ_WIDGET_ANDROID)
370 } else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_DIR) ||
371 !strcmp(aProperty, XRE_USER_APP_DATA_DIR)) {
372 rv = GetUserAppDataDirectory(getter_AddRefs(file));
374 #if defined(XP_UNIX) || defined(XP_MACOSX)
375 else if (!strcmp(aProperty, XRE_SYS_NATIVE_MANIFESTS)) {
376 rv = ::GetSystemParentDirectory(getter_AddRefs(file));
377 } else if (!strcmp(aProperty, XRE_USER_NATIVE_MANIFESTS)) {
378 rv = GetUserDataDirectoryHome(getter_AddRefs(file), false);
379 NS_ENSURE_SUCCESS(rv, rv);
380 # if defined(XP_MACOSX)
381 rv = file->AppendNative("Mozilla"_ns);
382 # else // defined(XP_MACOSX)
383 rv = file->AppendNative(".mozilla"_ns);
384 # endif // defined(XP_MACOSX)
386 #endif // defined(XP_UNIX) || defined(XP_MACOSX)
387 else if (!strcmp(aProperty, XRE_UPDATE_ROOT_DIR)) {
388 rv = GetUpdateRootDir(getter_AddRefs(file));
389 } else if (!strcmp(aProperty, XRE_OLD_UPDATE_ROOT_DIR)) {
390 rv = GetUpdateRootDir(getter_AddRefs(file), true);
391 } else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_FILE)) {
392 rv = GetUserAppDataDirectory(getter_AddRefs(file));
393 NS_ENSURE_SUCCESS(rv, rv);
394 rv = file->AppendNative(nsLiteralCString(APP_REGISTRY_NAME));
395 } else if (!strcmp(aProperty, NS_APP_USER_PROFILES_ROOT_DIR)) {
396 rv = GetUserProfilesRootDir(getter_AddRefs(file));
397 } else if (!strcmp(aProperty, NS_APP_USER_PROFILES_LOCAL_ROOT_DIR)) {
398 rv = GetUserProfilesLocalDir(getter_AddRefs(file));
399 } else if (!strcmp(aProperty, XRE_EXECUTABLE_FILE)) {
400 rv = XRE_GetBinaryPath(getter_AddRefs(file));
402 #if defined(XP_UNIX) || defined(XP_MACOSX)
403 else if (!strcmp(aProperty, XRE_SYS_LOCAL_EXTENSION_PARENT_DIR)) {
404 # ifdef ENABLE_SYSTEM_EXTENSION_DIRS
405 rv = GetSystemExtensionsDirectory(getter_AddRefs(file));
406 # endif
408 #endif // defined(XP_UNIX) || defined(XP_MACOSX)
409 #if defined(XP_UNIX) && !defined(XP_MACOSX)
410 else if (!strcmp(aProperty, XRE_SYS_SHARE_EXTENSION_PARENT_DIR)) {
411 # ifdef ENABLE_SYSTEM_EXTENSION_DIRS
412 # if defined(__OpenBSD__) || defined(__FreeBSD__)
413 static const char* const sysLExtDir = "/usr/local/share/mozilla/extensions";
414 # else
415 static const char* const sysLExtDir = "/usr/share/mozilla/extensions";
416 # endif
417 rv = NS_NewNativeLocalFile(nsDependentCString(sysLExtDir), false,
418 getter_AddRefs(file));
419 # endif
421 #endif // defined(XP_UNIX) && !defined(XP_MACOSX)
422 else if (!strcmp(aProperty, XRE_USER_SYS_EXTENSION_DIR)) {
423 #ifdef ENABLE_SYSTEM_EXTENSION_DIRS
424 rv = GetSysUserExtensionsDirectory(getter_AddRefs(file));
425 #endif
426 } else if (!strcmp(aProperty, XRE_USER_RUNTIME_DIR)) {
427 #if defined(XP_UNIX)
428 nsPrintfCString path("/run/user/%d/%s/", getuid(), GetAppName());
429 ToLowerCase(path);
430 rv = NS_NewNativeLocalFile(path, false, getter_AddRefs(file));
431 #endif
432 } else if (!strcmp(aProperty, XRE_APP_DISTRIBUTION_DIR)) {
433 bool persistent = false;
434 rv = GetFile(NS_GRE_DIR, &persistent, getter_AddRefs(file));
435 NS_ENSURE_SUCCESS(rv, rv);
436 rv = file->AppendNative("distribution"_ns);
437 } else if (!strcmp(aProperty, XRE_APP_FEATURES_DIR)) {
438 rv = GetAppDir()->Clone(getter_AddRefs(file));
439 NS_ENSURE_SUCCESS(rv, rv);
440 rv = file->AppendNative("features"_ns);
441 } else if (!strcmp(aProperty, XRE_ADDON_APP_DIR)) {
442 nsCOMPtr<nsIDirectoryServiceProvider> dirsvc(
443 do_GetService("@mozilla.org/file/directory_service;1", &rv));
444 NS_ENSURE_SUCCESS(rv, rv);
445 bool unused;
446 rv = dirsvc->GetFile("XCurProcD", &unused, getter_AddRefs(file));
448 #if defined(MOZ_CONTENT_TEMP_DIR)
449 else if (!strcmp(aProperty, NS_APP_CONTENT_PROCESS_TEMP_DIR)) {
450 if (!mContentTempDir) {
451 rv = LoadContentProcessTempDir();
452 NS_ENSURE_SUCCESS(rv, rv);
454 rv = mContentTempDir->Clone(getter_AddRefs(file));
456 #endif // defined(MOZ_CONTENT_TEMP_DIR)
457 else if (!strcmp(aProperty, NS_APP_USER_CHROME_DIR)) {
458 // It isn't clear why this uses GetProfileStartupDir instead of
459 // GetProfileDir. It could theoretically matter in a non-main
460 // process where some other directory provider has defined
461 // NS_APP_USER_PROFILE_50_DIR. In that scenario, using
462 // GetProfileStartupDir means this will fail instead of succeed.
463 rv = GetProfileStartupDir(getter_AddRefs(file));
464 if (NS_FAILED(rv)) {
465 return rv;
467 rv = file->AppendNative("chrome"_ns);
468 } else if (!strcmp(aProperty, NS_APP_PREFS_50_DIR)) {
469 rv = GetProfileDir(getter_AddRefs(file));
470 if (NS_FAILED(rv)) {
471 return rv;
473 } else if (!strcmp(aProperty, NS_APP_PREFS_50_FILE)) {
474 rv = GetProfileDir(getter_AddRefs(file));
475 if (NS_FAILED(rv)) {
476 return rv;
478 rv = file->AppendNative("prefs.js"_ns);
479 } else if (!strcmp(aProperty, NS_APP_PREFS_OVERRIDE_DIR)) {
480 rv = GetProfileDir(getter_AddRefs(file));
481 if (NS_FAILED(rv)) {
482 return rv;
484 rv = file->AppendNative(nsLiteralCString(PREF_OVERRIDE_DIRNAME));
485 NS_ENSURE_SUCCESS(rv, rv);
486 rv = EnsureDirectoryExists(file);
487 } else {
488 // We don't know anything about this property. Fail without warning, because
489 // otherwise we'll get too much warning spam due to
490 // nsDirectoryService::Get() trying everything it gets with every provider.
491 return NS_ERROR_FAILURE;
494 NS_ENSURE_SUCCESS(rv, rv);
495 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
497 file.forget(aFile);
498 return NS_OK;
501 static void LoadDirIntoArray(nsIFile* dir, const char* const* aAppendList,
502 nsCOMArray<nsIFile>& aDirectories) {
503 if (!dir) return;
505 nsCOMPtr<nsIFile> subdir;
506 dir->Clone(getter_AddRefs(subdir));
507 if (!subdir) return;
509 for (const char* const* a = aAppendList; *a; ++a) {
510 subdir->AppendNative(nsDependentCString(*a));
513 bool exists;
514 if (NS_SUCCEEDED(subdir->Exists(&exists)) && exists) {
515 aDirectories.AppendObject(subdir);
519 #if defined(MOZ_CONTENT_TEMP_DIR)
521 static const char* GetProcessTempBaseDirKey() { return NS_OS_TEMP_DIR; }
524 // Sets mContentTempDir so that it refers to the appropriate temp dir.
525 // If the sandbox is enabled, NS_APP_CONTENT_PROCESS_TEMP_DIR, otherwise
526 // NS_OS_TEMP_DIR is used.
528 nsresult nsXREDirProvider::LoadContentProcessTempDir() {
529 // The parent is responsible for creating the sandbox temp dir.
530 if (XRE_IsParentProcess()) {
531 mContentProcessSandboxTempDir =
532 CreateProcessSandboxTempDir(GeckoProcessType_Content);
533 mContentTempDir = mContentProcessSandboxTempDir;
534 } else {
535 mContentTempDir = !IsContentSandboxDisabled()
536 ? GetProcessSandboxTempDir(GeckoProcessType_Content)
537 : nullptr;
540 if (!mContentTempDir) {
541 nsresult rv =
542 NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mContentTempDir));
543 if (NS_WARN_IF(NS_FAILED(rv))) {
544 return rv;
548 return NS_OK;
551 static bool IsContentSandboxDisabled() {
552 return !mozilla::BrowserTabsRemoteAutostart() ||
553 (!mozilla::IsContentSandboxEnabled());
557 // If a process sandbox temp dir is to be used, returns an nsIFile
558 // for the directory. Returns null if an error occurs.
560 static already_AddRefed<nsIFile> GetProcessSandboxTempDir(
561 GeckoProcessType type) {
562 nsCOMPtr<nsIFile> localFile;
564 nsresult rv = NS_GetSpecialDirectory(GetProcessTempBaseDirKey(),
565 getter_AddRefs(localFile));
566 if (NS_WARN_IF(NS_FAILED(rv))) {
567 return nullptr;
570 MOZ_ASSERT(type == GeckoProcessType_Content);
572 const char* prefKey = "security.sandbox.content.tempDirSuffix";
573 nsAutoString tempDirSuffix;
574 rv = mozilla::Preferences::GetString(prefKey, tempDirSuffix);
575 if (NS_WARN_IF(NS_FAILED(rv)) || tempDirSuffix.IsEmpty()) {
576 return nullptr;
579 rv = localFile->Append(u"Temp-"_ns + tempDirSuffix);
580 if (NS_WARN_IF(NS_FAILED(rv))) {
581 return nullptr;
584 return localFile.forget();
588 // Create a temporary directory for use from sandboxed processes.
589 // Only called in the parent. The path is derived from a UUID stored in a
590 // pref which is available to content processes. Returns null
591 // if the content sandbox is disabled or if an error occurs.
593 static already_AddRefed<nsIFile> CreateProcessSandboxTempDir(
594 GeckoProcessType procType) {
595 if ((procType == GeckoProcessType_Content) && IsContentSandboxDisabled()) {
596 return nullptr;
599 MOZ_ASSERT(procType == GeckoProcessType_Content);
601 // Get (and create if blank) temp directory suffix pref.
602 const char* pref = "security.sandbox.content.tempDirSuffix";
604 nsresult rv;
605 nsAutoString tempDirSuffix;
606 mozilla::Preferences::GetString(pref, tempDirSuffix);
608 if (tempDirSuffix.IsEmpty()) {
609 nsID uuid;
610 rv = nsID::GenerateUUIDInPlace(uuid);
611 if (NS_WARN_IF(NS_FAILED(rv))) {
612 return nullptr;
615 char uuidChars[NSID_LENGTH];
616 uuid.ToProvidedString(uuidChars);
617 tempDirSuffix.AssignASCII(uuidChars, NSID_LENGTH);
618 # ifdef XP_UNIX
619 // Braces in a path are somewhat annoying to deal with
620 // and pretty alien on Unix
621 tempDirSuffix.StripChars(u"{}");
622 # endif
624 // Save the pref
625 rv = mozilla::Preferences::SetString(pref, tempDirSuffix);
626 if (NS_WARN_IF(NS_FAILED(rv))) {
627 // If we fail to save the pref we don't want to create the temp dir,
628 // because we won't be able to clean it up later.
629 return nullptr;
632 nsCOMPtr<nsIPrefService> prefsvc = mozilla::Preferences::GetService();
633 if (!prefsvc || NS_FAILED((rv = prefsvc->SavePrefFile(nullptr)))) {
634 // Again, if we fail to save the pref file we might not be able to clean
635 // up the temp directory, so don't create one. Note that in the case
636 // the preference values allows an off main thread save, the successful
637 // return from the call doesn't mean we actually saved the file. See
638 // bug 1364496 for details.
639 NS_WARNING("Failed to save pref file, cannot create temp dir.");
640 return nullptr;
644 nsCOMPtr<nsIFile> sandboxTempDir = GetProcessSandboxTempDir(procType);
645 if (!sandboxTempDir) {
646 NS_WARNING("Failed to determine sandbox temp dir path.");
647 return nullptr;
650 // Remove the directory. It may exist due to a previous crash.
651 if (NS_FAILED(DeleteDirIfExists(sandboxTempDir))) {
652 NS_WARNING("Failed to reset sandbox temp dir.");
653 return nullptr;
656 // Create the directory
657 rv = sandboxTempDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
658 if (NS_FAILED(rv)) {
659 NS_WARNING("Failed to create sandbox temp dir.");
660 return nullptr;
663 return sandboxTempDir.forget();
666 static nsresult DeleteDirIfExists(nsIFile* dir) {
667 if (dir) {
668 // Don't return an error if the directory doesn't exist.
669 nsresult rv = dir->Remove(/* aRecursive */ true);
670 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
671 return rv;
674 return NS_OK;
677 #endif // defined(MOZ_CONTENT_TEMP_DIR)
679 static const char* const kAppendPrefDir[] = {"defaults", "preferences",
680 nullptr};
681 #ifdef MOZ_BACKGROUNDTASKS
682 static const char* const kAppendBackgroundTasksPrefDir[] = {
683 "defaults", "backgroundtasks", nullptr};
684 #endif
686 NS_IMETHODIMP
687 nsXREDirProvider::GetFiles(const char* aProperty,
688 nsISimpleEnumerator** aResult) {
689 nsresult rv = NS_ERROR_FAILURE;
690 *aResult = nullptr;
692 if (!strcmp(aProperty, NS_APP_PREFS_DEFAULTS_DIR_LIST)) {
693 nsCOMArray<nsIFile> directories;
695 LoadDirIntoArray(mXULAppDir, kAppendPrefDir, directories);
696 #ifdef MOZ_BACKGROUNDTASKS
697 if (mozilla::BackgroundTasks::IsBackgroundTaskMode()) {
698 LoadDirIntoArray(mGREDir, kAppendBackgroundTasksPrefDir, directories);
699 LoadDirIntoArray(mXULAppDir, kAppendBackgroundTasksPrefDir, directories);
701 #endif
703 rv = NS_NewArrayEnumerator(aResult, directories, NS_GET_IID(nsIFile));
704 } else if (!strcmp(aProperty, NS_APP_CHROME_DIR_LIST)) {
705 // NS_APP_CHROME_DIR_LIST is only used to get default (native) icons
706 // for OS window decoration.
708 static const char* const kAppendChromeDir[] = {"chrome", nullptr};
709 nsCOMArray<nsIFile> directories;
710 LoadDirIntoArray(mXULAppDir, kAppendChromeDir, directories);
712 rv = NS_NewArrayEnumerator(aResult, directories, NS_GET_IID(nsIFile));
714 NS_ENSURE_SUCCESS(rv, rv);
716 return NS_SUCCESS_AGGREGATE_RESULT;
719 NS_IMETHODIMP
720 nsXREDirProvider::GetDirectory(nsIFile** aResult) {
721 NS_ENSURE_TRUE(mProfileDir, NS_ERROR_NOT_INITIALIZED);
722 return mProfileDir->Clone(aResult);
725 void nsXREDirProvider::InitializeUserPrefs() {
726 if (!mPrefsInitialized) {
727 mozilla::Preferences::InitializeUserPrefs();
731 void nsXREDirProvider::FinishInitializingUserPrefs() {
732 if (!mPrefsInitialized) {
733 mozilla::Preferences::FinishInitializingUserPrefs();
734 mPrefsInitialized = true;
738 NS_IMETHODIMP
739 nsXREDirProvider::DoStartup() {
740 nsresult rv;
742 if (!mAppStarted) {
743 nsCOMPtr<nsIObserverService> obsSvc =
744 mozilla::services::GetObserverService();
745 if (!obsSvc) return NS_ERROR_FAILURE;
747 mAppStarted = true;
750 Make sure we've setup prefs before profile-do-change to be able to use
751 them to track crashes and because we want to begin crash tracking before
752 other code run from this notification since they may cause crashes.
754 MOZ_ASSERT(mPrefsInitialized);
756 bool safeModeNecessary = false;
757 nsCOMPtr<nsIAppStartup> appStartup(
758 mozilla::components::AppStartup::Service());
759 if (appStartup) {
760 rv = appStartup->TrackStartupCrashBegin(&safeModeNecessary);
761 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)
762 NS_WARNING("Error while beginning startup crash tracking");
764 if (!gSafeMode && safeModeNecessary) {
765 appStartup->RestartInSafeMode(nsIAppStartup::eForceQuit);
766 return NS_OK;
770 static const char16_t kStartup[] = {'s', 't', 'a', 'r',
771 't', 'u', 'p', '\0'};
772 obsSvc->NotifyObservers(nullptr, "profile-do-change", kStartup);
774 // Initialize the Enterprise Policies service in the parent process
775 // In the content process it's loaded on demand when needed
776 if (XRE_IsParentProcess()) {
777 nsCOMPtr<nsIObserver> policies(
778 do_GetService("@mozilla.org/enterprisepolicies;1"));
779 if (policies) {
780 policies->Observe(nullptr, "policies-startup", nullptr);
784 #ifdef MOZ_THUNDERBIRD
785 bool bgtaskMode = false;
786 # ifdef MOZ_BACKGROUNDTASKS
787 bgtaskMode = mozilla::BackgroundTasks::IsBackgroundTaskMode();
788 # endif
789 if (!bgtaskMode &&
790 mozilla::Preferences::GetBool(
791 "security.prompt_for_master_password_on_startup", false)) {
792 // Prompt for the master password prior to opening application windows,
793 // to avoid the race that triggers multiple prompts (see bug 177175).
794 // We use this code until we have a better solution, possibly as
795 // described in bug 177175 comment 384.
796 nsCOMPtr<nsIPK11TokenDB> db =
797 do_GetService("@mozilla.org/security/pk11tokendb;1");
798 if (db) {
799 nsCOMPtr<nsIPK11Token> token;
800 if (NS_SUCCEEDED(db->GetInternalKeyToken(getter_AddRefs(token)))) {
801 mozilla::Unused << token->Login(false);
803 } else {
804 NS_WARNING("Failed to get nsIPK11TokenDB service.");
807 #endif
809 bool initExtensionManager =
810 #ifdef MOZ_BACKGROUNDTASKS
811 !mozilla::BackgroundTasks::IsBackgroundTaskMode();
812 #else
813 true;
814 #endif
815 if (initExtensionManager) {
816 // Init the Extension Manager
817 nsCOMPtr<nsIObserver> em =
818 do_GetService("@mozilla.org/addons/integration;1");
819 if (em) {
820 em->Observe(nullptr, "addons-startup", nullptr);
821 } else {
822 NS_WARNING("Failed to create Addons Manager.");
826 obsSvc->NotifyObservers(nullptr, "profile-after-change", kStartup);
828 // Any component that has registered for the profile-after-change category
829 // should also be created at this time.
830 (void)NS_CreateServicesFromCategory("profile-after-change", nullptr,
831 "profile-after-change");
833 if (gSafeMode && safeModeNecessary) {
834 static const char16_t kCrashed[] = {'c', 'r', 'a', 's',
835 'h', 'e', 'd', '\0'};
836 obsSvc->NotifyObservers(nullptr, "safemode-forced", kCrashed);
839 // 1 = Regular mode, 2 = Safe mode, 3 = Safe mode forced
840 int mode = 1;
841 if (gSafeMode) {
842 if (safeModeNecessary)
843 mode = 3;
844 else
845 mode = 2;
847 mozilla::Telemetry::Accumulate(mozilla::Telemetry::SAFE_MODE_USAGE, mode);
849 obsSvc->NotifyObservers(nullptr, "profile-initial-state", nullptr);
851 #if defined(MOZ_CONTENT_TEMP_DIR)
852 // Makes sure the content temp dir has been loaded if it hasn't been
853 // already. In the parent this ensures it has been created before we attempt
854 // to start any content processes.
855 if (!mContentTempDir) {
856 mozilla::Unused << NS_WARN_IF(NS_FAILED(LoadContentProcessTempDir()));
858 #endif
860 return NS_OK;
863 void nsXREDirProvider::DoShutdown() {
864 AUTO_PROFILER_LABEL("nsXREDirProvider::DoShutdown", OTHER);
866 if (mAppStarted) {
867 mozilla::AppShutdown::AdvanceShutdownPhase(
868 mozilla::ShutdownPhase::AppShutdownNetTeardown, nullptr);
869 mozilla::AppShutdown::AdvanceShutdownPhase(
870 mozilla::ShutdownPhase::AppShutdownTeardown, nullptr);
872 #ifdef DEBUG
873 // Not having this causes large intermittent leaks. See bug 1340425.
874 if (JSContext* cx = mozilla::dom::danger::GetJSContext()) {
875 JS_GC(cx);
877 #endif
879 mozilla::AppShutdown::AdvanceShutdownPhase(
880 mozilla::ShutdownPhase::AppShutdown, nullptr);
881 mozilla::AppShutdown::AdvanceShutdownPhase(
882 mozilla::ShutdownPhase::AppShutdownQM, nullptr);
883 mozilla::AppShutdown::AdvanceShutdownPhase(
884 mozilla::ShutdownPhase::AppShutdownTelemetry, nullptr);
885 mAppStarted = false;
888 gDataDirProfileLocal = nullptr;
889 gDataDirProfile = nullptr;
891 #if defined(MOZ_CONTENT_TEMP_DIR)
892 if (XRE_IsParentProcess()) {
893 mozilla::Unused << DeleteDirIfExists(mContentProcessSandboxTempDir);
895 #endif
898 #ifdef XP_WIN
899 static nsresult GetShellFolderPath(KNOWNFOLDERID folder, nsAString& _retval) {
900 DWORD flags = KF_FLAG_SIMPLE_IDLIST | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_ALIAS;
901 PWSTR path = nullptr;
903 if (!SUCCEEDED(SHGetKnownFolderPath(folder, flags, NULL, &path))) {
904 return NS_ERROR_NOT_AVAILABLE;
907 _retval = nsDependentString(path);
908 CoTaskMemFree(path);
909 return NS_OK;
913 * Provides a fallback for getting the path to APPDATA or LOCALAPPDATA by
914 * querying the registry when the call to SHGetSpecialFolderLocation or
915 * SHGetPathFromIDListW is unable to provide these paths (Bug 513958).
917 static nsresult GetRegWindowsAppDataFolder(bool aLocal, nsAString& _retval) {
918 HKEY key;
919 LPCWSTR keyName =
920 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
921 DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, keyName, 0, KEY_READ, &key);
922 if (res != ERROR_SUCCESS) {
923 _retval.SetLength(0);
924 return NS_ERROR_NOT_AVAILABLE;
927 DWORD type, size;
928 res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"), nullptr,
929 &type, nullptr, &size);
930 // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
931 // buffer size must not equal 0, and the buffer size be a multiple of 2.
932 if (res != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0) {
933 ::RegCloseKey(key);
934 _retval.SetLength(0);
935 return NS_ERROR_NOT_AVAILABLE;
938 // |size| may or may not include room for the terminating null character
939 DWORD resultLen = size / 2;
941 if (!_retval.SetLength(resultLen, mozilla::fallible)) {
942 ::RegCloseKey(key);
943 _retval.SetLength(0);
944 return NS_ERROR_NOT_AVAILABLE;
947 auto begin = _retval.BeginWriting();
949 res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"), nullptr,
950 nullptr, (LPBYTE)begin, &size);
951 ::RegCloseKey(key);
952 if (res != ERROR_SUCCESS) {
953 _retval.SetLength(0);
954 return NS_ERROR_NOT_AVAILABLE;
957 if (!_retval.CharAt(resultLen - 1)) {
958 // It was already null terminated.
959 _retval.Truncate(resultLen - 1);
962 return NS_OK;
964 #endif
966 static nsresult HashInstallPath(nsAString& aInstallPath, nsAString& aPathHash) {
967 mozilla::UniquePtr<NS_tchar[]> hash;
968 bool success = ::GetInstallHash(PromiseFlatString(aInstallPath).get(), hash);
969 if (!success) {
970 return NS_ERROR_FAILURE;
973 // The hash string is a NS_tchar*, which is wchar* in Windows and char*
974 // elsewhere.
975 #ifdef XP_WIN
976 aPathHash.Assign(hash.get());
977 #else
978 aPathHash.AssignASCII(hash.get());
979 #endif
980 return NS_OK;
984 * Gets a hash of the installation directory.
986 nsresult nsXREDirProvider::GetInstallHash(nsAString& aPathHash) {
987 nsAutoString stringToHash;
989 #ifdef XP_WIN
990 if (mozilla::widget::WinUtils::HasPackageIdentity()) {
991 // For packages, the install path includes the version number, so it isn't
992 // a stable or consistent identifier for the installation. The package
993 // family name is though, so use that instead of the path.
994 stringToHash = mozilla::widget::WinUtils::GetPackageFamilyName();
995 } else
996 #endif
998 nsCOMPtr<nsIFile> installDir;
999 nsCOMPtr<nsIFile> appFile;
1000 bool per = false;
1001 nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
1002 NS_ENSURE_SUCCESS(rv, rv);
1003 rv = appFile->GetParent(getter_AddRefs(installDir));
1004 NS_ENSURE_SUCCESS(rv, rv);
1006 // It is possible that the path we have is on a case insensitive
1007 // filesystem in which case the path may vary depending on how the
1008 // application is called. We want to normalize the case somehow.
1009 #ifdef XP_WIN
1010 // Windows provides a way to get the correct case.
1011 if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks(
1012 installDir)) {
1013 NS_WARNING("Failed to resolve install directory.");
1015 #elif defined(MOZ_WIDGET_COCOA)
1016 // On OSX roundtripping through an FSRef fixes the case.
1017 FSRef ref;
1018 nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(installDir);
1019 rv = macFile->GetFSRef(&ref);
1020 NS_ENSURE_SUCCESS(rv, rv);
1021 rv = NS_NewLocalFileWithFSRef(&ref, true, getter_AddRefs(macFile));
1022 NS_ENSURE_SUCCESS(rv, rv);
1023 installDir = static_cast<nsIFile*>(macFile);
1024 #endif
1025 // On linux XRE_EXECUTABLE_FILE already seems to be set to the correct path.
1027 rv = installDir->GetPath(stringToHash);
1028 NS_ENSURE_SUCCESS(rv, rv);
1031 // If we somehow failed to get an actual value, hashing an empty string could
1032 // potentially cause some serious problems given all the things this hash is
1033 // used for. So we don't allow that.
1034 if (stringToHash.IsEmpty()) {
1035 return NS_ERROR_FAILURE;
1038 return HashInstallPath(stringToHash, aPathHash);
1042 * Before bug 1555319 the directory hashed can have had an incorrect case.
1043 * Access to that hash is still available through this function. It is needed so
1044 * we can migrate users who may have an incorrect hash in profiles.ini. This
1045 * support can probably be removed in a few releases time.
1047 nsresult nsXREDirProvider::GetLegacyInstallHash(nsAString& aPathHash) {
1048 nsCOMPtr<nsIFile> installDir;
1049 nsCOMPtr<nsIFile> appFile;
1050 bool per = false;
1051 nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
1052 NS_ENSURE_SUCCESS(rv, rv);
1053 rv = appFile->GetParent(getter_AddRefs(installDir));
1054 NS_ENSURE_SUCCESS(rv, rv);
1056 nsAutoString installPath;
1057 rv = installDir->GetPath(installPath);
1058 NS_ENSURE_SUCCESS(rv, rv);
1060 #ifdef XP_WIN
1061 # if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
1062 // Convert a 64-bit install path to what would have been the 32-bit install
1063 // path to allow users to migrate their profiles from one to the other.
1064 PWSTR pathX86 = nullptr;
1065 HRESULT hres =
1066 SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, 0, nullptr, &pathX86);
1067 if (SUCCEEDED(hres)) {
1068 nsDependentString strPathX86(pathX86);
1069 if (!StringBeginsWith(installPath, strPathX86,
1070 nsCaseInsensitiveStringComparator)) {
1071 PWSTR path = nullptr;
1072 hres = SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, nullptr, &path);
1073 if (SUCCEEDED(hres)) {
1074 if (StringBeginsWith(installPath, nsDependentString(path),
1075 nsCaseInsensitiveStringComparator)) {
1076 installPath.Replace(0, wcslen(path), strPathX86);
1079 CoTaskMemFree(path);
1082 CoTaskMemFree(pathX86);
1083 # endif
1084 #endif
1085 return HashInstallPath(installPath, aPathHash);
1088 nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult,
1089 bool aGetOldLocation) {
1090 #ifndef XP_WIN
1091 // There is no old update location on platforms other than Windows. Windows is
1092 // the only platform for which we migrated the update directory.
1093 if (aGetOldLocation) {
1094 return NS_ERROR_NOT_IMPLEMENTED;
1096 #endif
1097 nsCOMPtr<nsIFile> updRoot;
1098 nsCOMPtr<nsIFile> appFile;
1099 bool per = false;
1100 nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
1101 NS_ENSURE_SUCCESS(rv, rv);
1102 rv = appFile->GetParent(getter_AddRefs(updRoot));
1103 NS_ENSURE_SUCCESS(rv, rv);
1105 #ifdef XP_MACOSX
1106 nsCOMPtr<nsIFile> appRootDirFile;
1107 nsCOMPtr<nsIFile> localDir;
1108 nsAutoString appDirPath;
1109 if (NS_FAILED(appFile->GetParent(getter_AddRefs(appRootDirFile))) ||
1110 NS_FAILED(appRootDirFile->GetPath(appDirPath)) ||
1111 NS_FAILED(GetUserDataDirectoryHome(getter_AddRefs(localDir), true))) {
1112 return NS_ERROR_FAILURE;
1115 int32_t dotIndex = appDirPath.RFind(u".app");
1116 if (dotIndex == kNotFound) {
1117 dotIndex = appDirPath.Length();
1119 appDirPath = Substring(appDirPath, 1, dotIndex - 1);
1121 bool hasVendor = GetAppVendor() && strlen(GetAppVendor()) != 0;
1122 if (hasVendor || GetAppName()) {
1123 if (NS_FAILED(localDir->AppendNative(
1124 nsDependentCString(hasVendor ? GetAppVendor() : GetAppName())))) {
1125 return NS_ERROR_FAILURE;
1127 } else if (NS_FAILED(localDir->AppendNative("Mozilla"_ns))) {
1128 return NS_ERROR_FAILURE;
1131 if (NS_FAILED(localDir->Append(u"updates"_ns)) ||
1132 NS_FAILED(localDir->AppendRelativePath(appDirPath))) {
1133 return NS_ERROR_FAILURE;
1136 localDir.forget(aResult);
1137 return NS_OK;
1139 #elif XP_WIN
1140 nsAutoString installPath;
1141 rv = updRoot->GetPath(installPath);
1142 NS_ENSURE_SUCCESS(rv, rv);
1144 mozilla::UniquePtr<wchar_t[]> updatePath;
1145 HRESULT hrv;
1146 if (aGetOldLocation) {
1147 hrv =
1148 GetOldUpdateDirectory(PromiseFlatString(installPath).get(), updatePath);
1149 } else {
1150 hrv = GetCommonUpdateDirectory(PromiseFlatString(installPath).get(),
1151 updatePath);
1153 if (FAILED(hrv)) {
1154 return NS_ERROR_FAILURE;
1156 nsAutoString updatePathStr;
1157 updatePathStr.Assign(updatePath.get());
1158 updRoot->InitWithPath(updatePathStr);
1159 updRoot.forget(aResult);
1160 return NS_OK;
1161 #else
1162 updRoot.forget(aResult);
1163 return NS_OK;
1164 #endif // XP_WIN
1167 nsresult nsXREDirProvider::GetProfileStartupDir(nsIFile** aResult) {
1168 if (mProfileDir) {
1169 return mProfileDir->Clone(aResult);
1172 // Profile directories are only set up in the parent process.
1173 // We don't expect every caller to check if they are in the right process,
1174 // so fail immediately to avoid warning spam.
1175 NS_WARNING_ASSERTION(!XRE_IsParentProcess(),
1176 "tried to get profile in parent too early");
1177 return NS_ERROR_FAILURE;
1180 nsresult nsXREDirProvider::GetProfileDir(nsIFile** aResult) {
1181 if (!mProfileDir) {
1182 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1183 getter_AddRefs(mProfileDir));
1184 // Guard against potential buggy directory providers that fail while also
1185 // returning something.
1186 if (NS_FAILED(rv)) {
1187 MOZ_ASSERT(!mProfileDir,
1188 "Directory provider failed but returned a value");
1189 mProfileDir = nullptr;
1192 // If we failed to get mProfileDir, this will warn for us if appropriate.
1193 return GetProfileStartupDir(aResult);
1196 NS_IMETHODIMP
1197 nsXREDirProvider::SetUserDataDirectory(nsIFile* aFile, bool aLocal) {
1198 if (aLocal) {
1199 NS_IF_RELEASE(gDataDirHomeLocal);
1200 NS_IF_ADDREF(gDataDirHomeLocal = aFile);
1201 } else {
1202 NS_IF_RELEASE(gDataDirHome);
1203 NS_IF_ADDREF(gDataDirHome = aFile);
1206 return NS_OK;
1209 /* static */
1210 nsresult nsXREDirProvider::SetUserDataProfileDirectory(nsCOMPtr<nsIFile>& aFile,
1211 bool aLocal) {
1212 if (aLocal) {
1213 gDataDirProfileLocal = aFile;
1214 } else {
1215 gDataDirProfile = aFile;
1218 return NS_OK;
1221 nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile,
1222 bool aLocal) {
1223 // Copied from nsAppFileLocationProvider (more or less)
1224 nsresult rv;
1225 nsCOMPtr<nsIFile> localDir;
1227 if (aLocal && gDataDirHomeLocal) {
1228 return gDataDirHomeLocal->Clone(aFile);
1230 if (!aLocal && gDataDirHome) {
1231 return gDataDirHome->Clone(aFile);
1234 #if defined(XP_MACOSX)
1235 FSRef fsRef;
1236 OSType folderType;
1237 if (aLocal) {
1238 folderType = kCachedDataFolderType;
1239 } else {
1240 # ifdef MOZ_THUNDERBIRD
1241 folderType = kDomainLibraryFolderType;
1242 # else
1243 folderType = kApplicationSupportFolderType;
1244 # endif
1246 OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef);
1247 NS_ENSURE_FALSE(err, NS_ERROR_FAILURE);
1249 rv = NS_NewNativeLocalFile(""_ns, true, getter_AddRefs(localDir));
1250 NS_ENSURE_SUCCESS(rv, rv);
1252 nsCOMPtr<nsILocalFileMac> dirFileMac = do_QueryInterface(localDir);
1253 NS_ENSURE_TRUE(dirFileMac, NS_ERROR_UNEXPECTED);
1255 rv = dirFileMac->InitWithFSRef(&fsRef);
1256 NS_ENSURE_SUCCESS(rv, rv);
1258 localDir = dirFileMac;
1259 #elif defined(XP_IOS)
1260 nsAutoCString userDir;
1261 if (GetUIKitDirectory(aLocal, userDir)) {
1262 rv = NS_NewNativeLocalFile(userDir, true, getter_AddRefs(localDir));
1263 } else {
1264 rv = NS_ERROR_FAILURE;
1266 NS_ENSURE_SUCCESS(rv, rv);
1267 #elif defined(XP_WIN)
1268 nsString path;
1269 if (aLocal) {
1270 rv = GetShellFolderPath(FOLDERID_LocalAppData, path);
1271 if (NS_FAILED(rv)) rv = GetRegWindowsAppDataFolder(aLocal, path);
1273 if (!aLocal || NS_FAILED(rv)) {
1274 rv = GetShellFolderPath(FOLDERID_RoamingAppData, path);
1275 if (NS_FAILED(rv)) {
1276 if (!aLocal) rv = GetRegWindowsAppDataFolder(aLocal, path);
1279 NS_ENSURE_SUCCESS(rv, rv);
1281 rv = NS_NewLocalFile(path, true, getter_AddRefs(localDir));
1282 #elif defined(XP_UNIX)
1283 const char* homeDir = getenv("HOME");
1284 if (!homeDir || !*homeDir) return NS_ERROR_FAILURE;
1286 # ifdef ANDROID /* We want (ProfD == ProfLD) on Android. */
1287 aLocal = false;
1288 # endif
1290 if (aLocal) {
1291 // If $XDG_CACHE_HOME is defined use it, otherwise use $HOME/.cache.
1292 const char* cacheHome = getenv("XDG_CACHE_HOME");
1293 if (cacheHome && *cacheHome) {
1294 rv = NS_NewNativeLocalFile(nsDependentCString(cacheHome), true,
1295 getter_AddRefs(localDir));
1296 } else {
1297 rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true,
1298 getter_AddRefs(localDir));
1299 if (NS_SUCCEEDED(rv)) rv = localDir->AppendNative(".cache"_ns);
1301 } else {
1302 rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true,
1303 getter_AddRefs(localDir));
1305 #else
1306 # error "Don't know how to get product dir on your platform"
1307 #endif
1309 NS_IF_ADDREF(*aFile = localDir);
1310 return rv;
1313 nsresult nsXREDirProvider::GetSysUserExtensionsDirectory(nsIFile** aFile) {
1314 nsCOMPtr<nsIFile> localDir;
1315 nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false);
1316 NS_ENSURE_SUCCESS(rv, rv);
1318 rv = AppendSysUserExtensionPath(localDir);
1319 NS_ENSURE_SUCCESS(rv, rv);
1321 rv = EnsureDirectoryExists(localDir);
1322 NS_ENSURE_SUCCESS(rv, rv);
1324 localDir.forget(aFile);
1325 return NS_OK;
1328 #if defined(XP_UNIX) || defined(XP_MACOSX)
1329 nsresult nsXREDirProvider::GetSystemExtensionsDirectory(nsIFile** aFile) {
1330 nsresult rv;
1331 nsCOMPtr<nsIFile> localDir;
1333 rv = GetSystemParentDirectory(getter_AddRefs(localDir));
1334 if (NS_SUCCEEDED(rv)) {
1335 constexpr auto sExtensions =
1336 # if defined(XP_MACOSX)
1337 "Extensions"_ns
1338 # else
1339 "extensions"_ns
1340 # endif
1343 rv = localDir->AppendNative(sExtensions);
1344 if (NS_SUCCEEDED(rv)) {
1345 localDir.forget(aFile);
1348 return rv;
1350 #endif
1352 nsresult nsXREDirProvider::GetUserDataDirectory(nsIFile** aFile, bool aLocal) {
1353 nsCOMPtr<nsIFile> localDir;
1355 if (aLocal && gDataDirProfileLocal) {
1356 return gDataDirProfileLocal->Clone(aFile);
1358 if (!aLocal && gDataDirProfile) {
1359 return gDataDirProfile->Clone(aFile);
1362 nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), aLocal);
1363 NS_ENSURE_SUCCESS(rv, rv);
1365 rv = AppendProfilePath(localDir, aLocal);
1366 NS_ENSURE_SUCCESS(rv, rv);
1368 rv = EnsureDirectoryExists(localDir);
1369 NS_ENSURE_SUCCESS(rv, rv);
1371 nsXREDirProvider::SetUserDataProfileDirectory(localDir, aLocal);
1373 localDir.forget(aFile);
1374 return NS_OK;
1377 nsresult nsXREDirProvider::EnsureDirectoryExists(nsIFile* aDirectory) {
1378 nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0700);
1380 if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
1381 rv = NS_OK;
1383 return rv;
1386 nsresult nsXREDirProvider::AppendSysUserExtensionPath(nsIFile* aFile) {
1387 NS_ASSERTION(aFile, "Null pointer!");
1389 nsresult rv;
1391 #if defined(XP_MACOSX) || defined(XP_WIN)
1393 static const char* const sXR = "Mozilla";
1394 rv = aFile->AppendNative(nsDependentCString(sXR));
1395 NS_ENSURE_SUCCESS(rv, rv);
1397 static const char* const sExtensions = "Extensions";
1398 rv = aFile->AppendNative(nsDependentCString(sExtensions));
1399 NS_ENSURE_SUCCESS(rv, rv);
1401 #elif defined(XP_UNIX)
1403 static const char* const sXR = ".mozilla";
1404 rv = aFile->AppendNative(nsDependentCString(sXR));
1405 NS_ENSURE_SUCCESS(rv, rv);
1407 static const char* const sExtensions = "extensions";
1408 rv = aFile->AppendNative(nsDependentCString(sExtensions));
1409 NS_ENSURE_SUCCESS(rv, rv);
1411 #else
1412 # error "Don't know how to get XRE user extension path on your platform"
1413 #endif
1414 return NS_OK;
1417 nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) {
1418 NS_ASSERTION(aFile, "Null pointer!");
1420 // If there is no XREAppData then there is no information to use to build
1421 // the profile path so just do nothing. This should only happen in xpcshell
1422 // tests.
1423 if (!gAppData) {
1424 return NS_OK;
1427 nsAutoCString profile;
1428 nsAutoCString appName;
1429 nsAutoCString vendor;
1430 if (gAppData->profile) {
1431 profile = gAppData->profile;
1432 } else {
1433 appName = gAppData->name;
1434 vendor = gAppData->vendor;
1437 nsresult rv = NS_OK;
1439 #if defined(XP_MACOSX)
1440 if (!profile.IsEmpty()) {
1441 rv = AppendProfileString(aFile, profile.get());
1442 } else {
1443 // Note that MacOS ignores the vendor when creating the profile hierarchy -
1444 // all application preferences directories live alongside one another in
1445 // ~/Library/Application Support/
1446 rv = aFile->AppendNative(appName);
1448 NS_ENSURE_SUCCESS(rv, rv);
1450 #elif defined(XP_WIN)
1451 if (!profile.IsEmpty()) {
1452 rv = AppendProfileString(aFile, profile.get());
1453 } else {
1454 if (!vendor.IsEmpty()) {
1455 rv = aFile->AppendNative(vendor);
1456 NS_ENSURE_SUCCESS(rv, rv);
1458 rv = aFile->AppendNative(appName);
1460 NS_ENSURE_SUCCESS(rv, rv);
1462 #elif defined(ANDROID)
1463 // The directory used for storing profiles
1464 // The parent of this directory is set in GetUserDataDirectoryHome
1465 // XXX: handle gAppData->profile properly
1466 // XXXsmaug ...and the rest of the profile creation!
1467 rv = aFile->AppendNative(nsDependentCString("mozilla"));
1468 NS_ENSURE_SUCCESS(rv, rv);
1469 #elif defined(XP_UNIX)
1470 nsAutoCString folder;
1471 // Make it hidden (by starting with "."), except when local (the
1472 // profile is already under ~/.cache or XDG_CACHE_HOME).
1473 if (!aLocal) folder.Assign('.');
1475 if (!profile.IsEmpty()) {
1476 // Skip any leading path characters
1477 const char* profileStart = profile.get();
1478 while (*profileStart == '/' || *profileStart == '\\') profileStart++;
1480 // On the off chance that someone wanted their folder to be hidden don't
1481 // let it become ".."
1482 if (*profileStart == '.' && !aLocal) profileStart++;
1484 folder.Append(profileStart);
1485 ToLowerCase(folder);
1487 rv = AppendProfileString(aFile, folder.BeginReading());
1488 } else {
1489 if (!vendor.IsEmpty()) {
1490 folder.Append(vendor);
1491 ToLowerCase(folder);
1493 rv = aFile->AppendNative(folder);
1494 NS_ENSURE_SUCCESS(rv, rv);
1496 folder.Truncate();
1499 // This can be the case in tests.
1500 if (!appName.IsEmpty()) {
1501 folder.Append(appName);
1502 ToLowerCase(folder);
1504 rv = aFile->AppendNative(folder);
1507 NS_ENSURE_SUCCESS(rv, rv);
1509 #else
1510 # error "Don't know how to get profile path on your platform"
1511 #endif
1512 return NS_OK;
1515 nsresult nsXREDirProvider::AppendProfileString(nsIFile* aFile,
1516 const char* aPath) {
1517 NS_ASSERTION(aFile, "Null file!");
1518 NS_ASSERTION(aPath, "Null path!");
1520 nsAutoCString pathDup(aPath);
1522 char* path = pathDup.BeginWriting();
1524 nsresult rv;
1525 char* subdir;
1526 while ((subdir = NS_strtok("/\\", &path))) {
1527 rv = aFile->AppendNative(nsDependentCString(subdir));
1528 NS_ENSURE_SUCCESS(rv, rv);
1531 return NS_OK;