1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Documentation for libpref is in modules/libpref/docs/index.rst.
13 #include "SharedPrefMap.h"
15 #include "base/basictypes.h"
16 #include "MainThreadUtils.h"
17 #include "mozilla/AppShutdown.h"
18 #include "mozilla/ArenaAllocatorExtensions.h"
19 #include "mozilla/ArenaAllocator.h"
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/Attributes.h"
22 #include "mozilla/Components.h"
23 #include "mozilla/dom/PContent.h"
24 #include "mozilla/dom/Promise.h"
25 #include "mozilla/dom/RemoteType.h"
26 #include "mozilla/HashFunctions.h"
27 #include "mozilla/HashTable.h"
28 #include "mozilla/Logging.h"
29 #include "mozilla/Maybe.h"
30 #include "mozilla/MemoryReporting.h"
31 #include "mozilla/Omnijar.h"
32 #include "mozilla/Preferences.h"
33 #include "mozilla/ProfilerLabels.h"
34 #include "mozilla/ProfilerMarkers.h"
35 #include "mozilla/ResultExtensions.h"
36 #include "mozilla/SchedulerGroup.h"
37 #include "mozilla/ScopeExit.h"
38 #include "mozilla/ServoStyleSet.h"
39 #include "mozilla/SpinEventLoopUntil.h"
40 #include "mozilla/StaticMutex.h"
41 #include "mozilla/StaticPrefsAll.h"
42 #include "mozilla/StaticPtr.h"
43 #include "mozilla/SyncRunnable.h"
44 #include "mozilla/Telemetry.h"
45 #include "mozilla/TelemetryEventEnums.h"
46 #include "mozilla/Try.h"
47 #include "mozilla/UniquePtrExtensions.h"
48 #include "mozilla/URLPreloader.h"
49 #include "mozilla/Variant.h"
50 #include "mozilla/Vector.h"
51 #include "nsAppDirectoryServiceDefs.h"
52 #include "nsCategoryManagerUtils.h"
53 #include "nsClassHashtable.h"
54 #include "nsCOMArray.h"
56 #include "nsComponentManagerUtils.h"
57 #include "nsContentUtils.h"
59 #include "nsTHashMap.h"
60 #include "nsDirectoryServiceDefs.h"
61 #include "nsIConsoleService.h"
63 #include "nsIMemoryReporter.h"
64 #include "nsIObserver.h"
65 #include "nsIObserverService.h"
66 #include "nsIOutputStream.h"
67 #include "nsIPrefBranch.h"
68 #include "nsIPrefLocalizedString.h"
69 #include "nsIRelativeFilePref.h"
70 #include "nsISafeOutputStream.h"
71 #include "nsISimpleEnumerator.h"
72 #include "nsIStringBundle.h"
73 #include "nsISupportsImpl.h"
74 #include "nsISupportsPrimitives.h"
75 #include "nsIZipReader.h"
76 #include "nsNetUtil.h"
77 #include "nsPrintfCString.h"
78 #include "nsProxyRelease.h"
79 #include "nsReadableUtils.h"
80 #include "nsRefPtrHashtable.h"
81 #include "nsRelativeFilePref.h"
84 #include "nsThreadUtils.h"
85 #include "nsUTF8Utils.h"
86 #include "nsWeakReference.h"
87 #include "nsXPCOMCID.h"
89 #include "nsXULAppAPI.h"
90 #include "nsZipArchive.h"
92 #include "PLDHashTable.h"
95 #include "xpcpublic.h"
96 #include "js/RootingAPI.h"
97 #ifdef MOZ_BACKGROUNDTASKS
98 # include "mozilla/BackgroundTasks.h"
106 # include "mozmemory.h"
110 # include "windows.h"
113 #if defined(MOZ_WIDGET_GTK)
114 # include "mozilla/WidgetUtilsGtk.h"
115 #endif // defined(MOZ_WIDGET_GTK)
117 #ifdef MOZ_WIDGET_COCOA
118 # include "ChannelPrefsUtil.h"
121 using namespace mozilla
;
124 using ipc::FileDescriptor
;
128 # define ENSURE_PARENT_PROCESS(func, pref) \
130 if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
131 nsPrintfCString msg( \
132 "ENSURE_PARENT_PROCESS: called %s on %s in a non-parent process", \
134 NS_ERROR(msg.get()); \
135 return NS_ERROR_NOT_AVAILABLE; \
141 # define ENSURE_PARENT_PROCESS(func, pref) \
142 if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
143 return NS_ERROR_NOT_AVAILABLE; \
148 // Forward declarations.
149 namespace mozilla::StaticPrefs
{
151 static void InitAll();
152 static void StartObservingAlwaysPrefs();
153 static void InitOncePrefs();
154 static void InitStaticPrefsFromShared();
155 static void RegisterOncePrefs(SharedPrefMapBuilder
& aBuilder
);
156 static void ShutdownAlwaysPrefs();
158 } // namespace mozilla::StaticPrefs
160 //===========================================================================
161 // Low-level types and operations
162 //===========================================================================
164 Atomic
<bool, mozilla::Relaxed
> sPrefTelemetryEventEnabled(false);
166 typedef nsTArray
<nsCString
> PrefSaveData
;
168 // 1 MB should be enough for everyone.
169 static const uint32_t MAX_PREF_LENGTH
= 1 * 1024 * 1024;
170 // Actually, 4kb should be enough for everyone.
171 static const uint32_t MAX_ADVISABLE_PREF_LENGTH
= 4 * 1024;
173 // This is used for pref names and string pref values. We encode the string
174 // length, then a '/', then the string chars. This encoding means there are no
175 // special chars that are forbidden or require escaping.
176 static void SerializeAndAppendString(const nsCString
& aChars
, nsCString
& aStr
) {
177 aStr
.AppendInt(uint64_t(aChars
.Length()));
182 static char* DeserializeString(char* aChars
, nsCString
& aStr
) {
184 uint32_t length
= strtol(p
, &p
, 10);
185 MOZ_ASSERT(p
[0] == '/');
186 p
++; // move past the '/'
187 aStr
.Assign(p
, length
);
188 p
+= length
; // move past the string itself
192 // Keep this in sync with PrefValue in parser/src/lib.rs.
194 // PrefValues within Pref objects own their chars. PrefValues passed around
195 // as arguments don't own their chars.
196 const char* mStringVal
;
200 PrefValue() = default;
202 explicit PrefValue(bool aVal
) : mBoolVal(aVal
) {}
204 explicit PrefValue(int32_t aVal
) : mIntVal(aVal
) {}
206 explicit PrefValue(const char* aVal
) : mStringVal(aVal
) {}
208 bool Equals(PrefType aType
, PrefValue aValue
) {
210 case PrefType::String
: {
211 if (mStringVal
&& aValue
.mStringVal
) {
212 return strcmp(mStringVal
, aValue
.mStringVal
) == 0;
214 if (!mStringVal
&& !aValue
.mStringVal
) {
221 return mIntVal
== aValue
.mIntVal
;
224 return mBoolVal
== aValue
.mBoolVal
;
227 MOZ_CRASH("Unhandled enum value");
231 template <typename T
>
234 void Init(PrefType aNewType
, PrefValue aNewValue
) {
235 if (aNewType
== PrefType::String
) {
236 MOZ_ASSERT(aNewValue
.mStringVal
);
237 aNewValue
.mStringVal
= moz_xstrdup(aNewValue
.mStringVal
);
242 void Clear(PrefType aType
) {
243 if (aType
== PrefType::String
) {
244 free(const_cast<char*>(mStringVal
));
247 // Zero the entire value (regardless of type) via mStringVal.
248 mStringVal
= nullptr;
251 void Replace(bool aHasValue
, PrefType aOldType
, PrefType aNewType
,
252 PrefValue aNewValue
) {
256 Init(aNewType
, aNewValue
);
259 void ToDomPrefValue(PrefType aType
, dom::PrefValue
* aDomValue
) {
261 case PrefType::String
:
262 *aDomValue
= nsDependentCString(mStringVal
);
266 *aDomValue
= mIntVal
;
270 *aDomValue
= mBoolVal
;
278 PrefType
FromDomPrefValue(const dom::PrefValue
& aDomValue
) {
279 switch (aDomValue
.type()) {
280 case dom::PrefValue::TnsCString
:
281 mStringVal
= aDomValue
.get_nsCString().get();
282 return PrefType::String
;
284 case dom::PrefValue::Tint32_t
:
285 mIntVal
= aDomValue
.get_int32_t();
286 return PrefType::Int
;
288 case dom::PrefValue::Tbool
:
289 mBoolVal
= aDomValue
.get_bool();
290 return PrefType::Bool
;
297 void SerializeAndAppend(PrefType aType
, nsCString
& aStr
) {
300 aStr
.Append(mBoolVal
? 'T' : 'F');
304 aStr
.AppendInt(mIntVal
);
307 case PrefType::String
: {
308 SerializeAndAppendString(nsDependentCString(mStringVal
), aStr
);
318 void ToString(PrefType aType
, nsCString
& aStr
) {
321 aStr
.Append(mBoolVal
? "true" : "false");
325 aStr
.AppendInt(mIntVal
);
328 case PrefType::String
: {
329 aStr
.Append(nsDependentCString(mStringVal
));
338 static char* Deserialize(PrefType aType
, char* aStr
,
339 Maybe
<dom::PrefValue
>* aDomValue
) {
345 *aDomValue
= Some(true);
346 } else if (*p
== 'F') {
347 *aDomValue
= Some(false);
349 *aDomValue
= Some(false);
350 NS_ERROR("bad bool pref value");
355 case PrefType::Int
: {
356 *aDomValue
= Some(int32_t(strtol(p
, &p
, 10)));
360 case PrefType::String
: {
362 p
= DeserializeString(p
, str
);
363 *aDomValue
= Some(str
);
374 bool PrefValue::Get() const {
379 int32_t PrefValue::Get() const {
384 nsDependentCString
PrefValue::Get() const {
385 return nsDependentCString(mStringVal
);
389 const char* PrefTypeToString(PrefType aType
) {
393 case PrefType::String
:
400 MOZ_CRASH("Unhandled enum value");
405 // Assign to aResult a quoted, escaped copy of aOriginal.
406 static void StrEscape(const char* aOriginal
, nsCString
& aResult
) {
407 if (aOriginal
== nullptr) {
408 aResult
.AssignLiteral("\"\"");
412 // JavaScript does not allow quotes, slashes, or line terminators inside
413 // strings so we must escape them. ECMAScript defines four line terminators,
414 // but we're only worrying about \r and \n here. We currently feed our pref
415 // script to the JS interpreter as Latin-1 so we won't encounter \u2028
416 // (line separator) or \u2029 (paragraph separator).
418 // WARNING: There are hints that we may be moving to storing prefs as utf8.
419 // If we ever feed them to the JS compiler as UTF8 then we'll have to worry
420 // about the multibyte sequences that would be interpreted as \u2028 and
426 // Paranoid worst case all slashes will free quickly.
427 for (p
= aOriginal
; *p
; ++p
) {
430 aResult
.AppendLiteral("\\n");
434 aResult
.AppendLiteral("\\r");
438 aResult
.AppendLiteral("\\\\");
442 aResult
.AppendLiteral("\\\"");
454 // Mimic the behaviour of nsTStringRepr::ToFloat before bug 840706 to preserve
455 // error case handling for parsing pref strings. Many callers do not check error
456 // codes, so the returned values may be used even if an error is set.
458 // This method should never return NaN, but may return +-inf if the provided
459 // number is too large to fit in a float.
460 static float ParsePrefFloat(const nsCString
& aString
, nsresult
* aError
) {
461 if (aString
.IsEmpty()) {
462 *aError
= NS_ERROR_ILLEGAL_VALUE
;
466 // PR_strtod does a locale-independent conversion.
467 char* stopped
= nullptr;
468 float result
= PR_strtod(aString
.get(), &stopped
);
470 // Defensively avoid potential breakage caused by returning NaN into
471 // unsuspecting code. AFAIK this should never happen as PR_strtod cannot
472 // return NaN as currently configured.
473 if (std::isnan(result
)) {
474 MOZ_ASSERT_UNREACHABLE("PR_strtod shouldn't return NaN");
475 *aError
= NS_ERROR_ILLEGAL_VALUE
;
479 *aError
= (stopped
== aString
.EndReading()) ? NS_OK
: NS_ERROR_ILLEGAL_VALUE
;
483 struct PreferenceMarker
{
484 static constexpr Span
<const char> MarkerTypeName() {
485 return MakeStringSpan("Preference");
487 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter
& aWriter
,
488 const ProfilerString8View
& aPrefName
,
489 const Maybe
<PrefValueKind
>& aPrefKind
,
491 const ProfilerString8View
& aPrefValue
) {
492 aWriter
.StringProperty("prefName", aPrefName
);
493 aWriter
.StringProperty("prefKind", PrefValueKindToString(aPrefKind
));
494 aWriter
.StringProperty("prefType", PrefTypeToString(aPrefType
));
495 aWriter
.StringProperty("prefValue", aPrefValue
);
497 static MarkerSchema
MarkerTypeDisplay() {
498 using MS
= MarkerSchema
;
499 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
};
500 schema
.AddKeyLabelFormatSearchable("prefName", "Name", MS::Format::String
,
501 MS::Searchable::Searchable
);
502 schema
.AddKeyLabelFormat("prefKind", "Kind", MS::Format::String
);
503 schema
.AddKeyLabelFormat("prefType", "Type", MS::Format::String
);
504 schema
.AddKeyLabelFormat("prefValue", "Value", MS::Format::String
);
505 schema
.SetTableLabel(
506 "{marker.name} — {marker.data.prefName}: {marker.data.prefValue} "
507 "({marker.data.prefType})");
512 static Span
<const char> PrefValueKindToString(
513 const Maybe
<PrefValueKind
>& aKind
) {
515 return *aKind
== PrefValueKind::Default
? MakeStringSpan("Default")
516 : MakeStringSpan("User");
521 static Span
<const char> PrefTypeToString(PrefType type
) {
529 case PrefType::String
:
532 MOZ_ASSERT_UNREACHABLE("Unknown preference type.");
546 mCallbacksObjects(0),
547 mCallbacksDomains(0),
552 size_t mStringValues
;
553 size_t mRootBranches
;
554 size_t mPrefNameArena
;
555 size_t mCallbacksObjects
;
556 size_t mCallbacksDomains
;
559 } // namespace mozilla
561 static StaticRefPtr
<SharedPrefMap
> gSharedMap
;
563 // Arena for Pref names.
564 // Never access sPrefNameArena directly, always use PrefNameArena()
565 // because it must only be accessed on the Main Thread
566 typedef ArenaAllocator
<4096, 1> NameArena
;
567 static NameArena
* sPrefNameArena
;
569 static inline NameArena
& PrefNameArena() {
570 MOZ_ASSERT(NS_IsMainThread());
572 if (!sPrefNameArena
) {
573 sPrefNameArena
= new NameArena();
575 return *sPrefNameArena
;
580 // Three forward declarations for immediately below
582 static bool IsPreferenceSanitized(const Pref
* const aPref
);
583 static bool ShouldSanitizePreference(const Pref
* const aPref
);
585 // Note that this never changes in the parent process, and is only read in
586 // content processes.
587 static bool gContentProcessPrefsAreInited
= false;
591 explicit Pref(const nsACString
& aName
)
592 : mName(ArenaStrdup(aName
, PrefNameArena()), aName
.Length()),
593 mType(static_cast<uint32_t>(PrefType::None
)),
597 mHasDefaultValue(false),
598 mHasUserValue(false),
599 mIsSkippedByIteration(false),
604 // There's no need to free mName because it's allocated in memory owned by
607 mDefaultValue
.Clear(Type());
608 mUserValue
.Clear(Type());
611 const char* Name() const { return mName
.get(); }
612 const nsDependentCString
& NameString() const { return mName
; }
616 PrefType
Type() const { return static_cast<PrefType
>(mType
); }
617 void SetType(PrefType aType
) { mType
= static_cast<uint32_t>(aType
); }
619 bool IsType(PrefType aType
) const { return Type() == aType
; }
620 bool IsTypeNone() const { return IsType(PrefType::None
); }
621 bool IsTypeString() const { return IsType(PrefType::String
); }
622 bool IsTypeInt() const { return IsType(PrefType::Int
); }
623 bool IsTypeBool() const { return IsType(PrefType::Bool
); }
627 bool IsLocked() const { return mIsLocked
; }
628 void SetIsLocked(bool aValue
) { mIsLocked
= aValue
; }
629 bool IsSkippedByIteration() const { return mIsSkippedByIteration
; }
630 void SetIsSkippedByIteration(bool aValue
) { mIsSkippedByIteration
= aValue
; }
632 bool IsSticky() const { return mIsSticky
; }
634 bool IsSanitized() const { return mIsSanitized
; }
636 bool HasDefaultValue() const { return mHasDefaultValue
; }
637 bool HasUserValue() const { return mHasUserValue
; }
639 template <typename T
>
640 void AddToMap(SharedPrefMapBuilder
& aMap
) {
641 // Sanitized preferences should never be added to the shared pref map
642 MOZ_ASSERT(!ShouldSanitizePreference(this));
643 aMap
.Add(NameString(),
644 {HasDefaultValue(), HasUserValue(), IsSticky(), IsLocked(),
645 /* isSanitized */ false, IsSkippedByIteration()},
646 HasDefaultValue() ? mDefaultValue
.Get
<T
>() : T(),
647 HasUserValue() ? mUserValue
.Get
<T
>() : T());
650 void AddToMap(SharedPrefMapBuilder
& aMap
) {
652 AddToMap
<bool>(aMap
);
653 } else if (IsTypeInt()) {
654 AddToMap
<int32_t>(aMap
);
655 } else if (IsTypeString()) {
656 AddToMap
<nsDependentCString
>(aMap
);
658 MOZ_ASSERT_UNREACHABLE("Unexpected preference type");
664 #define CHECK_SANITIZATION() \
665 if (IsPreferenceSanitized(this)) { \
666 if (!sPrefTelemetryEventEnabled.exchange(true)) { \
667 sPrefTelemetryEventEnabled = true; \
668 Telemetry::SetEventRecordingEnabled("security"_ns, true); \
670 Telemetry::RecordEvent( \
671 Telemetry::EventID::Security_Prefusage_Contentprocess, \
672 mozilla::Some(Name()), mozilla::Nothing()); \
673 if (sCrashOnBlocklistedPref) { \
674 MOZ_CRASH_UNSAFE_PRINTF( \
675 "Should not access the preference '%s' in the Content Processes", \
680 bool GetBoolValue(PrefValueKind aKind
= PrefValueKind::User
) const {
681 MOZ_ASSERT(IsTypeBool());
682 MOZ_ASSERT(aKind
== PrefValueKind::Default
? HasDefaultValue()
685 CHECK_SANITIZATION();
687 return aKind
== PrefValueKind::Default
? mDefaultValue
.mBoolVal
688 : mUserValue
.mBoolVal
;
691 int32_t GetIntValue(PrefValueKind aKind
= PrefValueKind::User
) const {
692 MOZ_ASSERT(IsTypeInt());
693 MOZ_ASSERT(aKind
== PrefValueKind::Default
? HasDefaultValue()
696 CHECK_SANITIZATION();
698 return aKind
== PrefValueKind::Default
? mDefaultValue
.mIntVal
699 : mUserValue
.mIntVal
;
702 const char* GetBareStringValue(
703 PrefValueKind aKind
= PrefValueKind::User
) const {
704 MOZ_ASSERT(IsTypeString());
705 MOZ_ASSERT(aKind
== PrefValueKind::Default
? HasDefaultValue()
708 CHECK_SANITIZATION();
710 return aKind
== PrefValueKind::Default
? mDefaultValue
.mStringVal
711 : mUserValue
.mStringVal
;
714 #undef CHECK_SANITIZATION
716 nsDependentCString
GetStringValue(
717 PrefValueKind aKind
= PrefValueKind::User
) const {
718 return nsDependentCString(GetBareStringValue(aKind
));
721 void ToDomPref(dom::Pref
* aDomPref
, bool aIsDestinationWebContentProcess
) {
722 MOZ_ASSERT(XRE_IsParentProcess());
724 aDomPref
->name() = mName
;
726 aDomPref
->isLocked() = mIsLocked
;
728 aDomPref
->isSanitized() =
729 aIsDestinationWebContentProcess
&& ShouldSanitizePreference(this);
731 if (mHasDefaultValue
) {
732 aDomPref
->defaultValue() = Some(dom::PrefValue());
733 mDefaultValue
.ToDomPrefValue(Type(), &aDomPref
->defaultValue().ref());
735 aDomPref
->defaultValue() = Nothing();
739 !(aDomPref
->isSanitized() && sOmitBlocklistedPrefValues
)) {
740 aDomPref
->userValue() = Some(dom::PrefValue());
741 mUserValue
.ToDomPrefValue(Type(), &aDomPref
->userValue().ref());
743 aDomPref
->userValue() = Nothing();
746 MOZ_ASSERT(aDomPref
->defaultValue().isNothing() ||
747 aDomPref
->userValue().isNothing() ||
748 (mIsSanitized
&& sOmitBlocklistedPrefValues
) ||
749 (aDomPref
->defaultValue().ref().type() ==
750 aDomPref
->userValue().ref().type()));
753 void FromDomPref(const dom::Pref
& aDomPref
, bool* aValueChanged
) {
754 MOZ_ASSERT(!XRE_IsParentProcess());
755 MOZ_ASSERT(mName
== aDomPref
.name());
757 mIsLocked
= aDomPref
.isLocked();
758 mIsSanitized
= aDomPref
.isSanitized();
760 const Maybe
<dom::PrefValue
>& defaultValue
= aDomPref
.defaultValue();
761 bool defaultValueChanged
= false;
762 if (defaultValue
.isSome()) {
764 PrefType type
= value
.FromDomPrefValue(defaultValue
.ref());
765 if (!ValueMatches(PrefValueKind::Default
, type
, value
)) {
766 // Type() is PrefType::None if it's a newly added pref. This is ok.
767 mDefaultValue
.Replace(mHasDefaultValue
, Type(), type
, value
);
769 mHasDefaultValue
= true;
770 defaultValueChanged
= true;
773 // Note: we never clear a default value.
775 const Maybe
<dom::PrefValue
>& userValue
= aDomPref
.userValue();
776 bool userValueChanged
= false;
777 if (userValue
.isSome()) {
779 PrefType type
= value
.FromDomPrefValue(userValue
.ref());
780 if (!ValueMatches(PrefValueKind::User
, type
, value
)) {
781 // Type() is PrefType::None if it's a newly added pref. This is ok.
782 mUserValue
.Replace(mHasUserValue
, Type(), type
, value
);
784 mHasUserValue
= true;
785 userValueChanged
= true;
787 } else if (mHasUserValue
) {
789 userValueChanged
= true;
792 if (userValueChanged
|| (defaultValueChanged
&& !mHasUserValue
)) {
793 *aValueChanged
= true;
797 void FromWrapper(PrefWrapper
& aWrapper
);
799 bool HasAdvisablySizedValues() {
800 MOZ_ASSERT(XRE_IsParentProcess());
802 if (!IsTypeString()) {
806 if (mHasDefaultValue
&&
807 strlen(mDefaultValue
.mStringVal
) > MAX_ADVISABLE_PREF_LENGTH
) {
812 strlen(mUserValue
.mStringVal
) > MAX_ADVISABLE_PREF_LENGTH
) {
820 bool ValueMatches(PrefValueKind aKind
, PrefType aType
, PrefValue aValue
) {
821 return IsType(aType
) &&
822 (aKind
== PrefValueKind::Default
823 ? mHasDefaultValue
&& mDefaultValue
.Equals(aType
, aValue
)
824 : mHasUserValue
&& mUserValue
.Equals(aType
, aValue
));
828 void ClearUserValue() {
829 mUserValue
.Clear(Type());
830 mHasUserValue
= false;
833 nsresult
SetDefaultValue(PrefType aType
, PrefValue aValue
, bool aIsSticky
,
834 bool aIsLocked
, bool* aValueChanged
) {
835 // Types must always match when setting the default value.
836 if (!IsType(aType
)) {
837 return NS_ERROR_UNEXPECTED
;
840 // Should we set the default value? Only if the pref is not locked, and
841 // doing so would change the default value.
846 if (!ValueMatches(PrefValueKind::Default
, aType
, aValue
)) {
847 mDefaultValue
.Replace(mHasDefaultValue
, Type(), aType
, aValue
);
848 mHasDefaultValue
= true;
852 if (!mHasUserValue
) {
853 *aValueChanged
= true;
855 // What if we change the default to be the same as the user value?
856 // Should we clear the user value? Currently we don't.
862 nsresult
SetUserValue(PrefType aType
, PrefValue aValue
, bool aFromInit
,
863 bool* aValueChanged
) {
864 // If we have a default value, types must match when setting the user
866 if (mHasDefaultValue
&& !IsType(aType
)) {
867 return NS_ERROR_UNEXPECTED
;
870 // Should we clear the user value, if present? Only if the new user value
871 // matches the default value, and the pref isn't sticky, and we aren't
872 // force-setting it during initialization.
873 if (ValueMatches(PrefValueKind::Default
, aType
, aValue
) && !mIsSticky
&&
878 *aValueChanged
= true;
882 // Otherwise, should we set the user value? Only if doing so would
883 // change the user value.
884 } else if (!ValueMatches(PrefValueKind::User
, aType
, aValue
)) {
885 mUserValue
.Replace(mHasUserValue
, Type(), aType
, aValue
);
886 SetType(aType
); // needed because we may have changed the type
887 mHasUserValue
= true;
889 *aValueChanged
= true;
895 // Prefs are serialized in a manner that mirrors dom::Pref. The two should be
896 // kept in sync. E.g. if something is added to one it should also be added to
897 // the other. (It would be nice to be able to use the code generated from
898 // IPDL for serializing dom::Pref here instead of writing by hand this
899 // serialization/deserialization. Unfortunately, that generated code is
900 // difficult to use directly, outside of the IPDL IPC code.)
902 // The grammar for the serialized prefs has the following form.
904 // <pref> = <type> <locked> <sanitized> ':' <name> ':' <value>? ':'
906 // <type> = 'B' | 'I' | 'S'
907 // <locked> = 'L' | '-'
908 // <sanitized> = 'S' | '-'
909 // <name> = <string-value>
910 // <value> = <bool-value> | <int-value> | <string-value>
911 // <bool-value> = 'T' | 'F'
912 // <int-value> = an integer literal accepted by strtol()
913 // <string-value> = <int-value> '/' <chars>
914 // <chars> = any char sequence of length dictated by the preceding
917 // No whitespace is tolerated between tokens. <type> must match the types of
920 // The serialization is text-based, rather than binary, for the following
923 // - The size difference wouldn't be much different between text-based and
924 // binary. Most of the space is for strings (pref names and string pref
925 // values), which would be the same in both styles. And other differences
926 // would be minimal, e.g. small integers are shorter in text but long
927 // integers are longer in text.
929 // - Likewise, speed differences should be negligible.
931 // - It's much easier to debug a text-based serialization. E.g. you can
932 // print it and inspect it easily in a debugger.
934 // Examples of unlocked boolean prefs:
935 // - "B--:8/my.bool1:F:T\n"
936 // - "B--:8/my.bool2:F:\n"
937 // - "B--:8/my.bool3::T\n"
939 // Examples of sanitized, unlocked boolean prefs:
940 // - "B-S:8/my.bool1:F:T\n"
941 // - "B-S:8/my.bool2:F:\n"
942 // - "B-S:8/my.bool3::T\n"
944 // Examples of locked integer prefs:
945 // - "IL-:7/my.int1:0:1\n"
946 // - "IL-:7/my.int2:123:\n"
947 // - "IL-:7/my.int3::-99\n"
949 // Examples of unlocked string prefs:
950 // - "S--:10/my.string1:3/abc:4/wxyz\n"
951 // - "S--:10/my.string2:5/1.234:\n"
952 // - "S--:10/my.string3::7/string!\n"
954 void SerializeAndAppend(nsCString
& aStr
, bool aSanitizeUserValue
) {
964 case PrefType::String
: {
974 aStr
.Append(mIsLocked
? 'L' : '-');
975 aStr
.Append(aSanitizeUserValue
? 'S' : '-');
978 SerializeAndAppendString(mName
, aStr
);
981 if (mHasDefaultValue
) {
982 mDefaultValue
.SerializeAndAppend(Type(), aStr
);
986 if (mHasUserValue
&& !(aSanitizeUserValue
&& sOmitBlocklistedPrefValues
)) {
987 mUserValue
.SerializeAndAppend(Type(), aStr
);
992 static char* Deserialize(char* aStr
, dom::Pref
* aDomPref
) {
998 type
= PrefType::Bool
;
999 } else if (*p
== 'I') {
1000 type
= PrefType::Int
;
1001 } else if (*p
== 'S') {
1002 type
= PrefType::String
;
1004 NS_ERROR("bad pref type");
1005 type
= PrefType::None
;
1007 p
++; // move past the type char
1013 } else if (*p
== '-') {
1016 NS_ERROR("bad pref locked status");
1019 p
++; // move past the isLocked char
1025 } else if (*p
== '-') {
1026 isSanitized
= false;
1028 NS_ERROR("bad pref sanitized status");
1029 isSanitized
= false;
1031 p
++; // move past the isSanitized char
1033 MOZ_ASSERT(*p
== ':');
1034 p
++; // move past the ':'
1038 p
= DeserializeString(p
, name
);
1040 MOZ_ASSERT(*p
== ':');
1041 p
++; // move past the ':' preceding the default value
1043 Maybe
<dom::PrefValue
> maybeDefaultValue
;
1045 dom::PrefValue defaultValue
;
1046 p
= PrefValue::Deserialize(type
, p
, &maybeDefaultValue
);
1049 MOZ_ASSERT(*p
== ':');
1050 p
++; // move past the ':' between the default and user values
1052 Maybe
<dom::PrefValue
> maybeUserValue
;
1054 dom::PrefValue userValue
;
1055 p
= PrefValue::Deserialize(type
, p
, &maybeUserValue
);
1058 MOZ_ASSERT(*p
== '\n');
1059 p
++; // move past the '\n' following the user value
1061 *aDomPref
= dom::Pref(name
, isLocked
, isSanitized
, maybeDefaultValue
,
1067 void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
, PrefsSizes
& aSizes
) {
1068 // Note: mName is allocated in sPrefNameArena, measured elsewhere.
1069 aSizes
.mPrefValues
+= aMallocSizeOf(this);
1070 if (IsTypeString()) {
1071 if (mHasDefaultValue
) {
1072 aSizes
.mStringValues
+= aMallocSizeOf(mDefaultValue
.mStringVal
);
1074 if (mHasUserValue
) {
1075 aSizes
.mStringValues
+= aMallocSizeOf(mUserValue
.mStringVal
);
1080 void RelocateName(NameArena
* aArena
) {
1081 mName
.Rebind(ArenaStrdup(mName
.get(), *aArena
), mName
.Length());
1085 nsDependentCString mName
; // allocated in sPrefNameArena
1088 uint32_t mIsSticky
: 1;
1089 uint32_t mIsLocked
: 1;
1090 uint32_t mIsSanitized
: 1;
1091 uint32_t mHasDefaultValue
: 1;
1092 uint32_t mHasUserValue
: 1;
1093 uint32_t mIsSkippedByIteration
: 1;
1095 PrefValue mDefaultValue
;
1096 PrefValue mUserValue
;
1100 using Key
= UniquePtr
<Pref
>;
1101 using Lookup
= const char*;
1103 static HashNumber
hash(const Lookup aLookup
) { return HashString(aLookup
); }
1105 static bool match(const Key
& aKey
, const Lookup aLookup
) {
1106 if (!aLookup
|| !aKey
->Name()) {
1110 return strcmp(aLookup
, aKey
->Name()) == 0;
1114 using PrefWrapperBase
= Variant
<Pref
*, SharedPrefMap::Pref
>;
1115 class MOZ_STACK_CLASS PrefWrapper
: public PrefWrapperBase
{
1116 using SharedPref
= const SharedPrefMap::Pref
;
1119 MOZ_IMPLICIT
PrefWrapper(Pref
* aPref
) : PrefWrapperBase(AsVariant(aPref
)) {}
1121 MOZ_IMPLICIT
PrefWrapper(const SharedPrefMap::Pref
& aPref
)
1122 : PrefWrapperBase(AsVariant(aPref
)) {}
1126 bool IsType(PrefType aType
) const { return Type() == aType
; }
1127 bool IsTypeNone() const { return IsType(PrefType::None
); }
1128 bool IsTypeString() const { return IsType(PrefType::String
); }
1129 bool IsTypeInt() const { return IsType(PrefType::Int
); }
1130 bool IsTypeBool() const { return IsType(PrefType::Bool
); }
1132 #define FORWARD(retType, method) \
1133 retType method() const { \
1135 retType operator()(const Pref* aPref) { return aPref->method(); } \
1136 retType operator()(SharedPref& aPref) { return aPref.method(); } \
1138 return match(Matcher()); \
1141 FORWARD(bool, IsLocked
)
1142 FORWARD(bool, IsSanitized
)
1143 FORWARD(bool, IsSticky
)
1144 FORWARD(bool, HasDefaultValue
)
1145 FORWARD(bool, HasUserValue
)
1146 FORWARD(const char*, Name
)
1147 FORWARD(nsCString
, NameString
)
1148 FORWARD(PrefType
, Type
)
1151 #define FORWARD(retType, method) \
1152 retType method(PrefValueKind aKind = PrefValueKind::User) const { \
1154 PrefValueKind mKind; \
1156 retType operator()(const Pref* aPref) { return aPref->method(mKind); } \
1157 retType operator()(SharedPref& aPref) { return aPref.method(mKind); } \
1159 return match(Matcher{aKind}); \
1162 FORWARD(bool, GetBoolValue
)
1163 FORWARD(int32_t, GetIntValue
)
1164 FORWARD(nsCString
, GetStringValue
)
1165 FORWARD(const char*, GetBareStringValue
)
1168 PrefValue
GetValue(PrefValueKind aKind
= PrefValueKind::User
) const {
1170 case PrefType::Bool
:
1171 return PrefValue
{GetBoolValue(aKind
)};
1173 return PrefValue
{GetIntValue(aKind
)};
1174 case PrefType::String
:
1175 return PrefValue
{GetBareStringValue(aKind
)};
1176 case PrefType::None
:
1177 // This check will be performed in the above functions; but for NoneType
1178 // we need to do it explicitly, then fall-through.
1179 if (IsPreferenceSanitized(Name())) {
1180 if (!sPrefTelemetryEventEnabled
.exchange(true)) {
1181 sPrefTelemetryEventEnabled
= true;
1182 Telemetry::SetEventRecordingEnabled("security"_ns
, true);
1185 Telemetry::RecordEvent(
1186 Telemetry::EventID::Security_Prefusage_Contentprocess
,
1187 mozilla::Some(Name()), mozilla::Nothing());
1189 if (sCrashOnBlocklistedPref
) {
1190 MOZ_CRASH_UNSAFE_PRINTF(
1191 "Should not access the preference '%s' in the Content "
1198 MOZ_ASSERT_UNREACHABLE("Unexpected pref type");
1203 Result
<PrefValueKind
, nsresult
> WantValueKind(PrefType aType
,
1204 PrefValueKind aKind
) const {
1205 // WantValueKind may short-circuit GetValue functions and cause them to
1206 // return early, before this check occurs in GetFooValue()
1207 if (this->is
<Pref
*>() && IsPreferenceSanitized(this->as
<Pref
*>())) {
1208 if (!sPrefTelemetryEventEnabled
.exchange(true)) {
1209 sPrefTelemetryEventEnabled
= true;
1210 Telemetry::SetEventRecordingEnabled("security"_ns
, true);
1213 Telemetry::RecordEvent(
1214 Telemetry::EventID::Security_Prefusage_Contentprocess
,
1215 mozilla::Some(Name()), mozilla::Nothing());
1217 if (sCrashOnBlocklistedPref
) {
1218 MOZ_CRASH_UNSAFE_PRINTF(
1219 "Should not access the preference '%s' in the Content Processes",
1222 } else if (!this->is
<Pref
*>()) {
1223 // While we could use Name() above, and avoid the Variant checks, it
1224 // would less efficient than needed and we can instead do a debug-only
1225 // assert here to limit the inefficientcy
1226 MOZ_ASSERT(!IsPreferenceSanitized(Name()),
1227 "We should never have a sanitized SharedPrefMap::Pref.");
1230 if (Type() != aType
) {
1231 return Err(NS_ERROR_UNEXPECTED
);
1234 if (aKind
== PrefValueKind::Default
|| IsLocked() || !HasUserValue()) {
1235 if (!HasDefaultValue()) {
1236 return Err(NS_ERROR_UNEXPECTED
);
1238 return PrefValueKind::Default
;
1240 return PrefValueKind::User
;
1243 nsresult
GetValue(PrefValueKind aKind
, bool* aResult
) const {
1245 MOZ_TRY_VAR(kind
, WantValueKind(PrefType::Bool
, aKind
));
1247 *aResult
= GetBoolValue(kind
);
1251 nsresult
GetValue(PrefValueKind aKind
, int32_t* aResult
) const {
1253 MOZ_TRY_VAR(kind
, WantValueKind(PrefType::Int
, aKind
));
1255 *aResult
= GetIntValue(kind
);
1259 nsresult
GetValue(PrefValueKind aKind
, uint32_t* aResult
) const {
1260 return GetValue(aKind
, reinterpret_cast<int32_t*>(aResult
));
1263 nsresult
GetValue(PrefValueKind aKind
, float* aResult
) const {
1264 nsAutoCString result
;
1265 nsresult rv
= GetValue(aKind
, result
);
1266 if (NS_SUCCEEDED(rv
)) {
1267 // ParsePrefFloat() does a locale-independent conversion.
1268 // FIXME: Other `GetValue` overloads don't clobber `aResult` on error.
1269 *aResult
= ParsePrefFloat(result
, &rv
);
1274 nsresult
GetValue(PrefValueKind aKind
, nsACString
& aResult
) const {
1276 MOZ_TRY_VAR(kind
, WantValueKind(PrefType::String
, aKind
));
1278 aResult
= GetStringValue(kind
);
1282 nsresult
GetValue(PrefValueKind aKind
, nsACString
* aResult
) const {
1283 return GetValue(aKind
, *aResult
);
1286 // Returns false if this pref doesn't have a user value worth saving.
1287 bool UserValueToStringForSaving(nsCString
& aStr
) {
1288 // Should we save the user value, if present? Only if it does not match the
1289 // default value, or it is sticky.
1290 if (HasUserValue() &&
1291 (!ValueMatches(PrefValueKind::Default
, Type(), GetValue()) ||
1293 if (IsTypeString()) {
1294 StrEscape(GetStringValue().get(), aStr
);
1296 } else if (IsTypeInt()) {
1297 aStr
.AppendInt(GetIntValue());
1299 } else if (IsTypeBool()) {
1300 aStr
= GetBoolValue() ? "true" : "false";
1305 // Do not save default prefs that haven't changed.
1309 bool Matches(PrefType aType
, PrefValueKind aKind
, PrefValue
& aValue
,
1310 bool aIsSticky
, bool aIsLocked
) const {
1311 return (ValueMatches(aKind
, aType
, aValue
) && aIsSticky
== IsSticky() &&
1312 aIsLocked
== IsLocked());
1315 bool ValueMatches(PrefValueKind aKind
, PrefType aType
,
1316 const PrefValue
& aValue
) const {
1317 if (!IsType(aType
)) {
1320 if (!(aKind
== PrefValueKind::Default
? HasDefaultValue()
1321 : HasUserValue())) {
1325 case PrefType::Bool
:
1326 return GetBoolValue(aKind
) == aValue
.mBoolVal
;
1328 return GetIntValue(aKind
) == aValue
.mIntVal
;
1329 case PrefType::String
:
1330 return strcmp(GetBareStringValue(aKind
), aValue
.mStringVal
) == 0;
1332 MOZ_ASSERT_UNREACHABLE("Unexpected preference type");
1338 void Pref::FromWrapper(PrefWrapper
& aWrapper
) {
1339 MOZ_ASSERT(aWrapper
.is
<SharedPrefMap::Pref
>());
1340 auto pref
= aWrapper
.as
<SharedPrefMap::Pref
>();
1342 MOZ_ASSERT(IsTypeNone());
1343 MOZ_ASSERT(mName
== pref
.NameString());
1345 mType
= uint32_t(pref
.Type());
1347 mIsLocked
= pref
.IsLocked();
1348 mIsSanitized
= pref
.IsSanitized();
1349 mIsSticky
= pref
.IsSticky();
1351 mHasDefaultValue
= pref
.HasDefaultValue();
1352 mHasUserValue
= pref
.HasUserValue();
1354 if (mHasDefaultValue
) {
1355 mDefaultValue
.Init(Type(), aWrapper
.GetValue(PrefValueKind::Default
));
1357 if (mHasUserValue
) {
1358 mUserValue
.Init(Type(), aWrapper
.GetValue(PrefValueKind::User
));
1362 class CallbackNode
{
1364 CallbackNode(const nsACString
& aDomain
, PrefChangedFunc aFunc
, void* aData
,
1365 Preferences::MatchKind aMatchKind
)
1366 : mDomain(AsVariant(nsCString(aDomain
))),
1369 mNextAndMatchKind(aMatchKind
) {}
1371 CallbackNode(const char* const* aDomains
, PrefChangedFunc aFunc
, void* aData
,
1372 Preferences::MatchKind aMatchKind
)
1373 : mDomain(AsVariant(aDomains
)),
1376 mNextAndMatchKind(aMatchKind
) {}
1378 // mDomain is a UniquePtr<>, so any uses of Domain() should only be temporary
1380 const Variant
<nsCString
, const char* const*>& Domain() const {
1384 PrefChangedFunc
Func() const { return mFunc
; }
1385 void ClearFunc() { mFunc
= nullptr; }
1387 void* Data() const { return mData
; }
1389 Preferences::MatchKind
MatchKind() const {
1390 return static_cast<Preferences::MatchKind
>(mNextAndMatchKind
&
1394 bool DomainIs(const nsACString
& aDomain
) const {
1395 return mDomain
.is
<nsCString
>() && mDomain
.as
<nsCString
>() == aDomain
;
1398 bool DomainIs(const char* const* aPrefs
) const {
1399 return mDomain
== AsVariant(aPrefs
);
1402 bool Matches(const nsACString
& aPrefName
) const {
1403 auto match
= [&](const nsACString
& aStr
) {
1404 return MatchKind() == Preferences::ExactMatch
1406 : StringBeginsWith(aPrefName
, aStr
);
1409 if (mDomain
.is
<nsCString
>()) {
1410 return match(mDomain
.as
<nsCString
>());
1412 for (const char* const* ptr
= mDomain
.as
<const char* const*>(); *ptr
;
1414 if (match(nsDependentCString(*ptr
))) {
1421 CallbackNode
* Next() const {
1422 return reinterpret_cast<CallbackNode
*>(mNextAndMatchKind
& kNextMask
);
1425 void SetNext(CallbackNode
* aNext
) {
1426 uintptr_t matchKind
= mNextAndMatchKind
& kMatchKindMask
;
1427 mNextAndMatchKind
= reinterpret_cast<uintptr_t>(aNext
);
1428 MOZ_ASSERT((mNextAndMatchKind
& kMatchKindMask
) == 0);
1429 mNextAndMatchKind
|= matchKind
;
1432 void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
, PrefsSizes
& aSizes
) {
1433 aSizes
.mCallbacksObjects
+= aMallocSizeOf(this);
1434 if (mDomain
.is
<nsCString
>()) {
1435 aSizes
.mCallbacksDomains
+=
1436 mDomain
.as
<nsCString
>().SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
1441 static const uintptr_t kMatchKindMask
= uintptr_t(0x1);
1442 static const uintptr_t kNextMask
= ~kMatchKindMask
;
1444 Variant
<nsCString
, const char* const*> mDomain
;
1446 // If someone attempts to remove the node from the callback list while
1447 // NotifyCallbacks() is running, |func| is set to nullptr. Such nodes will
1448 // be removed at the end of NotifyCallbacks().
1449 PrefChangedFunc mFunc
;
1452 // Conceptually this is two fields:
1453 // - CallbackNode* mNext;
1454 // - Preferences::MatchKind mMatchKind;
1455 // They are combined into a tagged pointer to save memory.
1456 uintptr_t mNextAndMatchKind
;
1459 using PrefsHashTable
= HashSet
<UniquePtr
<Pref
>, PrefHasher
>;
1461 // The main prefs hash table. Inside a function so we can assert it's only
1462 // accessed on the main thread. (That assertion can be avoided but only do so
1463 // with great care!)
1464 static inline PrefsHashTable
*& HashTable(bool aOffMainThread
= false) {
1465 MOZ_ASSERT(NS_IsMainThread() || ServoStyleSet::IsInServoTraversal());
1466 static PrefsHashTable
* sHashTable
= nullptr;
1471 // This defines the type used to store our `once` mirrors checker. We can't use
1472 // HashMap for now due to alignment restrictions when dealing with
1473 // std::function<void()> (see bug 1557617).
1474 typedef std::function
<void()> AntiFootgunCallback
;
1476 bool operator()(char const* a
, char const* b
) const {
1477 return std::strcmp(a
, b
) < 0;
1480 typedef std::map
<const char*, AntiFootgunCallback
, CompareStr
> AntiFootgunMap
;
1481 static StaticAutoPtr
<AntiFootgunMap
> gOnceStaticPrefsAntiFootgun
;
1484 // The callback list contains all the priority callbacks followed by the
1485 // non-priority callbacks. gLastPriorityNode records where the first part ends.
1486 static CallbackNode
* gFirstCallback
= nullptr;
1487 static CallbackNode
* gLastPriorityNode
= nullptr;
1490 # define ACCESS_COUNTS
1493 #ifdef ACCESS_COUNTS
1494 using AccessCountsHashTable
= nsTHashMap
<nsCStringHashKey
, uint32_t>;
1495 static StaticAutoPtr
<AccessCountsHashTable
> gAccessCounts
;
1497 static void AddAccessCount(const nsACString
& aPrefName
) {
1498 // FIXME: Servo reads preferences from background threads in unsafe ways (bug
1499 // 1474789), and triggers assertions here if we try to add usage count entries
1500 // from background threads.
1501 if (NS_IsMainThread()) {
1502 JS::AutoSuppressGCAnalysis nogc
; // Hash functions will not GC.
1503 uint32_t& count
= gAccessCounts
->LookupOrInsert(aPrefName
);
1508 static void AddAccessCount(const char* aPrefName
) {
1509 AddAccessCount(nsDependentCString(aPrefName
));
1512 static void MOZ_MAYBE_UNUSED
AddAccessCount(const nsACString
& aPrefName
) {}
1514 static void AddAccessCount(const char* aPrefName
) {}
1517 // These are only used during the call to NotifyCallbacks().
1518 static bool gCallbacksInProgress
= false;
1519 static bool gShouldCleanupDeadNodes
= false;
1521 class PrefsHashIter
{
1522 using Iterator
= decltype(HashTable()->modIter());
1523 using ElemType
= Pref
*;
1528 explicit PrefsHashIter(PrefsHashTable
* aTable
) : mIter(aTable
->modIter()) {}
1531 friend class PrefsHashIter
;
1533 PrefsHashIter
& mParent
;
1536 Elem(PrefsHashIter
& aIter
, bool aDone
) : mParent(aIter
), mDone(aDone
) {}
1538 Iterator
& Iter() { return mParent
.mIter
; }
1541 Elem
& operator*() { return *this; }
1547 return Iter().get().get();
1549 ElemType
get() const { return const_cast<Elem
*>(this)->get(); }
1551 ElemType
operator->() { return get(); }
1552 ElemType
operator->() const { return get(); }
1554 operator ElemType() { return get(); }
1556 void Remove() { Iter().remove(); }
1558 Elem
& operator++() {
1561 mDone
= Iter().done();
1565 bool operator!=(Elem
& other
) {
1566 return mDone
!= other
.mDone
|| this->get() != other
.get();
1570 Elem
begin() { return Elem(*this, mIter
.done()); }
1572 Elem
end() { return Elem(*this, true); }
1576 using Iterator
= decltype(HashTable()->iter());
1577 using ElemType
= PrefWrapper
;
1579 using HashElem
= PrefsHashIter::Elem
;
1580 using SharedElem
= SharedPrefMap::Pref
;
1582 using ElemTypeVariant
= Variant
<HashElem
, SharedElem
>;
1584 SharedPrefMap
* mSharedMap
;
1585 PrefsHashTable
* mHashTable
;
1586 PrefsHashIter mIter
;
1588 ElemTypeVariant mPos
;
1589 ElemTypeVariant mEnd
;
1591 Maybe
<PrefWrapper
> mEntry
;
1594 PrefsIter(PrefsHashTable
* aHashTable
, SharedPrefMap
* aSharedMap
)
1595 : mSharedMap(aSharedMap
),
1596 mHashTable(aHashTable
),
1598 mPos(AsVariant(mIter
.begin())),
1599 mEnd(AsVariant(mIter
.end())) {
1606 #define MATCH(type, ...) \
1610 type operator()(HashElem& pos) { \
1611 HashElem& end MOZ_MAYBE_UNUSED = mIter.mEnd.as<HashElem>(); \
1614 type operator()(SharedElem& pos) { \
1615 SharedElem& end MOZ_MAYBE_UNUSED = mIter.mEnd.as<SharedElem>(); \
1619 return mPos.match(Matcher{*this}); \
1622 bool Done() { MATCH(bool, return pos
== end
); }
1624 PrefWrapper
MakeEntry() { MATCH(PrefWrapper
, return PrefWrapper(pos
)); }
1634 return !Done() || NextIterator();
1637 bool NextIterator() {
1638 if (mPos
.is
<HashElem
>() && mSharedMap
) {
1639 mPos
= AsVariant(mSharedMap
->begin());
1640 mEnd
= AsVariant(mSharedMap
->end());
1646 bool IteratingBase() { return mPos
.is
<SharedElem
>(); }
1648 PrefWrapper
& Entry() {
1649 MOZ_ASSERT(!Done());
1651 if (!mEntry
.isSome()) {
1652 mEntry
.emplace(MakeEntry());
1654 return mEntry
.ref();
1659 friend class PrefsIter
;
1664 Elem(PrefsIter
& aIter
, bool aDone
) : mParent(aIter
), mDone(aDone
) {
1668 void Next() { mDone
= !mParent
.Next(); }
1670 void SkipDuplicates() {
1672 (mParent
.IteratingBase() ? mParent
.mHashTable
->has(ref().Name())
1673 : ref().IsTypeNone())) {
1679 Elem
& operator*() { return *this; }
1681 ElemType
& ref() { return mParent
.Entry(); }
1682 const ElemType
& ref() const { return const_cast<Elem
*>(this)->ref(); }
1684 ElemType
* operator->() { return &ref(); }
1685 const ElemType
* operator->() const { return &ref(); }
1687 operator ElemType() { return ref(); }
1689 Elem
& operator++() {
1696 bool operator!=(Elem
& other
) {
1697 if (mDone
!= other
.mDone
) {
1703 return &this->ref() != &other
.ref();
1707 Elem
begin() { return {*this, Done()}; }
1709 Elem
end() { return {*this, true}; }
1712 static Pref
* pref_HashTableLookup(const char* aPrefName
);
1714 static void NotifyCallbacks(const nsCString
& aPrefName
,
1715 const PrefWrapper
* aPref
= nullptr);
1717 static void NotifyCallbacks(const nsCString
& aPrefName
,
1718 const PrefWrapper
& aPref
) {
1719 NotifyCallbacks(aPrefName
, &aPref
);
1722 // The approximate number of preferences in the dynamic hashtable for the parent
1723 // and content processes, respectively. These numbers are used to determine the
1724 // initial size of the dynamic preference hashtables, and should be chosen to
1725 // avoid rehashing during normal usage. The actual number of preferences will,
1726 // or course, change over time, but these numbers only need to be within a
1727 // binary order of magnitude of the actual values to remain effective.
1729 // The number for the parent process should reflect the total number of
1730 // preferences in the database, since the parent process needs to initially
1731 // build a dynamic hashtable of the entire preference database. The number for
1732 // the child process should reflect the number of preferences which are likely
1733 // to change after the startup of the first content process, since content
1734 // processes only store changed preferences on top of a snapshot of the database
1735 // created at startup.
1737 // Note: The capacity of a hashtable doubles when its length reaches an exact
1738 // power of two. A table with an initial length of 64 is twice as large as one
1739 // with an initial length of 63. This is important in content processes, where
1740 // lookup speed is less critical and we pay the price of the additional overhead
1741 // for each content process. So the initial content length should generally be
1742 // *under* the next power-of-two larger than its expected length.
1743 constexpr size_t kHashTableInitialLengthParent
= 3000;
1744 constexpr size_t kHashTableInitialLengthContent
= 64;
1746 static PrefSaveData
pref_savePrefs() {
1747 MOZ_ASSERT(NS_IsMainThread());
1749 PrefSaveData
savedPrefs(HashTable()->count());
1751 for (auto& pref
: PrefsIter(HashTable(), gSharedMap
)) {
1752 nsAutoCString prefValueStr
;
1753 if (!pref
->UserValueToStringForSaving(prefValueStr
)) {
1757 nsAutoCString prefNameStr
;
1758 StrEscape(pref
->Name(), prefNameStr
);
1760 nsPrintfCString
str("user_pref(%s, %s);", prefNameStr
.get(),
1761 prefValueStr
.get());
1763 savedPrefs
.AppendElement(str
);
1769 static Pref
* pref_HashTableLookup(const char* aPrefName
) {
1770 MOZ_ASSERT(NS_IsMainThread() || ServoStyleSet::IsInServoTraversal());
1772 MOZ_ASSERT_IF(!XRE_IsParentProcess(), gContentProcessPrefsAreInited
);
1774 // We use readonlyThreadsafeLookup() because we often have concurrent lookups
1775 // from multiple Stylo threads. This is safe because those threads cannot
1776 // modify sHashTable, and the main thread is blocked while Stylo threads are
1777 // doing these lookups.
1778 auto p
= HashTable()->readonlyThreadsafeLookup(aPrefName
);
1779 return p
? p
->get() : nullptr;
1782 // While notifying preference callbacks, this holds the wrapper for the
1783 // preference being notified, in order to optimize lookups.
1785 // Note: Callbacks and lookups only happen on the main thread, so this is safe
1786 // to use without locking.
1787 static const PrefWrapper
* gCallbackPref
;
1789 Maybe
<PrefWrapper
> pref_SharedLookup(const char* aPrefName
) {
1790 MOZ_DIAGNOSTIC_ASSERT(gSharedMap
, "gSharedMap must be initialized");
1791 if (Maybe
<SharedPrefMap::Pref
> pref
= gSharedMap
->Get(aPrefName
)) {
1797 Maybe
<PrefWrapper
> pref_Lookup(const char* aPrefName
,
1798 bool aIncludeTypeNone
= false) {
1799 MOZ_ASSERT(NS_IsMainThread() || ServoStyleSet::IsInServoTraversal());
1801 AddAccessCount(aPrefName
);
1803 if (gCallbackPref
&& strcmp(aPrefName
, gCallbackPref
->Name()) == 0) {
1804 return Some(*gCallbackPref
);
1806 if (Pref
* pref
= pref_HashTableLookup(aPrefName
)) {
1807 if (aIncludeTypeNone
|| !pref
->IsTypeNone() || pref
->IsSanitized()) {
1810 } else if (gSharedMap
) {
1811 return pref_SharedLookup(aPrefName
);
1817 static Result
<Pref
*, nsresult
> pref_LookupForModify(
1818 const nsCString
& aPrefName
,
1819 const std::function
<bool(const PrefWrapper
&)>& aCheckFn
) {
1820 Maybe
<PrefWrapper
> wrapper
=
1821 pref_Lookup(aPrefName
.get(), /* includeTypeNone */ true);
1822 if (wrapper
.isNothing()) {
1823 return Err(NS_ERROR_INVALID_ARG
);
1825 if (!aCheckFn(*wrapper
)) {
1828 if (wrapper
->is
<Pref
*>()) {
1829 return wrapper
->as
<Pref
*>();
1832 Pref
* pref
= new Pref(aPrefName
);
1833 if (!HashTable()->putNew(aPrefName
.get(), pref
)) {
1835 return Err(NS_ERROR_OUT_OF_MEMORY
);
1837 pref
->FromWrapper(*wrapper
);
1841 static nsresult
pref_SetPref(const nsCString
& aPrefName
, PrefType aType
,
1842 PrefValueKind aKind
, PrefValue aValue
,
1843 bool aIsSticky
, bool aIsLocked
, bool aFromInit
) {
1844 MOZ_ASSERT(XRE_IsParentProcess());
1845 MOZ_ASSERT(NS_IsMainThread());
1847 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads
)) {
1849 "pref_SetPref: Attempt to write pref %s after XPCOMShutdownThreads "
1852 if (nsContentUtils::IsInitialized()) {
1853 xpc_DumpJSStack(true, true, false);
1855 MOZ_ASSERT(false, "Late preference writes should be avoided.");
1856 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN
;
1860 return NS_ERROR_OUT_OF_MEMORY
;
1863 Pref
* pref
= nullptr;
1866 pref_LookupForModify(aPrefName
, [&](const PrefWrapper
& aWrapper
) {
1867 return !aWrapper
.Matches(aType
, aKind
, aValue
, aIsSticky
, aIsLocked
);
1869 if (result
.isOk() && !(pref
= result
.unwrap())) {
1870 // No changes required.
1876 auto p
= HashTable()->lookupForAdd(aPrefName
.get());
1878 pref
= new Pref(aPrefName
);
1879 pref
->SetType(aType
);
1880 if (!HashTable()->add(p
, pref
)) {
1882 return NS_ERROR_OUT_OF_MEMORY
;
1889 bool valueChanged
= false;
1891 if (aKind
== PrefValueKind::Default
) {
1892 rv
= pref
->SetDefaultValue(aType
, aValue
, aIsSticky
, aIsLocked
,
1895 MOZ_ASSERT(!aIsLocked
); // `locked` is disallowed in user pref files
1896 rv
= pref
->SetUserValue(aType
, aValue
, aFromInit
, &valueChanged
);
1898 if (NS_FAILED(rv
)) {
1900 nsPrintfCString("Rejected attempt to change type of pref %s's %s value "
1903 (aKind
== PrefValueKind::Default
) ? "default" : "user",
1904 PrefTypeToString(pref
->Type()), PrefTypeToString(aType
))
1911 if (!aFromInit
&& profiler_thread_is_being_profiled_for_markers()) {
1912 nsAutoCString value
;
1913 aValue
.ToString(aType
, value
);
1914 profiler_add_marker(
1915 "Preference Write", baseprofiler::category::OTHER_PreferenceRead
, {},
1916 PreferenceMarker
{}, aPrefName
, Some(aKind
), aType
, value
);
1919 if (aKind
== PrefValueKind::User
) {
1920 Preferences::HandleDirty();
1922 NotifyCallbacks(aPrefName
, PrefWrapper(pref
));
1928 // Removes |node| from callback list. Returns the node after the deleted one.
1929 static CallbackNode
* pref_RemoveCallbackNode(CallbackNode
* aNode
,
1930 CallbackNode
* aPrevNode
) {
1931 MOZ_ASSERT(!aPrevNode
|| aPrevNode
->Next() == aNode
);
1932 MOZ_ASSERT(aPrevNode
|| gFirstCallback
== aNode
);
1933 MOZ_ASSERT(!gCallbacksInProgress
);
1935 CallbackNode
* next_node
= aNode
->Next();
1937 aPrevNode
->SetNext(next_node
);
1939 gFirstCallback
= next_node
;
1941 if (gLastPriorityNode
== aNode
) {
1942 gLastPriorityNode
= aPrevNode
;
1948 static void NotifyCallbacks(const nsCString
& aPrefName
,
1949 const PrefWrapper
* aPref
) {
1950 bool reentered
= gCallbacksInProgress
;
1952 gCallbackPref
= aPref
;
1953 auto cleanup
= MakeScopeExit([]() { gCallbackPref
= nullptr; });
1955 // Nodes must not be deleted while gCallbacksInProgress is true.
1956 // Nodes that need to be deleted are marked for deletion by nulling
1957 // out the |func| pointer. We release them at the end of this function
1958 // if we haven't reentered.
1959 gCallbacksInProgress
= true;
1961 for (CallbackNode
* node
= gFirstCallback
; node
; node
= node
->Next()) {
1963 if (node
->Matches(aPrefName
)) {
1964 (node
->Func())(aPrefName
.get(), node
->Data());
1969 gCallbacksInProgress
= reentered
;
1971 if (gShouldCleanupDeadNodes
&& !gCallbacksInProgress
) {
1972 CallbackNode
* prev_node
= nullptr;
1973 CallbackNode
* node
= gFirstCallback
;
1976 if (!node
->Func()) {
1977 node
= pref_RemoveCallbackNode(node
, prev_node
);
1980 node
= node
->Next();
1983 gShouldCleanupDeadNodes
= false;
1987 if (XRE_IsParentProcess() &&
1988 !StaticPrefs::preferences_force_disable_check_once_policy() &&
1989 (StaticPrefs::preferences_check_once_policy() || xpc::IsInAutomation())) {
1990 // Check that we aren't modifying a `once`-mirrored pref using that pref
1991 // name. We have about 100 `once`-mirrored prefs. std::map performs a
1992 // search in O(log n), so this is fast enough.
1993 MOZ_ASSERT(gOnceStaticPrefsAntiFootgun
);
1994 auto search
= gOnceStaticPrefsAntiFootgun
->find(aPrefName
.get());
1995 if (search
!= gOnceStaticPrefsAntiFootgun
->end()) {
1996 // Run the callback.
2003 //===========================================================================
2005 //===========================================================================
2009 // Keep this in sync with PrefFn in parser/src/lib.rs.
2010 typedef void (*PrefsParserPrefFn
)(const char* aPrefName
, PrefType aType
,
2011 PrefValueKind aKind
, PrefValue aValue
,
2012 bool aIsSticky
, bool aIsLocked
);
2014 // Keep this in sync with ErrorFn in parser/src/lib.rs.
2016 // `aMsg` is just a borrow of the string, and must be copied if it is used
2017 // outside the lifetime of the prefs_parser_parse() call.
2018 typedef void (*PrefsParserErrorFn
)(const char* aMsg
);
2020 // Keep this in sync with prefs_parser_parse() in parser/src/lib.rs.
2021 bool prefs_parser_parse(const char* aPath
, PrefValueKind aKind
,
2022 const char* aBuf
, size_t aLen
,
2023 PrefsParserPrefFn aPrefFn
, PrefsParserErrorFn aErrorFn
);
2029 ~Parser() = default;
2031 bool Parse(PrefValueKind aKind
, const char* aPath
, const nsCString
& aBuf
) {
2032 MOZ_ASSERT(XRE_IsParentProcess());
2033 return prefs_parser_parse(aPath
, aKind
, aBuf
.get(), aBuf
.Length(),
2034 HandlePref
, HandleError
);
2038 static void HandlePref(const char* aPrefName
, PrefType aType
,
2039 PrefValueKind aKind
, PrefValue aValue
, bool aIsSticky
,
2041 MOZ_ASSERT(XRE_IsParentProcess());
2042 pref_SetPref(nsDependentCString(aPrefName
), aType
, aKind
, aValue
, aIsSticky
,
2044 /* fromInit */ true);
2047 static void HandleError(const char* aMsg
) {
2049 nsCOMPtr
<nsIConsoleService
> console
=
2050 do_GetService("@mozilla.org/consoleservice;1", &rv
);
2051 if (NS_SUCCEEDED(rv
)) {
2052 console
->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg
).get());
2057 printf_stderr("%s\n", aMsg
);
2062 // The following code is test code for the gtest.
2064 static void TestParseErrorHandlePref(const char* aPrefName
, PrefType aType
,
2065 PrefValueKind aKind
, PrefValue aValue
,
2066 bool aIsSticky
, bool aIsLocked
) {}
2068 static nsCString gTestParseErrorMsgs
;
2070 static void TestParseErrorHandleError(const char* aMsg
) {
2071 gTestParseErrorMsgs
.Append(aMsg
);
2072 gTestParseErrorMsgs
.Append('\n');
2075 // Keep this in sync with the declaration in test/gtest/Parser.cpp.
2076 void TestParseError(PrefValueKind aKind
, const char* aText
,
2077 nsCString
& aErrorMsg
) {
2078 prefs_parser_parse("test", aKind
, aText
, strlen(aText
),
2079 TestParseErrorHandlePref
, TestParseErrorHandleError
);
2081 // Copy the error messages into the outparam, then clear them from
2082 // gTestParseErrorMsgs.
2083 aErrorMsg
.Assign(gTestParseErrorMsgs
);
2084 gTestParseErrorMsgs
.Truncate();
2087 //===========================================================================
2088 // nsPrefBranch et al.
2089 //===========================================================================
2092 class PreferenceServiceReporter
;
2093 } // namespace mozilla
2095 class PrefCallback
: public PLDHashEntryHdr
{
2096 friend class mozilla::PreferenceServiceReporter
;
2099 typedef PrefCallback
* KeyType
;
2100 typedef const PrefCallback
* KeyTypePointer
;
2102 static const PrefCallback
* KeyToPointer(PrefCallback
* aKey
) { return aKey
; }
2104 static PLDHashNumber
HashKey(const PrefCallback
* aKey
) {
2105 uint32_t hash
= HashString(aKey
->mDomain
);
2106 return AddToHash(hash
, aKey
->mCanonical
);
2110 // Create a PrefCallback with a strong reference to its observer.
2111 PrefCallback(const nsACString
& aDomain
, nsIObserver
* aObserver
,
2112 nsPrefBranch
* aBranch
)
2116 mStrongRef(aObserver
) {
2117 MOZ_COUNT_CTOR(PrefCallback
);
2118 nsCOMPtr
<nsISupports
> canonical
= do_QueryInterface(aObserver
);
2119 mCanonical
= canonical
;
2122 // Create a PrefCallback with a weak reference to its observer.
2123 PrefCallback(const nsACString
& aDomain
, nsISupportsWeakReference
* aObserver
,
2124 nsPrefBranch
* aBranch
)
2127 mWeakRef(do_GetWeakReference(aObserver
)),
2128 mStrongRef(nullptr) {
2129 MOZ_COUNT_CTOR(PrefCallback
);
2130 nsCOMPtr
<nsISupports
> canonical
= do_QueryInterface(aObserver
);
2131 mCanonical
= canonical
;
2134 // This is explicitly not a copy constructor.
2135 explicit PrefCallback(const PrefCallback
*& aCopy
)
2136 : mDomain(aCopy
->mDomain
),
2137 mBranch(aCopy
->mBranch
),
2138 mWeakRef(aCopy
->mWeakRef
),
2139 mStrongRef(aCopy
->mStrongRef
),
2140 mCanonical(aCopy
->mCanonical
) {
2141 MOZ_COUNT_CTOR(PrefCallback
);
2144 PrefCallback(const PrefCallback
&) = delete;
2145 PrefCallback(PrefCallback
&&) = default;
2147 MOZ_COUNTED_DTOR(PrefCallback
)
2149 bool KeyEquals(const PrefCallback
* aKey
) const {
2150 // We want to be able to look up a weakly-referencing PrefCallback after
2151 // its observer has died so we can remove it from the table. Once the
2152 // callback's observer dies, its canonical pointer is stale -- in
2153 // particular, we may have allocated a new observer in the same spot in
2154 // memory! So we can't just compare canonical pointers to determine whether
2155 // aKey refers to the same observer as this.
2157 // Our workaround is based on the way we use this hashtable: When we ask
2158 // the hashtable to remove a PrefCallback whose weak reference has expired,
2159 // we use as the key for removal the same object as was inserted into the
2160 // hashtable. Thus we can say that if one of the keys' weak references has
2161 // expired, the two keys are equal iff they're the same object.
2163 if (IsExpired() || aKey
->IsExpired()) {
2164 return this == aKey
;
2167 if (mCanonical
!= aKey
->mCanonical
) {
2171 return mDomain
.Equals(aKey
->mDomain
);
2174 PrefCallback
* GetKey() const { return const_cast<PrefCallback
*>(this); }
2176 // Get a reference to the callback's observer, or null if the observer was
2177 // weakly referenced and has been destroyed.
2178 already_AddRefed
<nsIObserver
> GetObserver() const {
2180 nsCOMPtr
<nsIObserver
> copy
= mStrongRef
;
2181 return copy
.forget();
2184 nsCOMPtr
<nsIObserver
> observer
= do_QueryReferent(mWeakRef
);
2185 return observer
.forget();
2188 const nsCString
& GetDomain() const { return mDomain
; }
2190 nsPrefBranch
* GetPrefBranch() const { return mBranch
; }
2192 // Has this callback's weak reference died?
2193 bool IsExpired() const {
2194 if (!IsWeak()) return false;
2196 nsCOMPtr
<nsIObserver
> observer(do_QueryReferent(mWeakRef
));
2200 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const {
2201 size_t n
= aMallocSizeOf(this);
2202 n
+= mDomain
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
2204 // All the other fields are non-owning pointers, so we don't measure them.
2209 enum { ALLOW_MEMMOVE
= true };
2213 nsPrefBranch
* mBranch
;
2215 // Exactly one of mWeakRef and mStrongRef should be non-null.
2217 nsCOMPtr
<nsIObserver
> mStrongRef
;
2219 // We need a canonical nsISupports pointer, per bug 578392.
2220 nsISupports
* mCanonical
;
2222 bool IsWeak() const { return !!mWeakRef
; }
2225 class nsPrefBranch final
: public nsIPrefBranch
,
2227 public nsSupportsWeakReference
{
2228 friend class mozilla::PreferenceServiceReporter
;
2232 NS_DECL_NSIPREFBRANCH
2235 nsPrefBranch(const char* aPrefRoot
, PrefValueKind aKind
);
2236 nsPrefBranch() = delete;
2238 static void NotifyObserver(const char* aNewpref
, void* aData
);
2240 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const;
2243 using PrefName
= nsCString
;
2245 virtual ~nsPrefBranch();
2247 int32_t GetRootLength() const { return mPrefRoot
.Length(); }
2249 nsresult
GetDefaultFromPropertiesFile(const char* aPrefName
,
2250 nsAString
& aReturn
);
2252 // As SetCharPref, but without any check on the length of |aValue|.
2253 nsresult
SetCharPrefNoLengthCheck(const char* aPrefName
,
2254 const nsACString
& aValue
);
2256 // Reject strings that are more than 1Mb, warn if strings are more than 16kb.
2257 nsresult
CheckSanityOfStringLength(const char* aPrefName
,
2258 const nsAString
& aValue
);
2259 nsresult
CheckSanityOfStringLength(const char* aPrefName
,
2260 const nsACString
& aValue
);
2261 nsresult
CheckSanityOfStringLength(const char* aPrefName
,
2262 const uint32_t aLength
);
2264 void RemoveExpiredCallback(PrefCallback
* aCallback
);
2266 PrefName
GetPrefName(const char* aPrefName
) const {
2267 return GetPrefName(nsDependentCString(aPrefName
));
2270 PrefName
GetPrefName(const nsACString
& aPrefName
) const;
2272 void FreeObserverList(void);
2274 const nsCString mPrefRoot
;
2275 PrefValueKind mKind
;
2277 bool mFreeingObserverList
;
2278 nsClassHashtable
<PrefCallback
, PrefCallback
> mObservers
;
2281 class nsPrefLocalizedString final
: public nsIPrefLocalizedString
{
2283 nsPrefLocalizedString();
2286 NS_FORWARD_NSISUPPORTSPRIMITIVE(mUnicodeString
->)
2287 NS_FORWARD_NSISUPPORTSSTRING(mUnicodeString
->)
2292 virtual ~nsPrefLocalizedString();
2294 nsCOMPtr
<nsISupportsString
> mUnicodeString
;
2297 //----------------------------------------------------------------------------
2299 //----------------------------------------------------------------------------
2301 nsPrefBranch::nsPrefBranch(const char* aPrefRoot
, PrefValueKind aKind
)
2302 : mPrefRoot(aPrefRoot
), mKind(aKind
), mFreeingObserverList(false) {
2303 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
2304 if (observerService
) {
2305 ++mRefCnt
; // must be > 0 when we call this, or we'll get deleted!
2307 // Add weakly so we don't have to clean up at shutdown.
2308 observerService
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, true);
2313 nsPrefBranch::~nsPrefBranch() { FreeObserverList(); }
2315 NS_IMPL_ISUPPORTS(nsPrefBranch
, nsIPrefBranch
, nsIObserver
,
2316 nsISupportsWeakReference
)
2319 nsPrefBranch::GetRoot(nsACString
& aRoot
) {
2325 nsPrefBranch::GetPrefType(const char* aPrefName
, int32_t* aRetVal
) {
2326 NS_ENSURE_ARG(aPrefName
);
2328 const PrefName
& prefName
= GetPrefName(aPrefName
);
2329 *aRetVal
= Preferences::GetType(prefName
.get());
2334 nsPrefBranch::GetBoolPrefWithDefault(const char* aPrefName
, bool aDefaultValue
,
2335 uint8_t aArgc
, bool* aRetVal
) {
2336 nsresult rv
= GetBoolPref(aPrefName
, aRetVal
);
2337 if (NS_FAILED(rv
) && aArgc
== 1) {
2338 *aRetVal
= aDefaultValue
;
2346 nsPrefBranch::GetBoolPref(const char* aPrefName
, bool* aRetVal
) {
2347 NS_ENSURE_ARG(aPrefName
);
2349 const PrefName
& pref
= GetPrefName(aPrefName
);
2350 return Preferences::GetBool(pref
.get(), aRetVal
, mKind
);
2354 nsPrefBranch::SetBoolPref(const char* aPrefName
, bool aValue
) {
2355 NS_ENSURE_ARG(aPrefName
);
2357 const PrefName
& pref
= GetPrefName(aPrefName
);
2358 return Preferences::SetBool(pref
.get(), aValue
, mKind
);
2362 nsPrefBranch::GetFloatPrefWithDefault(const char* aPrefName
,
2363 float aDefaultValue
, uint8_t aArgc
,
2365 nsresult rv
= GetFloatPref(aPrefName
, aRetVal
);
2367 if (NS_FAILED(rv
) && aArgc
== 1) {
2368 *aRetVal
= aDefaultValue
;
2376 nsPrefBranch::GetFloatPref(const char* aPrefName
, float* aRetVal
) {
2377 NS_ENSURE_ARG(aPrefName
);
2379 nsAutoCString stringVal
;
2380 nsresult rv
= GetCharPref(aPrefName
, stringVal
);
2381 if (NS_SUCCEEDED(rv
)) {
2382 // ParsePrefFloat() does a locale-independent conversion.
2383 *aRetVal
= ParsePrefFloat(stringVal
, &rv
);
2390 nsPrefBranch::GetCharPrefWithDefault(const char* aPrefName
,
2391 const nsACString
& aDefaultValue
,
2392 uint8_t aArgc
, nsACString
& aRetVal
) {
2393 nsresult rv
= GetCharPref(aPrefName
, aRetVal
);
2395 if (NS_FAILED(rv
) && aArgc
== 1) {
2396 aRetVal
= aDefaultValue
;
2404 nsPrefBranch::GetCharPref(const char* aPrefName
, nsACString
& aRetVal
) {
2405 NS_ENSURE_ARG(aPrefName
);
2407 const PrefName
& pref
= GetPrefName(aPrefName
);
2408 return Preferences::GetCString(pref
.get(), aRetVal
, mKind
);
2412 nsPrefBranch::SetCharPref(const char* aPrefName
, const nsACString
& aValue
) {
2413 nsresult rv
= CheckSanityOfStringLength(aPrefName
, aValue
);
2414 if (NS_FAILED(rv
)) {
2417 return SetCharPrefNoLengthCheck(aPrefName
, aValue
);
2420 nsresult
nsPrefBranch::SetCharPrefNoLengthCheck(const char* aPrefName
,
2421 const nsACString
& aValue
) {
2422 NS_ENSURE_ARG(aPrefName
);
2424 const PrefName
& pref
= GetPrefName(aPrefName
);
2425 return Preferences::SetCString(pref
.get(), aValue
, mKind
);
2429 nsPrefBranch::GetStringPref(const char* aPrefName
,
2430 const nsACString
& aDefaultValue
, uint8_t aArgc
,
2431 nsACString
& aRetVal
) {
2432 nsCString utf8String
;
2433 nsresult rv
= GetCharPref(aPrefName
, utf8String
);
2434 if (NS_SUCCEEDED(rv
)) {
2435 aRetVal
= utf8String
;
2440 aRetVal
= aDefaultValue
;
2448 nsPrefBranch::SetStringPref(const char* aPrefName
, const nsACString
& aValue
) {
2449 nsresult rv
= CheckSanityOfStringLength(aPrefName
, aValue
);
2450 if (NS_FAILED(rv
)) {
2454 return SetCharPrefNoLengthCheck(aPrefName
, aValue
);
2458 nsPrefBranch::GetIntPrefWithDefault(const char* aPrefName
,
2459 int32_t aDefaultValue
, uint8_t aArgc
,
2461 nsresult rv
= GetIntPref(aPrefName
, aRetVal
);
2463 if (NS_FAILED(rv
) && aArgc
== 1) {
2464 *aRetVal
= aDefaultValue
;
2472 nsPrefBranch::GetIntPref(const char* aPrefName
, int32_t* aRetVal
) {
2473 NS_ENSURE_ARG(aPrefName
);
2474 const PrefName
& pref
= GetPrefName(aPrefName
);
2475 return Preferences::GetInt(pref
.get(), aRetVal
, mKind
);
2479 nsPrefBranch::SetIntPref(const char* aPrefName
, int32_t aValue
) {
2480 NS_ENSURE_ARG(aPrefName
);
2482 const PrefName
& pref
= GetPrefName(aPrefName
);
2483 return Preferences::SetInt(pref
.get(), aValue
, mKind
);
2487 nsPrefBranch::GetComplexValue(const char* aPrefName
, const nsIID
& aType
,
2489 NS_ENSURE_ARG(aPrefName
);
2492 nsAutoCString utf8String
;
2494 // We have to do this one first because it's different to all the rest.
2495 if (aType
.Equals(NS_GET_IID(nsIPrefLocalizedString
))) {
2496 nsCOMPtr
<nsIPrefLocalizedString
> theString(
2497 do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID
, &rv
));
2498 if (NS_FAILED(rv
)) {
2502 const PrefName
& pref
= GetPrefName(aPrefName
);
2503 bool bNeedDefault
= false;
2505 if (mKind
== PrefValueKind::Default
) {
2506 bNeedDefault
= true;
2508 // if there is no user (or locked) value
2509 if (!Preferences::HasUserValue(pref
.get()) &&
2510 !Preferences::IsLocked(pref
.get())) {
2511 bNeedDefault
= true;
2515 // if we need to fetch the default value, do that instead, otherwise use the
2516 // value we pulled in at the top of this function
2518 nsAutoString utf16String
;
2519 rv
= GetDefaultFromPropertiesFile(pref
.get(), utf16String
);
2520 if (NS_SUCCEEDED(rv
)) {
2521 theString
->SetData(utf16String
);
2524 rv
= GetCharPref(aPrefName
, utf8String
);
2525 if (NS_SUCCEEDED(rv
)) {
2526 theString
->SetData(NS_ConvertUTF8toUTF16(utf8String
));
2530 if (NS_SUCCEEDED(rv
)) {
2531 theString
.forget(reinterpret_cast<nsIPrefLocalizedString
**>(aRetVal
));
2537 // if we can't get the pref, there's no point in being here
2538 rv
= GetCharPref(aPrefName
, utf8String
);
2539 if (NS_FAILED(rv
)) {
2543 if (aType
.Equals(NS_GET_IID(nsIFile
))) {
2544 ENSURE_PARENT_PROCESS("GetComplexValue(nsIFile)", aPrefName
);
2546 nsCOMPtr
<nsIFile
> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
));
2548 if (NS_SUCCEEDED(rv
)) {
2549 rv
= file
->SetPersistentDescriptor(utf8String
);
2550 if (NS_SUCCEEDED(rv
)) {
2551 file
.forget(reinterpret_cast<nsIFile
**>(aRetVal
));
2558 if (aType
.Equals(NS_GET_IID(nsIRelativeFilePref
))) {
2559 ENSURE_PARENT_PROCESS("GetComplexValue(nsIRelativeFilePref)", aPrefName
);
2561 nsACString::const_iterator keyBegin
, strEnd
;
2562 utf8String
.BeginReading(keyBegin
);
2563 utf8String
.EndReading(strEnd
);
2565 // The pref has the format: [fromKey]a/b/c
2566 if (*keyBegin
++ != '[') {
2567 return NS_ERROR_FAILURE
;
2570 nsACString::const_iterator
keyEnd(keyBegin
);
2571 if (!FindCharInReadable(']', keyEnd
, strEnd
)) {
2572 return NS_ERROR_FAILURE
;
2575 nsAutoCString
key(Substring(keyBegin
, keyEnd
));
2577 nsCOMPtr
<nsIFile
> fromFile
;
2578 nsCOMPtr
<nsIProperties
> directoryService(
2579 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
, &rv
));
2580 if (NS_FAILED(rv
)) {
2584 rv
= directoryService
->Get(key
.get(), NS_GET_IID(nsIFile
),
2585 getter_AddRefs(fromFile
));
2586 if (NS_FAILED(rv
)) {
2590 nsCOMPtr
<nsIFile
> theFile
;
2591 rv
= NS_NewNativeLocalFile(""_ns
, true, getter_AddRefs(theFile
));
2592 if (NS_FAILED(rv
)) {
2596 rv
= theFile
->SetRelativeDescriptor(fromFile
, Substring(++keyEnd
, strEnd
));
2597 if (NS_FAILED(rv
)) {
2601 nsCOMPtr
<nsIRelativeFilePref
> relativePref
= new nsRelativeFilePref();
2602 Unused
<< relativePref
->SetFile(theFile
);
2603 Unused
<< relativePref
->SetRelativeToKey(key
);
2605 relativePref
.forget(reinterpret_cast<nsIRelativeFilePref
**>(aRetVal
));
2609 NS_WARNING("nsPrefBranch::GetComplexValue - Unsupported interface type");
2610 return NS_NOINTERFACE
;
2613 nsresult
nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName
,
2614 const nsAString
& aValue
) {
2615 return CheckSanityOfStringLength(aPrefName
, aValue
.Length());
2618 nsresult
nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName
,
2619 const nsACString
& aValue
) {
2620 return CheckSanityOfStringLength(aPrefName
, aValue
.Length());
2623 nsresult
nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName
,
2624 const uint32_t aLength
) {
2625 if (aLength
> MAX_PREF_LENGTH
) {
2626 return NS_ERROR_ILLEGAL_VALUE
;
2628 if (aLength
<= MAX_ADVISABLE_PREF_LENGTH
) {
2633 nsCOMPtr
<nsIConsoleService
> console
=
2634 do_GetService("@mozilla.org/consoleservice;1", &rv
);
2635 if (NS_FAILED(rv
)) {
2639 nsAutoCString
message(nsPrintfCString(
2640 "Warning: attempting to write %d bytes to preference %s. This is bad "
2641 "for general performance and memory usage. Such an amount of data "
2642 "should rather be written to an external file.",
2643 aLength
, GetPrefName(aPrefName
).get()));
2645 rv
= console
->LogStringMessage(NS_ConvertUTF8toUTF16(message
).get());
2646 if (NS_FAILED(rv
)) {
2653 nsPrefBranch::SetComplexValue(const char* aPrefName
, const nsIID
& aType
,
2654 nsISupports
* aValue
) {
2655 ENSURE_PARENT_PROCESS("SetComplexValue", aPrefName
);
2656 NS_ENSURE_ARG(aPrefName
);
2658 nsresult rv
= NS_NOINTERFACE
;
2660 if (aType
.Equals(NS_GET_IID(nsIFile
))) {
2661 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(aValue
);
2663 return NS_NOINTERFACE
;
2666 nsAutoCString descriptorString
;
2667 rv
= file
->GetPersistentDescriptor(descriptorString
);
2668 if (NS_SUCCEEDED(rv
)) {
2669 rv
= SetCharPrefNoLengthCheck(aPrefName
, descriptorString
);
2674 if (aType
.Equals(NS_GET_IID(nsIRelativeFilePref
))) {
2675 nsCOMPtr
<nsIRelativeFilePref
> relFilePref
= do_QueryInterface(aValue
);
2677 return NS_NOINTERFACE
;
2680 nsCOMPtr
<nsIFile
> file
;
2681 relFilePref
->GetFile(getter_AddRefs(file
));
2683 return NS_NOINTERFACE
;
2686 nsAutoCString relativeToKey
;
2687 (void)relFilePref
->GetRelativeToKey(relativeToKey
);
2689 nsCOMPtr
<nsIFile
> relativeToFile
;
2690 nsCOMPtr
<nsIProperties
> directoryService(
2691 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
, &rv
));
2692 if (NS_FAILED(rv
)) {
2696 rv
= directoryService
->Get(relativeToKey
.get(), NS_GET_IID(nsIFile
),
2697 getter_AddRefs(relativeToFile
));
2698 if (NS_FAILED(rv
)) {
2702 nsAutoCString relDescriptor
;
2703 rv
= file
->GetRelativeDescriptor(relativeToFile
, relDescriptor
);
2704 if (NS_FAILED(rv
)) {
2708 nsAutoCString descriptorString
;
2709 descriptorString
.Append('[');
2710 descriptorString
.Append(relativeToKey
);
2711 descriptorString
.Append(']');
2712 descriptorString
.Append(relDescriptor
);
2713 return SetCharPrefNoLengthCheck(aPrefName
, descriptorString
);
2716 if (aType
.Equals(NS_GET_IID(nsIPrefLocalizedString
))) {
2717 nsCOMPtr
<nsISupportsString
> theString
= do_QueryInterface(aValue
);
2720 nsString wideString
;
2722 rv
= theString
->GetData(wideString
);
2723 if (NS_SUCCEEDED(rv
)) {
2724 // Check sanity of string length before any lengthy conversion
2725 rv
= CheckSanityOfStringLength(aPrefName
, wideString
);
2726 if (NS_FAILED(rv
)) {
2729 rv
= SetCharPrefNoLengthCheck(aPrefName
,
2730 NS_ConvertUTF16toUTF8(wideString
));
2736 NS_WARNING("nsPrefBranch::SetComplexValue - Unsupported interface type");
2737 return NS_NOINTERFACE
;
2741 nsPrefBranch::ClearUserPref(const char* aPrefName
) {
2742 NS_ENSURE_ARG(aPrefName
);
2744 const PrefName
& pref
= GetPrefName(aPrefName
);
2745 return Preferences::ClearUser(pref
.get());
2749 nsPrefBranch::PrefHasUserValue(const char* aPrefName
, bool* aRetVal
) {
2750 NS_ENSURE_ARG_POINTER(aRetVal
);
2751 NS_ENSURE_ARG(aPrefName
);
2753 const PrefName
& pref
= GetPrefName(aPrefName
);
2754 *aRetVal
= Preferences::HasUserValue(pref
.get());
2759 nsPrefBranch::PrefHasDefaultValue(const char* aPrefName
, bool* aRetVal
) {
2760 NS_ENSURE_ARG_POINTER(aRetVal
);
2761 NS_ENSURE_ARG(aPrefName
);
2763 const PrefName
& pref
= GetPrefName(aPrefName
);
2764 *aRetVal
= Preferences::HasDefaultValue(pref
.get());
2769 nsPrefBranch::LockPref(const char* aPrefName
) {
2770 NS_ENSURE_ARG(aPrefName
);
2772 const PrefName
& pref
= GetPrefName(aPrefName
);
2773 return Preferences::Lock(pref
.get());
2777 nsPrefBranch::PrefIsLocked(const char* aPrefName
, bool* aRetVal
) {
2778 NS_ENSURE_ARG_POINTER(aRetVal
);
2779 NS_ENSURE_ARG(aPrefName
);
2781 const PrefName
& pref
= GetPrefName(aPrefName
);
2782 *aRetVal
= Preferences::IsLocked(pref
.get());
2787 nsPrefBranch::PrefIsSanitized(const char* aPrefName
, bool* aRetVal
) {
2788 NS_ENSURE_ARG_POINTER(aRetVal
);
2789 NS_ENSURE_ARG(aPrefName
);
2791 const PrefName
& pref
= GetPrefName(aPrefName
);
2792 *aRetVal
= Preferences::IsSanitized(pref
.get());
2797 nsPrefBranch::UnlockPref(const char* aPrefName
) {
2798 NS_ENSURE_ARG(aPrefName
);
2800 const PrefName
& pref
= GetPrefName(aPrefName
);
2801 return Preferences::Unlock(pref
.get());
2805 nsPrefBranch::DeleteBranch(const char* aStartingAt
) {
2806 ENSURE_PARENT_PROCESS("DeleteBranch", aStartingAt
);
2807 NS_ENSURE_ARG(aStartingAt
);
2809 MOZ_ASSERT(NS_IsMainThread());
2812 return NS_ERROR_NOT_INITIALIZED
;
2815 const PrefName
& pref
= GetPrefName(aStartingAt
);
2816 nsAutoCString
branchName(pref
.get());
2818 // Add a trailing '.' if it doesn't already have one.
2819 if (branchName
.Length() > 1 && !StringEndsWith(branchName
, "."_ns
)) {
2823 const nsACString
& branchNameNoDot
=
2824 Substring(branchName
, 0, branchName
.Length() - 1);
2826 for (auto iter
= HashTable()->modIter(); !iter
.done(); iter
.next()) {
2827 // The first disjunct matches branches: e.g. a branch name "foo.bar."
2828 // matches a name "foo.bar.baz" (but it won't match "foo.barrel.baz").
2829 // The second disjunct matches leaf nodes: e.g. a branch name "foo.bar."
2830 // matches a name "foo.bar" (by ignoring the trailing '.').
2831 nsDependentCString
name(iter
.get()->Name());
2832 if (StringBeginsWith(name
, branchName
) || name
.Equals(branchNameNoDot
)) {
2834 // The saved callback pref may be invalid now.
2835 gCallbackPref
= nullptr;
2839 Preferences::HandleDirty();
2844 nsPrefBranch::GetChildList(const char* aStartingAt
,
2845 nsTArray
<nsCString
>& aChildArray
) {
2846 NS_ENSURE_ARG(aStartingAt
);
2848 MOZ_ASSERT(NS_IsMainThread());
2850 // This will contain a list of all the pref name strings. Allocated on the
2852 AutoTArray
<nsCString
, 32> prefArray
;
2854 const PrefName
& parent
= GetPrefName(aStartingAt
);
2855 size_t parentLen
= parent
.Length();
2856 for (auto& pref
: PrefsIter(HashTable(), gSharedMap
)) {
2857 if (strncmp(pref
->Name(), parent
.get(), parentLen
) == 0) {
2858 prefArray
.AppendElement(pref
->NameString());
2862 // Now that we've built up the list, run the callback on all the matching
2864 aChildArray
.SetCapacity(prefArray
.Length());
2865 for (auto& element
: prefArray
) {
2866 // we need to lop off mPrefRoot in case the user is planning to pass this
2867 // back to us because if they do we are going to add mPrefRoot again.
2868 aChildArray
.AppendElement(Substring(element
, mPrefRoot
.Length()));
2875 nsPrefBranch::AddObserverImpl(const nsACString
& aDomain
, nsIObserver
* aObserver
,
2877 UniquePtr
<PrefCallback
> pCallback
;
2879 NS_ENSURE_ARG(aObserver
);
2881 const nsCString
& prefName
= GetPrefName(aDomain
);
2883 // Hold a weak reference to the observer if so requested.
2885 nsCOMPtr
<nsISupportsWeakReference
> weakRefFactory
=
2886 do_QueryInterface(aObserver
);
2887 if (!weakRefFactory
) {
2888 // The caller didn't give us a object that supports weak reference...
2890 return NS_ERROR_INVALID_ARG
;
2893 // Construct a PrefCallback with a weak reference to the observer.
2894 pCallback
= MakeUnique
<PrefCallback
>(prefName
, weakRefFactory
, this);
2897 // Construct a PrefCallback with a strong reference to the observer.
2898 pCallback
= MakeUnique
<PrefCallback
>(prefName
, aObserver
, this);
2901 mObservers
.WithEntryHandle(pCallback
.get(), [&](auto&& p
) {
2904 nsPrintfCString("Ignoring duplicate observer: %s", prefName
.get())
2907 // We must pass a fully qualified preference name to the callback
2908 // aDomain == nullptr is the only possible failure, and we trapped it with
2909 // NS_ENSURE_ARG above.
2910 Preferences::RegisterCallback(NotifyObserver
, prefName
, pCallback
.get(),
2911 Preferences::PrefixMatch
,
2912 /* isPriority */ false);
2914 p
.Insert(std::move(pCallback
));
2922 nsPrefBranch::RemoveObserverImpl(const nsACString
& aDomain
,
2923 nsIObserver
* aObserver
) {
2924 NS_ENSURE_ARG(aObserver
);
2926 nsresult rv
= NS_OK
;
2928 // If we're in the middle of a call to FreeObserverList, don't process this
2929 // RemoveObserver call -- the observer in question will be removed soon, if
2930 // it hasn't been already.
2932 // It's important that we don't touch mObservers in any way -- even a Get()
2933 // which returns null might cause the hashtable to resize itself, which will
2934 // break the iteration in FreeObserverList.
2935 if (mFreeingObserverList
) {
2939 // Remove the relevant PrefCallback from mObservers and get an owning pointer
2940 // to it. Unregister the callback first, and then let the owning pointer go
2941 // out of scope and destroy the callback.
2942 const nsCString
& prefName
= GetPrefName(aDomain
);
2943 PrefCallback
key(prefName
, aObserver
, this);
2944 mozilla::UniquePtr
<PrefCallback
> pCallback
;
2945 mObservers
.Remove(&key
, &pCallback
);
2947 rv
= Preferences::UnregisterCallback(
2948 NotifyObserver
, prefName
, pCallback
.get(), Preferences::PrefixMatch
);
2955 nsPrefBranch::Observe(nsISupports
* aSubject
, const char* aTopic
,
2956 const char16_t
* aData
) {
2957 // Watch for xpcom shutdown and free our observers to eliminate any cyclic
2959 if (!nsCRT::strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
2966 void nsPrefBranch::NotifyObserver(const char* aNewPref
, void* aData
) {
2967 PrefCallback
* pCallback
= (PrefCallback
*)aData
;
2969 nsCOMPtr
<nsIObserver
> observer
= pCallback
->GetObserver();
2971 // The observer has expired. Let's remove this callback.
2972 pCallback
->GetPrefBranch()->RemoveExpiredCallback(pCallback
);
2976 // Remove any root this string may contain so as to not confuse the observer
2977 // by passing them something other than what they passed us as a topic.
2978 uint32_t len
= pCallback
->GetPrefBranch()->GetRootLength();
2979 nsDependentCString
suffix(aNewPref
+ len
);
2981 observer
->Observe(static_cast<nsIPrefBranch
*>(pCallback
->GetPrefBranch()),
2982 NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
,
2983 NS_ConvertASCIItoUTF16(suffix
).get());
2986 size_t nsPrefBranch::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const {
2987 size_t n
= aMallocSizeOf(this);
2989 n
+= mPrefRoot
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
2991 n
+= mObservers
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
2992 for (const auto& entry
: mObservers
) {
2993 const PrefCallback
* data
= entry
.GetWeak();
2994 n
+= data
->SizeOfIncludingThis(aMallocSizeOf
);
3000 void nsPrefBranch::FreeObserverList() {
3001 // We need to prevent anyone from modifying mObservers while we're iterating
3002 // over it. In particular, some clients will call RemoveObserver() when
3003 // they're removed and destructed via the iterator; we set
3004 // mFreeingObserverList to keep those calls from touching mObservers.
3005 mFreeingObserverList
= true;
3006 for (auto iter
= mObservers
.Iter(); !iter
.Done(); iter
.Next()) {
3007 auto callback
= iter
.UserData();
3008 Preferences::UnregisterCallback(nsPrefBranch::NotifyObserver
,
3009 callback
->GetDomain(), callback
,
3010 Preferences::PrefixMatch
);
3014 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
3015 if (observerService
) {
3016 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
3019 mFreeingObserverList
= false;
3022 void nsPrefBranch::RemoveExpiredCallback(PrefCallback
* aCallback
) {
3023 MOZ_ASSERT(aCallback
->IsExpired());
3024 mObservers
.Remove(aCallback
);
3027 nsresult
nsPrefBranch::GetDefaultFromPropertiesFile(const char* aPrefName
,
3028 nsAString
& aReturn
) {
3029 // The default value contains a URL to a .properties file.
3031 nsAutoCString propertyFileURL
;
3032 nsresult rv
= Preferences::GetCString(aPrefName
, propertyFileURL
,
3033 PrefValueKind::Default
);
3034 if (NS_FAILED(rv
)) {
3038 nsCOMPtr
<nsIStringBundleService
> bundleService
=
3039 components::StringBundle::Service();
3040 if (!bundleService
) {
3041 return NS_ERROR_FAILURE
;
3044 nsCOMPtr
<nsIStringBundle
> bundle
;
3045 rv
= bundleService
->CreateBundle(propertyFileURL
.get(),
3046 getter_AddRefs(bundle
));
3047 if (NS_FAILED(rv
)) {
3051 return bundle
->GetStringFromName(aPrefName
, aReturn
);
3054 nsPrefBranch::PrefName
nsPrefBranch::GetPrefName(
3055 const nsACString
& aPrefName
) const {
3056 if (mPrefRoot
.IsEmpty()) {
3057 return PrefName(PromiseFlatCString(aPrefName
));
3060 return PrefName(mPrefRoot
+ aPrefName
);
3063 //----------------------------------------------------------------------------
3064 // nsPrefLocalizedString
3065 //----------------------------------------------------------------------------
3067 nsPrefLocalizedString::nsPrefLocalizedString() = default;
3069 nsPrefLocalizedString::~nsPrefLocalizedString() = default;
3071 NS_IMPL_ISUPPORTS(nsPrefLocalizedString
, nsIPrefLocalizedString
,
3074 nsresult
nsPrefLocalizedString::Init() {
3076 mUnicodeString
= do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
, &rv
);
3081 //----------------------------------------------------------------------------
3082 // nsRelativeFilePref
3083 //----------------------------------------------------------------------------
3085 NS_IMPL_ISUPPORTS(nsRelativeFilePref
, nsIRelativeFilePref
)
3087 nsRelativeFilePref::nsRelativeFilePref() = default;
3089 nsRelativeFilePref::~nsRelativeFilePref() = default;
3092 nsRelativeFilePref::GetFile(nsIFile
** aFile
) {
3093 NS_ENSURE_ARG_POINTER(aFile
);
3095 NS_IF_ADDREF(*aFile
);
3100 nsRelativeFilePref::SetFile(nsIFile
* aFile
) {
3106 nsRelativeFilePref::GetRelativeToKey(nsACString
& aRelativeToKey
) {
3107 aRelativeToKey
.Assign(mRelativeToKey
);
3112 nsRelativeFilePref::SetRelativeToKey(const nsACString
& aRelativeToKey
) {
3113 mRelativeToKey
.Assign(aRelativeToKey
);
3117 //===========================================================================
3118 // class Preferences and related things
3119 //===========================================================================
3123 #define INITIAL_PREF_FILES 10
3125 void Preferences::HandleDirty() {
3126 MOZ_ASSERT(XRE_IsParentProcess());
3128 if (!HashTable() || !sPreferences
) {
3132 if (sPreferences
->mProfileShutdown
) {
3133 NS_WARNING("Setting user pref after profile shutdown.");
3137 if (!sPreferences
->mDirty
) {
3138 sPreferences
->mDirty
= true;
3140 if (sPreferences
->mCurrentFile
&& sPreferences
->AllowOffMainThreadSave() &&
3141 !sPreferences
->mSavePending
) {
3142 sPreferences
->mSavePending
= true;
3143 static const int PREF_DELAY_MS
= 500;
3144 NS_DelayedDispatchToCurrentThread(
3145 NewRunnableMethod("Preferences::SavePrefFileAsynchronous",
3147 &Preferences::SavePrefFileAsynchronous
),
3153 static nsresult
openPrefFile(nsIFile
* aFile
, PrefValueKind aKind
);
3155 static nsresult
parsePrefData(const nsCString
& aData
, PrefValueKind aKind
);
3158 static const char kPrefFileHeader
[] =
3159 "// Mozilla User Preferences"
3162 "// DO NOT EDIT THIS FILE."
3166 "// If you make changes to this file while the application is running,"
3168 "// the changes will be overwritten when the application exits."
3172 "// To change a preference value, you can either:"
3174 "// - modify it via the UI (e.g. via about:config in the browser); or"
3176 "// - set it within a user.js file in your profile."
3181 // Note: if sShutdown is true, sPreferences will be nullptr.
3182 StaticRefPtr
<Preferences
> Preferences::sPreferences
;
3183 bool Preferences::sShutdown
= false;
3185 // This globally enables or disables OMT pref writing, both sync and async.
3186 static int32_t sAllowOMTPrefWrite
= -1;
3188 // Write the preference data to a file.
3189 class PreferencesWriter final
{
3191 PreferencesWriter() = default;
3193 static nsresult
Write(nsIFile
* aFile
, PrefSaveData
& aPrefs
) {
3194 nsCOMPtr
<nsIOutputStream
> outStreamSink
;
3195 nsCOMPtr
<nsIOutputStream
> outStream
;
3196 uint32_t writeAmount
;
3199 // Execute a "safe" save by saving through a tempfile.
3200 rv
= NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStreamSink
), aFile
,
3202 if (NS_FAILED(rv
)) {
3206 rv
= NS_NewBufferedOutputStream(getter_AddRefs(outStream
),
3207 outStreamSink
.forget(), 4096);
3208 if (NS_FAILED(rv
)) {
3212 struct CharComparator
{
3213 bool LessThan(const nsCString
& aA
, const nsCString
& aB
) const {
3217 bool Equals(const nsCString
& aA
, const nsCString
& aB
) const {
3222 // Sort the preferences to make a readable file on disk.
3223 aPrefs
.Sort(CharComparator());
3225 // Write out the file header.
3226 outStream
->Write(kPrefFileHeader
, sizeof(kPrefFileHeader
) - 1,
3229 for (nsCString
& pref
: aPrefs
) {
3230 outStream
->Write(pref
.get(), pref
.Length(), &writeAmount
);
3231 outStream
->Write(NS_LINEBREAK
, NS_LINEBREAK_LEN
, &writeAmount
);
3234 // Tell the safe output stream to overwrite the real prefs file.
3235 // (It'll abort if there were any errors during writing.)
3236 nsCOMPtr
<nsISafeOutputStream
> safeStream
= do_QueryInterface(outStream
);
3237 MOZ_ASSERT(safeStream
, "expected a safe output stream!");
3239 rv
= safeStream
->Finish();
3243 if (NS_FAILED(rv
)) {
3244 NS_WARNING("failed to save prefs file! possible data loss");
3251 static void Flush() {
3252 MOZ_DIAGNOSTIC_ASSERT(sPendingWriteCount
>= 0);
3253 // SpinEventLoopUntil is unfortunate, but ultimately it's the best thing
3254 // we can do here given the constraint that we need to ensure that
3255 // the preferences on disk match what we have in memory. We could
3256 // easily perform the write here ourselves by doing exactly what
3257 // happens in PWRunnable::Run. This would be the right thing to do
3258 // if we're stuck here because other unrelated runnables are taking
3259 // a long time, and the wrong thing to do if PreferencesWriter::Write
3260 // is what takes a long time, as we would be trading a SpinEventLoopUntil
3261 // for a synchronous disk write, wherein we could not even spin the
3262 // event loop. Given that PWRunnable generally runs on a thread pool,
3263 // if we're stuck here, it's likely because of PreferencesWriter::Write
3264 // and not some other runnable. Thus, spin away.
3265 mozilla::SpinEventLoopUntil("PreferencesWriter::Flush"_ns
,
3266 []() { return sPendingWriteCount
<= 0; });
3269 // This is the data that all of the runnables (see below) will attempt
3270 // to write. It will always have the most up to date version, or be
3271 // null, if the up to date information has already been written out.
3272 static Atomic
<PrefSaveData
*> sPendingWriteData
;
3274 // This is the number of writes via PWRunnables which have been dispatched
3275 // but not yet completed. This is intended to be used by Flush to ensure
3276 // that there are no outstanding writes left incomplete, and thus our prefs
3277 // on disk are in sync with what we have in memory.
3278 static Atomic
<int> sPendingWriteCount
;
3280 // See PWRunnable::Run for details on why we need this lock.
3281 static StaticMutex sWritingToFile MOZ_UNANNOTATED
;
3284 Atomic
<PrefSaveData
*> PreferencesWriter::sPendingWriteData(nullptr);
3285 Atomic
<int> PreferencesWriter::sPendingWriteCount(0);
3286 StaticMutex
PreferencesWriter::sWritingToFile
;
3288 class PWRunnable
: public Runnable
{
3290 explicit PWRunnable(
3292 UniquePtr
<MozPromiseHolder
<Preferences::WritePrefFilePromise
>>
3294 : Runnable("PWRunnable"),
3296 mPromiseHolder(std::move(aPromiseHolder
)) {}
3298 NS_IMETHOD
Run() override
{
3299 // Preference writes are handled a bit strangely, in that a "newer"
3300 // write is generally regarded as always better. For this reason,
3301 // sPendingWriteData can be overwritten multiple times before anyone
3302 // gets around to actually using it, minimizing writes. However,
3303 // once we've acquired sPendingWriteData we've reached a
3304 // "point of no return" and have to complete the write.
3306 // Unfortunately, this design allows the following behaviour:
3308 // 1. write1 is queued up
3309 // 2. thread1 acquires write1
3310 // 3. write2 is queued up
3311 // 4. thread2 acquires write2
3312 // 5. thread1 and thread2 concurrently clobber each other
3314 // To avoid this, we use this lock to ensure that only one thread
3315 // at a time is trying to acquire the write, and when it does,
3316 // all other threads are prevented from acquiring writes until it
3317 // completes the write. New writes are still allowed to be queued
3320 // Although it's atomic, the acquire needs to be guarded by the mutex
3321 // to avoid reordering of writes -- we don't want an older write to
3322 // run after a newer one. To avoid this causing too much waiting, we check
3323 // if sPendingWriteData is already null before acquiring the mutex. If it
3324 // is, then there's definitely no work to be done (or someone is in the
3325 // middle of doing it for us).
3327 // Note that every time a new write is queued up, a new write task is
3328 // is also queued up, so there will always be a task that can see the newest
3331 // Ideally this lock wouldn't be necessary, and the PreferencesWriter
3332 // would be used more carefully, but it's hard to untangle all that.
3333 nsresult rv
= NS_OK
;
3334 if (PreferencesWriter::sPendingWriteData
) {
3335 StaticMutexAutoLock
lock(PreferencesWriter::sWritingToFile
);
3336 // If we get a nullptr on the exchange, it means that somebody
3337 // else has already processed the request, and we can just return.
3338 UniquePtr
<PrefSaveData
> prefs(
3339 PreferencesWriter::sPendingWriteData
.exchange(nullptr));
3341 rv
= PreferencesWriter::Write(mFile
, *prefs
);
3342 // Make a copy of these so we can have them in runnable lambda.
3343 // nsIFile is only there so that we would never release the
3344 // ref counted pointer off main thread.
3345 nsresult rvCopy
= rv
;
3346 nsCOMPtr
<nsIFile
> fileCopy(mFile
);
3347 SchedulerGroup::Dispatch(NS_NewRunnableFunction(
3348 "Preferences::WriterRunnable",
3349 [fileCopy
, rvCopy
, promiseHolder
= std::move(mPromiseHolder
)] {
3350 MOZ_RELEASE_ASSERT(NS_IsMainThread());
3351 if (NS_FAILED(rvCopy
)) {
3352 Preferences::HandleDirty();
3354 if (promiseHolder
) {
3355 promiseHolder
->ResolveIfExists(true, __func__
);
3360 // We've completed the write to the best of our abilities, whether
3361 // we had prefs to write or another runnable got to them first. If
3362 // PreferencesWriter::Write failed, this is still correct as the
3363 // write is no longer outstanding, and the above HandleDirty call
3364 // will just start the cycle again.
3365 PreferencesWriter::sPendingWriteCount
--;
3371 if (mPromiseHolder
) {
3372 mPromiseHolder
->RejectIfExists(NS_ERROR_ABORT
, __func__
);
3377 nsCOMPtr
<nsIFile
> mFile
;
3378 UniquePtr
<MozPromiseHolder
<Preferences::WritePrefFilePromise
>> mPromiseHolder
;
3381 // Although this is a member of Preferences, it measures sPreferences and
3382 // several other global structures.
3384 void Preferences::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
,
3385 PrefsSizes
& aSizes
) {
3386 if (!sPreferences
) {
3390 aSizes
.mMisc
+= aMallocSizeOf(sPreferences
.get());
3392 aSizes
.mRootBranches
+=
3393 static_cast<nsPrefBranch
*>(sPreferences
->mRootBranch
.get())
3394 ->SizeOfIncludingThis(aMallocSizeOf
) +
3395 static_cast<nsPrefBranch
*>(sPreferences
->mDefaultRootBranch
.get())
3396 ->SizeOfIncludingThis(aMallocSizeOf
);
3399 class PreferenceServiceReporter final
: public nsIMemoryReporter
{
3400 ~PreferenceServiceReporter() = default;
3404 NS_DECL_NSIMEMORYREPORTER
3407 static const uint32_t kSuspectReferentCount
= 1000;
3410 NS_IMPL_ISUPPORTS(PreferenceServiceReporter
, nsIMemoryReporter
)
3412 MOZ_DEFINE_MALLOC_SIZE_OF(PreferenceServiceMallocSizeOf
)
3415 PreferenceServiceReporter::CollectReports(
3416 nsIHandleReportCallback
* aHandleReport
, nsISupports
* aData
,
3418 MOZ_ASSERT(NS_IsMainThread());
3420 MallocSizeOf mallocSizeOf
= PreferenceServiceMallocSizeOf
;
3423 Preferences::AddSizeOfIncludingThis(mallocSizeOf
, sizes
);
3426 sizes
.mHashTable
+= HashTable()->shallowSizeOfIncludingThis(mallocSizeOf
);
3427 for (auto iter
= HashTable()->iter(); !iter
.done(); iter
.next()) {
3428 iter
.get()->AddSizeOfIncludingThis(mallocSizeOf
, sizes
);
3432 sizes
.mPrefNameArena
+= PrefNameArena().SizeOfExcludingThis(mallocSizeOf
);
3434 for (CallbackNode
* node
= gFirstCallback
; node
; node
= node
->Next()) {
3435 node
->AddSizeOfIncludingThis(mallocSizeOf
, sizes
);
3439 sizes
.mMisc
+= mallocSizeOf(gSharedMap
);
3442 #ifdef ACCESS_COUNTS
3443 if (gAccessCounts
) {
3444 sizes
.mMisc
+= gAccessCounts
->ShallowSizeOfIncludingThis(mallocSizeOf
);
3448 MOZ_COLLECT_REPORT("explicit/preferences/hash-table", KIND_HEAP
, UNITS_BYTES
,
3449 sizes
.mHashTable
, "Memory used by libpref's hash table.");
3451 MOZ_COLLECT_REPORT("explicit/preferences/pref-values", KIND_HEAP
, UNITS_BYTES
,
3453 "Memory used by PrefValues hanging off the hash table.");
3455 MOZ_COLLECT_REPORT("explicit/preferences/string-values", KIND_HEAP
,
3456 UNITS_BYTES
, sizes
.mStringValues
,
3457 "Memory used by libpref's string pref values.");
3459 MOZ_COLLECT_REPORT("explicit/preferences/root-branches", KIND_HEAP
,
3460 UNITS_BYTES
, sizes
.mRootBranches
,
3461 "Memory used by libpref's root branches.");
3463 MOZ_COLLECT_REPORT("explicit/preferences/pref-name-arena", KIND_HEAP
,
3464 UNITS_BYTES
, sizes
.mPrefNameArena
,
3465 "Memory used by libpref's arena for pref names.");
3467 MOZ_COLLECT_REPORT("explicit/preferences/callbacks/objects", KIND_HEAP
,
3468 UNITS_BYTES
, sizes
.mCallbacksObjects
,
3469 "Memory used by pref callback objects.");
3471 MOZ_COLLECT_REPORT("explicit/preferences/callbacks/domains", KIND_HEAP
,
3472 UNITS_BYTES
, sizes
.mCallbacksDomains
,
3473 "Memory used by pref callback domains (pref names and "
3476 MOZ_COLLECT_REPORT("explicit/preferences/misc", KIND_HEAP
, UNITS_BYTES
,
3477 sizes
.mMisc
, "Miscellaneous memory used by libpref.");
3480 if (XRE_IsParentProcess()) {
3481 MOZ_COLLECT_REPORT("explicit/preferences/shared-memory-map", KIND_NONHEAP
,
3482 UNITS_BYTES
, gSharedMap
->MapSize(),
3483 "The shared memory mapping used to share a "
3484 "snapshot of preference values across processes.");
3488 nsPrefBranch
* rootBranch
=
3489 static_cast<nsPrefBranch
*>(Preferences::GetRootBranch());
3494 size_t numStrong
= 0;
3495 size_t numWeakAlive
= 0;
3496 size_t numWeakDead
= 0;
3497 nsTArray
<nsCString
> suspectPreferences
;
3498 // Count of the number of referents for each preference.
3499 nsTHashMap
<nsCStringHashKey
, uint32_t> prefCounter
;
3501 for (const auto& entry
: rootBranch
->mObservers
) {
3502 auto* callback
= entry
.GetWeak();
3504 if (callback
->IsWeak()) {
3505 nsCOMPtr
<nsIObserver
> callbackRef
= do_QueryReferent(callback
->mWeakRef
);
3515 const uint32_t currentCount
= prefCounter
.Get(callback
->GetDomain()) + 1;
3516 prefCounter
.InsertOrUpdate(callback
->GetDomain(), currentCount
);
3518 // Keep track of preferences that have a suspiciously large number of
3519 // referents (a symptom of a leak).
3520 if (currentCount
== kSuspectReferentCount
) {
3521 suspectPreferences
.AppendElement(callback
->GetDomain());
3525 for (uint32_t i
= 0; i
< suspectPreferences
.Length(); i
++) {
3526 nsCString
& suspect
= suspectPreferences
[i
];
3527 const uint32_t totalReferentCount
= prefCounter
.Get(suspect
);
3529 nsPrintfCString
suspectPath(
3530 "preference-service-suspect/"
3531 "referent(pref=%s)",
3534 aHandleReport
->Callback(
3535 /* process = */ ""_ns
, suspectPath
, KIND_OTHER
, UNITS_COUNT
,
3537 "A preference with a suspiciously large number "
3538 "referents (symptom of a leak)."_ns
,
3543 "preference-service/referent/strong", KIND_OTHER
, UNITS_COUNT
, numStrong
,
3544 "The number of strong referents held by the preference service.");
3547 "preference-service/referent/weak/alive", KIND_OTHER
, UNITS_COUNT
,
3549 "The number of weak referents held by the preference service that are "
3553 "preference-service/referent/weak/dead", KIND_OTHER
, UNITS_COUNT
,
3555 "The number of weak referents held by the preference service that are "
3563 class AddPreferencesMemoryReporterRunnable
: public Runnable
{
3565 AddPreferencesMemoryReporterRunnable()
3566 : Runnable("AddPreferencesMemoryReporterRunnable") {}
3568 NS_IMETHOD
Run() override
{
3569 return RegisterStrongMemoryReporter(new PreferenceServiceReporter());
3575 // A list of changed prefs sent from the parent via shared memory.
3576 static StaticAutoPtr
<nsTArray
<dom::Pref
>> gChangedDomPrefs
;
3578 static const char kTelemetryPref
[] = "toolkit.telemetry.enabled";
3579 static const char kChannelPref
[] = "app.update.channel";
3581 #ifdef MOZ_WIDGET_ANDROID
3583 static Maybe
<bool> TelemetryPrefValue() {
3584 // Leave it unchanged if it's already set.
3585 // XXX: how could it already be set?
3586 if (Preferences::GetType(kTelemetryPref
) != nsIPrefBranch::PREF_INVALID
) {
3590 // Determine the correct default for toolkit.telemetry.enabled. If this
3591 // build has MOZ_TELEMETRY_ON_BY_DEFAULT *or* we're on the beta channel,
3592 // telemetry is on by default, otherwise not. This is necessary so that
3593 // beta users who are testing final release builds don't flipflop defaults.
3594 # ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
3597 nsAutoCString channelPrefValue
;
3598 Unused
<< Preferences::GetCString(kChannelPref
, channelPrefValue
,
3599 PrefValueKind::Default
);
3600 return Some(channelPrefValue
.EqualsLiteral("beta"));
3605 void Preferences::SetupTelemetryPref() {
3606 MOZ_ASSERT(XRE_IsParentProcess());
3608 Maybe
<bool> telemetryPrefValue
= TelemetryPrefValue();
3609 if (telemetryPrefValue
.isSome()) {
3610 Preferences::SetBool(kTelemetryPref
, *telemetryPrefValue
,
3611 PrefValueKind::Default
);
3615 #else // !MOZ_WIDGET_ANDROID
3617 static bool TelemetryPrefValue() {
3618 // For platforms with Unified Telemetry (here meaning not-Android),
3619 // toolkit.telemetry.enabled determines whether we send "extended" data.
3620 // We only want extended data from pre-release channels due to size.
3622 constexpr auto channel
= MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL
) ""_ns
;
3624 // Easy cases: Nightly, Aurora, Beta.
3625 if (channel
.EqualsLiteral("nightly") || channel
.EqualsLiteral("aurora") ||
3626 channel
.EqualsLiteral("beta")) {
3630 # ifndef MOZILLA_OFFICIAL
3631 // Local developer builds: non-official builds on the "default" channel.
3632 if (channel
.EqualsLiteral("default")) {
3637 // Release Candidate builds: builds that think they are release builds, but
3638 // are shipped to beta users.
3639 if (channel
.EqualsLiteral("release")) {
3640 nsAutoCString channelPrefValue
;
3641 Unused
<< Preferences::GetCString(kChannelPref
, channelPrefValue
,
3642 PrefValueKind::Default
);
3643 if (channelPrefValue
.EqualsLiteral("beta")) {
3652 void Preferences::SetupTelemetryPref() {
3653 MOZ_ASSERT(XRE_IsParentProcess());
3655 Preferences::SetBool(kTelemetryPref
, TelemetryPrefValue(),
3656 PrefValueKind::Default
);
3657 Preferences::Lock(kTelemetryPref
);
3660 #endif // MOZ_WIDGET_ANDROID
3663 already_AddRefed
<Preferences
> Preferences::GetInstanceForService() {
3665 return do_AddRef(sPreferences
);
3672 sPreferences
= new Preferences();
3674 MOZ_ASSERT(!HashTable());
3675 HashTable() = new PrefsHashTable(XRE_IsParentProcess()
3676 ? kHashTableInitialLengthParent
3677 : kHashTableInitialLengthContent
);
3680 gOnceStaticPrefsAntiFootgun
= new AntiFootgunMap();
3683 #ifdef ACCESS_COUNTS
3684 MOZ_ASSERT(!gAccessCounts
);
3685 gAccessCounts
= new AccessCountsHashTable();
3688 nsresult rv
= InitInitialObjects(/* isStartup */ true);
3689 if (NS_FAILED(rv
)) {
3690 sPreferences
= nullptr;
3694 if (!XRE_IsParentProcess()) {
3695 MOZ_ASSERT(gChangedDomPrefs
);
3696 for (unsigned int i
= 0; i
< gChangedDomPrefs
->Length(); i
++) {
3697 Preferences::SetPreference(gChangedDomPrefs
->ElementAt(i
));
3699 gChangedDomPrefs
= nullptr;
3701 // Check if there is a deployment configuration file. If so, set up the
3702 // pref config machinery, which will actually read the file.
3703 nsAutoCString lockFileName
;
3704 nsresult rv
= Preferences::GetCString("general.config.filename",
3705 lockFileName
, PrefValueKind::User
);
3706 if (NS_SUCCEEDED(rv
)) {
3707 NS_CreateServicesFromCategory(
3708 "pref-config-startup",
3709 static_cast<nsISupports
*>(static_cast<void*>(sPreferences
)),
3710 "pref-config-startup");
3713 nsCOMPtr
<nsIObserverService
> observerService
=
3714 services::GetObserverService();
3715 if (!observerService
) {
3716 sPreferences
= nullptr;
3720 observerService
->AddObserver(sPreferences
,
3721 "profile-before-change-telemetry", true);
3722 rv
= observerService
->AddObserver(sPreferences
, "profile-before-change",
3725 observerService
->AddObserver(sPreferences
, "suspend_process_notification",
3728 if (NS_FAILED(rv
)) {
3729 sPreferences
= nullptr;
3734 const char* defaultPrefs
= getenv("MOZ_DEFAULT_PREFS");
3736 parsePrefData(nsCString(defaultPrefs
), PrefValueKind::Default
);
3739 // Preferences::GetInstanceForService() can be called from GetService(), and
3740 // RegisterStrongMemoryReporter calls GetService(nsIMemoryReporter). To
3741 // avoid a potential recursive GetService() call, we can't register the
3742 // memory reporter here; instead, do it off a runnable.
3743 RefPtr
<AddPreferencesMemoryReporterRunnable
> runnable
=
3744 new AddPreferencesMemoryReporterRunnable();
3745 NS_DispatchToMainThread(runnable
);
3747 return do_AddRef(sPreferences
);
3751 bool Preferences::IsServiceAvailable() { return !!sPreferences
; }
3754 bool Preferences::InitStaticMembers() {
3755 MOZ_ASSERT(NS_IsMainThread() || ServoStyleSet::IsInServoTraversal());
3757 if (MOZ_LIKELY(sPreferences
)) {
3762 MOZ_ASSERT(NS_IsMainThread());
3763 nsCOMPtr
<nsIPrefService
> prefService
=
3764 do_GetService(NS_PREFSERVICE_CONTRACTID
);
3767 return sPreferences
!= nullptr;
3771 void Preferences::Shutdown() {
3773 sShutdown
= true; // Don't create the singleton instance after here.
3774 sPreferences
= nullptr;
3775 StaticPrefs::ShutdownAlwaysPrefs();
3779 Preferences::Preferences()
3780 : mRootBranch(new nsPrefBranch("", PrefValueKind::User
)),
3781 mDefaultRootBranch(new nsPrefBranch("", PrefValueKind::Default
)) {}
3783 Preferences::~Preferences() {
3784 MOZ_ASSERT(!sPreferences
);
3786 MOZ_ASSERT(!gCallbacksInProgress
);
3788 CallbackNode
* node
= gFirstCallback
;
3790 CallbackNode
* next_node
= node
->Next();
3794 gLastPriorityNode
= gFirstCallback
= nullptr;
3797 HashTable() = nullptr;
3800 gOnceStaticPrefsAntiFootgun
= nullptr;
3803 #ifdef ACCESS_COUNTS
3804 gAccessCounts
= nullptr;
3807 gSharedMap
= nullptr;
3809 PrefNameArena().Clear();
3812 NS_IMPL_ISUPPORTS(Preferences
, nsIPrefService
, nsIObserver
, nsIPrefBranch
,
3813 nsISupportsWeakReference
)
3816 void Preferences::SerializePreferences(nsCString
& aStr
,
3817 bool aIsDestinationWebContentProcess
) {
3818 MOZ_RELEASE_ASSERT(InitStaticMembers());
3822 for (auto iter
= HashTable()->iter(); !iter
.done(); iter
.next()) {
3823 Pref
* pref
= iter
.get().get();
3824 if (!pref
->IsTypeNone() && pref
->HasAdvisablySizedValues()) {
3825 pref
->SerializeAndAppend(aStr
, aIsDestinationWebContentProcess
&&
3826 ShouldSanitizePreference(pref
));
3834 void Preferences::DeserializePreferences(char* aStr
, size_t aPrefsLen
) {
3835 MOZ_ASSERT(!XRE_IsParentProcess());
3837 MOZ_ASSERT(!gChangedDomPrefs
);
3838 gChangedDomPrefs
= new nsTArray
<dom::Pref
>();
3841 while (*p
!= '\0') {
3843 p
= Pref::Deserialize(p
, &pref
);
3844 gChangedDomPrefs
->AppendElement(pref
);
3847 // We finished parsing on a '\0'. That should be the last char in the shared
3848 // memory. (aPrefsLen includes the '\0'.)
3849 MOZ_ASSERT(p
== aStr
+ aPrefsLen
- 1);
3851 MOZ_ASSERT(!gContentProcessPrefsAreInited
);
3852 gContentProcessPrefsAreInited
= true;
3856 FileDescriptor
Preferences::EnsureSnapshot(size_t* aSize
) {
3857 MOZ_ASSERT(XRE_IsParentProcess());
3858 MOZ_ASSERT(NS_IsMainThread());
3861 SharedPrefMapBuilder builder
;
3863 nsTArray
<Pref
*> toRepopulate
;
3864 NameArena
* newPrefNameArena
= new NameArena();
3865 for (auto iter
= HashTable()->modIter(); !iter
.done(); iter
.next()) {
3866 if (!ShouldSanitizePreference(iter
.get().get())) {
3867 iter
.get()->AddToMap(builder
);
3869 Pref
* pref
= iter
.getMutable().release();
3870 pref
->RelocateName(newPrefNameArena
);
3871 toRepopulate
.AppendElement(pref
);
3875 // Store the current value of `once`-mirrored prefs. After this point they
3876 // will be immutable.
3877 StaticPrefs::RegisterOncePrefs(builder
);
3879 gSharedMap
= new SharedPrefMap(std::move(builder
));
3881 // Once we've built a snapshot of the database, there's no need to continue
3882 // storing dynamic copies of the preferences it contains. Once we reset the
3883 // hashtable, preference lookups will fall back to the snapshot for any
3884 // preferences not in the dynamic hashtable.
3886 // And since the majority of the database is now contained in the snapshot,
3887 // we can initialize the hashtable with the expected number of per-session
3888 // changed preferences, rather than the expected total number of
3890 HashTable()->clearAndCompact();
3891 Unused
<< HashTable()->reserve(kHashTableInitialLengthContent
);
3893 delete sPrefNameArena
;
3894 sPrefNameArena
= newPrefNameArena
;
3895 gCallbackPref
= nullptr;
3897 for (uint32_t i
= 0; i
< toRepopulate
.Length(); i
++) {
3898 auto pref
= toRepopulate
[i
];
3899 auto p
= HashTable()->lookupForAdd(pref
->Name());
3900 MOZ_ASSERT(!p
.found());
3901 Unused
<< HashTable()->add(p
, pref
);
3905 *aSize
= gSharedMap
->MapSize();
3906 return gSharedMap
->CloneFileDescriptor();
3910 void Preferences::InitSnapshot(const FileDescriptor
& aHandle
, size_t aSize
) {
3911 MOZ_ASSERT(!XRE_IsParentProcess());
3912 MOZ_ASSERT(!gSharedMap
);
3914 gSharedMap
= new SharedPrefMap(aHandle
, aSize
);
3916 StaticPrefs::InitStaticPrefsFromShared();
3920 void Preferences::InitializeUserPrefs() {
3921 MOZ_ASSERT(XRE_IsParentProcess());
3922 MOZ_ASSERT(!sPreferences
->mCurrentFile
, "Should only initialize prefs once");
3924 // Prefs which are set before we initialize the profile are silently
3925 // discarded. This is stupid, but there are various tests which depend on
3927 sPreferences
->ResetUserPrefs();
3929 nsCOMPtr
<nsIFile
> prefsFile
= sPreferences
->ReadSavedPrefs();
3930 sPreferences
->ReadUserOverridePrefs();
3932 sPreferences
->mDirty
= false;
3934 // Don't set mCurrentFile until we're done so that dirty flags work properly.
3935 sPreferences
->mCurrentFile
= std::move(prefsFile
);
3939 void Preferences::FinishInitializingUserPrefs() {
3940 sPreferences
->NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID
);
3944 Preferences::Observe(nsISupports
* aSubject
, const char* aTopic
,
3945 const char16_t
* someData
) {
3946 if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
3947 return NS_ERROR_NOT_AVAILABLE
;
3950 nsresult rv
= NS_OK
;
3952 if (!nsCRT::strcmp(aTopic
, "profile-before-change")) {
3953 // Normally prefs aren't written after this point, and so we kick off
3954 // an asynchronous pref save so that I/O can be done in parallel with
3956 if (AllowOffMainThreadSave()) {
3957 SavePrefFile(nullptr);
3960 } else if (!nsCRT::strcmp(aTopic
, "profile-before-change-telemetry")) {
3961 // It's possible that a profile-before-change observer after ours
3962 // set a pref. A blocking save here re-saves if necessary and also waits
3963 // for any pending saves to complete.
3964 SavePrefFileBlocking();
3965 MOZ_ASSERT(!mDirty
, "Preferences should not be dirty");
3966 mProfileShutdown
= true;
3968 } else if (!nsCRT::strcmp(aTopic
, "suspend_process_notification")) {
3969 // Our process is being suspended. The OS may wake our process later,
3970 // or it may kill the process. In case our process is going to be killed
3971 // from the suspended state, we save preferences before suspending.
3972 rv
= SavePrefFileBlocking();
3979 Preferences::ReadDefaultPrefsFromFile(nsIFile
* aFile
) {
3980 ENSURE_PARENT_PROCESS("Preferences::ReadDefaultPrefsFromFile", "all prefs");
3983 NS_ERROR("ReadDefaultPrefsFromFile requires a parameter");
3984 return NS_ERROR_INVALID_ARG
;
3987 return openPrefFile(aFile
, PrefValueKind::Default
);
3991 Preferences::ReadUserPrefsFromFile(nsIFile
* aFile
) {
3992 ENSURE_PARENT_PROCESS("Preferences::ReadUserPrefsFromFile", "all prefs");
3995 NS_ERROR("ReadUserPrefsFromFile requires a parameter");
3996 return NS_ERROR_INVALID_ARG
;
3999 return openPrefFile(aFile
, PrefValueKind::User
);
4003 Preferences::ResetPrefs() {
4004 ENSURE_PARENT_PROCESS("Preferences::ResetPrefs", "all prefs");
4007 return NS_ERROR_NOT_AVAILABLE
;
4010 HashTable()->clearAndCompact();
4011 Unused
<< HashTable()->reserve(kHashTableInitialLengthParent
);
4013 PrefNameArena().Clear();
4015 return InitInitialObjects(/* isStartup */ false);
4018 nsresult
Preferences::ResetUserPrefs() {
4019 ENSURE_PARENT_PROCESS("Preferences::ResetUserPrefs", "all prefs");
4020 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
4021 MOZ_ASSERT(NS_IsMainThread());
4023 Vector
<const char*> prefNames
;
4024 for (auto iter
= HashTable()->modIter(); !iter
.done(); iter
.next()) {
4025 Pref
* pref
= iter
.get().get();
4027 if (pref
->HasUserValue()) {
4028 if (!prefNames
.append(pref
->Name())) {
4029 return NS_ERROR_OUT_OF_MEMORY
;
4032 pref
->ClearUserValue();
4033 if (!pref
->HasDefaultValue()) {
4039 for (const char* prefName
: prefNames
) {
4040 NotifyCallbacks(nsDependentCString(prefName
));
4043 Preferences::HandleDirty();
4047 bool Preferences::AllowOffMainThreadSave() {
4048 // Put in a preference that allows us to disable off main thread preference
4050 if (sAllowOMTPrefWrite
< 0) {
4052 Preferences::GetBool("preferences.allow.omt-write", &value
);
4053 sAllowOMTPrefWrite
= value
? 1 : 0;
4056 return !!sAllowOMTPrefWrite
;
4059 nsresult
Preferences::SavePrefFileBlocking() {
4061 return SavePrefFileInternal(nullptr, SaveMethod::Blocking
);
4064 // If we weren't dirty to start, SavePrefFileInternal will early exit so
4065 // there is no guarantee that we don't have oustanding async saves in the
4066 // pipe. Since the contract of SavePrefFileOnMainThread is that the file on
4067 // disk matches the preferences, we have to make sure those requests are
4070 if (AllowOffMainThreadSave()) {
4071 PreferencesWriter::Flush();
4077 nsresult
Preferences::SavePrefFileAsynchronous() {
4078 return SavePrefFileInternal(nullptr, SaveMethod::Asynchronous
);
4082 Preferences::SavePrefFile(nsIFile
* aFile
) {
4083 // This is the method accessible from service API. Make it off main thread.
4084 return SavePrefFileInternal(aFile
, SaveMethod::Asynchronous
);
4088 Preferences::BackupPrefFile(nsIFile
* aFile
, JSContext
* aCx
,
4089 Promise
** aPromise
) {
4090 MOZ_ASSERT(NS_IsMainThread());
4093 return NS_ERROR_INVALID_ARG
;
4097 bool equalsCurrent
= false;
4098 nsresult rv
= aFile
->Equals(mCurrentFile
, &equalsCurrent
);
4100 if (NS_FAILED(rv
)) {
4104 if (equalsCurrent
) {
4105 return NS_ERROR_INVALID_ARG
;
4110 RefPtr
<Promise
> promise
=
4111 Promise::Create(xpc::CurrentNativeGlobal(aCx
), result
);
4113 if (MOZ_UNLIKELY(result
.Failed())) {
4114 return result
.StealNSResult();
4117 nsMainThreadPtrHandle
<Promise
> domPromiseHolder(
4118 new nsMainThreadPtrHolder
<Promise
>("Preferences::BackupPrefFile promise",
4121 auto mozPromiseHolder
= MakeUnique
<MozPromiseHolder
<WritePrefFilePromise
>>();
4122 RefPtr
<WritePrefFilePromise
> writePrefPromise
=
4123 mozPromiseHolder
->Ensure(__func__
);
4125 nsresult rv
= WritePrefFile(aFile
, SaveMethod::Asynchronous
,
4126 std::move(mozPromiseHolder
));
4127 if (NS_FAILED(rv
)) {
4128 // WritePrefFile is responsible for rejecting the underlying MozPromise in
4129 // the event that it the method failed somewhere.
4133 writePrefPromise
->Then(
4134 GetMainThreadSerialEventTarget(), __func__
,
4135 [domPromiseHolder
](bool) {
4136 MOZ_ASSERT(NS_IsMainThread());
4137 domPromiseHolder
.get()->MaybeResolveWithUndefined();
4139 [domPromiseHolder
](nsresult rv
) {
4140 MOZ_ASSERT(NS_IsMainThread());
4141 domPromiseHolder
.get()->MaybeReject(rv
);
4144 promise
.forget(aPromise
);
4149 void Preferences::SetPreference(const dom::Pref
& aDomPref
) {
4150 MOZ_ASSERT(!XRE_IsParentProcess());
4151 NS_ENSURE_TRUE(InitStaticMembers(), (void)0);
4153 const nsCString
& prefName
= aDomPref
.name();
4156 auto p
= HashTable()->lookupForAdd(prefName
.get());
4158 pref
= new Pref(prefName
);
4159 if (!HashTable()->add(p
, pref
)) {
4167 bool valueChanged
= false;
4168 pref
->FromDomPref(aDomPref
, &valueChanged
);
4170 // When the parent process clears a pref's user value we get a DomPref here
4171 // with no default value and no user value. There are two possibilities.
4173 // - There was an existing pref with only a user value. FromDomPref() will
4174 // have just cleared that user value, so the pref can be removed.
4176 // - There was no existing pref. FromDomPref() will have done nothing, and
4177 // `pref` will be valueless. We will end up adding and removing the value
4178 // needlessly, but that's ok because this case is rare.
4180 if (!pref
->HasDefaultValue() && !pref
->HasUserValue() &&
4181 !pref
->IsSanitized()) {
4182 // If the preference exists in the shared map, we need to keep the dynamic
4183 // entry around to mask it.
4184 if (gSharedMap
->Has(pref
->Name())) {
4185 pref
->SetType(PrefType::None
);
4187 HashTable()->remove(prefName
.get());
4192 // Note: we don't have to worry about HandleDirty() because we are setting
4193 // prefs in the content process that have come from the parent process.
4197 NotifyCallbacks(prefName
, PrefWrapper(pref
));
4199 NotifyCallbacks(prefName
);
4205 void Preferences::GetPreference(dom::Pref
* aDomPref
,
4206 const GeckoProcessType aDestinationProcessType
,
4207 const nsACString
& aDestinationRemoteType
) {
4208 MOZ_ASSERT(XRE_IsParentProcess());
4209 bool destIsWebContent
=
4210 aDestinationProcessType
== GeckoProcessType_Content
&&
4211 (StringBeginsWith(aDestinationRemoteType
, WEB_REMOTE_TYPE
) ||
4212 StringBeginsWith(aDestinationRemoteType
, PREALLOC_REMOTE_TYPE
) ||
4213 StringBeginsWith(aDestinationRemoteType
, PRIVILEGEDMOZILLA_REMOTE_TYPE
));
4215 Pref
* pref
= pref_HashTableLookup(aDomPref
->name().get());
4216 if (pref
&& pref
->HasAdvisablySizedValues()) {
4217 pref
->ToDomPref(aDomPref
, destIsWebContent
);
4222 bool Preferences::ArePrefsInitedInContentProcess() {
4223 MOZ_ASSERT(!XRE_IsParentProcess());
4224 return gContentProcessPrefsAreInited
;
4229 Preferences::GetBranch(const char* aPrefRoot
, nsIPrefBranch
** aRetVal
) {
4230 if ((nullptr != aPrefRoot
) && (*aPrefRoot
!= '\0')) {
4231 // TODO: Cache this stuff and allow consumers to share branches (hold weak
4232 // references, I think).
4233 RefPtr
<nsPrefBranch
> prefBranch
=
4234 new nsPrefBranch(aPrefRoot
, PrefValueKind::User
);
4235 prefBranch
.forget(aRetVal
);
4237 // Special case: caching the default root.
4238 nsCOMPtr
<nsIPrefBranch
> root(sPreferences
->mRootBranch
);
4239 root
.forget(aRetVal
);
4246 Preferences::GetDefaultBranch(const char* aPrefRoot
, nsIPrefBranch
** aRetVal
) {
4247 if (!aPrefRoot
|| !aPrefRoot
[0]) {
4248 nsCOMPtr
<nsIPrefBranch
> root(sPreferences
->mDefaultRootBranch
);
4249 root
.forget(aRetVal
);
4253 // TODO: Cache this stuff and allow consumers to share branches (hold weak
4254 // references, I think).
4255 RefPtr
<nsPrefBranch
> prefBranch
=
4256 new nsPrefBranch(aPrefRoot
, PrefValueKind::Default
);
4258 return NS_ERROR_OUT_OF_MEMORY
;
4261 prefBranch
.forget(aRetVal
);
4266 Preferences::ReadStats(nsIPrefStatsCallback
* aCallback
) {
4267 #ifdef ACCESS_COUNTS
4268 for (const auto& entry
: *gAccessCounts
) {
4269 aCallback
->Visit(entry
.GetKey(), entry
.GetData());
4274 return NS_ERROR_NOT_IMPLEMENTED
;
4279 Preferences::ResetStats() {
4280 #ifdef ACCESS_COUNTS
4281 gAccessCounts
->Clear();
4284 return NS_ERROR_NOT_IMPLEMENTED
;
4288 // We would much prefer to use C++ lambdas, but we cannot convert
4289 // lambdas that capture (here, the underlying observer) to C pointer
4290 // to functions. So, here we are, with icky C callbacks. Be aware
4291 // that nothing is thread-safe here because there's a single global
4292 // `nsIPrefObserver` instance. Use this from the main thread only.
4293 nsIPrefObserver
* PrefObserver
= nullptr;
4295 void HandlePref(const char* aPrefName
, PrefType aType
, PrefValueKind aKind
,
4296 PrefValue aValue
, bool aIsSticky
, bool aIsLocked
) {
4297 MOZ_ASSERT(NS_IsMainThread());
4299 if (!PrefObserver
) {
4303 const char* kind
= aKind
== PrefValueKind::Default
? "Default" : "User";
4306 case PrefType::String
:
4307 PrefObserver
->OnStringPref(kind
, aPrefName
, aValue
.mStringVal
, aIsSticky
,
4311 PrefObserver
->OnIntPref(kind
, aPrefName
, aValue
.mIntVal
, aIsSticky
,
4314 case PrefType::Bool
:
4315 PrefObserver
->OnBoolPref(kind
, aPrefName
, aValue
.mBoolVal
, aIsSticky
,
4319 PrefObserver
->OnError("Unexpected pref type.");
4323 void HandleError(const char* aMsg
) {
4324 MOZ_ASSERT(NS_IsMainThread());
4326 if (!PrefObserver
) {
4330 PrefObserver
->OnError(aMsg
);
4334 Preferences::ParsePrefsFromBuffer(const nsTArray
<uint8_t>& aBytes
,
4335 nsIPrefObserver
* aObserver
,
4336 const char* aPathLabel
) {
4337 MOZ_ASSERT(NS_IsMainThread());
4339 // We need a null-terminated buffer.
4340 nsTArray
<uint8_t> data
= aBytes
.Clone();
4341 data
.AppendElement(0);
4343 // Parsing as default handles both `pref` and `user_pref`.
4344 PrefObserver
= aObserver
;
4345 prefs_parser_parse(aPathLabel
? aPathLabel
: "<ParsePrefsFromBuffer data>",
4346 PrefValueKind::Default
, (const char*)data
.Elements(),
4347 data
.Length() - 1, HandlePref
, HandleError
);
4348 PrefObserver
= nullptr;
4354 Preferences::GetUserPrefsFileLastModifiedAtStartup(PRTime
* aLastModified
) {
4355 *aLastModified
= mUserPrefsFileLastModifiedAtStartup
;
4360 Preferences::GetDirty(bool* aRetVal
) {
4365 nsresult
Preferences::NotifyServiceObservers(const char* aTopic
) {
4366 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
4367 if (!observerService
) {
4368 return NS_ERROR_FAILURE
;
4371 auto subject
= static_cast<nsIPrefService
*>(this);
4372 observerService
->NotifyObservers(subject
, aTopic
, nullptr);
4377 already_AddRefed
<nsIFile
> Preferences::ReadSavedPrefs() {
4378 nsCOMPtr
<nsIFile
> file
;
4380 NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE
, getter_AddRefs(file
));
4381 if (NS_WARN_IF(NS_FAILED(rv
))) {
4385 rv
= openPrefFile(file
, PrefValueKind::User
);
4386 if (rv
== NS_ERROR_FILE_NOT_FOUND
) {
4387 // This is a normal case for new users.
4390 // Store the last modified time of the file while we've got it.
4391 // We don't really care if this fails.
4392 Unused
<< file
->GetLastModifiedTime(&mUserPrefsFileLastModifiedAtStartup
);
4394 if (NS_FAILED(rv
)) {
4395 // Save a backup copy of the current (invalid) prefs file, since all prefs
4396 // from the error line to the end of the file will be lost (bug 361102).
4397 // TODO we should notify the user about it (bug 523725).
4398 Telemetry::ScalarSet(
4399 Telemetry::ScalarID::PREFERENCES_PREFS_FILE_WAS_INVALID
, true);
4400 MakeBackupPrefFile(file
);
4404 return file
.forget();
4407 void Preferences::ReadUserOverridePrefs() {
4408 nsCOMPtr
<nsIFile
> aFile
;
4410 NS_GetSpecialDirectory(NS_APP_PREFS_50_DIR
, getter_AddRefs(aFile
));
4411 if (NS_WARN_IF(NS_FAILED(rv
))) {
4415 aFile
->AppendNative("user.js"_ns
);
4416 rv
= openPrefFile(aFile
, PrefValueKind::User
);
4419 nsresult
Preferences::MakeBackupPrefFile(nsIFile
* aFile
) {
4420 // Example: this copies "prefs.js" to "Invalidprefs.js" in the same directory.
4421 // "Invalidprefs.js" is removed if it exists, prior to making the copy.
4422 nsAutoString newFilename
;
4423 nsresult rv
= aFile
->GetLeafName(newFilename
);
4424 NS_ENSURE_SUCCESS(rv
, rv
);
4426 newFilename
.InsertLiteral(u
"Invalid", 0);
4427 nsCOMPtr
<nsIFile
> newFile
;
4428 rv
= aFile
->GetParent(getter_AddRefs(newFile
));
4429 NS_ENSURE_SUCCESS(rv
, rv
);
4431 rv
= newFile
->Append(newFilename
);
4432 NS_ENSURE_SUCCESS(rv
, rv
);
4434 bool exists
= false;
4435 newFile
->Exists(&exists
);
4437 rv
= newFile
->Remove(false);
4438 NS_ENSURE_SUCCESS(rv
, rv
);
4441 rv
= aFile
->CopyTo(nullptr, newFilename
);
4442 NS_ENSURE_SUCCESS(rv
, rv
);
4447 nsresult
Preferences::SavePrefFileInternal(nsIFile
* aFile
,
4448 SaveMethod aSaveMethod
) {
4449 ENSURE_PARENT_PROCESS("Preferences::SavePrefFileInternal", "all prefs");
4451 // We allow different behavior here when aFile argument is not null, but it
4452 // happens to be the same as the current file. It is not clear that we
4453 // should, but it does give us a "force" save on the unmodified pref file
4454 // (see the original bug 160377 when we added this.)
4456 if (nullptr == aFile
) {
4457 mSavePending
= false;
4459 // Off main thread writing only if allowed.
4460 if (!AllowOffMainThreadSave()) {
4461 aSaveMethod
= SaveMethod::Blocking
;
4464 // The mDirty flag tells us if we should write to mCurrentFile. We only
4465 // check this flag when the caller wants to write to the default.
4470 // Check for profile shutdown after mDirty because the runnables from
4471 // HandleDirty() can still be pending.
4472 if (mProfileShutdown
) {
4473 NS_WARNING("Cannot save pref file after profile shutdown.");
4474 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN
;
4477 // It's possible that we never got a prefs file.
4478 nsresult rv
= NS_OK
;
4480 rv
= WritePrefFile(mCurrentFile
, aSaveMethod
);
4483 // If we succeeded writing to mCurrentFile, reset the dirty flag.
4484 if (NS_SUCCEEDED(rv
)) {
4490 // We only allow off main thread writes on mCurrentFile using this method.
4491 // If you want to write asynchronously, use BackupPrefFile instead.
4492 return WritePrefFile(aFile
, SaveMethod::Blocking
);
4496 nsresult
Preferences::WritePrefFile(
4497 nsIFile
* aFile
, SaveMethod aSaveMethod
,
4498 UniquePtr
<MozPromiseHolder
<WritePrefFilePromise
>>
4499 aPromiseHolder
/* = nullptr */) {
4500 MOZ_ASSERT(XRE_IsParentProcess());
4502 #define REJECT_IF_PROMISE_HOLDER_EXISTS(rv) \
4503 if (aPromiseHolder) { \
4504 aPromiseHolder->RejectIfExists(rv, __func__); \
4509 REJECT_IF_PROMISE_HOLDER_EXISTS(NS_ERROR_NOT_INITIALIZED
);
4512 AUTO_PROFILER_LABEL("Preferences::WritePrefFile", OTHER
);
4514 if (AllowOffMainThreadSave()) {
4515 UniquePtr
<PrefSaveData
> prefs
= MakeUnique
<PrefSaveData
>(pref_savePrefs());
4517 nsresult rv
= NS_OK
;
4518 bool writingToCurrent
= false;
4521 rv
= mCurrentFile
->Equals(aFile
, &writingToCurrent
);
4522 if (NS_FAILED(rv
)) {
4523 REJECT_IF_PROMISE_HOLDER_EXISTS(rv
);
4527 // Put the newly constructed preference data into sPendingWriteData
4528 // for the next request to pick up
4529 prefs
.reset(PreferencesWriter::sPendingWriteData
.exchange(prefs
.release()));
4530 if (prefs
&& !writingToCurrent
) {
4531 MOZ_ASSERT(!aPromiseHolder
,
4532 "Shouldn't be able to enter here if aPromiseHolder is set");
4533 // There was a previous request writing to the default location that
4534 // hasn't been processed. It will do the work of eventually writing this
4535 // latest batch of data to disk.
4539 // There were no previous requests. Dispatch one since sPendingWriteData has
4540 // the up to date information.
4541 nsCOMPtr
<nsIEventTarget
> target
=
4542 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
, &rv
);
4543 if (NS_SUCCEEDED(rv
)) {
4544 bool async
= aSaveMethod
== SaveMethod::Asynchronous
;
4546 // Increment sPendingWriteCount, even though it's redundant to track this
4547 // in the case of a sync runnable; it just makes it easier to simply
4548 // decrement this inside PWRunnable. We cannot use the constructor /
4549 // destructor for increment/decrement, as on dispatch failure we might
4550 // leak the runnable in order to not destroy it on the wrong thread, which
4551 // would make us get stuck in an infinite SpinEventLoopUntil inside
4552 // PreferencesWriter::Flush. Better that in future code we miss an
4553 // increment of sPendingWriteCount and cause a simple crash due to it
4554 // ending up negative.
4556 // If aPromiseHolder is not null, ownership is transferred to PWRunnable.
4557 // The PWRunnable will automatically reject the MozPromise if it is
4558 // destroyed before being resolved or rejected by the Run method.
4559 PreferencesWriter::sPendingWriteCount
++;
4561 rv
= target
->Dispatch(new PWRunnable(aFile
, std::move(aPromiseHolder
)),
4562 nsIEventTarget::DISPATCH_NORMAL
);
4564 rv
= SyncRunnable::DispatchToThread(
4565 target
, new PWRunnable(aFile
, std::move(aPromiseHolder
)), true);
4567 if (NS_FAILED(rv
)) {
4568 // If our dispatch failed, we should correct our bookkeeping to
4569 // avoid shutdown hangs.
4570 PreferencesWriter::sPendingWriteCount
--;
4571 // No need to reject the aPromiseHolder here, as the PWRunnable will
4572 // have already done so.
4578 // If we can't get the thread for writing, for whatever reason, do the main
4579 // thread write after making some noise.
4580 MOZ_ASSERT(false, "failed to get the target thread for OMT pref write");
4583 // This will do a main thread write. It is safe to do it this way because
4584 // AllowOffMainThreadSave() returns a consistent value for the lifetime of
4585 // the parent process.
4586 PrefSaveData prefsData
= pref_savePrefs();
4588 // If we were given a MozPromiseHolder, this means the caller is attempting
4589 // to write prefs asynchronously to the disk - but if we get here, it means
4590 // that AllowOffMainThreadSave() return false, and that we will be forced
4591 // to write on the main thread instead. We still have to resolve or reject
4592 // that MozPromise regardless.
4593 nsresult rv
= PreferencesWriter::Write(aFile
, prefsData
);
4594 if (aPromiseHolder
) {
4596 "Cannot write to prefs asynchronously, as AllowOffMainThreadSave() "
4598 if (NS_SUCCEEDED(rv
)) {
4599 aPromiseHolder
->ResolveIfExists(true, __func__
);
4601 aPromiseHolder
->RejectIfExists(rv
, __func__
);
4606 #undef REJECT_IF_PROMISE_HOLDER_EXISTS
4609 static nsresult
openPrefFile(nsIFile
* aFile
, PrefValueKind aKind
) {
4610 MOZ_ASSERT(XRE_IsParentProcess());
4613 MOZ_TRY_VAR(data
, URLPreloader::ReadFile(aFile
));
4615 nsAutoString filenameUtf16
;
4616 aFile
->GetLeafName(filenameUtf16
);
4617 NS_ConvertUTF16toUTF8
filename(filenameUtf16
);
4620 aFile
->GetPath(path
);
4623 if (!parser
.Parse(aKind
, NS_ConvertUTF16toUTF8(path
).get(), data
)) {
4624 return NS_ERROR_FILE_CORRUPTED
;
4630 static nsresult
parsePrefData(const nsCString
& aData
, PrefValueKind aKind
) {
4631 const nsCString path
= "$MOZ_DEFAULT_PREFS"_ns
;
4634 if (!parser
.Parse(aKind
, path
.get(), aData
)) {
4635 return NS_ERROR_FILE_CORRUPTED
;
4641 static int pref_CompareFileNames(nsIFile
* aFile1
, nsIFile
* aFile2
) {
4642 nsAutoCString filename1
, filename2
;
4643 aFile1
->GetNativeLeafName(filename1
);
4644 aFile2
->GetNativeLeafName(filename2
);
4646 return Compare(filename2
, filename1
);
4649 // Load default pref files from a directory. The files in the directory are
4650 // sorted reverse-alphabetically.
4651 static nsresult
pref_LoadPrefsInDir(nsIFile
* aDir
) {
4652 MOZ_ASSERT(XRE_IsParentProcess());
4656 nsCOMPtr
<nsIDirectoryEnumerator
> dirIterator
;
4658 // This may fail in some normal cases, such as embedders who do not use a
4660 rv
= aDir
->GetDirectoryEntries(getter_AddRefs(dirIterator
));
4661 if (NS_FAILED(rv
)) {
4662 // If the directory doesn't exist, then we have no reason to complain. We
4663 // loaded everything (and nothing) successfully.
4664 if (rv
== NS_ERROR_FILE_NOT_FOUND
) {
4670 nsCOMArray
<nsIFile
> prefFiles(INITIAL_PREF_FILES
);
4671 nsCOMPtr
<nsIFile
> prefFile
;
4673 while (NS_SUCCEEDED(dirIterator
->GetNextFile(getter_AddRefs(prefFile
))) &&
4675 nsAutoCString leafName
;
4676 prefFile
->GetNativeLeafName(leafName
);
4678 !leafName
.IsEmpty(),
4679 "Failure in default prefs: directory enumerator returned empty file?");
4681 // Skip non-js files.
4682 if (StringEndsWith(leafName
, ".js"_ns
,
4683 nsCaseInsensitiveCStringComparator
)) {
4684 prefFiles
.AppendObject(prefFile
);
4688 if (prefFiles
.Count() == 0) {
4689 NS_WARNING("No default pref files found.");
4690 if (NS_SUCCEEDED(rv
)) {
4691 rv
= NS_SUCCESS_FILE_DIRECTORY_EMPTY
;
4696 prefFiles
.Sort(pref_CompareFileNames
);
4698 uint32_t arrayCount
= prefFiles
.Count();
4700 for (i
= 0; i
< arrayCount
; ++i
) {
4701 rv2
= openPrefFile(prefFiles
[i
], PrefValueKind::Default
);
4702 if (NS_FAILED(rv2
)) {
4703 NS_ERROR("Default pref file not parsed successfully.");
4711 static nsresult
pref_ReadPrefFromJar(nsZipArchive
* aJarReader
,
4712 const char* aName
) {
4714 MOZ_TRY_VAR(manifest
,
4715 URLPreloader::ReadZip(aJarReader
, nsDependentCString(aName
)));
4718 if (!parser
.Parse(PrefValueKind::Default
, aName
, manifest
)) {
4719 return NS_ERROR_FILE_CORRUPTED
;
4725 static nsresult
pref_ReadDefaultPrefs(const RefPtr
<nsZipArchive
> jarReader
,
4727 UniquePtr
<nsZipFind
> find
;
4728 nsTArray
<nsCString
> prefEntries
;
4729 const char* entryName
;
4730 uint16_t entryNameLen
;
4732 nsresult rv
= jarReader
->FindInit(path
, getter_Transfers(find
));
4733 NS_ENSURE_SUCCESS(rv
, rv
);
4735 while (NS_SUCCEEDED(find
->FindNext(&entryName
, &entryNameLen
))) {
4736 prefEntries
.AppendElement(Substring(entryName
, entryNameLen
));
4740 for (uint32_t i
= prefEntries
.Length(); i
--;) {
4741 rv
= pref_ReadPrefFromJar(jarReader
, prefEntries
[i
].get());
4742 if (NS_FAILED(rv
)) {
4743 NS_WARNING("Error parsing preferences.");
4750 static nsCString
PrefValueToString(const bool* b
) {
4751 return nsCString(*b
? "true" : "false");
4753 static nsCString
PrefValueToString(const int* i
) {
4754 return nsPrintfCString("%d", *i
);
4756 static nsCString
PrefValueToString(const uint32_t* u
) {
4757 return nsPrintfCString("%d", *u
);
4759 static nsCString
PrefValueToString(const float* f
) {
4760 return nsPrintfCString("%f", *f
);
4762 static nsCString
PrefValueToString(const nsACString
* s
) {
4763 return nsCString(*s
);
4765 static nsCString
PrefValueToString(const nsACString
& s
) { return nsCString(s
); }
4767 // These preference getter wrappers allow us to look up the value for static
4768 // preferences based on their native types, rather than manually mapping them to
4769 // the appropriate Preferences::Get* functions.
4770 // We define these methods in a struct which is made friend of Preferences in
4771 // order to access private members.
4773 template <typename T
>
4774 static nsresult
GetPrefValue(const char* aPrefName
, T
&& aResult
,
4775 PrefValueKind aKind
) {
4776 nsresult rv
= NS_ERROR_UNEXPECTED
;
4777 NS_ENSURE_TRUE(Preferences::InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
4779 if (Maybe
<PrefWrapper
> pref
= pref_Lookup(aPrefName
)) {
4780 rv
= pref
->GetValue(aKind
, std::forward
<T
>(aResult
));
4782 if (profiler_thread_is_being_profiled_for_markers()) {
4783 profiler_add_marker(
4784 "Preference Read", baseprofiler::category::OTHER_PreferenceRead
, {},
4786 ProfilerString8View::WrapNullTerminatedString(aPrefName
),
4787 Some(aKind
), pref
->Type(), PrefValueToString(aResult
));
4794 template <typename T
>
4795 static nsresult
GetSharedPrefValue(const char* aName
, T
* aResult
) {
4796 nsresult rv
= NS_ERROR_UNEXPECTED
;
4798 if (Maybe
<PrefWrapper
> pref
= pref_SharedLookup(aName
)) {
4799 rv
= pref
->GetValue(PrefValueKind::User
, aResult
);
4801 if (profiler_thread_is_being_profiled_for_markers()) {
4802 profiler_add_marker(
4803 "Preference Read", baseprofiler::category::OTHER_PreferenceRead
, {},
4805 ProfilerString8View::WrapNullTerminatedString(aName
),
4806 Nothing() /* indicates Shared */, pref
->Type(),
4807 PrefValueToString(aResult
));
4814 template <typename T
>
4815 static T
GetPref(const char* aPrefName
, T aFallback
,
4816 PrefValueKind aKind
= PrefValueKind::User
) {
4817 T result
= aFallback
;
4818 GetPrefValue(aPrefName
, &result
, aKind
);
4822 template <typename T
, typename V
>
4823 static void MOZ_NEVER_INLINE
AssignMirror(T
& aMirror
, V aValue
) {
4827 static void MOZ_NEVER_INLINE
AssignMirror(DataMutexString
& aMirror
,
4828 nsCString
&& aValue
) {
4829 auto lock
= aMirror
.Lock();
4830 lock
->Assign(std::move(aValue
));
4833 static void MOZ_NEVER_INLINE
AssignMirror(DataMutexString
& aMirror
,
4834 const nsLiteralCString
& aValue
) {
4835 auto lock
= aMirror
.Lock();
4836 lock
->Assign(aValue
);
4839 static void ClearMirror(DataMutexString
& aMirror
) {
4840 auto lock
= aMirror
.Lock();
4841 lock
->Assign(nsCString());
4844 template <typename T
>
4845 static void UpdateMirror(const char* aPref
, void* aMirror
) {
4846 StripAtomic
<T
> value
;
4848 nsresult rv
= GetPrefValue(aPref
, &value
, PrefValueKind::User
);
4849 if (NS_SUCCEEDED(rv
)) {
4850 AssignMirror(*static_cast<T
*>(aMirror
),
4851 std::forward
<StripAtomic
<T
>>(value
));
4853 // GetPrefValue() can fail if the update is caused by the pref being
4854 // deleted or if it fails to make a cast. This assertion is the only place
4855 // where we safeguard these. In this case the mirror variable will be
4856 // untouched, thus keeping the value it had prior to the change.
4857 // (Note that this case won't happen for a deletion via DeleteBranch()
4858 // unless bug 343600 is fixed, but it will happen for a deletion via
4859 // ClearUserPref().)
4860 NS_WARNING(nsPrintfCString("Pref changed failure: %s\n", aPref
).get());
4865 template <typename T
>
4866 static nsresult
RegisterCallback(void* aMirror
, const nsACString
& aPref
) {
4867 return Preferences::RegisterCallback(UpdateMirror
<T
>, aPref
, aMirror
,
4868 Preferences::ExactMatch
,
4869 /* isPriority */ true);
4873 // Initialize default preference JavaScript buffers from appropriate TEXT
4876 nsresult
Preferences::InitInitialObjects(bool aIsStartup
) {
4877 MOZ_ASSERT(NS_IsMainThread());
4879 if (!XRE_IsParentProcess()) {
4880 MOZ_DIAGNOSTIC_ASSERT(gSharedMap
);
4882 StaticPrefs::StartObservingAlwaysPrefs();
4887 // Initialize static prefs before prefs from data files so that the latter
4888 // will override the former.
4889 StaticPrefs::InitAll();
4891 // In the omni.jar case, we load the following prefs:
4892 // - jar:$gre/omni.jar!/greprefs.js
4893 // - jar:$gre/omni.jar!/defaults/pref/*.js
4895 // In the non-omni.jar case, we load:
4896 // - $gre/greprefs.js
4898 // In both cases, we also load:
4899 // - $gre/defaults/pref/*.js
4901 // This is kept for bug 591866 (channel-prefs.js should not be in omni.jar)
4902 // in the `$app == $gre` case; we load all files instead of channel-prefs.js
4903 // only to have the same behaviour as `$app != $gre`, where this is required
4904 // as a supported location for GRE preferences.
4906 // When `$app != $gre`, we additionally load, in the omni.jar case:
4907 // - jar:$app/omni.jar!/defaults/preferences/*.js
4908 // - $app/defaults/preferences/*.js
4910 // and in the non-omni.jar case:
4911 // - $app/defaults/preferences/*.js
4913 // When `$app == $gre`, we additionally load, in the omni.jar case:
4914 // - jar:$gre/omni.jar!/defaults/preferences/*.js
4916 // Thus, in the omni.jar case, we always load app-specific default
4917 // preferences from omni.jar, whether or not `$app == $gre`.
4919 nsresult rv
= NS_ERROR_FAILURE
;
4920 UniquePtr
<nsZipFind
> find
;
4921 nsTArray
<nsCString
> prefEntries
;
4922 const char* entryName
;
4923 uint16_t entryNameLen
;
4925 RefPtr
<nsZipArchive
> jarReader
= Omnijar::GetReader(Omnijar::GRE
);
4927 #ifdef MOZ_WIDGET_ANDROID
4928 // Try to load an architecture-specific greprefs.js first. This will be
4929 // present in FAT AAR builds of GeckoView on Android.
4930 const char* abi
= getenv("MOZ_ANDROID_CPU_ABI");
4933 path
.AppendPrintf("%s/greprefs.js", abi
);
4934 rv
= pref_ReadPrefFromJar(jarReader
, path
.get());
4937 if (NS_FAILED(rv
)) {
4938 // Fallback to toplevel greprefs.js if arch-specific load fails.
4939 rv
= pref_ReadPrefFromJar(jarReader
, "greprefs.js");
4942 // Load jar:$gre/omni.jar!/greprefs.js.
4943 rv
= pref_ReadPrefFromJar(jarReader
, "greprefs.js");
4945 NS_ENSURE_SUCCESS(rv
, rv
);
4947 // Load jar:$gre/omni.jar!/defaults/pref/*.js.
4948 rv
= pref_ReadDefaultPrefs(jarReader
, "defaults/pref/*.js$");
4949 NS_ENSURE_SUCCESS(rv
, rv
);
4951 #ifdef MOZ_BACKGROUNDTASKS
4952 if (BackgroundTasks::IsBackgroundTaskMode()) {
4953 rv
= pref_ReadDefaultPrefs(jarReader
, "defaults/backgroundtasks/*.js$");
4954 NS_ENSURE_SUCCESS(rv
, rv
);
4958 #ifdef MOZ_WIDGET_ANDROID
4959 // Load jar:$gre/omni.jar!/defaults/pref/$MOZ_ANDROID_CPU_ABI/*.js.
4961 path
.AppendPrintf("jar:$gre/omni.jar!/defaults/pref/%s/*.js$", abi
);
4962 pref_ReadDefaultPrefs(jarReader
, path
.get());
4963 NS_ENSURE_SUCCESS(rv
, rv
);
4966 // Load $gre/greprefs.js.
4967 nsCOMPtr
<nsIFile
> greprefsFile
;
4968 rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(greprefsFile
));
4969 NS_ENSURE_SUCCESS(rv
, rv
);
4971 rv
= greprefsFile
->AppendNative("greprefs.js"_ns
);
4972 NS_ENSURE_SUCCESS(rv
, rv
);
4974 rv
= openPrefFile(greprefsFile
, PrefValueKind::Default
);
4975 if (NS_FAILED(rv
)) {
4977 "Error parsing GRE default preferences. Is this an old-style "
4982 // Load $gre/defaults/pref/*.js.
4983 nsCOMPtr
<nsIFile
> defaultPrefDir
;
4984 rv
= NS_GetSpecialDirectory(NS_APP_PREF_DEFAULTS_50_DIR
,
4985 getter_AddRefs(defaultPrefDir
));
4986 NS_ENSURE_SUCCESS(rv
, rv
);
4988 rv
= pref_LoadPrefsInDir(defaultPrefDir
);
4989 if (NS_FAILED(rv
)) {
4990 NS_WARNING("Error parsing application default preferences.");
4993 #ifdef MOZ_WIDGET_COCOA
4994 // On macOS, channel-prefs.js is no longer bundled with the application and
4995 // the "app.update.channel" pref is now read from a Framework instead.
4996 // Previously, channel-prefs.js was read as one of the files in
4997 // NS_APP_PREF_DEFAULTS_50_DIR (see just above). See bug 1799332 for more
4999 nsAutoCString appUpdatePrefKey
;
5000 appUpdatePrefKey
.Assign(kChannelPref
);
5001 nsAutoCString appUpdatePrefValue
;
5002 PrefValue channelPrefValue
;
5003 channelPrefValue
.mStringVal
= MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL
);
5004 if (ChannelPrefsUtil::GetChannelPrefValue(appUpdatePrefValue
)) {
5005 channelPrefValue
.mStringVal
= appUpdatePrefValue
.get();
5007 pref_SetPref(appUpdatePrefKey
, PrefType::String
, PrefValueKind::Default
,
5009 /* isSticky */ false,
5010 /* isLocked */ true,
5011 /* fromInit */ true);
5014 // Load jar:$app/omni.jar!/defaults/preferences/*.js
5015 // or jar:$gre/omni.jar!/defaults/preferences/*.js.
5016 RefPtr
<nsZipArchive
> appJarReader
= Omnijar::GetReader(Omnijar::APP
);
5018 // GetReader(Omnijar::APP) returns null when `$app == $gre`, in
5019 // which case we look for app-specific default preferences in $gre.
5020 if (!appJarReader
) {
5021 appJarReader
= Omnijar::GetReader(Omnijar::GRE
);
5025 rv
= appJarReader
->FindInit("defaults/preferences/*.js$",
5026 getter_Transfers(find
));
5027 NS_ENSURE_SUCCESS(rv
, rv
);
5028 prefEntries
.Clear();
5029 while (NS_SUCCEEDED(find
->FindNext(&entryName
, &entryNameLen
))) {
5030 prefEntries
.AppendElement(Substring(entryName
, entryNameLen
));
5033 for (uint32_t i
= prefEntries
.Length(); i
--;) {
5034 rv
= pref_ReadPrefFromJar(appJarReader
, prefEntries
[i
].get());
5035 if (NS_FAILED(rv
)) {
5036 NS_WARNING("Error parsing preferences.");
5040 #ifdef MOZ_BACKGROUNDTASKS
5041 if (BackgroundTasks::IsBackgroundTaskMode()) {
5042 rv
= appJarReader
->FindInit("defaults/backgroundtasks/*.js$",
5043 getter_Transfers(find
));
5044 NS_ENSURE_SUCCESS(rv
, rv
);
5045 prefEntries
.Clear();
5046 while (NS_SUCCEEDED(find
->FindNext(&entryName
, &entryNameLen
))) {
5047 prefEntries
.AppendElement(Substring(entryName
, entryNameLen
));
5050 for (uint32_t i
= prefEntries
.Length(); i
--;) {
5051 rv
= pref_ReadPrefFromJar(appJarReader
, prefEntries
[i
].get());
5052 if (NS_FAILED(rv
)) {
5053 NS_WARNING("Error parsing preferences.");
5060 nsCOMPtr
<nsIProperties
> dirSvc(
5061 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
, &rv
));
5062 NS_ENSURE_SUCCESS(rv
, rv
);
5064 nsCOMPtr
<nsISimpleEnumerator
> list
;
5065 dirSvc
->Get(NS_APP_PREFS_DEFAULTS_DIR_LIST
, NS_GET_IID(nsISimpleEnumerator
),
5066 getter_AddRefs(list
));
5069 while (NS_SUCCEEDED(list
->HasMoreElements(&hasMore
)) && hasMore
) {
5070 nsCOMPtr
<nsISupports
> elem
;
5071 list
->GetNext(getter_AddRefs(elem
));
5076 nsCOMPtr
<nsIFile
> path
= do_QueryInterface(elem
);
5081 // Do we care if a file provided by this process fails to load?
5082 pref_LoadPrefsInDir(path
);
5086 #if defined(MOZ_WIDGET_GTK)
5087 // To ensure the system-wide preferences are not overwritten by
5088 // firefox/browser/defauts/preferences/*.js we need to load
5089 // the /etc/firefox/defaults/pref/*.js settings as last.
5090 // Under Flatpak, the NS_OS_SYSTEM_CONFIG_DIR points to /app/etc/firefox
5091 nsCOMPtr
<nsIFile
> defaultSystemPrefDir
;
5092 rv
= NS_GetSpecialDirectory(NS_OS_SYSTEM_CONFIG_DIR
,
5093 getter_AddRefs(defaultSystemPrefDir
));
5094 NS_ENSURE_SUCCESS(rv
, rv
);
5095 defaultSystemPrefDir
->AppendNative("defaults"_ns
);
5096 defaultSystemPrefDir
->AppendNative("pref"_ns
);
5098 rv
= pref_LoadPrefsInDir(defaultSystemPrefDir
);
5099 if (NS_FAILED(rv
)) {
5100 NS_WARNING("Error parsing application default preferences.");
5104 if (XRE_IsParentProcess()) {
5105 SetupTelemetryPref();
5109 // Now that all prefs have their initial values, install the callbacks for
5110 // `always`-mirrored static prefs. We do this now rather than in
5111 // StaticPrefs::InitAll() so that the callbacks don't need to be traversed
5112 // while we load prefs from data files.
5113 StaticPrefs::StartObservingAlwaysPrefs();
5116 NS_CreateServicesFromCategory(NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID
, nullptr,
5117 NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID
);
5119 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
5120 if (NS_WARN_IF(!observerService
)) {
5121 return NS_ERROR_FAILURE
;
5124 observerService
->NotifyObservers(nullptr, NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID
,
5131 nsresult
Preferences::GetBool(const char* aPrefName
, bool* aResult
,
5132 PrefValueKind aKind
) {
5133 MOZ_ASSERT(aResult
);
5134 return Internals::GetPrefValue(aPrefName
, aResult
, aKind
);
5138 nsresult
Preferences::GetInt(const char* aPrefName
, int32_t* aResult
,
5139 PrefValueKind aKind
) {
5140 MOZ_ASSERT(aResult
);
5141 return Internals::GetPrefValue(aPrefName
, aResult
, aKind
);
5145 nsresult
Preferences::GetFloat(const char* aPrefName
, float* aResult
,
5146 PrefValueKind aKind
) {
5147 MOZ_ASSERT(aResult
);
5148 return Internals::GetPrefValue(aPrefName
, aResult
, aKind
);
5152 nsresult
Preferences::GetCString(const char* aPrefName
, nsACString
& aResult
,
5153 PrefValueKind aKind
) {
5154 aResult
.SetIsVoid(true);
5155 return Internals::GetPrefValue(aPrefName
, aResult
, aKind
);
5159 nsresult
Preferences::GetString(const char* aPrefName
, nsAString
& aResult
,
5160 PrefValueKind aKind
) {
5161 nsAutoCString result
;
5162 nsresult rv
= Preferences::GetCString(aPrefName
, result
, aKind
);
5163 if (NS_SUCCEEDED(rv
)) {
5164 CopyUTF8toUTF16(result
, aResult
);
5170 nsresult
Preferences::GetLocalizedCString(const char* aPrefName
,
5171 nsACString
& aResult
,
5172 PrefValueKind aKind
) {
5173 nsAutoString result
;
5174 nsresult rv
= GetLocalizedString(aPrefName
, result
, aKind
);
5175 if (NS_SUCCEEDED(rv
)) {
5176 CopyUTF16toUTF8(result
, aResult
);
5182 nsresult
Preferences::GetLocalizedString(const char* aPrefName
,
5184 PrefValueKind aKind
) {
5185 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5186 nsCOMPtr
<nsIPrefLocalizedString
> prefLocalString
;
5187 nsresult rv
= GetRootBranch(aKind
)->GetComplexValue(
5188 aPrefName
, NS_GET_IID(nsIPrefLocalizedString
),
5189 getter_AddRefs(prefLocalString
));
5190 if (NS_SUCCEEDED(rv
)) {
5191 MOZ_ASSERT(prefLocalString
, "Succeeded but the result is NULL");
5192 prefLocalString
->GetData(aResult
);
5198 nsresult
Preferences::GetComplex(const char* aPrefName
, const nsIID
& aType
,
5199 void** aResult
, PrefValueKind aKind
) {
5200 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5201 return GetRootBranch(aKind
)->GetComplexValue(aPrefName
, aType
, aResult
);
5205 bool Preferences::GetBool(const char* aPrefName
, bool aFallback
,
5206 PrefValueKind aKind
) {
5207 return Internals::GetPref(aPrefName
, aFallback
, aKind
);
5211 int32_t Preferences::GetInt(const char* aPrefName
, int32_t aFallback
,
5212 PrefValueKind aKind
) {
5213 return Internals::GetPref(aPrefName
, aFallback
, aKind
);
5217 uint32_t Preferences::GetUint(const char* aPrefName
, uint32_t aFallback
,
5218 PrefValueKind aKind
) {
5219 return Internals::GetPref(aPrefName
, aFallback
, aKind
);
5223 float Preferences::GetFloat(const char* aPrefName
, float aFallback
,
5224 PrefValueKind aKind
) {
5225 return Internals::GetPref(aPrefName
, aFallback
, aKind
);
5229 nsresult
Preferences::SetCString(const char* aPrefName
,
5230 const nsACString
& aValue
,
5231 PrefValueKind aKind
) {
5232 ENSURE_PARENT_PROCESS("SetCString", aPrefName
);
5233 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5235 if (aValue
.Length() > MAX_PREF_LENGTH
) {
5236 return NS_ERROR_ILLEGAL_VALUE
;
5239 // It's ok to stash a pointer to the temporary PromiseFlatCString's chars in
5240 // pref because pref_SetPref() duplicates those chars.
5241 PrefValue prefValue
;
5242 const nsCString
& flat
= PromiseFlatCString(aValue
);
5243 prefValue
.mStringVal
= flat
.get();
5244 return pref_SetPref(nsDependentCString(aPrefName
), PrefType::String
, aKind
,
5246 /* isSticky */ false,
5247 /* isLocked */ false,
5248 /* fromInit */ false);
5252 nsresult
Preferences::SetBool(const char* aPrefName
, bool aValue
,
5253 PrefValueKind aKind
) {
5254 ENSURE_PARENT_PROCESS("SetBool", aPrefName
);
5255 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5257 PrefValue prefValue
;
5258 prefValue
.mBoolVal
= aValue
;
5259 return pref_SetPref(nsDependentCString(aPrefName
), PrefType::Bool
, aKind
,
5261 /* isSticky */ false,
5262 /* isLocked */ false,
5263 /* fromInit */ false);
5267 nsresult
Preferences::SetInt(const char* aPrefName
, int32_t aValue
,
5268 PrefValueKind aKind
) {
5269 ENSURE_PARENT_PROCESS("SetInt", aPrefName
);
5270 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5272 PrefValue prefValue
;
5273 prefValue
.mIntVal
= aValue
;
5274 return pref_SetPref(nsDependentCString(aPrefName
), PrefType::Int
, aKind
,
5276 /* isSticky */ false,
5277 /* isLocked */ false,
5278 /* fromInit */ false);
5282 nsresult
Preferences::SetComplex(const char* aPrefName
, const nsIID
& aType
,
5283 nsISupports
* aValue
, PrefValueKind aKind
) {
5284 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5285 return GetRootBranch(aKind
)->SetComplexValue(aPrefName
, aType
, aValue
);
5289 nsresult
Preferences::Lock(const char* aPrefName
) {
5290 ENSURE_PARENT_PROCESS("Lock", aPrefName
);
5291 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5293 const auto& prefName
= nsDependentCString(aPrefName
);
5297 pref_LookupForModify(prefName
, [](const PrefWrapper
& aPref
) {
5298 return !aPref
.IsLocked();
5302 pref
->SetIsLocked(true);
5303 NotifyCallbacks(prefName
, PrefWrapper(pref
));
5310 nsresult
Preferences::Unlock(const char* aPrefName
) {
5311 ENSURE_PARENT_PROCESS("Unlock", aPrefName
);
5312 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5314 const auto& prefName
= nsDependentCString(aPrefName
);
5318 pref_LookupForModify(prefName
, [](const PrefWrapper
& aPref
) {
5319 return aPref
.IsLocked();
5323 pref
->SetIsLocked(false);
5324 NotifyCallbacks(prefName
, PrefWrapper(pref
));
5331 bool Preferences::IsLocked(const char* aPrefName
) {
5332 NS_ENSURE_TRUE(InitStaticMembers(), false);
5334 Maybe
<PrefWrapper
> pref
= pref_Lookup(aPrefName
);
5335 return pref
.isSome() && pref
->IsLocked();
5339 bool Preferences::IsSanitized(const char* aPrefName
) {
5340 NS_ENSURE_TRUE(InitStaticMembers(), false);
5342 Maybe
<PrefWrapper
> pref
= pref_Lookup(aPrefName
);
5343 return pref
.isSome() && pref
->IsSanitized();
5347 nsresult
Preferences::ClearUser(const char* aPrefName
) {
5348 ENSURE_PARENT_PROCESS("ClearUser", aPrefName
);
5349 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5351 const auto& prefName
= nsDependentCString
{aPrefName
};
5352 auto result
= pref_LookupForModify(
5353 prefName
, [](const PrefWrapper
& aPref
) { return aPref
.HasUserValue(); });
5354 if (result
.isErr()) {
5358 if (Pref
* pref
= result
.unwrap()) {
5359 pref
->ClearUserValue();
5361 if (!pref
->HasDefaultValue()) {
5363 !gSharedMap
|| !pref
->IsSanitized() || !gSharedMap
->Has(pref
->Name()),
5364 "A sanitized pref should never be in the shared pref map.");
5365 if (!pref
->IsSanitized() &&
5366 (!gSharedMap
|| !gSharedMap
->Has(pref
->Name()))) {
5367 HashTable()->remove(aPrefName
);
5369 pref
->SetType(PrefType::None
);
5372 NotifyCallbacks(prefName
);
5374 NotifyCallbacks(prefName
, PrefWrapper(pref
));
5377 Preferences::HandleDirty();
5383 bool Preferences::HasUserValue(const char* aPrefName
) {
5384 NS_ENSURE_TRUE(InitStaticMembers(), false);
5386 Maybe
<PrefWrapper
> pref
= pref_Lookup(aPrefName
);
5387 return pref
.isSome() && pref
->HasUserValue();
5391 bool Preferences::HasDefaultValue(const char* aPrefName
) {
5392 NS_ENSURE_TRUE(InitStaticMembers(), false);
5394 Maybe
<PrefWrapper
> pref
= pref_Lookup(aPrefName
);
5395 return pref
.isSome() && pref
->HasDefaultValue();
5399 int32_t Preferences::GetType(const char* aPrefName
) {
5400 NS_ENSURE_TRUE(InitStaticMembers(), nsIPrefBranch::PREF_INVALID
);
5403 return PREF_INVALID
;
5406 Maybe
<PrefWrapper
> pref
= pref_Lookup(aPrefName
);
5407 if (!pref
.isSome()) {
5408 return PREF_INVALID
;
5411 switch (pref
->Type()) {
5412 case PrefType::String
:
5418 case PrefType::Bool
:
5421 case PrefType::None
:
5422 if (IsPreferenceSanitized(aPrefName
)) {
5423 if (!sPrefTelemetryEventEnabled
.exchange(true)) {
5424 sPrefTelemetryEventEnabled
= true;
5425 Telemetry::SetEventRecordingEnabled("security"_ns
, true);
5428 Telemetry::RecordEvent(
5429 Telemetry::EventID::Security_Prefusage_Contentprocess
,
5430 mozilla::Some(aPrefName
), mozilla::Nothing());
5432 if (sCrashOnBlocklistedPref
) {
5433 MOZ_CRASH_UNSAFE_PRINTF(
5434 "Should not access the preference '%s' in the Content Processes",
5437 return PREF_INVALID
;
5448 nsresult
Preferences::AddStrongObserver(nsIObserver
* aObserver
,
5449 const nsACString
& aPref
) {
5450 MOZ_ASSERT(aObserver
);
5451 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5452 return sPreferences
->mRootBranch
->AddObserver(aPref
, aObserver
, false);
5456 nsresult
Preferences::AddWeakObserver(nsIObserver
* aObserver
,
5457 const nsACString
& aPref
) {
5458 MOZ_ASSERT(aObserver
);
5459 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5460 return sPreferences
->mRootBranch
->AddObserver(aPref
, aObserver
, true);
5464 nsresult
Preferences::RemoveObserver(nsIObserver
* aObserver
,
5465 const nsACString
& aPref
) {
5466 MOZ_ASSERT(aObserver
);
5468 MOZ_ASSERT(!sPreferences
);
5469 return NS_OK
; // Observers have been released automatically.
5471 NS_ENSURE_TRUE(sPreferences
, NS_ERROR_NOT_AVAILABLE
);
5472 return sPreferences
->mRootBranch
->RemoveObserver(aPref
, aObserver
);
5475 template <typename T
>
5476 static void AssertNotMallocAllocated(T
* aPtr
) {
5477 #if defined(DEBUG) && defined(MOZ_MEMORY)
5478 jemalloc_ptr_info_t info
;
5479 jemalloc_ptr_info((void*)aPtr
, &info
);
5480 MOZ_ASSERT(info
.tag
== TagUnknown
);
5485 nsresult
Preferences::AddStrongObservers(nsIObserver
* aObserver
,
5486 const char* const* aPrefs
) {
5487 MOZ_ASSERT(aObserver
);
5488 for (uint32_t i
= 0; aPrefs
[i
]; i
++) {
5489 AssertNotMallocAllocated(aPrefs
[i
]);
5492 pref
.AssignLiteral(aPrefs
[i
], strlen(aPrefs
[i
]));
5493 nsresult rv
= AddStrongObserver(aObserver
, pref
);
5494 NS_ENSURE_SUCCESS(rv
, rv
);
5500 nsresult
Preferences::AddWeakObservers(nsIObserver
* aObserver
,
5501 const char* const* aPrefs
) {
5502 MOZ_ASSERT(aObserver
);
5503 for (uint32_t i
= 0; aPrefs
[i
]; i
++) {
5504 AssertNotMallocAllocated(aPrefs
[i
]);
5507 pref
.AssignLiteral(aPrefs
[i
], strlen(aPrefs
[i
]));
5508 nsresult rv
= AddWeakObserver(aObserver
, pref
);
5509 NS_ENSURE_SUCCESS(rv
, rv
);
5515 nsresult
Preferences::RemoveObservers(nsIObserver
* aObserver
,
5516 const char* const* aPrefs
) {
5517 MOZ_ASSERT(aObserver
);
5519 MOZ_ASSERT(!sPreferences
);
5520 return NS_OK
; // Observers have been released automatically.
5522 NS_ENSURE_TRUE(sPreferences
, NS_ERROR_NOT_AVAILABLE
);
5524 for (uint32_t i
= 0; aPrefs
[i
]; i
++) {
5525 nsresult rv
= RemoveObserver(aObserver
, nsDependentCString(aPrefs
[i
]));
5526 NS_ENSURE_SUCCESS(rv
, rv
);
5531 template <typename T
>
5533 nsresult
Preferences::RegisterCallbackImpl(PrefChangedFunc aCallback
,
5534 T
& aPrefNode
, void* aData
,
5535 MatchKind aMatchKind
,
5537 NS_ENSURE_ARG(aCallback
);
5539 NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE
);
5541 auto node
= new CallbackNode(aPrefNode
, aCallback
, aData
, aMatchKind
);
5544 // Add to the start of the list.
5545 node
->SetNext(gFirstCallback
);
5546 gFirstCallback
= node
;
5547 if (!gLastPriorityNode
) {
5548 gLastPriorityNode
= node
;
5551 // Add to the start of the non-priority part of the list.
5552 if (gLastPriorityNode
) {
5553 node
->SetNext(gLastPriorityNode
->Next());
5554 gLastPriorityNode
->SetNext(node
);
5556 node
->SetNext(gFirstCallback
);
5557 gFirstCallback
= node
;
5565 nsresult
Preferences::RegisterCallback(PrefChangedFunc aCallback
,
5566 const nsACString
& aPrefNode
, void* aData
,
5567 MatchKind aMatchKind
, bool aIsPriority
) {
5568 return RegisterCallbackImpl(aCallback
, aPrefNode
, aData
, aMatchKind
,
5573 nsresult
Preferences::RegisterCallbacks(PrefChangedFunc aCallback
,
5574 const char* const* aPrefs
, void* aData
,
5575 MatchKind aMatchKind
) {
5576 return RegisterCallbackImpl(aCallback
, aPrefs
, aData
, aMatchKind
);
5580 nsresult
Preferences::RegisterCallbackAndCall(PrefChangedFunc aCallback
,
5581 const nsACString
& aPref
,
5583 MatchKind aMatchKind
) {
5584 MOZ_ASSERT(aCallback
);
5585 nsresult rv
= RegisterCallback(aCallback
, aPref
, aClosure
, aMatchKind
);
5586 if (NS_SUCCEEDED(rv
)) {
5587 (*aCallback
)(PromiseFlatCString(aPref
).get(), aClosure
);
5593 nsresult
Preferences::RegisterCallbacksAndCall(PrefChangedFunc aCallback
,
5594 const char* const* aPrefs
,
5596 MOZ_ASSERT(aCallback
);
5599 RegisterCallbacks(aCallback
, aPrefs
, aClosure
, MatchKind::ExactMatch
);
5600 if (NS_SUCCEEDED(rv
)) {
5601 for (const char* const* ptr
= aPrefs
; *ptr
; ptr
++) {
5602 (*aCallback
)(*ptr
, aClosure
);
5608 template <typename T
>
5610 nsresult
Preferences::UnregisterCallbackImpl(PrefChangedFunc aCallback
,
5611 T
& aPrefNode
, void* aData
,
5612 MatchKind aMatchKind
) {
5613 MOZ_ASSERT(aCallback
);
5615 MOZ_ASSERT(!sPreferences
);
5616 return NS_OK
; // Observers have been released automatically.
5618 NS_ENSURE_TRUE(sPreferences
, NS_ERROR_NOT_AVAILABLE
);
5620 nsresult rv
= NS_ERROR_FAILURE
;
5621 CallbackNode
* node
= gFirstCallback
;
5622 CallbackNode
* prev_node
= nullptr;
5625 if (node
->Func() == aCallback
&& node
->Data() == aData
&&
5626 node
->MatchKind() == aMatchKind
&& node
->DomainIs(aPrefNode
)) {
5627 if (gCallbacksInProgress
) {
5628 // Postpone the node removal until after callbacks enumeration is
5631 gShouldCleanupDeadNodes
= true;
5633 node
= node
->Next();
5635 node
= pref_RemoveCallbackNode(node
, prev_node
);
5640 node
= node
->Next();
5647 nsresult
Preferences::UnregisterCallback(PrefChangedFunc aCallback
,
5648 const nsACString
& aPrefNode
,
5649 void* aData
, MatchKind aMatchKind
) {
5650 return UnregisterCallbackImpl
<const nsACString
&>(aCallback
, aPrefNode
, aData
,
5655 nsresult
Preferences::UnregisterCallbacks(PrefChangedFunc aCallback
,
5656 const char* const* aPrefs
,
5657 void* aData
, MatchKind aMatchKind
) {
5658 return UnregisterCallbackImpl(aCallback
, aPrefs
, aData
, aMatchKind
);
5661 template <typename T
>
5662 static void AddMirrorCallback(T
* aMirror
, const nsACString
& aPref
) {
5663 MOZ_ASSERT(NS_IsMainThread());
5665 Internals::RegisterCallback
<T
>(aMirror
, aPref
);
5668 // Don't inline because it explodes compile times.
5669 template <typename T
>
5670 static MOZ_NEVER_INLINE
void AddMirror(T
* aMirror
, const nsACString
& aPref
,
5671 StripAtomic
<T
> aDefault
) {
5672 *aMirror
= Internals::GetPref(PromiseFlatCString(aPref
).get(), aDefault
);
5673 AddMirrorCallback(aMirror
, aPref
);
5676 static MOZ_NEVER_INLINE
void AddMirror(DataMutexString
& aMirror
,
5677 const nsACString
& aPref
) {
5678 auto lock
= aMirror
.Lock();
5679 nsCString
result(*lock
);
5680 Internals::GetPrefValue(PromiseFlatCString(aPref
).get(), result
,
5681 PrefValueKind::User
);
5682 lock
->Assign(std::move(result
));
5683 AddMirrorCallback(&aMirror
, aPref
);
5686 // The InitPref_*() functions below end in a `_<type>` suffix because they are
5687 // used by the PREF macro definition in InitAll() below.
5689 static void InitPref_bool(const nsCString
& aName
, bool aDefaultValue
) {
5690 MOZ_ASSERT(XRE_IsParentProcess());
5692 value
.mBoolVal
= aDefaultValue
;
5693 pref_SetPref(aName
, PrefType::Bool
, PrefValueKind::Default
, value
,
5694 /* isSticky */ false,
5695 /* isLocked */ false,
5696 /* fromInit */ true);
5699 static void InitPref_int32_t(const nsCString
& aName
, int32_t aDefaultValue
) {
5700 MOZ_ASSERT(XRE_IsParentProcess());
5702 value
.mIntVal
= aDefaultValue
;
5703 pref_SetPref(aName
, PrefType::Int
, PrefValueKind::Default
, value
,
5704 /* isSticky */ false,
5705 /* isLocked */ false,
5706 /* fromInit */ true);
5709 static void InitPref_uint32_t(const nsCString
& aName
, uint32_t aDefaultValue
) {
5710 InitPref_int32_t(aName
, int32_t(aDefaultValue
));
5713 static void InitPref_float(const nsCString
& aName
, float aDefaultValue
) {
5714 MOZ_ASSERT(XRE_IsParentProcess());
5716 // Convert the value in a locale-independent way, including a trailing ".0"
5717 // if necessary to distinguish floating-point from integer prefs when viewing
5718 // them in about:config.
5719 nsAutoCString defaultValue
;
5720 defaultValue
.AppendFloat(aDefaultValue
);
5721 if (!defaultValue
.Contains('.') && !defaultValue
.Contains('e')) {
5722 defaultValue
.AppendLiteral(".0");
5724 value
.mStringVal
= defaultValue
.get();
5725 pref_SetPref(aName
, PrefType::String
, PrefValueKind::Default
, value
,
5726 /* isSticky */ false,
5727 /* isLocked */ false,
5728 /* fromInit */ true);
5731 static void InitPref_String(const nsCString
& aName
, const char* aDefaultValue
) {
5732 MOZ_ASSERT(XRE_IsParentProcess());
5734 value
.mStringVal
= aDefaultValue
;
5735 pref_SetPref(aName
, PrefType::String
, PrefValueKind::Default
, value
,
5736 /* isSticky */ false,
5737 /* isLocked */ false,
5738 /* fromInit */ true);
5741 static void InitPref(const nsCString
& aName
, bool aDefaultValue
) {
5742 InitPref_bool(aName
, aDefaultValue
);
5744 static void InitPref(const nsCString
& aName
, int32_t aDefaultValue
) {
5745 InitPref_int32_t(aName
, aDefaultValue
);
5747 static void InitPref(const nsCString
& aName
, uint32_t aDefaultValue
) {
5748 InitPref_uint32_t(aName
, aDefaultValue
);
5750 static void InitPref(const nsCString
& aName
, float aDefaultValue
) {
5751 InitPref_float(aName
, aDefaultValue
);
5754 template <typename T
>
5755 static void InitAlwaysPref(const nsCString
& aName
, T
* aCache
,
5756 StripAtomic
<T
> aDefaultValue
) {
5757 // Only called in the parent process. Set/reset the pref value and the
5758 // `always` mirror to the default value.
5759 // `once` mirrors will be initialized lazily in InitOncePrefs().
5760 InitPref(aName
, aDefaultValue
);
5761 *aCache
= aDefaultValue
;
5764 static void InitAlwaysPref(const nsCString
& aName
, DataMutexString
& aCache
,
5765 const nsLiteralCString
& aDefaultValue
) {
5766 // Only called in the parent process. Set/reset the pref value and the
5767 // `always` mirror to the default value.
5768 // `once` mirrors will be initialized lazily in InitOncePrefs().
5769 InitPref_String(aName
, aDefaultValue
.get());
5770 Internals::AssignMirror(aCache
, aDefaultValue
);
5773 static Atomic
<bool> sOncePrefRead(false);
5774 static StaticMutex sOncePrefMutex MOZ_UNANNOTATED
;
5776 namespace StaticPrefs
{
5778 void MaybeInitOncePrefs() {
5779 if (MOZ_LIKELY(sOncePrefRead
)) {
5780 // `once`-mirrored prefs have already been initialized to their default
5784 StaticMutexAutoLock
lock(sOncePrefMutex
);
5785 if (NS_IsMainThread()) {
5788 RefPtr
<Runnable
> runnable
= NS_NewRunnableFunction(
5789 "Preferences::MaybeInitOncePrefs", [&]() { InitOncePrefs(); });
5790 // This logic needs to run on the main thread
5791 SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(), runnable
);
5793 sOncePrefRead
= true;
5796 // For mirrored prefs we generate a variable definition.
5797 #define NEVER_PREF(name, cpp_type, value)
5798 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, default_value) \
5799 cpp_type sMirror_##full_id(default_value);
5800 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, default_value) \
5801 cpp_type sMirror_##full_id("DataMutexString");
5802 #define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \
5803 cpp_type sMirror_##full_id(default_value);
5804 #include "mozilla/StaticPrefListAll.h"
5807 #undef ALWAYS_DATAMUTEX_PREF
5810 static void InitAll() {
5811 MOZ_ASSERT(NS_IsMainThread());
5812 MOZ_ASSERT(XRE_IsParentProcess());
5814 // For all prefs we generate some initialization code.
5816 // The InitPref_*() functions have a type suffix to avoid ambiguity between
5817 // prefs having int32_t and float default values. That suffix is not needed
5818 // for the InitAlwaysPref() functions because they take a pointer parameter,
5819 // which prevents automatic int-to-float coercion.
5820 #define NEVER_PREF(name, cpp_type, value) \
5821 InitPref_##cpp_type(name ""_ns, value);
5822 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) \
5823 InitAlwaysPref(name ""_ns, &sMirror_##full_id, value);
5824 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value) \
5825 InitAlwaysPref(name ""_ns, sMirror_##full_id, value);
5826 #define ONCE_PREF(name, base_id, full_id, cpp_type, value) \
5827 InitPref_##cpp_type(name ""_ns, value);
5828 #include "mozilla/StaticPrefListAll.h"
5831 #undef ALWAYS_DATAMUTEX_PREF
5835 static void StartObservingAlwaysPrefs() {
5836 MOZ_ASSERT(NS_IsMainThread());
5838 // Call AddMirror so that our mirrors for `always` prefs will stay updated.
5839 // The call to AddMirror re-reads the current pref value into the mirror, so
5840 // our mirror will now be up-to-date even if some of the prefs have changed
5841 // since the call to InitAll().
5842 #define NEVER_PREF(name, cpp_type, value)
5843 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) \
5844 AddMirror(&sMirror_##full_id, name ""_ns, sMirror_##full_id);
5845 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value) \
5846 AddMirror(sMirror_##full_id, name ""_ns);
5847 #define ONCE_PREF(name, base_id, full_id, cpp_type, value)
5848 #include "mozilla/StaticPrefListAll.h"
5851 #undef ALWAYS_DATAMUTEX_PREF
5855 static void InitOncePrefs() {
5856 // For `once`-mirrored prefs we generate some initialization code. This is
5857 // done in case the pref value was updated when reading pref data files. It's
5858 // necessary because we don't have callbacks registered for `once`-mirrored
5861 // In debug builds, we also install a mechanism that can check if the
5862 // preference value is modified after `once`-mirrored prefs are initialized.
5863 // In tests this would indicate a likely misuse of a `once`-mirrored pref and
5864 // suggest that it should instead be `always`-mirrored.
5865 #define NEVER_PREF(name, cpp_type, value)
5866 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value)
5867 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value)
5869 # define ONCE_PREF(name, base_id, full_id, cpp_type, value) \
5871 MOZ_ASSERT(gOnceStaticPrefsAntiFootgun); \
5872 sMirror_##full_id = Internals::GetPref(name, cpp_type(value)); \
5873 auto checkPref = [&]() { \
5874 MOZ_ASSERT(sOncePrefRead); \
5875 cpp_type staticPrefValue = full_id(); \
5876 cpp_type preferenceValue = \
5877 Internals::GetPref(GetPrefName_##base_id(), cpp_type(value)); \
5878 MOZ_ASSERT(staticPrefValue == preferenceValue, \
5879 "Preference '" name \
5880 "' got modified since StaticPrefs::" #full_id \
5881 " was initialized. Consider using an `always` mirror kind " \
5884 gOnceStaticPrefsAntiFootgun->insert( \
5885 std::pair<const char*, AntiFootgunCallback>(GetPrefName_##base_id(), \
5886 std::move(checkPref))); \
5889 # define ONCE_PREF(name, base_id, full_id, cpp_type, value) \
5890 sMirror_##full_id = Internals::GetPref(name, cpp_type(value));
5893 #include "mozilla/StaticPrefListAll.h"
5896 #undef ALWAYS_DATAMUTEX_PREF
5900 static void ShutdownAlwaysPrefs() {
5901 MOZ_ASSERT(NS_IsMainThread());
5903 // We may need to do clean up for leak detection for some StaticPrefs.
5904 #define NEVER_PREF(name, cpp_type, value)
5905 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value)
5906 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value) \
5907 Internals::ClearMirror(sMirror_##full_id);
5908 #define ONCE_PREF(name, base_id, full_id, cpp_type, value)
5909 #include "mozilla/StaticPrefListAll.h"
5912 #undef ALWAYS_DATAMUTEX_PREF
5916 } // namespace StaticPrefs
5918 static MOZ_MAYBE_UNUSED
void SaveOncePrefToSharedMap(
5919 SharedPrefMapBuilder
& aBuilder
, const nsACString
& aName
, bool aValue
) {
5920 auto oncePref
= MakeUnique
<Pref
>(aName
);
5921 oncePref
->SetType(PrefType::Bool
);
5922 oncePref
->SetIsSkippedByIteration(true);
5923 bool valueChanged
= false;
5924 MOZ_ALWAYS_SUCCEEDS(
5925 oncePref
->SetDefaultValue(PrefType::Bool
, PrefValue(aValue
),
5926 /* isSticky */ true,
5927 /* isLocked */ true, &valueChanged
));
5928 oncePref
->AddToMap(aBuilder
);
5931 static MOZ_MAYBE_UNUSED
void SaveOncePrefToSharedMap(
5932 SharedPrefMapBuilder
& aBuilder
, const nsACString
& aName
, int32_t aValue
) {
5933 auto oncePref
= MakeUnique
<Pref
>(aName
);
5934 oncePref
->SetType(PrefType::Int
);
5935 oncePref
->SetIsSkippedByIteration(true);
5936 bool valueChanged
= false;
5937 MOZ_ALWAYS_SUCCEEDS(
5938 oncePref
->SetDefaultValue(PrefType::Int
, PrefValue(aValue
),
5939 /* isSticky */ true,
5940 /* isLocked */ true, &valueChanged
));
5941 oncePref
->AddToMap(aBuilder
);
5944 static MOZ_MAYBE_UNUSED
void SaveOncePrefToSharedMap(
5945 SharedPrefMapBuilder
& aBuilder
, const nsACString
& aName
, uint32_t aValue
) {
5946 SaveOncePrefToSharedMap(aBuilder
, aName
, int32_t(aValue
));
5949 static MOZ_MAYBE_UNUSED
void SaveOncePrefToSharedMap(
5950 SharedPrefMapBuilder
& aBuilder
, const nsACString
& aName
, float aValue
) {
5951 auto oncePref
= MakeUnique
<Pref
>(aName
);
5952 oncePref
->SetType(PrefType::String
);
5953 oncePref
->SetIsSkippedByIteration(true);
5954 nsAutoCString value
;
5955 value
.AppendFloat(aValue
);
5956 bool valueChanged
= false;
5957 // It's ok to stash a pointer to the temporary PromiseFlatCString's chars in
5958 // pref because pref_SetPref() duplicates those chars.
5959 const nsCString
& flat
= PromiseFlatCString(value
);
5960 MOZ_ALWAYS_SUCCEEDS(
5961 oncePref
->SetDefaultValue(PrefType::String
, PrefValue(flat
.get()),
5962 /* isSticky */ true,
5963 /* isLocked */ true, &valueChanged
));
5964 oncePref
->AddToMap(aBuilder
);
5967 #define ONCE_PREF_NAME(name) "$$$" name "$$$"
5969 namespace StaticPrefs
{
5971 static void RegisterOncePrefs(SharedPrefMapBuilder
& aBuilder
) {
5972 MOZ_ASSERT(XRE_IsParentProcess());
5973 MOZ_DIAGNOSTIC_ASSERT(!gSharedMap
,
5974 "Must be called before gSharedMap has been created");
5975 MaybeInitOncePrefs();
5977 // For `once`-mirrored prefs we generate a save call, which saves the value
5978 // as it was at parent startup. It is stored in a special (hidden and locked)
5979 // entry in the global SharedPreferenceMap. In order for the entry to be
5980 // hidden and not appear in about:config nor ever be stored to disk, we set
5981 // its IsSkippedByIteration flag to true. We also distinguish it by adding a
5982 // "$$$" prefix and suffix to the preference name.
5983 #define NEVER_PREF(name, cpp_type, value)
5984 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value)
5985 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value)
5986 #define ONCE_PREF(name, base_id, full_id, cpp_type, value) \
5987 SaveOncePrefToSharedMap(aBuilder, ONCE_PREF_NAME(name) ""_ns, \
5988 cpp_type(sMirror_##full_id));
5989 #include "mozilla/StaticPrefListAll.h"
5992 #undef ALWAYS_DATAMUTEX_PREF
5996 // Disable thread safety analysis on this function, because it explodes build
5997 // times and memory usage.
5998 MOZ_NO_THREAD_SAFETY_ANALYSIS
5999 static void InitStaticPrefsFromShared() {
6000 MOZ_ASSERT(!XRE_IsParentProcess());
6001 MOZ_DIAGNOSTIC_ASSERT(gSharedMap
,
6002 "Must be called once gSharedMap has been created");
6005 # define ASSERT_PREF_NOT_SANITIZED(name, cpp_type) \
6006 if (IsString<cpp_type>::value && IsPreferenceSanitized(name)) { \
6007 MOZ_CRASH("Unexpected sanitized string preference '" name \
6009 "Static Preferences cannot be sanitized currently, because " \
6010 "they expect to be initialized from the Static Map, and " \
6011 "sanitized preferences are not present there."); \
6014 # define ASSERT_PREF_NOT_SANITIZED(name, cpp_type)
6017 // For mirrored static prefs we generate some initialization code. Each
6018 // mirror variable is already initialized in the binary with the default
6019 // value. If the pref value hasn't changed from the default in the main
6020 // process (the common case) then the overwriting here won't change the
6021 // mirror variable's value.
6023 // Note that the MOZ_ASSERT calls below can fail in one obscure case: when a
6024 // Firefox update occurs and we get a main process from the old binary (with
6025 // static prefs {A,B,C,D}) plus a new content process from the new binary
6026 // (with static prefs {A,B,C,D,E}). The content process' call to
6027 // GetSharedPrefValue() for pref E will fail because the shared pref map was
6028 // created by the main process, which doesn't have pref E.
6030 // This silent failure is safe. The mirror variable for pref E is already
6031 // initialized to the default value in the content process, and the main
6032 // process cannot have changed pref E because it doesn't know about it!
6034 // Nonetheless, it's useful to have the MOZ_ASSERT here for testing of debug
6035 // builds, where this scenario involving inconsistent binaries should not
6037 #define NEVER_PREF(name, cpp_type, default_value)
6038 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, default_value) \
6040 StripAtomic<cpp_type> val; \
6041 ASSERT_PREF_NOT_SANITIZED(name, cpp_type); \
6042 DebugOnly<nsresult> rv = Internals::GetSharedPrefValue(name, &val); \
6043 MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed accessing " name); \
6044 StaticPrefs::sMirror_##full_id = val; \
6046 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, default_value) \
6048 StripAtomic<cpp_type> val; \
6049 ASSERT_PREF_NOT_SANITIZED(name, cpp_type); \
6050 DebugOnly<nsresult> rv = Internals::GetSharedPrefValue(name, &val); \
6051 MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed accessing " name); \
6052 Internals::AssignMirror(StaticPrefs::sMirror_##full_id, \
6053 std::forward<StripAtomic<cpp_type>>(val)); \
6055 #define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \
6058 ASSERT_PREF_NOT_SANITIZED(name, cpp_type); \
6059 DebugOnly<nsresult> rv = \
6060 Internals::GetSharedPrefValue(ONCE_PREF_NAME(name), &val); \
6061 MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed accessing " name); \
6062 StaticPrefs::sMirror_##full_id = val; \
6064 #include "mozilla/StaticPrefListAll.h"
6067 #undef ALWAYS_DATAMUTEX_PREF
6069 #undef ASSERT_PREF_NOT_SANITIZED
6071 // `once`-mirrored prefs have been set to their value in the step above and
6072 // outside the parent process they are immutable. We set sOncePrefRead so
6073 // that we can directly skip any lazy initializations.
6074 sOncePrefRead
= true;
6077 } // namespace StaticPrefs
6079 } // namespace mozilla
6081 #undef ENSURE_PARENT_PROCESS
6083 //===========================================================================
6084 // Module and factory stuff
6085 //===========================================================================
6087 NS_IMPL_COMPONENT_FACTORY(nsPrefLocalizedString
) {
6088 auto str
= MakeRefPtr
<nsPrefLocalizedString
>();
6089 if (NS_SUCCEEDED(str
->Init())) {
6090 return str
.forget().downcast
<nsISupports
>();
6097 void UnloadPrefsModule() { Preferences::Shutdown(); }
6099 } // namespace mozilla
6101 // Preference Sanitization Related Code ---------------------------------------
6103 #define PREF_LIST_ENTRY(s) {s, (sizeof(s) / sizeof(char)) - 1}
6104 struct PrefListEntry
{
6105 const char* mPrefBranch
;
6109 // A preference is 'sanitized' (i.e. not sent to web content processes) if
6110 // one of two criteria are met:
6111 // 1. The pref name matches one of the prefixes in the following list
6112 // 2. The pref is dynamically named (i.e. not specified in all.js or
6113 // StaticPrefList.yml), a string pref, and it is NOT exempted in
6114 // sDynamicPrefOverrideList
6116 // This behavior is codified in ShouldSanitizePreference() below.
6117 // Exclusions of preferences can be defined in sOverrideRestrictionsList[].
6118 static const PrefListEntry sRestrictFromWebContentProcesses
[] = {
6119 // Remove prefs with user data
6120 PREF_LIST_ENTRY("datareporting.policy."),
6121 PREF_LIST_ENTRY("browser.download.lastDir"),
6122 PREF_LIST_ENTRY("browser.newtabpage.pinned"),
6123 PREF_LIST_ENTRY("browser.uiCustomization.state"),
6124 PREF_LIST_ENTRY("browser.urlbar"),
6125 PREF_LIST_ENTRY("devtools.debugger.pending-selected-location"),
6126 PREF_LIST_ENTRY("identity.fxaccounts.account.device.name"),
6127 PREF_LIST_ENTRY("identity.fxaccounts.account.telemetry.sanitized_uid"),
6128 PREF_LIST_ENTRY("identity.fxaccounts.lastSignedInUserHash"),
6129 PREF_LIST_ENTRY("print_printer"),
6130 PREF_LIST_ENTRY("services."),
6133 PREF_LIST_ENTRY("app.normandy.user_id"),
6134 PREF_LIST_ENTRY("browser.newtabpage.activity-stream.impressionId"),
6135 PREF_LIST_ENTRY("browser.pageActions.persistedActions"),
6136 PREF_LIST_ENTRY("browser.startup.lastColdStartupCheck"),
6137 PREF_LIST_ENTRY("dom.push.userAgentID"),
6138 PREF_LIST_ENTRY("extensions.webextensions.uuids"),
6139 PREF_LIST_ENTRY("privacy.userContext.extension"),
6140 PREF_LIST_ENTRY("toolkit.telemetry.cachedClientID"),
6141 PREF_LIST_ENTRY("toolkit.telemetry.cachedProfileGroupID"),
6143 // Remove IDs that could be used to correlate across origins
6144 PREF_LIST_ENTRY("app.update.lastUpdateTime."),
6146 "browser.contentblocking.cfr-milestone.milestone-shown-time"),
6147 PREF_LIST_ENTRY("browser.contextual-services.contextId"),
6148 PREF_LIST_ENTRY("browser.laterrun.bookkeeping.profileCreationTime"),
6149 PREF_LIST_ENTRY("browser.newtabpage.activity-stream.discoverystream."),
6150 PREF_LIST_ENTRY("browser.sessionstore.upgradeBackup.latestBuildID"),
6151 PREF_LIST_ENTRY("browser.shell.mostRecentDateSetAsDefault"),
6152 PREF_LIST_ENTRY("idle.lastDailyNotification"),
6153 PREF_LIST_ENTRY("media.gmp-gmpopenh264.lastUpdate"),
6154 PREF_LIST_ENTRY("media.gmp-manager.lastCheck"),
6155 PREF_LIST_ENTRY("places.database.lastMaintenance"),
6156 PREF_LIST_ENTRY("privacy.purge_trackers.last_purge"),
6157 PREF_LIST_ENTRY("storage.vacuum.last.places.sqlite"),
6158 PREF_LIST_ENTRY("toolkit.startup.last_success"),
6160 // Remove fingerprintable things
6161 PREF_LIST_ENTRY("browser.startup.homepage_override.buildID"),
6162 PREF_LIST_ENTRY("extensions.lastAppBuildId"),
6163 PREF_LIST_ENTRY("media.gmp-manager.buildID"),
6164 PREF_LIST_ENTRY("toolkit.telemetry.previousBuildID"),
6167 // Allowlist for prefs and branches blocklisted in
6168 // sRestrictFromWebContentProcesses[], including prefs from
6169 // StaticPrefList.yaml and *.js, to let them pass.
6170 static const PrefListEntry sOverrideRestrictionsList
[]{
6171 PREF_LIST_ENTRY("services.settings.clock_skew_seconds"),
6172 PREF_LIST_ENTRY("services.settings.last_update_seconds"),
6173 PREF_LIST_ENTRY("services.settings.loglevel"),
6174 // This is really a boolean dynamic pref, but one Nightly user
6175 // has it set as a string...
6176 PREF_LIST_ENTRY("services.settings.preview_enabled"),
6177 PREF_LIST_ENTRY("services.settings.server"),
6180 // These prefs are dynamically-named (i.e. not specified in prefs.js or
6181 // StaticPrefList) and would normally by blocklisted but we allow them through
6182 // anyway, so this override list acts as an allowlist
6183 static const PrefListEntry sDynamicPrefOverrideList
[]{
6184 PREF_LIST_ENTRY("accessibility.tabfocus"),
6185 PREF_LIST_ENTRY("app.update.channel"),
6186 PREF_LIST_ENTRY("apz.subtest"),
6187 PREF_LIST_ENTRY("autoadmin.global_config_url"), // Bug 1780575
6188 PREF_LIST_ENTRY("browser.contentblocking.category"),
6189 PREF_LIST_ENTRY("browser.dom.window.dump.file"),
6190 PREF_LIST_ENTRY("browser.search.region"),
6192 "browser.tabs.remote.testOnly.failPBrowserCreation.browsingContext"),
6193 PREF_LIST_ENTRY("browser.uitour.testingOrigins"),
6194 PREF_LIST_ENTRY("browser.urlbar.loglevel"),
6195 PREF_LIST_ENTRY("browser.urlbar.opencompanionsearch.enabled"),
6196 PREF_LIST_ENTRY("capability.policy"),
6197 PREF_LIST_ENTRY("dom.securecontext.allowlist"),
6198 PREF_LIST_ENTRY("extensions.foobaz"),
6200 "extensions.formautofill.creditCards.heuristics.testConfidence"),
6201 PREF_LIST_ENTRY("general.appversion.override"),
6202 PREF_LIST_ENTRY("general.buildID.override"),
6203 PREF_LIST_ENTRY("general.oscpu.override"),
6204 PREF_LIST_ENTRY("general.useragent.override"),
6205 PREF_LIST_ENTRY("general.platform.override"),
6206 PREF_LIST_ENTRY("gfx.blacklist."),
6207 PREF_LIST_ENTRY("font.system.whitelist"),
6208 PREF_LIST_ENTRY("font.name."),
6209 PREF_LIST_ENTRY("intl.date_time.pattern_override."),
6210 PREF_LIST_ENTRY("intl.hyphenation-alias."),
6211 PREF_LIST_ENTRY("logging.config.LOG_FILE"),
6212 PREF_LIST_ENTRY("media.audio_loopback_dev"),
6213 PREF_LIST_ENTRY("media.decoder-doctor."),
6214 PREF_LIST_ENTRY("media.cubeb.backend"),
6215 PREF_LIST_ENTRY("media.cubeb.output_device"),
6216 PREF_LIST_ENTRY("media.getusermedia.fake-camera-name"),
6217 PREF_LIST_ENTRY("media.hls.server.url"),
6218 PREF_LIST_ENTRY("media.peerconnection.nat_simulator.filtering_type"),
6219 PREF_LIST_ENTRY("media.peerconnection.nat_simulator.mapping_type"),
6220 PREF_LIST_ENTRY("media.peerconnection.nat_simulator.redirect_address"),
6221 PREF_LIST_ENTRY("media.peerconnection.nat_simulator.redirect_targets"),
6222 PREF_LIST_ENTRY("media.video_loopback_dev"),
6223 PREF_LIST_ENTRY("media.webspeech.service.endpoint"),
6224 PREF_LIST_ENTRY("network.gio.supported-protocols"),
6225 PREF_LIST_ENTRY("network.protocol-handler.external."),
6226 PREF_LIST_ENTRY("network.security.ports.banned"),
6227 PREF_LIST_ENTRY("nimbus.syncdatastore."),
6228 PREF_LIST_ENTRY("pdfjs."),
6229 PREF_LIST_ENTRY("plugins.force.wmode"),
6230 PREF_LIST_ENTRY("print.printer_"),
6231 PREF_LIST_ENTRY("print_printer"),
6232 PREF_LIST_ENTRY("places.interactions.customBlocklist"),
6233 PREF_LIST_ENTRY("remote.log.level"),
6234 // services.* preferences should be added in sOverrideRestrictionsList[] -
6235 // the whole preference branch gets sanitized by default.
6236 PREF_LIST_ENTRY("spellchecker.dictionary"),
6237 PREF_LIST_ENTRY("test.char"),
6238 PREF_LIST_ENTRY("Test.IPC."),
6239 PREF_LIST_ENTRY("exists.thenDoesNot"),
6240 PREF_LIST_ENTRY("type.String."),
6241 PREF_LIST_ENTRY("toolkit.mozprotocol.url"),
6242 PREF_LIST_ENTRY("toolkit.telemetry.log.level"),
6243 PREF_LIST_ENTRY("ui."),
6246 #undef PREF_LIST_ENTRY
6248 static bool ShouldSanitizePreference(const Pref
* const aPref
) {
6249 // In the parent process, we use a heuristic to decide if a pref
6250 // value should be sanitized before sending to subprocesses.
6251 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
6253 const char* prefName
= aPref
->Name();
6255 // If a pref starts with this magic string, it is a Once-Initialized pref
6256 // from Static Prefs. It should* not be in the above list and while it looks
6257 // like a dnyamically named pref, it is not.
6258 // * nothing enforces this
6259 if (strncmp(prefName
, "$$$", 3) == 0) {
6263 // First check against the denylist.
6264 // The services pref is an annoying one - it's much easier to blocklist
6265 // the whole branch and then add this one check to let this one annoying
6267 for (const auto& entry
: sRestrictFromWebContentProcesses
) {
6268 if (strncmp(entry
.mPrefBranch
, prefName
, entry
.mLen
) == 0) {
6269 for (const auto& pasEnt
: sOverrideRestrictionsList
) {
6270 if (strncmp(pasEnt
.mPrefBranch
, prefName
, pasEnt
.mLen
) == 0) {
6278 // Then check if it's a dynamically named string preference and not
6279 // in the override list
6280 if (aPref
->Type() == PrefType::String
&& !aPref
->HasDefaultValue()) {
6281 for (const auto& entry
: sDynamicPrefOverrideList
) {
6282 if (strncmp(entry
.mPrefBranch
, prefName
, entry
.mLen
) == 0) {
6292 // Forward Declaration - it's not defined in the .h, because we don't need to;
6293 // it's only used here.
6295 static bool IsPreferenceSanitized_Impl(const T
& aPref
);
6297 static bool IsPreferenceSanitized(const Pref
* const aPref
) {
6298 return IsPreferenceSanitized_Impl(*aPref
);
6301 static bool IsPreferenceSanitized(const PrefWrapper
& aPref
) {
6302 return IsPreferenceSanitized_Impl(aPref
);
6306 static bool IsPreferenceSanitized_Impl(const T
& aPref
) {
6307 if (aPref
.IsSanitized()) {
6308 MOZ_DIAGNOSTIC_ASSERT(!XRE_IsParentProcess());
6309 MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
6317 // This is the only Check Sanitization function exposed outside of
6318 // Preferences.cpp, because this is the only one ever called from
6319 // outside this file.
6320 bool IsPreferenceSanitized(const char* aPrefName
) {
6321 // Perform this comparison (see notes above) early to avoid a lookup
6322 // if we can avoid it.
6323 if (strncmp(aPrefName
, "$$$", 3) == 0) {
6327 if (!gContentProcessPrefsAreInited
) {
6331 if (Maybe
<PrefWrapper
> pref
= pref_Lookup(aPrefName
)) {
6332 if (pref
.isNothing()) {
6335 return IsPreferenceSanitized(pref
.value());
6341 Atomic
<bool, Relaxed
> sOmitBlocklistedPrefValues(false);
6342 Atomic
<bool, Relaxed
> sCrashOnBlocklistedPref(false);
6344 void OnFissionBlocklistPrefChange(const char* aPref
, void* aData
) {
6345 if (strcmp(aPref
, kFissionEnforceBlockList
) == 0) {
6346 sCrashOnBlocklistedPref
=
6347 StaticPrefs::fission_enforceBlocklistedPrefsInSubprocesses();
6348 } else if (strcmp(aPref
, kFissionOmitBlockListValues
) == 0) {
6349 sOmitBlocklistedPrefValues
=
6350 StaticPrefs::fission_omitBlocklistedPrefsInSubprocesses();
6352 MOZ_CRASH("Unknown pref passed to callback");
6356 } // namespace mozilla
6358 // This file contains the C wrappers for the C++ static pref getters, as used
6360 #include "init/StaticPrefsCGetters.cpp"