1 /* -*- Mode: C++; tab-width: 8; 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 "mozilla/ArrayUtils.h"
7 #include "mozilla/ScopeExit.h"
8 #include "mozilla/UniquePtr.h"
9 #include "mozilla/UniquePtrExtensions.h"
10 #include "mozilla/WidgetUtils.h"
11 #include "nsProfileLock.h"
21 # include "mozilla/PolicyChecks.h"
27 #include "nsToolkitProfileService.h"
28 #include "CmdLineAndEnvUtils.h"
32 # include <CoreFoundation/CoreFoundation.h>
33 # include "nsILocalFileMac.h"
37 # include "mozilla/WidgetUtilsGtk.h"
40 #include "nsAppDirectoryServiceDefs.h"
41 #include "nsDirectoryServiceDefs.h"
43 #include "nsXULAppAPI.h"
44 #include "nsThreadUtils.h"
46 #include "nsIRunnable.h"
47 #include "nsXREDirProvider.h"
48 #include "nsAppRunner.h"
50 #include "nsReadableUtils.h"
51 #include "nsNativeCharsetUtils.h"
52 #include "mozilla/Attributes.h"
53 #include "mozilla/Sprintf.h"
54 #include "nsPrintfCString.h"
55 #include "mozilla/UniquePtr.h"
56 #include "nsIToolkitShellService.h"
57 #include "mozilla/Telemetry.h"
58 #include "nsProxyRelease.h"
61 #ifdef MOZ_BACKGROUNDTASKS
62 # include "mozilla/BackgroundTasks.h"
63 # include "SpecialSystemDirectory.h"
66 using namespace mozilla
;
68 #define DEV_EDITION_NAME "dev-edition-default"
69 #define DEFAULT_NAME "default"
70 #define COMPAT_FILE u"compatibility.ini"_ns
71 #define PROFILE_DB_VERSION "2"
72 #define INSTALL_PREFIX "Install"
73 #define INSTALL_PREFIX_LENGTH 7
76 KeyValue(const char* aKey
, const char* aValue
) : key(aKey
), value(aValue
) {}
82 static bool GetStrings(const char* aString
, const char* aValue
,
84 nsTArray
<UniquePtr
<KeyValue
>>* array
=
85 static_cast<nsTArray
<UniquePtr
<KeyValue
>>*>(aClosure
);
86 array
->AppendElement(MakeUnique
<KeyValue
>(aString
, aValue
));
92 * Returns an array of the strings inside a section of an ini file.
94 nsTArray
<UniquePtr
<KeyValue
>> GetSectionStrings(nsINIParser
* aParser
,
95 const char* aSection
) {
96 nsTArray
<UniquePtr
<KeyValue
>> result
;
97 aParser
->GetStrings(aSection
, &GetStrings
, &result
);
101 void RemoveProfileRecursion(const nsCOMPtr
<nsIFile
>& aDirectoryOrFile
,
102 bool aIsIgnoreRoot
, bool aIsIgnoreLockfile
,
103 nsTArray
<nsCOMPtr
<nsIFile
>>& aOutUndeletedFiles
) {
104 auto guardDeletion
= MakeScopeExit(
105 [&] { aOutUndeletedFiles
.AppendElement(aDirectoryOrFile
); });
107 // We actually would not expect to see links in our profiles, but still.
109 NS_ENSURE_SUCCESS_VOID(aDirectoryOrFile
->IsSymlink(&isLink
));
111 // Only check to see if we have a directory if it isn't a link.
114 NS_ENSURE_SUCCESS_VOID(aDirectoryOrFile
->IsDirectory(&isDir
));
118 nsCOMPtr
<nsIDirectoryEnumerator
> dirEnum
;
119 NS_ENSURE_SUCCESS_VOID(
120 aDirectoryOrFile
->GetDirectoryEntries(getter_AddRefs(dirEnum
)));
123 while (NS_SUCCEEDED(dirEnum
->HasMoreElements(&more
)) && more
) {
124 nsCOMPtr
<nsISupports
> item
;
125 dirEnum
->GetNext(getter_AddRefs(item
));
126 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(item
);
128 // Do not delete the profile lock.
129 if (aIsIgnoreLockfile
&& nsProfileLock::IsMaybeLockFile(file
)) continue;
130 // If some children's remove fails, we still continue the loop.
131 RemoveProfileRecursion(file
, false, false, aOutUndeletedFiles
);
135 // Do not delete the root directory (yet).
136 if (!aIsIgnoreRoot
) {
137 NS_ENSURE_SUCCESS_VOID(aDirectoryOrFile
->Remove(false));
139 guardDeletion
.release();
142 void RemoveProfileFiles(nsIToolkitProfile
* aProfile
, bool aInBackground
) {
143 nsCOMPtr
<nsIFile
> rootDir
;
144 aProfile
->GetRootDir(getter_AddRefs(rootDir
));
145 nsCOMPtr
<nsIFile
> localDir
;
146 aProfile
->GetLocalDir(getter_AddRefs(localDir
));
148 // XXX If we get here with an active quota manager,
149 // something went very wrong. We want to assert this.
151 // Just lock the directories, don't mark the profile as locked or the lock
152 // will attempt to release its reference to the profile on the background
153 // thread which will assert.
154 nsCOMPtr
<nsIProfileLock
> lock
;
155 NS_ENSURE_SUCCESS_VOID(
156 NS_LockProfilePath(rootDir
, localDir
, nullptr, getter_AddRefs(lock
)));
158 nsCOMPtr
<nsIRunnable
> runnable
= NS_NewRunnableFunction(
159 "nsToolkitProfile::RemoveProfileFiles",
160 [rootDir
, localDir
, lock
]() mutable {
161 // We try to remove every single file and directory and collect
162 // those whose removal failed.
163 nsTArray
<nsCOMPtr
<nsIFile
>> undeletedFiles
;
164 // The root dir might contain the temp dir, so remove the temp dir
167 nsresult rv
= rootDir
->Equals(localDir
, &equals
);
168 if (NS_SUCCEEDED(rv
) && !equals
) {
169 RemoveProfileRecursion(localDir
,
170 /* aIsIgnoreRoot */ false,
171 /* aIsIgnoreLockfile */ false, undeletedFiles
);
173 // Now remove the content of the profile dir (except lockfile)
174 RemoveProfileRecursion(rootDir
,
175 /* aIsIgnoreRoot */ true,
176 /* aIsIgnoreLockfile */ true, undeletedFiles
);
178 // Retry loop if something was not deleted
179 if (undeletedFiles
.Length() > 0) {
180 uint32_t retries
= 1;
181 // XXX: Until bug 1716291 is fixed we just make one retry
182 while (undeletedFiles
.Length() > 0 && retries
<= 1) {
183 Unused
<< PR_Sleep(PR_MillisecondsToInterval(10 * retries
));
185 std::exchange(undeletedFiles
, nsTArray
<nsCOMPtr
<nsIFile
>>{})) {
186 RemoveProfileRecursion(file
,
187 /* aIsIgnoreRoot */ false,
188 /* aIsIgnoreLockfile */ true,
196 // XXX: Until bug 1716291 is fixed, we do not want to spam release
197 if (undeletedFiles
.Length() > 0) {
198 NS_WARNING("Unable to remove all files from the profile directory:");
199 // Log the file names of those we could not remove
200 for (auto&& file
: undeletedFiles
) {
201 nsAutoString leafName
;
202 if (NS_SUCCEEDED(file
->GetLeafName(leafName
))) {
203 NS_WARNING(NS_LossyConvertUTF16toASCII(leafName
).get());
208 // XXX: Activate this assert once bug 1716291 is fixed
209 // MOZ_ASSERT(undeletedFiles.Length() == 0);
211 // Now we can unlock the profile safely.
213 // nsIProfileLock is not threadsafe so release our reference to it on
215 NS_ReleaseOnMainThread("nsToolkitProfile::RemoveProfileFiles::Unlock",
218 if (undeletedFiles
.Length() == 0) {
219 // We can safely remove the (empty) remaining profile directory
220 // and lockfile, no other files are here.
221 // As we do this only if we had no other blockers, this is as safe
222 // as deleting the lockfile explicitely after unlocking.
223 Unused
<< rootDir
->Remove(true);
228 nsCOMPtr
<nsIEventTarget
> target
=
229 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
230 target
->Dispatch(runnable
, NS_DISPATCH_NORMAL
);
236 nsToolkitProfile::nsToolkitProfile(const nsACString
& aName
, nsIFile
* aRootDir
,
237 nsIFile
* aLocalDir
, bool aFromDB
)
240 mLocalDir(aLocalDir
),
243 mSection("Profile") {
244 NS_ASSERTION(aRootDir
, "No file!");
246 RefPtr
<nsToolkitProfile
> prev
=
247 nsToolkitProfileService::gService
->mProfiles
.getLast();
249 mIndex
= prev
->mIndex
+ 1;
251 mSection
.AppendInt(mIndex
);
253 nsToolkitProfileService::gService
->mProfiles
.insertBack(this);
255 // If this profile isn't in the database already add it.
257 nsINIParser
* db
= &nsToolkitProfileService::gService
->mProfileDB
;
258 db
->SetString(mSection
.get(), "Name", mName
.get());
260 bool isRelative
= false;
261 nsCString descriptor
;
262 nsToolkitProfileService::gService
->GetProfileDescriptor(this, descriptor
,
265 db
->SetString(mSection
.get(), "IsRelative", isRelative
? "1" : "0");
266 db
->SetString(mSection
.get(), "Path", descriptor
.get());
270 NS_IMPL_ISUPPORTS(nsToolkitProfile
, nsIToolkitProfile
)
273 nsToolkitProfile::GetRootDir(nsIFile
** aResult
) {
274 NS_ADDREF(*aResult
= mRootDir
);
279 nsToolkitProfile::GetLocalDir(nsIFile
** aResult
) {
280 NS_ADDREF(*aResult
= mLocalDir
);
285 nsToolkitProfile::GetName(nsACString
& aResult
) {
291 nsToolkitProfile::SetName(const nsACString
& aName
) {
292 NS_ASSERTION(nsToolkitProfileService::gService
, "Where did my service go?");
294 if (mName
.Equals(aName
)) {
298 // Changing the name from the dev-edition default profile name makes this
299 // profile no longer the dev-edition default.
300 if (mName
.EqualsLiteral(DEV_EDITION_NAME
) &&
301 nsToolkitProfileService::gService
->mDevEditionDefault
== this) {
302 nsToolkitProfileService::gService
->mDevEditionDefault
= nullptr;
307 nsresult rv
= nsToolkitProfileService::gService
->mProfileDB
.SetString(
308 mSection
.get(), "Name", mName
.get());
309 NS_ENSURE_SUCCESS(rv
, rv
);
311 // Setting the name to the dev-edition default profile name will cause this
312 // profile to become the dev-edition default.
313 if (aName
.EqualsLiteral(DEV_EDITION_NAME
) &&
314 !nsToolkitProfileService::gService
->mDevEditionDefault
) {
315 nsToolkitProfileService::gService
->mDevEditionDefault
= this;
321 nsresult
nsToolkitProfile::RemoveInternal(bool aRemoveFiles
,
322 bool aInBackground
) {
323 NS_ASSERTION(nsToolkitProfileService::gService
, "Whoa, my service is gone.");
325 if (mLock
) return NS_ERROR_FILE_IS_LOCKED
;
328 return NS_ERROR_NOT_INITIALIZED
;
332 RemoveProfileFiles(this, aInBackground
);
335 nsINIParser
* db
= &nsToolkitProfileService::gService
->mProfileDB
;
336 db
->DeleteSection(mSection
.get());
338 // We make some assumptions that the profile's index in the database is based
339 // on its position in the linked list. Removing a profile means we have to fix
340 // the index of later profiles in the list. The easiest way to do that is just
341 // to move the last profile into the profile's position and just update its
343 RefPtr
<nsToolkitProfile
> last
=
344 nsToolkitProfileService::gService
->mProfiles
.getLast();
346 // Update the section in the db.
347 last
->mIndex
= mIndex
;
348 db
->RenameSection(last
->mSection
.get(), mSection
.get());
349 last
->mSection
= mSection
;
351 if (last
!= getNext()) {
359 if (nsToolkitProfileService::gService
->mNormalDefault
== this) {
360 nsToolkitProfileService::gService
->mNormalDefault
= nullptr;
362 if (nsToolkitProfileService::gService
->mDevEditionDefault
== this) {
363 nsToolkitProfileService::gService
->mDevEditionDefault
= nullptr;
365 if (nsToolkitProfileService::gService
->mDedicatedProfile
== this) {
366 nsToolkitProfileService::gService
->SetDefaultProfile(nullptr);
373 nsToolkitProfile::Remove(bool removeFiles
) {
374 return RemoveInternal(removeFiles
, false /* in background */);
378 nsToolkitProfile::RemoveInBackground(bool removeFiles
) {
379 return RemoveInternal(removeFiles
, true /* in background */);
383 nsToolkitProfile::Lock(nsIProfileUnlocker
** aUnlocker
,
384 nsIProfileLock
** aResult
) {
386 NS_ADDREF(*aResult
= mLock
);
390 RefPtr
<nsToolkitProfileLock
> lock
= new nsToolkitProfileLock();
392 nsresult rv
= lock
->Init(this, aUnlocker
);
393 if (NS_FAILED(rv
)) return rv
;
395 NS_ADDREF(*aResult
= lock
);
399 NS_IMPL_ISUPPORTS(nsToolkitProfileLock
, nsIProfileLock
)
401 nsresult
nsToolkitProfileLock::Init(nsToolkitProfile
* aProfile
,
402 nsIProfileUnlocker
** aUnlocker
) {
404 rv
= Init(aProfile
->mRootDir
, aProfile
->mLocalDir
, aUnlocker
);
405 if (NS_SUCCEEDED(rv
)) mProfile
= aProfile
;
410 nsresult
nsToolkitProfileLock::Init(nsIFile
* aDirectory
,
411 nsIFile
* aLocalDirectory
,
412 nsIProfileUnlocker
** aUnlocker
) {
415 rv
= mLock
.Lock(aDirectory
, aUnlocker
);
417 if (NS_SUCCEEDED(rv
)) {
418 mDirectory
= aDirectory
;
419 mLocalDirectory
= aLocalDirectory
;
426 nsToolkitProfileLock::GetDirectory(nsIFile
** aResult
) {
428 NS_ERROR("Not initialized, or unlocked!");
429 return NS_ERROR_NOT_INITIALIZED
;
432 NS_ADDREF(*aResult
= mDirectory
);
437 nsToolkitProfileLock::GetLocalDirectory(nsIFile
** aResult
) {
438 if (!mLocalDirectory
) {
439 NS_ERROR("Not initialized, or unlocked!");
440 return NS_ERROR_NOT_INITIALIZED
;
443 NS_ADDREF(*aResult
= mLocalDirectory
);
448 nsToolkitProfileLock::Unlock() {
450 NS_ERROR("Unlocking a never-locked nsToolkitProfileLock!");
451 return NS_ERROR_UNEXPECTED
;
454 // XXX If we get here with an active quota manager,
455 // something went very wrong. We want to assert this.
460 mProfile
->mLock
= nullptr;
463 mDirectory
= nullptr;
464 mLocalDirectory
= nullptr;
470 nsToolkitProfileLock::GetReplacedLockTime(PRTime
* aResult
) {
471 mLock
.GetReplacedLockTime(aResult
);
475 nsToolkitProfileLock::~nsToolkitProfileLock() {
481 nsToolkitProfileService
* nsToolkitProfileService::gService
= nullptr;
483 NS_IMPL_ISUPPORTS(nsToolkitProfileService
, nsIToolkitProfileService
)
485 nsToolkitProfileService::nsToolkitProfileService()
486 : mStartupProfileSelected(false),
487 mStartWithLast(true),
489 mUseDevEditionProfile(false),
490 #ifdef MOZ_DEDICATED_PROFILES
491 mUseDedicatedProfile(!IsSnapEnvironment() && !UseLegacyProfiles()),
493 mUseDedicatedProfile(false),
495 mStartupReason(u
"unknown"_ns
),
496 mMaybeLockProfile(false),
497 mUpdateChannel(MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL
)),
498 mProfileDBExists(false),
499 mProfileDBFileSize(0),
500 mProfileDBModifiedTime(0) {
501 #ifdef MOZ_DEV_EDITION
502 mUseDevEditionProfile
= true;
506 nsToolkitProfileService::~nsToolkitProfileService() {
511 void nsToolkitProfileService::CompleteStartup() {
512 if (!mStartupProfileSelected
) {
516 ScalarSet(mozilla::Telemetry::ScalarID::STARTUP_PROFILE_SELECTION_REASON
,
519 if (mMaybeLockProfile
) {
520 nsCOMPtr
<nsIToolkitShellService
> shell
=
521 do_GetService(NS_TOOLKITSHELLSERVICE_CONTRACTID
);
527 nsresult rv
= shell
->IsDefaultApplication(&isDefaultApp
);
528 NS_ENSURE_SUCCESS_VOID(rv
);
531 mProfileDB
.SetString(mInstallSection
.get(), "Locked", "1");
533 // There is a very small chance that this could fail if something else
534 // overwrote the profiles database since we started up, probably less than
535 // a second ago. There isn't really a sane response here, all the other
536 // profile changes are already flushed so whether we fail to flush here or
537 // force quit the app makes no difference.
538 NS_ENSURE_SUCCESS_VOID(Flush());
543 // Tests whether the passed profile was last used by this install.
544 bool nsToolkitProfileService::IsProfileForCurrentInstall(
545 nsIToolkitProfile
* aProfile
) {
546 nsCOMPtr
<nsIFile
> profileDir
;
547 nsresult rv
= aProfile
->GetRootDir(getter_AddRefs(profileDir
));
548 NS_ENSURE_SUCCESS(rv
, false);
550 nsCOMPtr
<nsIFile
> compatFile
;
551 rv
= profileDir
->Clone(getter_AddRefs(compatFile
));
552 NS_ENSURE_SUCCESS(rv
, false);
554 rv
= compatFile
->Append(COMPAT_FILE
);
555 NS_ENSURE_SUCCESS(rv
, false);
557 nsINIParser compatData
;
558 rv
= compatData
.Init(compatFile
);
559 NS_ENSURE_SUCCESS(rv
, false);
562 * In xpcshell gDirServiceProvider doesn't have all the correct directories
563 * set so using NS_GetSpecialDirectory works better there. But in a normal
564 * app launch the component registry isn't initialized so
565 * NS_GetSpecialDirectory doesn't work. So we have to use two different
566 * paths to support testing.
568 nsCOMPtr
<nsIFile
> currentGreDir
;
569 rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(currentGreDir
));
570 if (rv
== NS_ERROR_NOT_INITIALIZED
) {
571 currentGreDir
= gDirServiceProvider
->GetGREDir();
572 MOZ_ASSERT(currentGreDir
, "No GRE dir found.");
573 } else if (NS_FAILED(rv
)) {
577 nsCString lastGreDirStr
;
578 rv
= compatData
.GetString("Compatibility", "LastPlatformDir", lastGreDirStr
);
579 // If this string is missing then this profile is from an ancient version.
580 // We'll opt to use it in this case.
585 nsCOMPtr
<nsIFile
> lastGreDir
;
586 rv
= NS_NewNativeLocalFile(""_ns
, false, getter_AddRefs(lastGreDir
));
587 NS_ENSURE_SUCCESS(rv
, false);
589 rv
= lastGreDir
->SetPersistentDescriptor(lastGreDirStr
);
590 NS_ENSURE_SUCCESS(rv
, false);
593 # if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
594 mozilla::PathString lastGreDirPath
, currentGreDirPath
;
595 lastGreDirPath
= lastGreDir
->NativePath();
596 currentGreDirPath
= currentGreDir
->NativePath();
597 if (lastGreDirPath
.Equals(currentGreDirPath
,
598 nsCaseInsensitiveStringComparator
)) {
602 // Convert a 64-bit install path to what would have been the 32-bit install
603 // path to allow users to migrate their profiles from one to the other.
604 PWSTR pathX86
= nullptr;
606 SHGetKnownFolderPath(FOLDERID_ProgramFilesX86
, 0, nullptr, &pathX86
);
607 if (SUCCEEDED(hres
)) {
608 nsDependentString
strPathX86(pathX86
);
609 if (!StringBeginsWith(currentGreDirPath
, strPathX86
,
610 nsCaseInsensitiveStringComparator
)) {
611 PWSTR path
= nullptr;
612 hres
= SHGetKnownFolderPath(FOLDERID_ProgramFiles
, 0, nullptr, &path
);
613 if (SUCCEEDED(hres
)) {
614 if (StringBeginsWith(currentGreDirPath
, nsDependentString(path
),
615 nsCaseInsensitiveStringComparator
)) {
616 currentGreDirPath
.Replace(0, wcslen(path
), strPathX86
);
622 CoTaskMemFree(pathX86
);
624 return lastGreDirPath
.Equals(currentGreDirPath
,
625 nsCaseInsensitiveStringComparator
);
630 rv
= lastGreDir
->Equals(currentGreDir
, &equal
);
631 NS_ENSURE_SUCCESS(rv
, false);
637 * Used the first time an install with dedicated profile support runs. Decides
638 * whether to mark the passed profile as the default for this install.
640 * The goal is to reduce disruption but ideally end up with the OS default
641 * install using the old default profile.
643 * If the decision is to use the profile then it will be unassigned as the
644 * dedicated default for other installs.
646 * We won't attempt to use the profile if it was last used by a different
649 * If the profile is currently in use by an install that was either the OS
650 * default install or the profile has been explicitely chosen by some other
651 * means then we won't use it.
653 * aResult will be set to true if we chose to make the profile the new dedicated
656 nsresult
nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
657 nsIToolkitProfile
* aProfile
, bool* aResult
) {
661 // If the profile was last used by a different install then we won't use it.
662 if (!IsProfileForCurrentInstall(aProfile
)) {
666 nsCString descriptor
;
667 rv
= GetProfileDescriptor(aProfile
, descriptor
, nullptr);
668 NS_ENSURE_SUCCESS(rv
, rv
);
670 // Get a list of all the installs.
671 nsTArray
<nsCString
> installs
= GetKnownInstalls();
673 // Cache the installs that use the profile.
674 nsTArray
<nsCString
> inUseInstalls
;
676 // See if the profile is already in use by an install that hasn't locked it.
677 for (uint32_t i
= 0; i
< installs
.Length(); i
++) {
678 const nsCString
& install
= installs
[i
];
681 rv
= mProfileDB
.GetString(install
.get(), "Default", path
);
686 // Is this install using the profile we care about?
687 if (!descriptor
.Equals(path
)) {
691 // Is this profile locked to this other install?
693 rv
= mProfileDB
.GetString(install
.get(), "Locked", isLocked
);
694 if (NS_SUCCEEDED(rv
) && isLocked
.Equals("1")) {
698 inUseInstalls
.AppendElement(install
);
701 // At this point we've decided to take the profile. Strip it from other
703 for (uint32_t i
= 0; i
< inUseInstalls
.Length(); i
++) {
704 // Removing the default setting entirely will make the install go through
705 // the first run process again at startup and create itself a new profile.
706 mProfileDB
.DeleteString(inUseInstalls
[i
].get(), "Default");
709 // Set this as the default profile for this install.
710 SetDefaultProfile(aProfile
);
712 // SetDefaultProfile will have locked this profile to this install so no
713 // other installs will steal it, but this was auto-selected so we want to
714 // unlock it so that other installs can potentially take it.
715 mProfileDB
.DeleteString(mInstallSection
.get(), "Locked");
717 // Persist the changes.
719 NS_ENSURE_SUCCESS(rv
, rv
);
721 // Once XPCOM is available check if this is the default application and if so
722 // lock the profile again.
723 mMaybeLockProfile
= true;
729 bool IsFileOutdated(nsIFile
* aFile
, bool aExists
, PRTime aLastModified
,
731 nsCOMPtr
<nsIFile
> file
;
732 nsresult rv
= aFile
->Clone(getter_AddRefs(file
));
738 rv
= aFile
->Exists(&exists
);
739 if (NS_FAILED(rv
) || exists
!= aExists
) {
748 rv
= aFile
->GetFileSize(&size
);
749 if (NS_FAILED(rv
) || size
!= aLastSize
) {
754 rv
= aFile
->GetLastModifiedTime(&time
);
755 if (NS_FAILED(rv
) || time
!= aLastModified
) {
762 nsresult
UpdateFileStats(nsIFile
* aFile
, bool* aExists
, PRTime
* aLastModified
,
763 int64_t* aLastSize
) {
764 nsCOMPtr
<nsIFile
> file
;
765 nsresult rv
= aFile
->Clone(getter_AddRefs(file
));
766 NS_ENSURE_SUCCESS(rv
, rv
);
768 rv
= file
->Exists(aExists
);
769 NS_ENSURE_SUCCESS(rv
, rv
);
777 rv
= file
->GetFileSize(aLastSize
);
778 NS_ENSURE_SUCCESS(rv
, rv
);
780 rv
= file
->GetLastModifiedTime(aLastModified
);
781 NS_ENSURE_SUCCESS(rv
, rv
);
787 nsToolkitProfileService::GetIsListOutdated(bool* aResult
) {
788 if (IsFileOutdated(mProfileDBFile
, mProfileDBExists
, mProfileDBModifiedTime
,
789 mProfileDBFileSize
)) {
798 struct ImportInstallsClosure
{
799 nsINIParser
* backupData
;
800 nsINIParser
* profileDB
;
803 static bool ImportInstalls(const char* aSection
, void* aClosure
) {
804 ImportInstallsClosure
* closure
=
805 static_cast<ImportInstallsClosure
*>(aClosure
);
807 nsTArray
<UniquePtr
<KeyValue
>> strings
=
808 GetSectionStrings(closure
->backupData
, aSection
);
809 if (strings
.IsEmpty()) {
813 nsCString
newSection(INSTALL_PREFIX
);
814 newSection
.Append(aSection
);
817 for (uint32_t i
= 0; i
< strings
.Length(); i
++) {
818 closure
->profileDB
->SetString(newSection
.get(), strings
[i
]->key
.get(),
819 strings
[i
]->value
.get());
825 nsresult
nsToolkitProfileService::Init() {
826 NS_ASSERTION(gDirServiceProvider
, "No dirserviceprovider!");
829 rv
= nsXREDirProvider::GetUserAppDataDirectory(getter_AddRefs(mAppData
));
830 NS_ENSURE_SUCCESS(rv
, rv
);
832 rv
= nsXREDirProvider::GetUserLocalDataDirectory(getter_AddRefs(mTempData
));
833 NS_ENSURE_SUCCESS(rv
, rv
);
835 rv
= mAppData
->Clone(getter_AddRefs(mProfileDBFile
));
836 NS_ENSURE_SUCCESS(rv
, rv
);
838 rv
= mProfileDBFile
->AppendNative("profiles.ini"_ns
);
839 NS_ENSURE_SUCCESS(rv
, rv
);
841 rv
= mAppData
->Clone(getter_AddRefs(mInstallDBFile
));
842 NS_ENSURE_SUCCESS(rv
, rv
);
844 rv
= mInstallDBFile
->AppendNative("installs.ini"_ns
);
845 NS_ENSURE_SUCCESS(rv
, rv
);
847 nsAutoCString buffer
;
849 rv
= UpdateFileStats(mProfileDBFile
, &mProfileDBExists
,
850 &mProfileDBModifiedTime
, &mProfileDBFileSize
);
851 if (NS_SUCCEEDED(rv
) && mProfileDBExists
) {
852 rv
= mProfileDB
.Init(mProfileDBFile
);
853 // Init does not fail on parsing errors, only on OOM/really unexpected
859 rv
= mProfileDB
.GetString("General", "StartWithLastProfile", buffer
);
860 if (NS_SUCCEEDED(rv
)) {
861 mStartWithLast
= !buffer
.EqualsLiteral("0");
864 rv
= mProfileDB
.GetString("General", "Version", buffer
);
866 // This is a profiles.ini written by an older version. We must restore
867 // any install data from the backup.
868 nsINIParser installDB
;
870 if (NS_SUCCEEDED(installDB
.Init(mInstallDBFile
))) {
871 // There is install data to import.
872 ImportInstallsClosure closure
= {&installDB
, &mProfileDB
};
873 installDB
.GetSections(&ImportInstalls
, &closure
);
876 rv
= mProfileDB
.SetString("General", "Version", PROFILE_DB_VERSION
);
877 NS_ENSURE_SUCCESS(rv
, rv
);
880 rv
= mProfileDB
.SetString("General", "StartWithLastProfile",
881 mStartWithLast
? "1" : "0");
882 NS_ENSURE_SUCCESS(rv
, rv
);
883 rv
= mProfileDB
.SetString("General", "Version", PROFILE_DB_VERSION
);
884 NS_ENSURE_SUCCESS(rv
, rv
);
887 nsCString installProfilePath
;
889 if (mUseDedicatedProfile
) {
890 nsString installHash
;
891 rv
= gDirServiceProvider
->GetInstallHash(installHash
);
892 NS_ENSURE_SUCCESS(rv
, rv
);
893 CopyUTF16toUTF8(installHash
, mInstallSection
);
894 mInstallSection
.Insert(INSTALL_PREFIX
, 0);
896 // Try to find the descriptor for the default profile for this install.
897 rv
= mProfileDB
.GetString(mInstallSection
.get(), "Default",
900 // Not having a value means this install doesn't appear in installs.ini so
901 // this is the first run for this install.
905 // Gets the install section that would have been created if the install
906 // path has incorrect casing (see bug 1555319). We use this later during
907 // profile selection.
908 rv
= gDirServiceProvider
->GetLegacyInstallHash(installHash
);
909 NS_ENSURE_SUCCESS(rv
, rv
);
910 CopyUTF16toUTF8(installHash
, mLegacyInstallSection
);
911 mLegacyInstallSection
.Insert(INSTALL_PREFIX
, 0);
917 nsToolkitProfile
* currentProfile
= nullptr;
919 #ifdef MOZ_DEV_EDITION
920 nsCOMPtr
<nsIFile
> ignoreDevEditionProfile
;
921 rv
= mAppData
->Clone(getter_AddRefs(ignoreDevEditionProfile
));
926 rv
= ignoreDevEditionProfile
->AppendNative("ignore-dev-edition-profile"_ns
);
931 bool shouldIgnoreSeparateProfile
;
932 rv
= ignoreDevEditionProfile
->Exists(&shouldIgnoreSeparateProfile
);
933 if (NS_FAILED(rv
)) return rv
;
935 mUseDevEditionProfile
= !shouldIgnoreSeparateProfile
;
938 nsCOMPtr
<nsIToolkitProfile
> autoSelectProfile
;
940 unsigned int nonDevEditionProfiles
= 0;
942 for (c
= 0; true; ++c
) {
943 nsAutoCString
profileID("Profile");
944 profileID
.AppendInt(c
);
946 rv
= mProfileDB
.GetString(profileID
.get(), "IsRelative", buffer
);
947 if (NS_FAILED(rv
)) break;
949 bool isRelative
= buffer
.EqualsLiteral("1");
951 nsAutoCString filePath
;
953 rv
= mProfileDB
.GetString(profileID
.get(), "Path", filePath
);
955 NS_ERROR("Malformed profiles.ini: Path= not found");
961 rv
= mProfileDB
.GetString(profileID
.get(), "Name", name
);
963 NS_ERROR("Malformed profiles.ini: Name= not found");
967 nsCOMPtr
<nsIFile
> rootDir
;
968 rv
= NS_NewNativeLocalFile(""_ns
, true, getter_AddRefs(rootDir
));
969 NS_ENSURE_SUCCESS(rv
, rv
);
972 rv
= rootDir
->SetRelativeDescriptor(mAppData
, filePath
);
974 rv
= rootDir
->SetPersistentDescriptor(filePath
);
976 if (NS_FAILED(rv
)) continue;
978 nsCOMPtr
<nsIFile
> localDir
;
980 rv
= NS_NewNativeLocalFile(""_ns
, true, getter_AddRefs(localDir
));
981 NS_ENSURE_SUCCESS(rv
, rv
);
983 rv
= localDir
->SetRelativeDescriptor(mTempData
, filePath
);
988 currentProfile
= new nsToolkitProfile(name
, rootDir
, localDir
, true);
990 // If a user has modified the ini file path it may make for a valid profile
991 // path but not match what we would have serialised and so may not match
992 // the path in the install section. Re-serialise it to get it in the
993 // expected form again.
995 nsCString descriptor
;
996 GetProfileDescriptor(currentProfile
, descriptor
, &nowRelative
);
998 if (isRelative
!= nowRelative
|| !descriptor
.Equals(filePath
)) {
999 mProfileDB
.SetString(profileID
.get(), "IsRelative",
1000 nowRelative
? "1" : "0");
1001 mProfileDB
.SetString(profileID
.get(), "Path", descriptor
.get());
1003 // Should we flush now? It costs some startup time and we will fix it on
1004 // the next startup anyway. If something else causes a flush then it will
1005 // be fixed in the ini file then.
1008 rv
= mProfileDB
.GetString(profileID
.get(), "Default", buffer
);
1009 if (NS_SUCCEEDED(rv
) && buffer
.EqualsLiteral("1")) {
1010 mNormalDefault
= currentProfile
;
1013 // Is this the default profile for this install?
1014 if (mUseDedicatedProfile
&& !mDedicatedProfile
&&
1015 installProfilePath
.Equals(descriptor
)) {
1016 // Found a profile for this install.
1017 mDedicatedProfile
= currentProfile
;
1020 if (name
.EqualsLiteral(DEV_EDITION_NAME
)) {
1021 mDevEditionDefault
= currentProfile
;
1023 nonDevEditionProfiles
++;
1024 autoSelectProfile
= currentProfile
;
1028 // If there is only one non-dev-edition profile then mark it as the default.
1029 if (!mNormalDefault
&& nonDevEditionProfiles
== 1) {
1030 SetNormalDefault(autoSelectProfile
);
1033 if (!mUseDedicatedProfile
) {
1034 if (mUseDevEditionProfile
) {
1035 // When using the separate dev-edition profile not finding it means this
1037 mIsFirstRun
= !mDevEditionDefault
;
1039 // If there are no normal profiles then this is a first run.
1040 mIsFirstRun
= nonDevEditionProfiles
== 0;
1048 nsToolkitProfileService::SetStartWithLastProfile(bool aValue
) {
1049 if (mStartWithLast
!= aValue
) {
1050 // Note: the skeleton ui (see PreXULSkeletonUI.cpp) depends on this
1051 // having this name and being under General. If that ever changes,
1052 // the skeleton UI will just need to be updated. If it changes frequently,
1053 // it's probably best we just mirror the value to the registry here.
1054 nsresult rv
= mProfileDB
.SetString("General", "StartWithLastProfile",
1055 aValue
? "1" : "0");
1056 NS_ENSURE_SUCCESS(rv
, rv
);
1057 mStartWithLast
= aValue
;
1063 nsToolkitProfileService::GetStartWithLastProfile(bool* aResult
) {
1064 *aResult
= mStartWithLast
;
1069 nsToolkitProfileService::GetProfiles(nsISimpleEnumerator
** aResult
) {
1070 *aResult
= new ProfileEnumerator(mProfiles
.getFirst());
1072 NS_ADDREF(*aResult
);
1077 nsToolkitProfileService::ProfileEnumerator::HasMoreElements(bool* aResult
) {
1078 *aResult
= mCurrent
? true : false;
1083 nsToolkitProfileService::ProfileEnumerator::GetNext(nsISupports
** aResult
) {
1084 if (!mCurrent
) return NS_ERROR_FAILURE
;
1086 NS_ADDREF(*aResult
= mCurrent
);
1088 mCurrent
= mCurrent
->getNext();
1093 nsToolkitProfileService::GetCurrentProfile(nsIToolkitProfile
** aResult
) {
1094 NS_IF_ADDREF(*aResult
= mCurrent
);
1099 nsToolkitProfileService::GetDefaultProfile(nsIToolkitProfile
** aResult
) {
1100 if (mUseDedicatedProfile
) {
1101 NS_IF_ADDREF(*aResult
= mDedicatedProfile
);
1105 if (mUseDevEditionProfile
) {
1106 NS_IF_ADDREF(*aResult
= mDevEditionDefault
);
1110 NS_IF_ADDREF(*aResult
= mNormalDefault
);
1114 void nsToolkitProfileService::SetNormalDefault(nsIToolkitProfile
* aProfile
) {
1115 if (mNormalDefault
== aProfile
) {
1119 if (mNormalDefault
) {
1120 nsToolkitProfile
* profile
=
1121 static_cast<nsToolkitProfile
*>(mNormalDefault
.get());
1122 mProfileDB
.DeleteString(profile
->mSection
.get(), "Default");
1125 mNormalDefault
= aProfile
;
1127 if (mNormalDefault
) {
1128 nsToolkitProfile
* profile
=
1129 static_cast<nsToolkitProfile
*>(mNormalDefault
.get());
1130 mProfileDB
.SetString(profile
->mSection
.get(), "Default", "1");
1135 nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile
* aProfile
) {
1136 if (mUseDedicatedProfile
) {
1137 if (mDedicatedProfile
!= aProfile
) {
1139 // Setting this to the empty string means no profile will be found on
1140 // startup but we'll recognise that this install has been used
1142 mProfileDB
.SetString(mInstallSection
.get(), "Default", "");
1144 nsCString profilePath
;
1145 nsresult rv
= GetProfileDescriptor(aProfile
, profilePath
, nullptr);
1146 NS_ENSURE_SUCCESS(rv
, rv
);
1148 mProfileDB
.SetString(mInstallSection
.get(), "Default",
1151 mDedicatedProfile
= aProfile
;
1153 // Some kind of choice has happened here, lock this profile to this
1155 mProfileDB
.SetString(mInstallSection
.get(), "Locked", "1");
1160 if (mUseDevEditionProfile
&& aProfile
!= mDevEditionDefault
) {
1161 // The separate profile is hardcoded.
1162 return NS_ERROR_FAILURE
;
1165 SetNormalDefault(aProfile
);
1170 // Gets the profile root directory descriptor for storing in profiles.ini or
1172 nsresult
nsToolkitProfileService::GetProfileDescriptor(
1173 nsIToolkitProfile
* aProfile
, nsACString
& aDescriptor
, bool* aIsRelative
) {
1174 nsCOMPtr
<nsIFile
> profileDir
;
1175 nsresult rv
= aProfile
->GetRootDir(getter_AddRefs(profileDir
));
1176 NS_ENSURE_SUCCESS(rv
, rv
);
1178 // if the profile dir is relative to appdir...
1180 rv
= mAppData
->Contains(profileDir
, &isRelative
);
1182 nsCString profilePath
;
1183 if (NS_SUCCEEDED(rv
) && isRelative
) {
1184 // we use a relative descriptor
1185 rv
= profileDir
->GetRelativeDescriptor(mAppData
, profilePath
);
1187 // otherwise, a persistent descriptor
1188 rv
= profileDir
->GetPersistentDescriptor(profilePath
);
1190 NS_ENSURE_SUCCESS(rv
, rv
);
1192 aDescriptor
.Assign(profilePath
);
1194 *aIsRelative
= isRelative
;
1200 nsresult
nsToolkitProfileService::CreateDefaultProfile(
1201 nsIToolkitProfile
** aResult
) {
1202 // Create a new default profile
1204 if (mUseDevEditionProfile
) {
1205 name
.AssignLiteral(DEV_EDITION_NAME
);
1206 } else if (mUseDedicatedProfile
) {
1207 name
.AppendPrintf("default-%s", mUpdateChannel
.get());
1209 name
.AssignLiteral(DEFAULT_NAME
);
1212 nsresult rv
= CreateUniqueProfile(nullptr, name
, aResult
);
1213 NS_ENSURE_SUCCESS(rv
, rv
);
1215 if (mUseDedicatedProfile
) {
1216 SetDefaultProfile(mCurrent
);
1217 } else if (mUseDevEditionProfile
) {
1218 mDevEditionDefault
= mCurrent
;
1220 SetNormalDefault(mCurrent
);
1227 * An implementation of SelectStartupProfile callable from JavaScript via XPCOM.
1228 * See nsIToolkitProfileService.idl.
1231 nsToolkitProfileService::SelectStartupProfile(
1232 const nsTArray
<nsCString
>& aArgv
, bool aIsResetting
,
1233 const nsACString
& aUpdateChannel
, const nsACString
& aLegacyInstallHash
,
1234 nsIFile
** aRootDir
, nsIFile
** aLocalDir
, nsIToolkitProfile
** aProfile
,
1236 int argc
= aArgv
.Length();
1237 // Our command line handling expects argv to be null-terminated so construct
1238 // an appropriate array.
1239 auto argv
= MakeUnique
<char*[]>(argc
+ 1);
1240 // Also, our command line handling removes things from the array without
1241 // freeing them so keep track of what we've created separately.
1242 auto allocated
= MakeUnique
<UniqueFreePtr
<char>[]>(argc
);
1244 for (int i
= 0; i
< argc
; i
++) {
1245 allocated
[i
].reset(ToNewCString(aArgv
[i
]));
1246 argv
[i
] = allocated
[i
].get();
1248 argv
[argc
] = nullptr;
1250 mUpdateChannel
= aUpdateChannel
;
1251 if (!aLegacyInstallHash
.IsEmpty()) {
1252 mLegacyInstallSection
.Assign(aLegacyInstallHash
);
1253 mLegacyInstallSection
.Insert(INSTALL_PREFIX
, 0);
1258 SelectStartupProfile(&argc
, argv
.get(), aIsResetting
, aRootDir
, aLocalDir
,
1259 aProfile
, aDidCreate
, &wasDefault
);
1261 // Since we were called outside of the normal startup path complete any
1263 if (NS_SUCCEEDED(rv
)) {
1270 static void SaltProfileName(nsACString
& aName
);
1273 * Selects or creates a profile to use based on the profiles database, any
1274 * environment variables and any command line arguments. Will not create
1275 * a profile if aIsResetting is true. The profile is selected based on this
1276 * order of preference:
1277 * * Environment variables (set when restarting the application).
1278 * * --profile command line argument.
1279 * * --createprofile command line argument (this also causes the app to exit).
1280 * * -p command line argument.
1281 * * A new profile created if this is the first run of the application.
1282 * * The default profile.
1283 * aRootDir and aLocalDir are set to the data and local directories for the
1284 * profile data. If a profile from the database was selected it will be
1285 * returned in aProfile.
1286 * aDidCreate will be set to true if a new profile was created.
1287 * This function should be called once at startup and will fail if called again.
1288 * aArgv should be an array of aArgc + 1 strings, the last element being null.
1289 * Both aArgv and aArgc will be mutated.
1291 nsresult
nsToolkitProfileService::SelectStartupProfile(
1292 int* aArgc
, char* aArgv
[], bool aIsResetting
, nsIFile
** aRootDir
,
1293 nsIFile
** aLocalDir
, nsIToolkitProfile
** aProfile
, bool* aDidCreate
,
1294 bool* aWasDefaultSelection
) {
1295 if (mStartupProfileSelected
) {
1296 return NS_ERROR_ALREADY_INITIALIZED
;
1299 mStartupProfileSelected
= true;
1300 *aDidCreate
= false;
1301 *aWasDefaultSelection
= false;
1306 // Use the profile specified in the environment variables (generally from an
1307 // app initiated restart).
1308 nsCOMPtr
<nsIFile
> lf
= GetFileFromEnv("XRE_PROFILE_PATH");
1310 nsCOMPtr
<nsIFile
> localDir
= GetFileFromEnv("XRE_PROFILE_LOCAL_PATH");
1315 // Clear out flags that we handled (or should have handled!) last startup.
1317 CheckArg(*aArgc
, aArgv
, "p", &dummy
);
1318 CheckArg(*aArgc
, aArgv
, "profile", &dummy
);
1319 CheckArg(*aArgc
, aArgv
, "profilemanager");
1321 nsCOMPtr
<nsIToolkitProfile
> profile
;
1322 GetProfileByDir(lf
, localDir
, getter_AddRefs(profile
));
1324 if (profile
&& mIsFirstRun
&& mUseDedicatedProfile
) {
1326 (mUseDevEditionProfile
? mDevEditionDefault
: mNormalDefault
)) {
1327 // This is the first run of a dedicated profile build where the selected
1328 // profile is the previous default so we should either make it the
1329 // default profile for this install or push the user to a new profile.
1332 rv
= MaybeMakeDefaultDedicatedProfile(profile
, &result
);
1333 NS_ENSURE_SUCCESS(rv
, rv
);
1335 mStartupReason
= u
"restart-claimed-default"_ns
;
1339 rv
= CreateDefaultProfile(getter_AddRefs(mCurrent
));
1340 if (NS_FAILED(rv
)) {
1341 *aProfile
= nullptr;
1346 NS_ENSURE_SUCCESS(rv
, rv
);
1348 mStartupReason
= u
"restart-skipped-default"_ns
;
1352 NS_IF_ADDREF(*aProfile
= mCurrent
);
1353 mCurrent
->GetRootDir(aRootDir
);
1354 mCurrent
->GetLocalDir(aLocalDir
);
1360 if (EnvHasValue("XRE_RESTARTED_BY_PROFILE_MANAGER")) {
1361 mStartupReason
= u
"profile-manager"_ns
;
1362 } else if (aIsResetting
) {
1363 mStartupReason
= u
"profile-reset"_ns
;
1365 mStartupReason
= u
"restart"_ns
;
1369 lf
.forget(aRootDir
);
1370 localDir
.forget(aLocalDir
);
1371 NS_IF_ADDREF(*aProfile
= profile
);
1375 // Check the -profile command line argument. It accepts a single argument that
1376 // gives the path to use for the profile.
1377 ArgResult ar
= CheckArg(*aArgc
, aArgv
, "profile", &arg
);
1378 if (ar
== ARG_BAD
) {
1379 PR_fprintf(PR_STDERR
, "Error: argument --profile requires a path\n");
1380 return NS_ERROR_FAILURE
;
1383 nsCOMPtr
<nsIFile
> lf
;
1384 rv
= XRE_GetFileFromPath(arg
, getter_AddRefs(lf
));
1385 NS_ENSURE_SUCCESS(rv
, rv
);
1387 // Make sure that the profile path exists and it's a directory.
1389 rv
= lf
->Exists(&exists
);
1390 NS_ENSURE_SUCCESS(rv
, rv
);
1392 rv
= lf
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
1393 NS_ENSURE_SUCCESS(rv
, rv
);
1396 rv
= lf
->IsDirectory(&isDir
);
1397 NS_ENSURE_SUCCESS(rv
, rv
);
1401 "Error: argument --profile requires a path to a directory\n");
1402 return NS_ERROR_FAILURE
;
1406 mStartupReason
= u
"argument-profile"_ns
;
1408 GetProfileByDir(lf
, nullptr, getter_AddRefs(mCurrent
));
1409 NS_ADDREF(*aRootDir
= lf
);
1410 // If the root dir matched a profile then use its local dir, otherwise use
1411 // the root dir as the local dir.
1413 mCurrent
->GetLocalDir(aLocalDir
);
1415 lf
.forget(aLocalDir
);
1418 NS_IF_ADDREF(*aProfile
= mCurrent
);
1422 // Check the -createprofile command line argument. It accepts a single
1423 // argument that is either the name for the new profile or the name followed
1424 // by the path to use.
1425 ar
= CheckArg(*aArgc
, aArgv
, "createprofile", &arg
, CheckArgFlag::RemoveArg
);
1426 if (ar
== ARG_BAD
) {
1427 PR_fprintf(PR_STDERR
,
1428 "Error: argument --createprofile requires a profile name\n");
1429 return NS_ERROR_FAILURE
;
1432 const char* delim
= strchr(arg
, ' ');
1433 nsCOMPtr
<nsIToolkitProfile
> profile
;
1435 nsCOMPtr
<nsIFile
> lf
;
1436 rv
= NS_NewNativeLocalFile(nsDependentCString(delim
+ 1), true,
1437 getter_AddRefs(lf
));
1438 if (NS_FAILED(rv
)) {
1439 PR_fprintf(PR_STDERR
, "Error: profile path not valid.\n");
1443 // As with --profile, assume that the given path will be used for the
1444 // main profile directory.
1445 rv
= CreateProfile(lf
, nsDependentCSubstring(arg
, delim
),
1446 getter_AddRefs(profile
));
1448 rv
= CreateProfile(nullptr, nsDependentCString(arg
),
1449 getter_AddRefs(profile
));
1451 // Some pathological arguments can make it this far
1452 if (NS_FAILED(rv
) || NS_FAILED(Flush())) {
1453 PR_fprintf(PR_STDERR
, "Error creating profile.\n");
1455 return NS_ERROR_ABORT
;
1458 // Check the -p command line argument. It either accepts a profile name and
1459 // uses that named profile or without a name it opens the profile manager.
1460 ar
= CheckArg(*aArgc
, aArgv
, "p", &arg
);
1461 if (ar
== ARG_BAD
) {
1462 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1465 rv
= GetProfileByName(nsDependentCString(arg
), getter_AddRefs(mCurrent
));
1466 if (NS_SUCCEEDED(rv
)) {
1467 mStartupReason
= u
"argument-p"_ns
;
1469 mCurrent
->GetRootDir(aRootDir
);
1470 mCurrent
->GetLocalDir(aLocalDir
);
1472 NS_ADDREF(*aProfile
= mCurrent
);
1476 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1479 ar
= CheckArg(*aArgc
, aArgv
, "profilemanager");
1480 if (ar
== ARG_FOUND
) {
1481 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1484 #ifdef MOZ_BACKGROUNDTASKS
1485 if (BackgroundTasks::IsBackgroundTaskMode()) {
1486 // There are two cases:
1487 // 1. ephemeral profile: create a new one in temporary directory.
1488 // 2. non-ephemeral (persistent) profile:
1489 // a. if no salted profile is known, create a new one in
1490 // background task-specific directory.
1491 // b. if salted profile is know, use salted path.
1492 nsString installHash
;
1493 rv
= gDirServiceProvider
->GetInstallHash(installHash
);
1494 NS_ENSURE_SUCCESS(rv
, rv
);
1496 nsCString
profilePrefix(BackgroundTasks::GetProfilePrefix(
1497 NS_LossyConvertUTF16toASCII(installHash
)));
1499 nsCString
taskName(BackgroundTasks::GetBackgroundTasks().ref());
1501 nsCOMPtr
<nsIFile
> file
;
1503 if (BackgroundTasks::IsEphemeralProfileTaskName(taskName
)) {
1504 // Background task mode does not enable legacy telemetry, so this is for
1505 // completeness and testing only.
1506 mStartupReason
= u
"backgroundtask-ephemeral"_ns
;
1508 nsCOMPtr
<nsIFile
> rootDir
;
1509 rv
= GetSpecialSystemDirectory(OS_TemporaryDirectory
,
1510 getter_AddRefs(rootDir
));
1511 NS_ENSURE_SUCCESS(rv
, rv
);
1513 nsresult rv
= BackgroundTasks::CreateEphemeralProfileDirectory(
1514 rootDir
, profilePrefix
, getter_AddRefs(file
));
1515 if (NS_WARN_IF(NS_FAILED(rv
))) {
1516 // In background task mode, NS_ERROR_UNEXPECTED is handled specially to
1517 // exit with a non-zero exit code.
1518 return NS_ERROR_UNEXPECTED
;
1522 // Background task mode does not enable legacy telemetry, so this is for
1523 // completeness and testing only.
1524 mStartupReason
= u
"backgroundtask-not-ephemeral"_ns
;
1526 // A non-ephemeral profile is required.
1527 nsCOMPtr
<nsIFile
> rootDir
;
1528 nsresult rv
= gDirServiceProvider
->GetBackgroundTasksProfilesRootDir(
1529 getter_AddRefs(rootDir
));
1530 NS_ENSURE_SUCCESS(rv
, rv
);
1532 nsAutoCString buffer
;
1533 rv
= mProfileDB
.GetString("BackgroundTasksProfiles", profilePrefix
.get(),
1535 if (NS_SUCCEEDED(rv
)) {
1536 // We have a record of one! Use it.
1537 rv
= rootDir
->Clone(getter_AddRefs(file
));
1538 NS_ENSURE_SUCCESS(rv
, rv
);
1540 rv
= file
->AppendNative(buffer
);
1541 NS_ENSURE_SUCCESS(rv
, rv
);
1543 nsCString saltedProfilePrefix
= profilePrefix
;
1544 SaltProfileName(saltedProfilePrefix
);
1546 nsresult rv
= BackgroundTasks::CreateNonEphemeralProfileDirectory(
1547 rootDir
, saltedProfilePrefix
, getter_AddRefs(file
));
1548 if (NS_WARN_IF(NS_FAILED(rv
))) {
1549 // In background task mode, NS_ERROR_UNEXPECTED is handled specially
1550 // to exit with a non-zero exit code.
1551 return NS_ERROR_UNEXPECTED
;
1555 // Keep a record of the salted name. It's okay if this doesn't succeed:
1556 // not great, but it's better for tasks (particularly,
1557 // `backgroundupdate`) to run and not persist state correctly than to
1560 mProfileDB
.SetString("BackgroundTasksProfiles", profilePrefix
.get(),
1561 saltedProfilePrefix
.get());
1562 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
1564 if (NS_SUCCEEDED(rv
)) {
1566 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
1571 nsCOMPtr
<nsIFile
> localDir
= file
;
1572 file
.forget(aRootDir
);
1573 localDir
.forget(aLocalDir
);
1575 // Background tasks never use profiles known to the profile service.
1576 *aProfile
= nullptr;
1582 if (mIsFirstRun
&& mUseDedicatedProfile
&&
1583 !mInstallSection
.Equals(mLegacyInstallSection
)) {
1584 // The default profile could be assigned to a hash generated from an
1585 // incorrectly cased version of the installation directory (see bug
1586 // 1555319). Ideally we'd do all this while loading profiles.ini but we
1587 // can't override the legacy section value before that for tests.
1588 nsCString defaultDescriptor
;
1589 rv
= mProfileDB
.GetString(mLegacyInstallSection
.get(), "Default",
1592 if (NS_SUCCEEDED(rv
)) {
1593 // There is a default here, need to see if it matches any profiles.
1595 nsCString descriptor
;
1597 for (RefPtr
<nsToolkitProfile
> profile
: mProfiles
) {
1598 GetProfileDescriptor(profile
, descriptor
, &isRelative
);
1600 if (descriptor
.Equals(defaultDescriptor
)) {
1601 // Found the default profile. Copy the install section over to
1602 // the correct location. We leave the old info in place for older
1603 // versions of Firefox to use.
1604 nsTArray
<UniquePtr
<KeyValue
>> strings
=
1605 GetSectionStrings(&mProfileDB
, mLegacyInstallSection
.get());
1606 for (const auto& kv
: strings
) {
1607 mProfileDB
.SetString(mInstallSection
.get(), kv
->key
.get(),
1611 // Flush now. This causes a small blip in startup but it should be
1612 // one time only whereas not flushing means we have to do this search
1613 // on every startup.
1616 // Now start up with the found profile.
1617 mDedicatedProfile
= profile
;
1618 mIsFirstRun
= false;
1625 // If this is a first run then create a new profile.
1627 // If we're configured to always show the profile manager then don't create
1628 // a new profile to use.
1629 if (!mStartWithLast
) {
1630 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1633 bool skippedDefaultProfile
= false;
1635 if (mUseDedicatedProfile
) {
1636 // This is the first run of a dedicated profile install. We have to decide
1637 // whether to use the default profile used by non-dedicated-profile
1638 // installs or to create a new profile.
1640 // Find what would have been the default profile for old installs.
1641 nsCOMPtr
<nsIToolkitProfile
> profile
= mNormalDefault
;
1642 if (mUseDevEditionProfile
) {
1643 profile
= mDevEditionDefault
;
1647 nsCOMPtr
<nsIFile
> rootDir
;
1648 profile
->GetRootDir(getter_AddRefs(rootDir
));
1650 nsCOMPtr
<nsIFile
> compat
;
1651 rootDir
->Clone(getter_AddRefs(compat
));
1652 compat
->Append(COMPAT_FILE
);
1655 rv
= compat
->Exists(&exists
);
1656 NS_ENSURE_SUCCESS(rv
, rv
);
1658 // If the file is missing then either this is an empty profile (likely
1659 // generated by bug 1518591) or it is from an ancient version. We'll opt
1660 // to leave it for older versions in this case.
1663 rv
= MaybeMakeDefaultDedicatedProfile(profile
, &result
);
1664 NS_ENSURE_SUCCESS(rv
, rv
);
1666 mStartupReason
= u
"firstrun-claimed-default"_ns
;
1669 rootDir
.forget(aRootDir
);
1670 profile
->GetLocalDir(aLocalDir
);
1671 profile
.forget(aProfile
);
1675 // We're going to create a new profile for this install even though
1676 // another default exists.
1677 skippedDefaultProfile
= true;
1682 rv
= CreateDefaultProfile(getter_AddRefs(mCurrent
));
1683 if (NS_SUCCEEDED(rv
)) {
1684 #ifdef MOZ_CREATE_LEGACY_PROFILE
1685 // If there is only one profile and it isn't meant to be the profile that
1686 // older versions of Firefox use then we must create a default profile
1687 // for older versions of Firefox to avoid the existing profile being
1689 if ((mUseDedicatedProfile
|| mUseDevEditionProfile
) &&
1690 mProfiles
.getFirst() == mProfiles
.getLast()) {
1691 nsCOMPtr
<nsIToolkitProfile
> newProfile
;
1692 CreateProfile(nullptr, nsLiteralCString(DEFAULT_NAME
),
1693 getter_AddRefs(newProfile
));
1694 SetNormalDefault(newProfile
);
1699 NS_ENSURE_SUCCESS(rv
, rv
);
1701 if (skippedDefaultProfile
) {
1702 mStartupReason
= u
"firstrun-skipped-default"_ns
;
1704 mStartupReason
= u
"firstrun-created-default"_ns
;
1707 // Use the new profile.
1708 mCurrent
->GetRootDir(aRootDir
);
1709 mCurrent
->GetLocalDir(aLocalDir
);
1710 NS_ADDREF(*aProfile
= mCurrent
);
1717 GetDefaultProfile(getter_AddRefs(mCurrent
));
1719 // None of the profiles was marked as default (generally only happens if the
1720 // user modifies profiles.ini manually). Let the user choose.
1722 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1725 // Let the caller know that the profile was selected by default.
1726 *aWasDefaultSelection
= true;
1727 mStartupReason
= u
"default"_ns
;
1729 // Use the selected profile.
1730 mCurrent
->GetRootDir(aRootDir
);
1731 mCurrent
->GetLocalDir(aLocalDir
);
1732 NS_ADDREF(*aProfile
= mCurrent
);
1738 * Creates a new profile for reset and mark it as the current profile.
1740 nsresult
nsToolkitProfileService::CreateResetProfile(
1741 nsIToolkitProfile
** aNewProfile
) {
1742 nsAutoCString oldProfileName
;
1743 mCurrent
->GetName(oldProfileName
);
1745 nsCOMPtr
<nsIToolkitProfile
> newProfile
;
1746 // Make the new profile name the old profile (or "default-") + the time in
1747 // seconds since epoch for uniqueness.
1748 nsAutoCString newProfileName
;
1749 if (!oldProfileName
.IsEmpty()) {
1750 newProfileName
.Assign(oldProfileName
);
1751 newProfileName
.Append("-");
1753 newProfileName
.AssignLiteral("default-");
1755 newProfileName
.AppendPrintf("%" PRId64
, PR_Now() / 1000);
1756 nsresult rv
= CreateProfile(nullptr, // choose a default dir for us
1757 newProfileName
, getter_AddRefs(newProfile
));
1758 if (NS_FAILED(rv
)) return rv
;
1760 mCurrent
= newProfile
;
1761 newProfile
.forget(aNewProfile
);
1763 // Don't flush the changes yet. That will happen once the migration
1764 // successfully completes.
1769 * This is responsible for deleting the old profile, copying its name to the
1770 * current profile and if the old profile was default making the new profile
1773 nsresult
nsToolkitProfileService::ApplyResetProfile(
1774 nsIToolkitProfile
* aOldProfile
) {
1775 // If the old profile would have been the default for old installs then mark
1776 // the new profile as such.
1777 if (mNormalDefault
== aOldProfile
) {
1778 SetNormalDefault(mCurrent
);
1781 if (mUseDedicatedProfile
&& mDedicatedProfile
== aOldProfile
) {
1782 bool wasLocked
= false;
1785 mProfileDB
.GetString(mInstallSection
.get(), "Locked", val
))) {
1786 wasLocked
= val
.Equals("1");
1789 SetDefaultProfile(mCurrent
);
1791 // Make the locked state match if necessary.
1793 mProfileDB
.DeleteString(mInstallSection
.get(), "Locked");
1798 nsresult rv
= aOldProfile
->GetName(name
);
1799 NS_ENSURE_SUCCESS(rv
, rv
);
1801 // Don't remove the old profile's files until after we've successfully flushed
1802 // the profile changes to disk.
1803 rv
= aOldProfile
->Remove(false);
1804 NS_ENSURE_SUCCESS(rv
, rv
);
1806 // Switching the name will make this the default for dev-edition if
1808 rv
= mCurrent
->SetName(name
);
1809 NS_ENSURE_SUCCESS(rv
, rv
);
1812 NS_ENSURE_SUCCESS(rv
, rv
);
1814 // Now that the profile changes are flushed, try to remove the old profile's
1815 // files. If we fail the worst that will happen is that an orphan directory is
1816 // left. Let this run in the background while we start up.
1817 RemoveProfileFiles(aOldProfile
, true);
1823 nsToolkitProfileService::GetProfileByName(const nsACString
& aName
,
1824 nsIToolkitProfile
** aResult
) {
1825 for (RefPtr
<nsToolkitProfile
> profile
: mProfiles
) {
1826 if (profile
->mName
.Equals(aName
)) {
1827 NS_ADDREF(*aResult
= profile
);
1832 return NS_ERROR_FAILURE
;
1836 * Finds a profile from the database that uses the given root and local
1839 void nsToolkitProfileService::GetProfileByDir(nsIFile
* aRootDir
,
1841 nsIToolkitProfile
** aResult
) {
1842 for (RefPtr
<nsToolkitProfile
> profile
: mProfiles
) {
1844 nsresult rv
= profile
->mRootDir
->Equals(aRootDir
, &equal
);
1845 if (NS_SUCCEEDED(rv
) && equal
) {
1847 // If no local directory was given then we will just use the normal
1848 // local directory for the profile.
1849 profile
.forget(aResult
);
1853 rv
= profile
->mLocalDir
->Equals(aLocalDir
, &equal
);
1854 if (NS_SUCCEEDED(rv
) && equal
) {
1855 profile
.forget(aResult
);
1862 nsresult
NS_LockProfilePath(nsIFile
* aPath
, nsIFile
* aTempPath
,
1863 nsIProfileUnlocker
** aUnlocker
,
1864 nsIProfileLock
** aResult
) {
1865 RefPtr
<nsToolkitProfileLock
> lock
= new nsToolkitProfileLock();
1867 nsresult rv
= lock
->Init(aPath
, aTempPath
, aUnlocker
);
1868 if (NS_FAILED(rv
)) return rv
;
1870 lock
.forget(aResult
);
1874 static void SaltProfileName(nsACString
& aName
) {
1876 NS_MakeRandomString(salt
, 8);
1879 aName
.Insert(salt
, 0, 9);
1883 nsToolkitProfileService::CreateUniqueProfile(nsIFile
* aRootDir
,
1884 const nsACString
& aNamePrefix
,
1885 nsIToolkitProfile
** aResult
) {
1886 nsCOMPtr
<nsIToolkitProfile
> profile
;
1887 nsresult rv
= GetProfileByName(aNamePrefix
, getter_AddRefs(profile
));
1888 if (NS_FAILED(rv
)) {
1889 return CreateProfile(aRootDir
, aNamePrefix
, aResult
);
1892 uint32_t suffix
= 1;
1894 nsPrintfCString
name("%s-%d", PromiseFlatCString(aNamePrefix
).get(),
1896 rv
= GetProfileByName(name
, getter_AddRefs(profile
));
1897 if (NS_FAILED(rv
)) {
1898 return CreateProfile(aRootDir
, name
, aResult
);
1905 nsToolkitProfileService::CreateProfile(nsIFile
* aRootDir
,
1906 const nsACString
& aName
,
1907 nsIToolkitProfile
** aResult
) {
1908 nsresult rv
= GetProfileByName(aName
, aResult
);
1909 if (NS_SUCCEEDED(rv
)) {
1913 nsCOMPtr
<nsIFile
> rootDir(aRootDir
);
1915 nsAutoCString dirName
;
1917 rv
= gDirServiceProvider
->GetUserProfilesRootDir(getter_AddRefs(rootDir
));
1918 NS_ENSURE_SUCCESS(rv
, rv
);
1921 SaltProfileName(dirName
);
1923 if (NS_IsNativeUTF8()) {
1924 rootDir
->AppendNative(dirName
);
1926 rootDir
->Append(NS_ConvertUTF8toUTF16(dirName
));
1930 nsCOMPtr
<nsIFile
> localDir
;
1933 rv
= mAppData
->Contains(rootDir
, &isRelative
);
1934 if (NS_SUCCEEDED(rv
) && isRelative
) {
1936 rv
= rootDir
->GetRelativeDescriptor(mAppData
, path
);
1937 NS_ENSURE_SUCCESS(rv
, rv
);
1939 rv
= NS_NewNativeLocalFile(""_ns
, true, getter_AddRefs(localDir
));
1940 NS_ENSURE_SUCCESS(rv
, rv
);
1942 rv
= localDir
->SetRelativeDescriptor(mTempData
, path
);
1948 rv
= rootDir
->Exists(&exists
);
1949 NS_ENSURE_SUCCESS(rv
, rv
);
1952 rv
= rootDir
->IsDirectory(&exists
);
1953 NS_ENSURE_SUCCESS(rv
, rv
);
1955 if (!exists
) return NS_ERROR_FILE_NOT_DIRECTORY
;
1957 nsCOMPtr
<nsIFile
> profileDirParent
;
1958 nsAutoString profileDirName
;
1960 rv
= rootDir
->GetParent(getter_AddRefs(profileDirParent
));
1961 NS_ENSURE_SUCCESS(rv
, rv
);
1963 rv
= rootDir
->GetLeafName(profileDirName
);
1964 NS_ENSURE_SUCCESS(rv
, rv
);
1966 // let's ensure that the profile directory exists.
1967 rv
= rootDir
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
1968 NS_ENSURE_SUCCESS(rv
, rv
);
1969 rv
= rootDir
->SetPermissions(0700);
1971 // If the profile is on the sdcard, this will fail but its non-fatal
1972 NS_ENSURE_SUCCESS(rv
, rv
);
1976 rv
= localDir
->Exists(&exists
);
1977 NS_ENSURE_SUCCESS(rv
, rv
);
1980 rv
= localDir
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
1981 NS_ENSURE_SUCCESS(rv
, rv
);
1984 // We created a new profile dir. Let's store a creation timestamp.
1985 // Note that this code path does not apply if the profile dir was
1986 // created prior to launching.
1987 rv
= CreateTimesInternal(rootDir
);
1988 NS_ENSURE_SUCCESS(rv
, rv
);
1990 nsCOMPtr
<nsIToolkitProfile
> profile
=
1991 new nsToolkitProfile(aName
, rootDir
, localDir
, false);
1993 if (aName
.Equals(DEV_EDITION_NAME
)) {
1994 mDevEditionDefault
= profile
;
1997 profile
.forget(aResult
);
2002 * Snaps (https://snapcraft.io/) use a different installation directory for
2003 * every version of an application. Since dedicated profiles uses the
2004 * installation directory to determine which profile to use this would lead
2005 * snap users getting a new profile on every application update.
2007 * However the only way to have multiple installation of a snap is to install
2008 * a new snap instance. Different snap instances have different user data
2009 * directories and so already will not share profiles, in fact one instance
2010 * will not even be able to see the other instance's profiles since
2011 * profiles.ini will be stored in different places.
2013 * So we can just disable dedicated profile support in this case and revert
2014 * back to the old method of just having a single default profile and still
2015 * get essentially the same benefits as dedicated profiles provides.
2017 bool nsToolkitProfileService::IsSnapEnvironment() {
2018 #ifdef MOZ_WIDGET_GTK
2019 return widget::IsRunningUnderSnap();
2026 * In some situations dedicated profile support does not work well. This
2027 * includes a handful of linux distributions which always install different
2028 * application versions to different locations, some application sandboxing
2029 * systems as well as enterprise deployments. This environment variable provides
2030 * a way to opt out of dedicated profiles for these cases.
2032 * For Windows, we provide a policy to accomplish the same thing.
2034 bool nsToolkitProfileService::UseLegacyProfiles() {
2035 bool legacyProfiles
= !!PR_GetEnv("MOZ_LEGACY_PROFILES");
2037 legacyProfiles
|= PolicyCheckBoolean(L
"LegacyProfiles");
2039 return legacyProfiles
;
2042 struct FindInstallsClosure
{
2043 nsINIParser
* installData
;
2044 nsTArray
<nsCString
>* installs
;
2047 static bool FindInstalls(const char* aSection
, void* aClosure
) {
2048 FindInstallsClosure
* closure
= static_cast<FindInstallsClosure
*>(aClosure
);
2050 // Check if the section starts with "Install"
2051 if (strncmp(aSection
, INSTALL_PREFIX
, INSTALL_PREFIX_LENGTH
) != 0) {
2055 nsCString
install(aSection
);
2056 closure
->installs
->AppendElement(install
);
2061 nsTArray
<nsCString
> nsToolkitProfileService::GetKnownInstalls() {
2062 nsTArray
<nsCString
> result
;
2063 FindInstallsClosure closure
= {&mProfileDB
, &result
};
2065 mProfileDB
.GetSections(&FindInstalls
, &closure
);
2070 nsresult
nsToolkitProfileService::CreateTimesInternal(nsIFile
* aProfileDir
) {
2071 nsresult rv
= NS_ERROR_FAILURE
;
2072 nsCOMPtr
<nsIFile
> creationLog
;
2073 rv
= aProfileDir
->Clone(getter_AddRefs(creationLog
));
2074 NS_ENSURE_SUCCESS(rv
, rv
);
2076 rv
= creationLog
->AppendNative("times.json"_ns
);
2077 NS_ENSURE_SUCCESS(rv
, rv
);
2079 bool exists
= false;
2080 creationLog
->Exists(&exists
);
2085 rv
= creationLog
->Create(nsIFile::NORMAL_FILE_TYPE
, 0700);
2086 NS_ENSURE_SUCCESS(rv
, rv
);
2088 // We don't care about microsecond resolution.
2089 int64_t msec
= PR_Now() / PR_USEC_PER_MSEC
;
2092 PRFileDesc
* writeFile
;
2093 rv
= creationLog
->OpenNSPRFileDesc(PR_WRONLY
, 0700, &writeFile
);
2094 NS_ENSURE_SUCCESS(rv
, rv
);
2096 PR_fprintf(writeFile
, "{\n\"created\": %lld,\n\"firstUse\": null\n}\n", msec
);
2097 PR_Close(writeFile
);
2102 nsToolkitProfileService::GetProfileCount(uint32_t* aResult
) {
2104 for (nsToolkitProfile
* profile
: mProfiles
) {
2113 nsToolkitProfileService::Flush() {
2114 if (GetIsListOutdated()) {
2115 return NS_ERROR_DATABASE_CHANGED
;
2120 // If we aren't using dedicated profiles then nothing about the list of
2121 // installs can have changed, so no need to update the backup.
2122 if (mUseDedicatedProfile
) {
2123 // Export the installs to the backup.
2124 nsTArray
<nsCString
> installs
= GetKnownInstalls();
2126 if (!installs
.IsEmpty()) {
2130 for (uint32_t i
= 0; i
< installs
.Length(); i
++) {
2131 nsTArray
<UniquePtr
<KeyValue
>> strings
=
2132 GetSectionStrings(&mProfileDB
, installs
[i
].get());
2133 if (strings
.IsEmpty()) {
2137 // Strip "Install" from the start.
2138 const nsDependentCSubstring
& install
=
2139 Substring(installs
[i
], INSTALL_PREFIX_LENGTH
);
2140 data
.AppendPrintf("[%s]\n", PromiseFlatCString(install
).get());
2142 for (uint32_t j
= 0; j
< strings
.Length(); j
++) {
2143 data
.AppendPrintf("%s=%s\n", strings
[j
]->key
.get(),
2144 strings
[j
]->value
.get());
2151 rv
= mInstallDBFile
->OpenANSIFileDesc("w", &writeFile
);
2152 NS_ENSURE_SUCCESS(rv
, rv
);
2154 uint32_t length
= data
.Length();
2155 if (fwrite(data
.get(), sizeof(char), length
, writeFile
) != length
) {
2157 return NS_ERROR_UNEXPECTED
;
2162 rv
= mInstallDBFile
->Remove(false);
2163 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
) {
2169 rv
= mProfileDB
.WriteToFile(mProfileDBFile
);
2170 NS_ENSURE_SUCCESS(rv
, rv
);
2172 rv
= UpdateFileStats(mProfileDBFile
, &mProfileDBExists
,
2173 &mProfileDBModifiedTime
, &mProfileDBFileSize
);
2174 NS_ENSURE_SUCCESS(rv
, rv
);
2179 already_AddRefed
<nsToolkitProfileService
> NS_GetToolkitProfileService() {
2180 if (!nsToolkitProfileService::gService
) {
2181 nsToolkitProfileService::gService
= new nsToolkitProfileService();
2182 nsresult rv
= nsToolkitProfileService::gService
->Init();
2183 if (NS_FAILED(rv
)) {
2184 NS_ERROR("nsToolkitProfileService::Init failed!");
2185 delete nsToolkitProfileService::gService
;
2190 return do_AddRef(nsToolkitProfileService::gService
);
2193 nsresult
XRE_GetFileFromPath(const char* aPath
, nsIFile
** aResult
) {
2194 #if defined(XP_MACOSX)
2195 int32_t pathLen
= strlen(aPath
);
2196 if (pathLen
> MAXPATHLEN
) return NS_ERROR_INVALID_ARG
;
2198 CFURLRef fullPath
= CFURLCreateFromFileSystemRepresentation(
2199 nullptr, (const UInt8
*)aPath
, pathLen
, true);
2200 if (!fullPath
) return NS_ERROR_FAILURE
;
2202 nsCOMPtr
<nsIFile
> lf
;
2203 nsresult rv
= NS_NewNativeLocalFile(""_ns
, true, getter_AddRefs(lf
));
2204 if (NS_SUCCEEDED(rv
)) {
2205 nsCOMPtr
<nsILocalFileMac
> lfMac
= do_QueryInterface(lf
, &rv
);
2206 if (NS_SUCCEEDED(rv
)) {
2207 rv
= lfMac
->InitWithCFURL(fullPath
);
2208 if (NS_SUCCEEDED(rv
)) {
2213 CFRelease(fullPath
);
2216 #elif defined(XP_UNIX)
2217 char fullPath
[MAXPATHLEN
];
2219 if (!realpath(aPath
, fullPath
)) return NS_ERROR_FAILURE
;
2221 return NS_NewNativeLocalFile(nsDependentCString(fullPath
), true, aResult
);
2222 #elif defined(XP_WIN)
2223 WCHAR fullPath
[MAXPATHLEN
];
2225 if (!_wfullpath(fullPath
, NS_ConvertUTF8toUTF16(aPath
).get(), MAXPATHLEN
))
2226 return NS_ERROR_FAILURE
;
2228 return NS_NewLocalFile(nsDependentString(fullPath
), true, aResult
);
2231 # error Platform-specific logic needed here.