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 mStartupFileVersion("0"_ns
),
497 mMaybeLockProfile(false),
498 mUpdateChannel(MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL
)),
499 mProfileDBExists(false),
500 mProfileDBFileSize(0),
501 mProfileDBModifiedTime(0) {
502 #ifdef MOZ_DEV_EDITION
503 mUseDevEditionProfile
= true;
507 nsToolkitProfileService::~nsToolkitProfileService() {
512 void nsToolkitProfileService::CompleteStartup() {
513 if (!mStartupProfileSelected
) {
517 ScalarSet(mozilla::Telemetry::ScalarID::STARTUP_PROFILE_SELECTION_REASON
,
519 ScalarSet(mozilla::Telemetry::ScalarID::STARTUP_PROFILE_DATABASE_VERSION
,
520 NS_ConvertUTF8toUTF16(mStartupFileVersion
));
521 ScalarSet(mozilla::Telemetry::ScalarID::STARTUP_PROFILE_COUNT
,
522 static_cast<uint32_t>(mProfiles
.length()));
524 if (mMaybeLockProfile
) {
525 nsCOMPtr
<nsIToolkitShellService
> shell
=
526 do_GetService(NS_TOOLKITSHELLSERVICE_CONTRACTID
);
532 nsresult rv
= shell
->IsDefaultApplication(&isDefaultApp
);
533 NS_ENSURE_SUCCESS_VOID(rv
);
536 mProfileDB
.SetString(mInstallSection
.get(), "Locked", "1");
538 // There is a very small chance that this could fail if something else
539 // overwrote the profiles database since we started up, probably less than
540 // a second ago. There isn't really a sane response here, all the other
541 // profile changes are already flushed so whether we fail to flush here or
542 // force quit the app makes no difference.
543 NS_ENSURE_SUCCESS_VOID(Flush());
548 // Tests whether the passed profile was last used by this install.
549 bool nsToolkitProfileService::IsProfileForCurrentInstall(
550 nsIToolkitProfile
* aProfile
) {
551 nsCOMPtr
<nsIFile
> profileDir
;
552 nsresult rv
= aProfile
->GetRootDir(getter_AddRefs(profileDir
));
553 NS_ENSURE_SUCCESS(rv
, false);
555 nsCOMPtr
<nsIFile
> compatFile
;
556 rv
= profileDir
->Clone(getter_AddRefs(compatFile
));
557 NS_ENSURE_SUCCESS(rv
, false);
559 rv
= compatFile
->Append(COMPAT_FILE
);
560 NS_ENSURE_SUCCESS(rv
, false);
562 nsINIParser compatData
;
563 rv
= compatData
.Init(compatFile
);
564 NS_ENSURE_SUCCESS(rv
, false);
567 * In xpcshell gDirServiceProvider doesn't have all the correct directories
568 * set so using NS_GetSpecialDirectory works better there. But in a normal
569 * app launch the component registry isn't initialized so
570 * NS_GetSpecialDirectory doesn't work. So we have to use two different
571 * paths to support testing.
573 nsCOMPtr
<nsIFile
> currentGreDir
;
574 rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(currentGreDir
));
575 if (rv
== NS_ERROR_NOT_INITIALIZED
) {
576 currentGreDir
= gDirServiceProvider
->GetGREDir();
577 MOZ_ASSERT(currentGreDir
, "No GRE dir found.");
578 } else if (NS_FAILED(rv
)) {
582 nsCString lastGreDirStr
;
583 rv
= compatData
.GetString("Compatibility", "LastPlatformDir", lastGreDirStr
);
584 // If this string is missing then this profile is from an ancient version.
585 // We'll opt to use it in this case.
590 nsCOMPtr
<nsIFile
> lastGreDir
;
591 rv
= NS_NewNativeLocalFile(""_ns
, false, getter_AddRefs(lastGreDir
));
592 NS_ENSURE_SUCCESS(rv
, false);
594 rv
= lastGreDir
->SetPersistentDescriptor(lastGreDirStr
);
595 NS_ENSURE_SUCCESS(rv
, false);
598 # if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
599 mozilla::PathString lastGreDirPath
, currentGreDirPath
;
600 lastGreDirPath
= lastGreDir
->NativePath();
601 currentGreDirPath
= currentGreDir
->NativePath();
602 if (lastGreDirPath
.Equals(currentGreDirPath
,
603 nsCaseInsensitiveStringComparator
)) {
607 // Convert a 64-bit install path to what would have been the 32-bit install
608 // path to allow users to migrate their profiles from one to the other.
609 PWSTR pathX86
= nullptr;
611 SHGetKnownFolderPath(FOLDERID_ProgramFilesX86
, 0, nullptr, &pathX86
);
612 if (SUCCEEDED(hres
)) {
613 nsDependentString
strPathX86(pathX86
);
614 if (!StringBeginsWith(currentGreDirPath
, strPathX86
,
615 nsCaseInsensitiveStringComparator
)) {
616 PWSTR path
= nullptr;
617 hres
= SHGetKnownFolderPath(FOLDERID_ProgramFiles
, 0, nullptr, &path
);
618 if (SUCCEEDED(hres
)) {
619 if (StringBeginsWith(currentGreDirPath
, nsDependentString(path
),
620 nsCaseInsensitiveStringComparator
)) {
621 currentGreDirPath
.Replace(0, wcslen(path
), strPathX86
);
627 CoTaskMemFree(pathX86
);
629 return lastGreDirPath
.Equals(currentGreDirPath
,
630 nsCaseInsensitiveStringComparator
);
635 rv
= lastGreDir
->Equals(currentGreDir
, &equal
);
636 NS_ENSURE_SUCCESS(rv
, false);
642 * Used the first time an install with dedicated profile support runs. Decides
643 * whether to mark the passed profile as the default for this install.
645 * The goal is to reduce disruption but ideally end up with the OS default
646 * install using the old default profile.
648 * If the decision is to use the profile then it will be unassigned as the
649 * dedicated default for other installs.
651 * We won't attempt to use the profile if it was last used by a different
654 * If the profile is currently in use by an install that was either the OS
655 * default install or the profile has been explicitely chosen by some other
656 * means then we won't use it.
658 * aResult will be set to true if we chose to make the profile the new dedicated
661 nsresult
nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
662 nsIToolkitProfile
* aProfile
, bool* aResult
) {
666 // If the profile was last used by a different install then we won't use it.
667 if (!IsProfileForCurrentInstall(aProfile
)) {
671 nsCString descriptor
;
672 rv
= GetProfileDescriptor(aProfile
, descriptor
, nullptr);
673 NS_ENSURE_SUCCESS(rv
, rv
);
675 // Get a list of all the installs.
676 nsTArray
<nsCString
> installs
= GetKnownInstalls();
678 // Cache the installs that use the profile.
679 nsTArray
<nsCString
> inUseInstalls
;
681 // See if the profile is already in use by an install that hasn't locked it.
682 for (uint32_t i
= 0; i
< installs
.Length(); i
++) {
683 const nsCString
& install
= installs
[i
];
686 rv
= mProfileDB
.GetString(install
.get(), "Default", path
);
691 // Is this install using the profile we care about?
692 if (!descriptor
.Equals(path
)) {
696 // Is this profile locked to this other install?
698 rv
= mProfileDB
.GetString(install
.get(), "Locked", isLocked
);
699 if (NS_SUCCEEDED(rv
) && isLocked
.Equals("1")) {
703 inUseInstalls
.AppendElement(install
);
706 // At this point we've decided to take the profile. Strip it from other
708 for (uint32_t i
= 0; i
< inUseInstalls
.Length(); i
++) {
709 // Removing the default setting entirely will make the install go through
710 // the first run process again at startup and create itself a new profile.
711 mProfileDB
.DeleteString(inUseInstalls
[i
].get(), "Default");
714 // Set this as the default profile for this install.
715 SetDefaultProfile(aProfile
);
717 // SetDefaultProfile will have locked this profile to this install so no
718 // other installs will steal it, but this was auto-selected so we want to
719 // unlock it so that other installs can potentially take it.
720 mProfileDB
.DeleteString(mInstallSection
.get(), "Locked");
722 // Persist the changes.
724 NS_ENSURE_SUCCESS(rv
, rv
);
726 // Once XPCOM is available check if this is the default application and if so
727 // lock the profile again.
728 mMaybeLockProfile
= true;
734 bool IsFileOutdated(nsIFile
* aFile
, bool aExists
, PRTime aLastModified
,
736 nsCOMPtr
<nsIFile
> file
;
737 nsresult rv
= aFile
->Clone(getter_AddRefs(file
));
743 rv
= aFile
->Exists(&exists
);
744 if (NS_FAILED(rv
) || exists
!= aExists
) {
753 rv
= aFile
->GetFileSize(&size
);
754 if (NS_FAILED(rv
) || size
!= aLastSize
) {
759 rv
= aFile
->GetLastModifiedTime(&time
);
760 if (NS_FAILED(rv
) || time
!= aLastModified
) {
767 nsresult
UpdateFileStats(nsIFile
* aFile
, bool* aExists
, PRTime
* aLastModified
,
768 int64_t* aLastSize
) {
769 nsCOMPtr
<nsIFile
> file
;
770 nsresult rv
= aFile
->Clone(getter_AddRefs(file
));
771 NS_ENSURE_SUCCESS(rv
, rv
);
773 rv
= file
->Exists(aExists
);
774 NS_ENSURE_SUCCESS(rv
, rv
);
782 rv
= file
->GetFileSize(aLastSize
);
783 NS_ENSURE_SUCCESS(rv
, rv
);
785 rv
= file
->GetLastModifiedTime(aLastModified
);
786 NS_ENSURE_SUCCESS(rv
, rv
);
792 nsToolkitProfileService::GetIsListOutdated(bool* aResult
) {
793 if (IsFileOutdated(mProfileDBFile
, mProfileDBExists
, mProfileDBModifiedTime
,
794 mProfileDBFileSize
)) {
803 struct ImportInstallsClosure
{
804 nsINIParser
* backupData
;
805 nsINIParser
* profileDB
;
808 static bool ImportInstalls(const char* aSection
, void* aClosure
) {
809 ImportInstallsClosure
* closure
=
810 static_cast<ImportInstallsClosure
*>(aClosure
);
812 nsTArray
<UniquePtr
<KeyValue
>> strings
=
813 GetSectionStrings(closure
->backupData
, aSection
);
814 if (strings
.IsEmpty()) {
818 nsCString
newSection(INSTALL_PREFIX
);
819 newSection
.Append(aSection
);
822 for (uint32_t i
= 0; i
< strings
.Length(); i
++) {
823 closure
->profileDB
->SetString(newSection
.get(), strings
[i
]->key
.get(),
824 strings
[i
]->value
.get());
830 nsresult
nsToolkitProfileService::Init() {
831 NS_ASSERTION(gDirServiceProvider
, "No dirserviceprovider!");
834 rv
= nsXREDirProvider::GetUserAppDataDirectory(getter_AddRefs(mAppData
));
835 NS_ENSURE_SUCCESS(rv
, rv
);
837 rv
= nsXREDirProvider::GetUserLocalDataDirectory(getter_AddRefs(mTempData
));
838 NS_ENSURE_SUCCESS(rv
, rv
);
840 rv
= mAppData
->Clone(getter_AddRefs(mProfileDBFile
));
841 NS_ENSURE_SUCCESS(rv
, rv
);
843 rv
= mProfileDBFile
->AppendNative("profiles.ini"_ns
);
844 NS_ENSURE_SUCCESS(rv
, rv
);
846 rv
= mAppData
->Clone(getter_AddRefs(mInstallDBFile
));
847 NS_ENSURE_SUCCESS(rv
, rv
);
849 rv
= mInstallDBFile
->AppendNative("installs.ini"_ns
);
850 NS_ENSURE_SUCCESS(rv
, rv
);
852 nsAutoCString buffer
;
854 rv
= UpdateFileStats(mProfileDBFile
, &mProfileDBExists
,
855 &mProfileDBModifiedTime
, &mProfileDBFileSize
);
856 if (NS_SUCCEEDED(rv
) && mProfileDBExists
) {
857 rv
= mProfileDB
.Init(mProfileDBFile
);
858 // Init does not fail on parsing errors, only on OOM/really unexpected
864 rv
= mProfileDB
.GetString("General", "StartWithLastProfile", buffer
);
865 if (NS_SUCCEEDED(rv
)) {
866 mStartWithLast
= !buffer
.EqualsLiteral("0");
869 rv
= mProfileDB
.GetString("General", "Version", mStartupFileVersion
);
871 // This is a profiles.ini written by an older version. We must restore
872 // any install data from the backup. We consider this old format to be
874 mStartupFileVersion
.AssignLiteral("1");
875 nsINIParser installDB
;
877 if (NS_SUCCEEDED(installDB
.Init(mInstallDBFile
))) {
878 // There is install data to import.
879 ImportInstallsClosure closure
= {&installDB
, &mProfileDB
};
880 installDB
.GetSections(&ImportInstalls
, &closure
);
883 rv
= mProfileDB
.SetString("General", "Version", PROFILE_DB_VERSION
);
884 NS_ENSURE_SUCCESS(rv
, rv
);
887 rv
= mProfileDB
.SetString("General", "StartWithLastProfile",
888 mStartWithLast
? "1" : "0");
889 NS_ENSURE_SUCCESS(rv
, rv
);
890 rv
= mProfileDB
.SetString("General", "Version", PROFILE_DB_VERSION
);
891 NS_ENSURE_SUCCESS(rv
, rv
);
894 nsCString installProfilePath
;
896 if (mUseDedicatedProfile
) {
897 nsString installHash
;
898 rv
= gDirServiceProvider
->GetInstallHash(installHash
);
899 NS_ENSURE_SUCCESS(rv
, rv
);
900 CopyUTF16toUTF8(installHash
, mInstallSection
);
901 mInstallSection
.Insert(INSTALL_PREFIX
, 0);
903 // Try to find the descriptor for the default profile for this install.
904 rv
= mProfileDB
.GetString(mInstallSection
.get(), "Default",
907 // Not having a value means this install doesn't appear in installs.ini so
908 // this is the first run for this install.
912 // Gets the install section that would have been created if the install
913 // path has incorrect casing (see bug 1555319). We use this later during
914 // profile selection.
915 rv
= gDirServiceProvider
->GetLegacyInstallHash(installHash
);
916 NS_ENSURE_SUCCESS(rv
, rv
);
917 CopyUTF16toUTF8(installHash
, mLegacyInstallSection
);
918 mLegacyInstallSection
.Insert(INSTALL_PREFIX
, 0);
924 nsToolkitProfile
* currentProfile
= nullptr;
926 #ifdef MOZ_DEV_EDITION
927 nsCOMPtr
<nsIFile
> ignoreDevEditionProfile
;
928 rv
= mAppData
->Clone(getter_AddRefs(ignoreDevEditionProfile
));
933 rv
= ignoreDevEditionProfile
->AppendNative("ignore-dev-edition-profile"_ns
);
938 bool shouldIgnoreSeparateProfile
;
939 rv
= ignoreDevEditionProfile
->Exists(&shouldIgnoreSeparateProfile
);
940 if (NS_FAILED(rv
)) return rv
;
942 mUseDevEditionProfile
= !shouldIgnoreSeparateProfile
;
945 nsCOMPtr
<nsIToolkitProfile
> autoSelectProfile
;
947 unsigned int nonDevEditionProfiles
= 0;
949 for (c
= 0; true; ++c
) {
950 nsAutoCString
profileID("Profile");
951 profileID
.AppendInt(c
);
953 rv
= mProfileDB
.GetString(profileID
.get(), "IsRelative", buffer
);
954 if (NS_FAILED(rv
)) break;
956 bool isRelative
= buffer
.EqualsLiteral("1");
958 nsAutoCString filePath
;
960 rv
= mProfileDB
.GetString(profileID
.get(), "Path", filePath
);
962 NS_ERROR("Malformed profiles.ini: Path= not found");
968 rv
= mProfileDB
.GetString(profileID
.get(), "Name", name
);
970 NS_ERROR("Malformed profiles.ini: Name= not found");
974 nsCOMPtr
<nsIFile
> rootDir
;
975 rv
= NS_NewNativeLocalFile(""_ns
, true, getter_AddRefs(rootDir
));
976 NS_ENSURE_SUCCESS(rv
, rv
);
979 rv
= rootDir
->SetRelativeDescriptor(mAppData
, filePath
);
981 rv
= rootDir
->SetPersistentDescriptor(filePath
);
983 if (NS_FAILED(rv
)) continue;
985 nsCOMPtr
<nsIFile
> localDir
;
987 rv
= NS_NewNativeLocalFile(""_ns
, true, getter_AddRefs(localDir
));
988 NS_ENSURE_SUCCESS(rv
, rv
);
990 rv
= localDir
->SetRelativeDescriptor(mTempData
, filePath
);
995 currentProfile
= new nsToolkitProfile(name
, rootDir
, localDir
, true);
997 // If a user has modified the ini file path it may make for a valid profile
998 // path but not match what we would have serialised and so may not match
999 // the path in the install section. Re-serialise it to get it in the
1000 // expected form again.
1002 nsCString descriptor
;
1003 GetProfileDescriptor(currentProfile
, descriptor
, &nowRelative
);
1005 if (isRelative
!= nowRelative
|| !descriptor
.Equals(filePath
)) {
1006 mProfileDB
.SetString(profileID
.get(), "IsRelative",
1007 nowRelative
? "1" : "0");
1008 mProfileDB
.SetString(profileID
.get(), "Path", descriptor
.get());
1010 // Should we flush now? It costs some startup time and we will fix it on
1011 // the next startup anyway. If something else causes a flush then it will
1012 // be fixed in the ini file then.
1015 rv
= mProfileDB
.GetString(profileID
.get(), "Default", buffer
);
1016 if (NS_SUCCEEDED(rv
) && buffer
.EqualsLiteral("1")) {
1017 mNormalDefault
= currentProfile
;
1020 // Is this the default profile for this install?
1021 if (mUseDedicatedProfile
&& !mDedicatedProfile
&&
1022 installProfilePath
.Equals(descriptor
)) {
1023 // Found a profile for this install.
1024 mDedicatedProfile
= currentProfile
;
1027 if (name
.EqualsLiteral(DEV_EDITION_NAME
)) {
1028 mDevEditionDefault
= currentProfile
;
1030 nonDevEditionProfiles
++;
1031 autoSelectProfile
= currentProfile
;
1035 // If there is only one non-dev-edition profile then mark it as the default.
1036 if (!mNormalDefault
&& nonDevEditionProfiles
== 1) {
1037 SetNormalDefault(autoSelectProfile
);
1040 if (!mUseDedicatedProfile
) {
1041 if (mUseDevEditionProfile
) {
1042 // When using the separate dev-edition profile not finding it means this
1044 mIsFirstRun
= !mDevEditionDefault
;
1046 // If there are no normal profiles then this is a first run.
1047 mIsFirstRun
= nonDevEditionProfiles
== 0;
1055 nsToolkitProfileService::SetStartWithLastProfile(bool aValue
) {
1056 if (mStartWithLast
!= aValue
) {
1057 // Note: the skeleton ui (see PreXULSkeletonUI.cpp) depends on this
1058 // having this name and being under General. If that ever changes,
1059 // the skeleton UI will just need to be updated. If it changes frequently,
1060 // it's probably best we just mirror the value to the registry here.
1061 nsresult rv
= mProfileDB
.SetString("General", "StartWithLastProfile",
1062 aValue
? "1" : "0");
1063 NS_ENSURE_SUCCESS(rv
, rv
);
1064 mStartWithLast
= aValue
;
1070 nsToolkitProfileService::GetStartWithLastProfile(bool* aResult
) {
1071 *aResult
= mStartWithLast
;
1076 nsToolkitProfileService::GetProfiles(nsISimpleEnumerator
** aResult
) {
1077 *aResult
= new ProfileEnumerator(mProfiles
.getFirst());
1079 NS_ADDREF(*aResult
);
1084 nsToolkitProfileService::ProfileEnumerator::HasMoreElements(bool* aResult
) {
1085 *aResult
= mCurrent
? true : false;
1090 nsToolkitProfileService::ProfileEnumerator::GetNext(nsISupports
** aResult
) {
1091 if (!mCurrent
) return NS_ERROR_FAILURE
;
1093 NS_ADDREF(*aResult
= mCurrent
);
1095 mCurrent
= mCurrent
->getNext();
1100 nsToolkitProfileService::GetCurrentProfile(nsIToolkitProfile
** aResult
) {
1101 NS_IF_ADDREF(*aResult
= mCurrent
);
1106 nsToolkitProfileService::GetDefaultProfile(nsIToolkitProfile
** aResult
) {
1107 if (mUseDedicatedProfile
) {
1108 NS_IF_ADDREF(*aResult
= mDedicatedProfile
);
1112 if (mUseDevEditionProfile
) {
1113 NS_IF_ADDREF(*aResult
= mDevEditionDefault
);
1117 NS_IF_ADDREF(*aResult
= mNormalDefault
);
1121 void nsToolkitProfileService::SetNormalDefault(nsIToolkitProfile
* aProfile
) {
1122 if (mNormalDefault
== aProfile
) {
1126 if (mNormalDefault
) {
1127 nsToolkitProfile
* profile
=
1128 static_cast<nsToolkitProfile
*>(mNormalDefault
.get());
1129 mProfileDB
.DeleteString(profile
->mSection
.get(), "Default");
1132 mNormalDefault
= aProfile
;
1134 if (mNormalDefault
) {
1135 nsToolkitProfile
* profile
=
1136 static_cast<nsToolkitProfile
*>(mNormalDefault
.get());
1137 mProfileDB
.SetString(profile
->mSection
.get(), "Default", "1");
1142 nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile
* aProfile
) {
1143 if (mUseDedicatedProfile
) {
1144 if (mDedicatedProfile
!= aProfile
) {
1146 // Setting this to the empty string means no profile will be found on
1147 // startup but we'll recognise that this install has been used
1149 mProfileDB
.SetString(mInstallSection
.get(), "Default", "");
1151 nsCString profilePath
;
1152 nsresult rv
= GetProfileDescriptor(aProfile
, profilePath
, nullptr);
1153 NS_ENSURE_SUCCESS(rv
, rv
);
1155 mProfileDB
.SetString(mInstallSection
.get(), "Default",
1158 mDedicatedProfile
= aProfile
;
1160 // Some kind of choice has happened here, lock this profile to this
1162 mProfileDB
.SetString(mInstallSection
.get(), "Locked", "1");
1167 if (mUseDevEditionProfile
&& aProfile
!= mDevEditionDefault
) {
1168 // The separate profile is hardcoded.
1169 return NS_ERROR_FAILURE
;
1172 SetNormalDefault(aProfile
);
1177 // Gets the profile root directory descriptor for storing in profiles.ini or
1179 nsresult
nsToolkitProfileService::GetProfileDescriptor(
1180 nsIToolkitProfile
* aProfile
, nsACString
& aDescriptor
, bool* aIsRelative
) {
1181 nsCOMPtr
<nsIFile
> profileDir
;
1182 nsresult rv
= aProfile
->GetRootDir(getter_AddRefs(profileDir
));
1183 NS_ENSURE_SUCCESS(rv
, rv
);
1185 // if the profile dir is relative to appdir...
1187 rv
= mAppData
->Contains(profileDir
, &isRelative
);
1189 nsCString profilePath
;
1190 if (NS_SUCCEEDED(rv
) && isRelative
) {
1191 // we use a relative descriptor
1192 rv
= profileDir
->GetRelativeDescriptor(mAppData
, profilePath
);
1194 // otherwise, a persistent descriptor
1195 rv
= profileDir
->GetPersistentDescriptor(profilePath
);
1197 NS_ENSURE_SUCCESS(rv
, rv
);
1199 aDescriptor
.Assign(profilePath
);
1201 *aIsRelative
= isRelative
;
1207 nsresult
nsToolkitProfileService::CreateDefaultProfile(
1208 nsIToolkitProfile
** aResult
) {
1209 // Create a new default profile
1211 if (mUseDevEditionProfile
) {
1212 name
.AssignLiteral(DEV_EDITION_NAME
);
1213 } else if (mUseDedicatedProfile
) {
1214 name
.AppendPrintf("default-%s", mUpdateChannel
.get());
1216 name
.AssignLiteral(DEFAULT_NAME
);
1219 nsresult rv
= CreateUniqueProfile(nullptr, name
, aResult
);
1220 NS_ENSURE_SUCCESS(rv
, rv
);
1222 if (mUseDedicatedProfile
) {
1223 SetDefaultProfile(mCurrent
);
1224 } else if (mUseDevEditionProfile
) {
1225 mDevEditionDefault
= mCurrent
;
1227 SetNormalDefault(mCurrent
);
1234 * An implementation of SelectStartupProfile callable from JavaScript via XPCOM.
1235 * See nsIToolkitProfileService.idl.
1238 nsToolkitProfileService::SelectStartupProfile(
1239 const nsTArray
<nsCString
>& aArgv
, bool aIsResetting
,
1240 const nsACString
& aUpdateChannel
, const nsACString
& aLegacyInstallHash
,
1241 nsIFile
** aRootDir
, nsIFile
** aLocalDir
, nsIToolkitProfile
** aProfile
,
1243 int argc
= aArgv
.Length();
1244 // Our command line handling expects argv to be null-terminated so construct
1245 // an appropriate array.
1246 auto argv
= MakeUnique
<char*[]>(argc
+ 1);
1247 // Also, our command line handling removes things from the array without
1248 // freeing them so keep track of what we've created separately.
1249 auto allocated
= MakeUnique
<UniqueFreePtr
<char>[]>(argc
);
1251 for (int i
= 0; i
< argc
; i
++) {
1252 allocated
[i
].reset(ToNewCString(aArgv
[i
]));
1253 argv
[i
] = allocated
[i
].get();
1255 argv
[argc
] = nullptr;
1257 mUpdateChannel
= aUpdateChannel
;
1258 if (!aLegacyInstallHash
.IsEmpty()) {
1259 mLegacyInstallSection
.Assign(aLegacyInstallHash
);
1260 mLegacyInstallSection
.Insert(INSTALL_PREFIX
, 0);
1265 SelectStartupProfile(&argc
, argv
.get(), aIsResetting
, aRootDir
, aLocalDir
,
1266 aProfile
, aDidCreate
, &wasDefault
);
1268 // Since we were called outside of the normal startup path complete any
1270 if (NS_SUCCEEDED(rv
)) {
1277 static void SaltProfileName(nsACString
& aName
);
1280 * Selects or creates a profile to use based on the profiles database, any
1281 * environment variables and any command line arguments. Will not create
1282 * a profile if aIsResetting is true. The profile is selected based on this
1283 * order of preference:
1284 * * Environment variables (set when restarting the application).
1285 * * --profile command line argument.
1286 * * --createprofile command line argument (this also causes the app to exit).
1287 * * -p command line argument.
1288 * * A new profile created if this is the first run of the application.
1289 * * The default profile.
1290 * aRootDir and aLocalDir are set to the data and local directories for the
1291 * profile data. If a profile from the database was selected it will be
1292 * returned in aProfile.
1293 * aDidCreate will be set to true if a new profile was created.
1294 * This function should be called once at startup and will fail if called again.
1295 * aArgv should be an array of aArgc + 1 strings, the last element being null.
1296 * Both aArgv and aArgc will be mutated.
1298 nsresult
nsToolkitProfileService::SelectStartupProfile(
1299 int* aArgc
, char* aArgv
[], bool aIsResetting
, nsIFile
** aRootDir
,
1300 nsIFile
** aLocalDir
, nsIToolkitProfile
** aProfile
, bool* aDidCreate
,
1301 bool* aWasDefaultSelection
) {
1302 if (mStartupProfileSelected
) {
1303 return NS_ERROR_ALREADY_INITIALIZED
;
1306 mStartupProfileSelected
= true;
1307 *aDidCreate
= false;
1308 *aWasDefaultSelection
= false;
1313 // Use the profile specified in the environment variables (generally from an
1314 // app initiated restart).
1315 nsCOMPtr
<nsIFile
> lf
= GetFileFromEnv("XRE_PROFILE_PATH");
1317 nsCOMPtr
<nsIFile
> localDir
= GetFileFromEnv("XRE_PROFILE_LOCAL_PATH");
1322 // Clear out flags that we handled (or should have handled!) last startup.
1324 CheckArg(*aArgc
, aArgv
, "p", &dummy
);
1325 CheckArg(*aArgc
, aArgv
, "profile", &dummy
);
1326 CheckArg(*aArgc
, aArgv
, "profilemanager");
1328 nsCOMPtr
<nsIToolkitProfile
> profile
;
1329 GetProfileByDir(lf
, localDir
, getter_AddRefs(profile
));
1331 if (profile
&& mIsFirstRun
&& mUseDedicatedProfile
) {
1333 (mUseDevEditionProfile
? mDevEditionDefault
: mNormalDefault
)) {
1334 // This is the first run of a dedicated profile build where the selected
1335 // profile is the previous default so we should either make it the
1336 // default profile for this install or push the user to a new profile.
1339 rv
= MaybeMakeDefaultDedicatedProfile(profile
, &result
);
1340 NS_ENSURE_SUCCESS(rv
, rv
);
1342 mStartupReason
= u
"restart-claimed-default"_ns
;
1346 rv
= CreateDefaultProfile(getter_AddRefs(mCurrent
));
1347 if (NS_FAILED(rv
)) {
1348 *aProfile
= nullptr;
1353 NS_ENSURE_SUCCESS(rv
, rv
);
1355 mStartupReason
= u
"restart-skipped-default"_ns
;
1359 NS_IF_ADDREF(*aProfile
= mCurrent
);
1360 mCurrent
->GetRootDir(aRootDir
);
1361 mCurrent
->GetLocalDir(aLocalDir
);
1367 if (EnvHasValue("XRE_RESTARTED_BY_PROFILE_MANAGER")) {
1368 mStartupReason
= u
"profile-manager"_ns
;
1369 } else if (aIsResetting
) {
1370 mStartupReason
= u
"profile-reset"_ns
;
1372 mStartupReason
= u
"restart"_ns
;
1376 lf
.forget(aRootDir
);
1377 localDir
.forget(aLocalDir
);
1378 NS_IF_ADDREF(*aProfile
= profile
);
1382 // Check the -profile command line argument. It accepts a single argument that
1383 // gives the path to use for the profile.
1384 ArgResult ar
= CheckArg(*aArgc
, aArgv
, "profile", &arg
);
1385 if (ar
== ARG_BAD
) {
1386 PR_fprintf(PR_STDERR
, "Error: argument --profile requires a path\n");
1387 return NS_ERROR_FAILURE
;
1390 nsCOMPtr
<nsIFile
> lf
;
1391 rv
= XRE_GetFileFromPath(arg
, getter_AddRefs(lf
));
1392 NS_ENSURE_SUCCESS(rv
, rv
);
1394 // Make sure that the profile path exists and it's a directory.
1396 rv
= lf
->Exists(&exists
);
1397 NS_ENSURE_SUCCESS(rv
, rv
);
1399 rv
= lf
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
1400 NS_ENSURE_SUCCESS(rv
, rv
);
1403 rv
= lf
->IsDirectory(&isDir
);
1404 NS_ENSURE_SUCCESS(rv
, rv
);
1408 "Error: argument --profile requires a path to a directory\n");
1409 return NS_ERROR_FAILURE
;
1413 mStartupReason
= u
"argument-profile"_ns
;
1415 GetProfileByDir(lf
, nullptr, getter_AddRefs(mCurrent
));
1416 NS_ADDREF(*aRootDir
= lf
);
1417 // If the root dir matched a profile then use its local dir, otherwise use
1418 // the root dir as the local dir.
1420 mCurrent
->GetLocalDir(aLocalDir
);
1422 lf
.forget(aLocalDir
);
1425 NS_IF_ADDREF(*aProfile
= mCurrent
);
1429 // Check the -createprofile command line argument. It accepts a single
1430 // argument that is either the name for the new profile or the name followed
1431 // by the path to use.
1432 ar
= CheckArg(*aArgc
, aArgv
, "createprofile", &arg
, CheckArgFlag::RemoveArg
);
1433 if (ar
== ARG_BAD
) {
1434 PR_fprintf(PR_STDERR
,
1435 "Error: argument --createprofile requires a profile name\n");
1436 return NS_ERROR_FAILURE
;
1439 const char* delim
= strchr(arg
, ' ');
1440 nsCOMPtr
<nsIToolkitProfile
> profile
;
1442 nsCOMPtr
<nsIFile
> lf
;
1443 rv
= NS_NewNativeLocalFile(nsDependentCString(delim
+ 1), true,
1444 getter_AddRefs(lf
));
1445 if (NS_FAILED(rv
)) {
1446 PR_fprintf(PR_STDERR
, "Error: profile path not valid.\n");
1450 // As with --profile, assume that the given path will be used for the
1451 // main profile directory.
1452 rv
= CreateProfile(lf
, nsDependentCSubstring(arg
, delim
),
1453 getter_AddRefs(profile
));
1455 rv
= CreateProfile(nullptr, nsDependentCString(arg
),
1456 getter_AddRefs(profile
));
1458 // Some pathological arguments can make it this far
1459 if (NS_FAILED(rv
) || NS_FAILED(Flush())) {
1460 PR_fprintf(PR_STDERR
, "Error creating profile.\n");
1462 return NS_ERROR_ABORT
;
1465 // Check the -p command line argument. It either accepts a profile name and
1466 // uses that named profile or without a name it opens the profile manager.
1467 ar
= CheckArg(*aArgc
, aArgv
, "p", &arg
);
1468 if (ar
== ARG_BAD
) {
1469 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1472 rv
= GetProfileByName(nsDependentCString(arg
), getter_AddRefs(mCurrent
));
1473 if (NS_SUCCEEDED(rv
)) {
1474 mStartupReason
= u
"argument-p"_ns
;
1476 mCurrent
->GetRootDir(aRootDir
);
1477 mCurrent
->GetLocalDir(aLocalDir
);
1479 NS_ADDREF(*aProfile
= mCurrent
);
1483 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1486 ar
= CheckArg(*aArgc
, aArgv
, "profilemanager");
1487 if (ar
== ARG_FOUND
) {
1488 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1491 #ifdef MOZ_BACKGROUNDTASKS
1492 if (BackgroundTasks::IsBackgroundTaskMode()) {
1493 // There are two cases:
1494 // 1. ephemeral profile: create a new one in temporary directory.
1495 // 2. non-ephemeral (persistent) profile:
1496 // a. if no salted profile is known, create a new one in
1497 // background task-specific directory.
1498 // b. if salted profile is know, use salted path.
1499 nsString installHash
;
1500 rv
= gDirServiceProvider
->GetInstallHash(installHash
);
1501 NS_ENSURE_SUCCESS(rv
, rv
);
1503 nsCString
profilePrefix(BackgroundTasks::GetProfilePrefix(
1504 NS_LossyConvertUTF16toASCII(installHash
)));
1506 nsCString
taskName(BackgroundTasks::GetBackgroundTasks().ref());
1508 nsCOMPtr
<nsIFile
> file
;
1510 if (BackgroundTasks::IsEphemeralProfileTaskName(taskName
)) {
1511 // Background task mode does not enable legacy telemetry, so this is for
1512 // completeness and testing only.
1513 mStartupReason
= u
"backgroundtask-ephemeral"_ns
;
1515 nsCOMPtr
<nsIFile
> rootDir
;
1516 rv
= GetSpecialSystemDirectory(OS_TemporaryDirectory
,
1517 getter_AddRefs(rootDir
));
1518 NS_ENSURE_SUCCESS(rv
, rv
);
1520 nsresult rv
= BackgroundTasks::CreateEphemeralProfileDirectory(
1521 rootDir
, profilePrefix
, getter_AddRefs(file
));
1522 if (NS_WARN_IF(NS_FAILED(rv
))) {
1523 // In background task mode, NS_ERROR_UNEXPECTED is handled specially to
1524 // exit with a non-zero exit code.
1525 return NS_ERROR_UNEXPECTED
;
1529 // Background task mode does not enable legacy telemetry, so this is for
1530 // completeness and testing only.
1531 mStartupReason
= u
"backgroundtask-not-ephemeral"_ns
;
1533 // A non-ephemeral profile is required.
1534 nsCOMPtr
<nsIFile
> rootDir
;
1535 nsresult rv
= gDirServiceProvider
->GetBackgroundTasksProfilesRootDir(
1536 getter_AddRefs(rootDir
));
1537 NS_ENSURE_SUCCESS(rv
, rv
);
1539 nsAutoCString buffer
;
1540 rv
= mProfileDB
.GetString("BackgroundTasksProfiles", profilePrefix
.get(),
1542 if (NS_SUCCEEDED(rv
)) {
1543 // We have a record of one! Use it.
1544 rv
= rootDir
->Clone(getter_AddRefs(file
));
1545 NS_ENSURE_SUCCESS(rv
, rv
);
1547 rv
= file
->AppendNative(buffer
);
1548 NS_ENSURE_SUCCESS(rv
, rv
);
1550 nsCString saltedProfilePrefix
= profilePrefix
;
1551 SaltProfileName(saltedProfilePrefix
);
1553 nsresult rv
= BackgroundTasks::CreateNonEphemeralProfileDirectory(
1554 rootDir
, saltedProfilePrefix
, getter_AddRefs(file
));
1555 if (NS_WARN_IF(NS_FAILED(rv
))) {
1556 // In background task mode, NS_ERROR_UNEXPECTED is handled specially
1557 // to exit with a non-zero exit code.
1558 return NS_ERROR_UNEXPECTED
;
1562 // Keep a record of the salted name. It's okay if this doesn't succeed:
1563 // not great, but it's better for tasks (particularly,
1564 // `backgroundupdate`) to run and not persist state correctly than to
1567 mProfileDB
.SetString("BackgroundTasksProfiles", profilePrefix
.get(),
1568 saltedProfilePrefix
.get());
1569 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
1571 if (NS_SUCCEEDED(rv
)) {
1573 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
1578 nsCOMPtr
<nsIFile
> localDir
= file
;
1579 file
.forget(aRootDir
);
1580 localDir
.forget(aLocalDir
);
1582 // Background tasks never use profiles known to the profile service.
1583 *aProfile
= nullptr;
1589 if (mIsFirstRun
&& mUseDedicatedProfile
&&
1590 !mInstallSection
.Equals(mLegacyInstallSection
)) {
1591 // The default profile could be assigned to a hash generated from an
1592 // incorrectly cased version of the installation directory (see bug
1593 // 1555319). Ideally we'd do all this while loading profiles.ini but we
1594 // can't override the legacy section value before that for tests.
1595 nsCString defaultDescriptor
;
1596 rv
= mProfileDB
.GetString(mLegacyInstallSection
.get(), "Default",
1599 if (NS_SUCCEEDED(rv
)) {
1600 // There is a default here, need to see if it matches any profiles.
1602 nsCString descriptor
;
1604 for (RefPtr
<nsToolkitProfile
> profile
: mProfiles
) {
1605 GetProfileDescriptor(profile
, descriptor
, &isRelative
);
1607 if (descriptor
.Equals(defaultDescriptor
)) {
1608 // Found the default profile. Copy the install section over to
1609 // the correct location. We leave the old info in place for older
1610 // versions of Firefox to use.
1611 nsTArray
<UniquePtr
<KeyValue
>> strings
=
1612 GetSectionStrings(&mProfileDB
, mLegacyInstallSection
.get());
1613 for (const auto& kv
: strings
) {
1614 mProfileDB
.SetString(mInstallSection
.get(), kv
->key
.get(),
1618 // Flush now. This causes a small blip in startup but it should be
1619 // one time only whereas not flushing means we have to do this search
1620 // on every startup.
1623 // Now start up with the found profile.
1624 mDedicatedProfile
= profile
;
1625 mIsFirstRun
= false;
1632 // If this is a first run then create a new profile.
1634 // If we're configured to always show the profile manager then don't create
1635 // a new profile to use.
1636 if (!mStartWithLast
) {
1637 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1640 bool skippedDefaultProfile
= false;
1642 if (mUseDedicatedProfile
) {
1643 // This is the first run of a dedicated profile install. We have to decide
1644 // whether to use the default profile used by non-dedicated-profile
1645 // installs or to create a new profile.
1647 // Find what would have been the default profile for old installs.
1648 nsCOMPtr
<nsIToolkitProfile
> profile
= mNormalDefault
;
1649 if (mUseDevEditionProfile
) {
1650 profile
= mDevEditionDefault
;
1654 nsCOMPtr
<nsIFile
> rootDir
;
1655 profile
->GetRootDir(getter_AddRefs(rootDir
));
1657 nsCOMPtr
<nsIFile
> compat
;
1658 rootDir
->Clone(getter_AddRefs(compat
));
1659 compat
->Append(COMPAT_FILE
);
1662 rv
= compat
->Exists(&exists
);
1663 NS_ENSURE_SUCCESS(rv
, rv
);
1665 // If the file is missing then either this is an empty profile (likely
1666 // generated by bug 1518591) or it is from an ancient version. We'll opt
1667 // to leave it for older versions in this case.
1670 rv
= MaybeMakeDefaultDedicatedProfile(profile
, &result
);
1671 NS_ENSURE_SUCCESS(rv
, rv
);
1673 mStartupReason
= u
"firstrun-claimed-default"_ns
;
1676 rootDir
.forget(aRootDir
);
1677 profile
->GetLocalDir(aLocalDir
);
1678 profile
.forget(aProfile
);
1682 // We're going to create a new profile for this install even though
1683 // another default exists.
1684 skippedDefaultProfile
= true;
1689 rv
= CreateDefaultProfile(getter_AddRefs(mCurrent
));
1690 if (NS_SUCCEEDED(rv
)) {
1691 #ifdef MOZ_CREATE_LEGACY_PROFILE
1692 // If there is only one profile and it isn't meant to be the profile that
1693 // older versions of Firefox use then we must create a default profile
1694 // for older versions of Firefox to avoid the existing profile being
1696 if ((mUseDedicatedProfile
|| mUseDevEditionProfile
) &&
1697 mProfiles
.getFirst() == mProfiles
.getLast()) {
1698 nsCOMPtr
<nsIToolkitProfile
> newProfile
;
1699 CreateProfile(nullptr, nsLiteralCString(DEFAULT_NAME
),
1700 getter_AddRefs(newProfile
));
1701 SetNormalDefault(newProfile
);
1706 NS_ENSURE_SUCCESS(rv
, rv
);
1708 if (skippedDefaultProfile
) {
1709 mStartupReason
= u
"firstrun-skipped-default"_ns
;
1711 mStartupReason
= u
"firstrun-created-default"_ns
;
1714 // Use the new profile.
1715 mCurrent
->GetRootDir(aRootDir
);
1716 mCurrent
->GetLocalDir(aLocalDir
);
1717 NS_ADDREF(*aProfile
= mCurrent
);
1724 GetDefaultProfile(getter_AddRefs(mCurrent
));
1726 // None of the profiles was marked as default (generally only happens if the
1727 // user modifies profiles.ini manually). Let the user choose.
1729 return NS_ERROR_SHOW_PROFILE_MANAGER
;
1732 // Let the caller know that the profile was selected by default.
1733 *aWasDefaultSelection
= true;
1734 mStartupReason
= u
"default"_ns
;
1736 // Use the selected profile.
1737 mCurrent
->GetRootDir(aRootDir
);
1738 mCurrent
->GetLocalDir(aLocalDir
);
1739 NS_ADDREF(*aProfile
= mCurrent
);
1745 * Creates a new profile for reset and mark it as the current profile.
1747 nsresult
nsToolkitProfileService::CreateResetProfile(
1748 nsIToolkitProfile
** aNewProfile
) {
1749 nsAutoCString oldProfileName
;
1750 mCurrent
->GetName(oldProfileName
);
1752 nsCOMPtr
<nsIToolkitProfile
> newProfile
;
1753 // Make the new profile name the old profile (or "default-") + the time in
1754 // seconds since epoch for uniqueness.
1755 nsAutoCString newProfileName
;
1756 if (!oldProfileName
.IsEmpty()) {
1757 newProfileName
.Assign(oldProfileName
);
1758 newProfileName
.Append("-");
1760 newProfileName
.AssignLiteral("default-");
1762 newProfileName
.AppendPrintf("%" PRId64
, PR_Now() / 1000);
1763 nsresult rv
= CreateProfile(nullptr, // choose a default dir for us
1764 newProfileName
, getter_AddRefs(newProfile
));
1765 if (NS_FAILED(rv
)) return rv
;
1767 mCurrent
= newProfile
;
1768 newProfile
.forget(aNewProfile
);
1770 // Don't flush the changes yet. That will happen once the migration
1771 // successfully completes.
1776 * This is responsible for deleting the old profile, copying its name to the
1777 * current profile and if the old profile was default making the new profile
1780 nsresult
nsToolkitProfileService::ApplyResetProfile(
1781 nsIToolkitProfile
* aOldProfile
) {
1782 // If the old profile would have been the default for old installs then mark
1783 // the new profile as such.
1784 if (mNormalDefault
== aOldProfile
) {
1785 SetNormalDefault(mCurrent
);
1788 if (mUseDedicatedProfile
&& mDedicatedProfile
== aOldProfile
) {
1789 bool wasLocked
= false;
1792 mProfileDB
.GetString(mInstallSection
.get(), "Locked", val
))) {
1793 wasLocked
= val
.Equals("1");
1796 SetDefaultProfile(mCurrent
);
1798 // Make the locked state match if necessary.
1800 mProfileDB
.DeleteString(mInstallSection
.get(), "Locked");
1805 nsresult rv
= aOldProfile
->GetName(name
);
1806 NS_ENSURE_SUCCESS(rv
, rv
);
1808 // Don't remove the old profile's files until after we've successfully flushed
1809 // the profile changes to disk.
1810 rv
= aOldProfile
->Remove(false);
1811 NS_ENSURE_SUCCESS(rv
, rv
);
1813 // Switching the name will make this the default for dev-edition if
1815 rv
= mCurrent
->SetName(name
);
1816 NS_ENSURE_SUCCESS(rv
, rv
);
1819 NS_ENSURE_SUCCESS(rv
, rv
);
1821 // Now that the profile changes are flushed, try to remove the old profile's
1822 // files. If we fail the worst that will happen is that an orphan directory is
1823 // left. Let this run in the background while we start up.
1824 RemoveProfileFiles(aOldProfile
, true);
1830 nsToolkitProfileService::GetProfileByName(const nsACString
& aName
,
1831 nsIToolkitProfile
** aResult
) {
1832 for (RefPtr
<nsToolkitProfile
> profile
: mProfiles
) {
1833 if (profile
->mName
.Equals(aName
)) {
1834 NS_ADDREF(*aResult
= profile
);
1839 return NS_ERROR_FAILURE
;
1843 * Finds a profile from the database that uses the given root and local
1846 void nsToolkitProfileService::GetProfileByDir(nsIFile
* aRootDir
,
1848 nsIToolkitProfile
** aResult
) {
1849 for (RefPtr
<nsToolkitProfile
> profile
: mProfiles
) {
1851 nsresult rv
= profile
->mRootDir
->Equals(aRootDir
, &equal
);
1852 if (NS_SUCCEEDED(rv
) && equal
) {
1854 // If no local directory was given then we will just use the normal
1855 // local directory for the profile.
1856 profile
.forget(aResult
);
1860 rv
= profile
->mLocalDir
->Equals(aLocalDir
, &equal
);
1861 if (NS_SUCCEEDED(rv
) && equal
) {
1862 profile
.forget(aResult
);
1869 nsresult
NS_LockProfilePath(nsIFile
* aPath
, nsIFile
* aTempPath
,
1870 nsIProfileUnlocker
** aUnlocker
,
1871 nsIProfileLock
** aResult
) {
1872 RefPtr
<nsToolkitProfileLock
> lock
= new nsToolkitProfileLock();
1874 nsresult rv
= lock
->Init(aPath
, aTempPath
, aUnlocker
);
1875 if (NS_FAILED(rv
)) return rv
;
1877 lock
.forget(aResult
);
1881 static void SaltProfileName(nsACString
& aName
) {
1883 NS_MakeRandomString(salt
, 8);
1886 aName
.Insert(salt
, 0, 9);
1890 nsToolkitProfileService::CreateUniqueProfile(nsIFile
* aRootDir
,
1891 const nsACString
& aNamePrefix
,
1892 nsIToolkitProfile
** aResult
) {
1893 nsCOMPtr
<nsIToolkitProfile
> profile
;
1894 nsresult rv
= GetProfileByName(aNamePrefix
, getter_AddRefs(profile
));
1895 if (NS_FAILED(rv
)) {
1896 return CreateProfile(aRootDir
, aNamePrefix
, aResult
);
1899 uint32_t suffix
= 1;
1901 nsPrintfCString
name("%s-%d", PromiseFlatCString(aNamePrefix
).get(),
1903 rv
= GetProfileByName(name
, getter_AddRefs(profile
));
1904 if (NS_FAILED(rv
)) {
1905 return CreateProfile(aRootDir
, name
, aResult
);
1912 nsToolkitProfileService::CreateProfile(nsIFile
* aRootDir
,
1913 const nsACString
& aName
,
1914 nsIToolkitProfile
** aResult
) {
1915 nsresult rv
= GetProfileByName(aName
, aResult
);
1916 if (NS_SUCCEEDED(rv
)) {
1920 nsCOMPtr
<nsIFile
> rootDir(aRootDir
);
1922 nsAutoCString dirName
;
1924 rv
= gDirServiceProvider
->GetUserProfilesRootDir(getter_AddRefs(rootDir
));
1925 NS_ENSURE_SUCCESS(rv
, rv
);
1928 SaltProfileName(dirName
);
1930 if (NS_IsNativeUTF8()) {
1931 rootDir
->AppendNative(dirName
);
1933 rootDir
->Append(NS_ConvertUTF8toUTF16(dirName
));
1937 nsCOMPtr
<nsIFile
> localDir
;
1940 rv
= mAppData
->Contains(rootDir
, &isRelative
);
1941 if (NS_SUCCEEDED(rv
) && isRelative
) {
1943 rv
= rootDir
->GetRelativeDescriptor(mAppData
, path
);
1944 NS_ENSURE_SUCCESS(rv
, rv
);
1946 rv
= NS_NewNativeLocalFile(""_ns
, true, getter_AddRefs(localDir
));
1947 NS_ENSURE_SUCCESS(rv
, rv
);
1949 rv
= localDir
->SetRelativeDescriptor(mTempData
, path
);
1955 rv
= rootDir
->Exists(&exists
);
1956 NS_ENSURE_SUCCESS(rv
, rv
);
1959 rv
= rootDir
->IsDirectory(&exists
);
1960 NS_ENSURE_SUCCESS(rv
, rv
);
1962 if (!exists
) return NS_ERROR_FILE_NOT_DIRECTORY
;
1964 nsCOMPtr
<nsIFile
> profileDirParent
;
1965 nsAutoString profileDirName
;
1967 rv
= rootDir
->GetParent(getter_AddRefs(profileDirParent
));
1968 NS_ENSURE_SUCCESS(rv
, rv
);
1970 rv
= rootDir
->GetLeafName(profileDirName
);
1971 NS_ENSURE_SUCCESS(rv
, rv
);
1973 // let's ensure that the profile directory exists.
1974 rv
= rootDir
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
1975 NS_ENSURE_SUCCESS(rv
, rv
);
1976 rv
= rootDir
->SetPermissions(0700);
1978 // If the profile is on the sdcard, this will fail but its non-fatal
1979 NS_ENSURE_SUCCESS(rv
, rv
);
1983 rv
= localDir
->Exists(&exists
);
1984 NS_ENSURE_SUCCESS(rv
, rv
);
1987 rv
= localDir
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
1988 NS_ENSURE_SUCCESS(rv
, rv
);
1991 // We created a new profile dir. Let's store a creation timestamp.
1992 // Note that this code path does not apply if the profile dir was
1993 // created prior to launching.
1994 rv
= CreateTimesInternal(rootDir
);
1995 NS_ENSURE_SUCCESS(rv
, rv
);
1997 nsCOMPtr
<nsIToolkitProfile
> profile
=
1998 new nsToolkitProfile(aName
, rootDir
, localDir
, false);
2000 if (aName
.Equals(DEV_EDITION_NAME
)) {
2001 mDevEditionDefault
= profile
;
2004 profile
.forget(aResult
);
2009 * Snaps (https://snapcraft.io/) use a different installation directory for
2010 * every version of an application. Since dedicated profiles uses the
2011 * installation directory to determine which profile to use this would lead
2012 * snap users getting a new profile on every application update.
2014 * However the only way to have multiple installation of a snap is to install
2015 * a new snap instance. Different snap instances have different user data
2016 * directories and so already will not share profiles, in fact one instance
2017 * will not even be able to see the other instance's profiles since
2018 * profiles.ini will be stored in different places.
2020 * So we can just disable dedicated profile support in this case and revert
2021 * back to the old method of just having a single default profile and still
2022 * get essentially the same benefits as dedicated profiles provides.
2024 bool nsToolkitProfileService::IsSnapEnvironment() {
2025 #ifdef MOZ_WIDGET_GTK
2026 return widget::IsRunningUnderSnap();
2033 * In some situations dedicated profile support does not work well. This
2034 * includes a handful of linux distributions which always install different
2035 * application versions to different locations, some application sandboxing
2036 * systems as well as enterprise deployments. This environment variable provides
2037 * a way to opt out of dedicated profiles for these cases.
2039 * For Windows, we provide a policy to accomplish the same thing.
2041 bool nsToolkitProfileService::UseLegacyProfiles() {
2042 bool legacyProfiles
= !!PR_GetEnv("MOZ_LEGACY_PROFILES");
2044 legacyProfiles
|= PolicyCheckBoolean(L
"LegacyProfiles");
2046 return legacyProfiles
;
2049 struct FindInstallsClosure
{
2050 nsINIParser
* installData
;
2051 nsTArray
<nsCString
>* installs
;
2054 static bool FindInstalls(const char* aSection
, void* aClosure
) {
2055 FindInstallsClosure
* closure
= static_cast<FindInstallsClosure
*>(aClosure
);
2057 // Check if the section starts with "Install"
2058 if (strncmp(aSection
, INSTALL_PREFIX
, INSTALL_PREFIX_LENGTH
) != 0) {
2062 nsCString
install(aSection
);
2063 closure
->installs
->AppendElement(install
);
2068 nsTArray
<nsCString
> nsToolkitProfileService::GetKnownInstalls() {
2069 nsTArray
<nsCString
> result
;
2070 FindInstallsClosure closure
= {&mProfileDB
, &result
};
2072 mProfileDB
.GetSections(&FindInstalls
, &closure
);
2077 nsresult
nsToolkitProfileService::CreateTimesInternal(nsIFile
* aProfileDir
) {
2078 nsresult rv
= NS_ERROR_FAILURE
;
2079 nsCOMPtr
<nsIFile
> creationLog
;
2080 rv
= aProfileDir
->Clone(getter_AddRefs(creationLog
));
2081 NS_ENSURE_SUCCESS(rv
, rv
);
2083 rv
= creationLog
->AppendNative("times.json"_ns
);
2084 NS_ENSURE_SUCCESS(rv
, rv
);
2086 bool exists
= false;
2087 creationLog
->Exists(&exists
);
2092 rv
= creationLog
->Create(nsIFile::NORMAL_FILE_TYPE
, 0700);
2093 NS_ENSURE_SUCCESS(rv
, rv
);
2095 // We don't care about microsecond resolution.
2096 int64_t msec
= PR_Now() / PR_USEC_PER_MSEC
;
2099 PRFileDesc
* writeFile
;
2100 rv
= creationLog
->OpenNSPRFileDesc(PR_WRONLY
, 0700, &writeFile
);
2101 NS_ENSURE_SUCCESS(rv
, rv
);
2103 PR_fprintf(writeFile
, "{\n\"created\": %lld,\n\"firstUse\": null\n}\n", msec
);
2104 PR_Close(writeFile
);
2109 nsToolkitProfileService::GetProfileCount(uint32_t* aResult
) {
2111 for (nsToolkitProfile
* profile
: mProfiles
) {
2120 nsToolkitProfileService::Flush() {
2121 if (GetIsListOutdated()) {
2122 return NS_ERROR_DATABASE_CHANGED
;
2127 // If we aren't using dedicated profiles then nothing about the list of
2128 // installs can have changed, so no need to update the backup.
2129 if (mUseDedicatedProfile
) {
2130 // Export the installs to the backup.
2131 nsTArray
<nsCString
> installs
= GetKnownInstalls();
2133 if (!installs
.IsEmpty()) {
2137 for (uint32_t i
= 0; i
< installs
.Length(); i
++) {
2138 nsTArray
<UniquePtr
<KeyValue
>> strings
=
2139 GetSectionStrings(&mProfileDB
, installs
[i
].get());
2140 if (strings
.IsEmpty()) {
2144 // Strip "Install" from the start.
2145 const nsDependentCSubstring
& install
=
2146 Substring(installs
[i
], INSTALL_PREFIX_LENGTH
);
2147 data
.AppendPrintf("[%s]\n", PromiseFlatCString(install
).get());
2149 for (uint32_t j
= 0; j
< strings
.Length(); j
++) {
2150 data
.AppendPrintf("%s=%s\n", strings
[j
]->key
.get(),
2151 strings
[j
]->value
.get());
2158 rv
= mInstallDBFile
->OpenANSIFileDesc("w", &writeFile
);
2159 NS_ENSURE_SUCCESS(rv
, rv
);
2161 uint32_t length
= data
.Length();
2162 if (fwrite(data
.get(), sizeof(char), length
, writeFile
) != length
) {
2164 return NS_ERROR_UNEXPECTED
;
2169 rv
= mInstallDBFile
->Remove(false);
2170 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
) {
2176 rv
= mProfileDB
.WriteToFile(mProfileDBFile
);
2177 NS_ENSURE_SUCCESS(rv
, rv
);
2179 rv
= UpdateFileStats(mProfileDBFile
, &mProfileDBExists
,
2180 &mProfileDBModifiedTime
, &mProfileDBFileSize
);
2181 NS_ENSURE_SUCCESS(rv
, rv
);
2186 already_AddRefed
<nsToolkitProfileService
> NS_GetToolkitProfileService() {
2187 if (!nsToolkitProfileService::gService
) {
2188 nsToolkitProfileService::gService
= new nsToolkitProfileService();
2189 nsresult rv
= nsToolkitProfileService::gService
->Init();
2190 if (NS_FAILED(rv
)) {
2191 NS_ERROR("nsToolkitProfileService::Init failed!");
2192 delete nsToolkitProfileService::gService
;
2197 return do_AddRef(nsToolkitProfileService::gService
);
2200 nsresult
XRE_GetFileFromPath(const char* aPath
, nsIFile
** aResult
) {
2201 #if defined(XP_MACOSX)
2202 int32_t pathLen
= strlen(aPath
);
2203 if (pathLen
> MAXPATHLEN
) return NS_ERROR_INVALID_ARG
;
2205 CFURLRef fullPath
= CFURLCreateFromFileSystemRepresentation(
2206 nullptr, (const UInt8
*)aPath
, pathLen
, true);
2207 if (!fullPath
) return NS_ERROR_FAILURE
;
2209 nsCOMPtr
<nsIFile
> lf
;
2210 nsresult rv
= NS_NewNativeLocalFile(""_ns
, true, getter_AddRefs(lf
));
2211 if (NS_SUCCEEDED(rv
)) {
2212 nsCOMPtr
<nsILocalFileMac
> lfMac
= do_QueryInterface(lf
, &rv
);
2213 if (NS_SUCCEEDED(rv
)) {
2214 rv
= lfMac
->InitWithCFURL(fullPath
);
2215 if (NS_SUCCEEDED(rv
)) {
2220 CFRelease(fullPath
);
2223 #elif defined(XP_UNIX)
2224 char fullPath
[MAXPATHLEN
];
2226 if (!realpath(aPath
, fullPath
)) return NS_ERROR_FAILURE
;
2228 return NS_NewNativeLocalFile(nsDependentCString(fullPath
), true, aResult
);
2229 #elif defined(XP_WIN)
2230 WCHAR fullPath
[MAXPATHLEN
];
2232 if (!_wfullpath(fullPath
, NS_ConvertUTF8toUTF16(aPath
).get(), MAXPATHLEN
))
2233 return NS_ERROR_FAILURE
;
2235 return NS_NewLocalFile(nsDependentString(fullPath
), true, aResult
);
2238 # error Platform-specific logic needed here.