--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -463,17 +463,16 @@ class Pref
public:
explicit Pref(const char* aName)
: mName(ArenaStrdup(aName, gPrefNameArena))
, mType(static_cast<uint32_t>(PrefType::None))
, mIsSticky(false)
, mIsLocked(false)
, mHasDefaultValue(false)
, mHasUserValue(false)
- , mHasChangedSinceInit(false)
, mDefaultValue()
, mUserValue()
{
}
~Pref()
{
// There's no need to free mName because it's allocated in memory owned by
@@ -495,28 +494,17 @@ public:
bool IsTypeNone() const { return IsType(PrefType::None); }
bool IsTypeString() const { return IsType(PrefType::String); }
bool IsTypeInt() const { return IsType(PrefType::Int); }
bool IsTypeBool() const { return IsType(PrefType::Bool); }
// Other properties.
bool IsLocked() const { return mIsLocked; }
- void SetIsLocked(bool aValue)
- {
- mIsLocked = aValue;
- OnChanged();
- }
-
- void OnChanged()
- {
- if (gSharedMap) {
- mHasChangedSinceInit = true;
- }
- }
+ void SetIsLocked(bool aValue) { mIsLocked = aValue; }
bool IsSticky() const { return mIsSticky; }
bool HasDefaultValue() const { return mHasDefaultValue; }
bool HasUserValue() const { return mHasUserValue; }
template<typename T>
void AddToMap(SharedPrefMapBuilder& aMap)
@@ -535,42 +523,16 @@ public:
AddToMap<int32_t>(aMap);
} else if (IsTypeString()) {
AddToMap<nsDependentCString>(aMap);
} else {
MOZ_ASSERT_UNREACHABLE("Unexpected preference type");
}
}
- // When a content process is created we could tell it about every pref. But
- // the content process also initializes prefs from file, so we save a lot of
- // IPC if we only tell it about prefs that have changed since initialization.
- //
- // Specifically, we send a pref if any of the following conditions are met.
- //
- // - If the pref has changed in any way (default value, user value, or other
- // attribute, such as whether it is locked) since being initialized from
- // file.
- //
- // - If the pref has a user value. (User values are more complicated than
- // default values, because they can be loaded from file after
- // initialization with Preferences::ReadUserPrefsFromFile(), so we are
- // conservative with them.)
- //
- // In other words, prefs that only have a default value and haven't changed
- // need not be sent. One could do better with effort, but it's ok to be
- // conservative and this still greatly reduces the number of prefs sent.
- //
- // Note: This function is only useful in the parent process.
- bool MustSendToContentProcesses() const
- {
- MOZ_ASSERT(XRE_IsParentProcess());
- return mHasChangedSinceInit;
- }
-
// Other operations.
bool MatchEntry(const char* aPrefName)
{
if (!mName || !aPrefName) {
return false;
}
@@ -678,18 +640,16 @@ public:
mHasUserValue = true;
userValueChanged = true;
}
} else if (mHasUserValue) {
ClearUserValue();
userValueChanged = true;
}
- OnChanged();
-
if (userValueChanged || (defaultValueChanged && !mHasUserValue)) {
*aValueChanged = true;
}
}
void FromWrapper(PrefWrapper& aWrapper);
bool HasAdvisablySizedValues()
@@ -727,17 +687,16 @@ private:
: mHasUserValue && mUserValue.Equals(aType, aValue));
}
public:
void ClearUserValue()
{
mUserValue.Clear(Type());
mHasUserValue = false;
- OnChanged();
}
nsresult SetDefaultValue(PrefType aType,
PrefValue aValue,
bool aIsSticky,
bool aIsLocked,
bool* aValueChanged)
{
@@ -750,17 +709,16 @@ public:
// doing so would change the default value.
if (!IsLocked()) {
if (aIsLocked) {
SetIsLocked(true);
}
if (!ValueMatches(PrefValueKind::Default, aType, aValue)) {
mDefaultValue.Replace(mHasDefaultValue, Type(), aType, aValue);
mHasDefaultValue = true;
- OnChanged();
if (aIsSticky) {
mIsSticky = true;
}
if (!mHasUserValue) {
*aValueChanged = true;
}
// What if we change the default to be the same as the user value?
// Should we clear the user value? Currently we don't.
@@ -793,48 +751,23 @@ public:
}
// Otherwise, should we set the user value? Only if doing so would
// change the user value.
} else if (!ValueMatches(PrefValueKind::User, aType, aValue)) {
mUserValue.Replace(mHasUserValue, Type(), aType, aValue);
SetType(aType); // needed because we may have changed the type
mHasUserValue = true;
- OnChanged();
if (!IsLocked()) {
*aValueChanged = true;
}
}
return NS_OK;
}
- // Returns false if this pref doesn't have a user value worth saving.
- bool UserValueToStringForSaving(nsCString& aStr)
- {
- // Should we save the user value, if present? Only if it does not match the
- // default value, or it is sticky.
- if (mHasUserValue &&
- (!ValueMatches(PrefValueKind::Default, Type(), mUserValue) ||
- mIsSticky)) {
- if (IsTypeString()) {
- StrEscape(mUserValue.mStringVal, aStr);
-
- } else if (IsTypeInt()) {
- aStr.AppendInt(mUserValue.mIntVal);
-
- } else if (IsTypeBool()) {
- aStr = mUserValue.mBoolVal ? "true" : "false";
- }
- return true;
- }
-
- // Do not save default prefs that haven't changed.
- return false;
- }
-
// Prefs are serialized in a manner that mirrors dom::Pref. The two should be
// kept in sync. E.g. if something is added to one it should also be added to
// the other. (It would be nice to be able to use the code generated from
// IPDL for serializing dom::Pref here instead of writing by hand this
// serialization/deserialization. Unfortunately, that generated code is
// difficult to use directly, outside of the IPDL IPC code.)
//
// The grammar for the serialized prefs has the following form.
@@ -1000,17 +933,16 @@ public:
private:
const char* mName; // allocated in gPrefNameArena
uint32_t mType : 2;
uint32_t mIsSticky : 1;
uint32_t mIsLocked : 1;
uint32_t mHasDefaultValue : 1;
uint32_t mHasUserValue : 1;
- uint32_t mHasChangedSinceInit : 1;
PrefValue mDefaultValue;
PrefValue mUserValue;
};
class PrefEntry : public PLDHashEntryHdr
{
public:
@@ -1157,16 +1089,40 @@ public:
{
PrefValueKind kind;
MOZ_TRY_VAR(kind, WantValueKind(PrefType::String, aKind));
aResult = GetStringValue(kind);
return NS_OK;
}
+ // Returns false if this pref doesn't have a user value worth saving.
+ bool UserValueToStringForSaving(nsCString& aStr)
+ {
+ // Should we save the user value, if present? Only if it does not match the
+ // default value, or it is sticky.
+ if (HasUserValue() &&
+ (!ValueMatches(PrefValueKind::Default, Type(), GetValue()) ||
+ IsSticky())) {
+ if (IsTypeString()) {
+ StrEscape(GetStringValue().get(), aStr);
+
+ } else if (IsTypeInt()) {
+ aStr.AppendInt(GetIntValue());
+
+ } else if (IsTypeBool()) {
+ aStr = GetBoolValue() ? "true" : "false";
+ }
+ return true;
+ }
+
+ // Do not save default prefs that haven't changed.
+ return false;
+ }
+
bool Matches(PrefType aType,
PrefValueKind aKind,
PrefValue& aValue,
bool aIsSticky,
bool aIsLocked) const
{
return (ValueMatches(aKind, aType, aValue) && aIsSticky == IsSticky() &&
aIsLocked == IsLocked());
@@ -1602,19 +1558,17 @@ constexpr size_t kHashTableInitialLength
static PrefSaveData
pref_savePrefs()
{
MOZ_ASSERT(NS_IsMainThread());
PrefSaveData savedPrefs(gHashTable->EntryCount());
- for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
- Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
-
+ for (auto& pref : PrefsIter(gHashTable, gSharedMap)) {
nsAutoCString prefValueStr;
if (!pref->UserValueToStringForSaving(prefValueStr)) {
continue;
}
nsAutoCString prefNameStr;
StrEscape(pref->Name(), prefNameStr);
@@ -4022,17 +3976,17 @@ NS_IMPL_ISUPPORTS(Preferences,
Preferences::SerializePreferences(nsCString& aStr)
{
MOZ_RELEASE_ASSERT(InitStaticMembers());
aStr.Truncate();
for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
- if (pref->MustSendToContentProcesses() && pref->HasAdvisablySizedValues()) {
+ if (pref->HasAdvisablySizedValues()) {
pref->SerializeAndAppend(aStr);
}
}
aStr.Append('\0');
}
/* static */ void
@@ -4070,16 +4024,28 @@ Preferences::EnsureSnapshot(size_t* aSiz
for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
pref->AddToMap(builder);
}
gSharedMap = new SharedPrefMap(std::move(builder));
+
+ // Once we've built a snapshot of the database, there's no need to continue
+ // storing dynamic copies of the preferences it contains. Once we reset the
+ // hashtable, preference lookups will fall back to the snapshot for any
+ // preferences not in the dynamic hashtable.
+ //
+ // And since the majority of the database is now contained in the snapshot,
+ // we can initialize the hashtable with the expected number of per-session
+ // changed preferences, rather than the expected total number of
+ // preferences.
+ gHashTable->ClearAndPrepareForLength(kHashTableInitialLengthContent);
+ gPrefNameArena.Clear();
}
*aSize = gSharedMap->MapSize();
return gSharedMap->CloneFileDescriptor();
}
/* static */ void
Preferences::InitSnapshot(const FileDescriptor& aHandle, size_t aSize)