LCOV - code coverage report
Current view: top level - modules/libpref - Preferences.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 12 1899 0.6 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include <ctype.h>
       8             : #include <stdlib.h>
       9             : #include <string.h>
      10             : 
      11             : #include "base/basictypes.h"
      12             : #include "GeckoProfiler.h"
      13             : #include "MainThreadUtils.h"
      14             : #include "mozilla/ArenaAllocatorExtensions.h"
      15             : #include "mozilla/ArenaAllocator.h"
      16             : #include "mozilla/ArrayUtils.h"
      17             : #include "mozilla/Attributes.h"
      18             : #include "mozilla/dom/PContent.h"
      19             : #include "mozilla/HashFunctions.h"
      20             : #include "mozilla/Logging.h"
      21             : #include "mozilla/Maybe.h"
      22             : #include "mozilla/MemoryReporting.h"
      23             : #include "mozilla/ModuleUtils.h"
      24             : #include "mozilla/Omnijar.h"
      25             : #include "mozilla/Preferences.h"
      26             : #include "mozilla/ResultExtensions.h"
      27             : #include "mozilla/ScopeExit.h"
      28             : #include "mozilla/Services.h"
      29             : #include "mozilla/ServoStyleSet.h"
      30             : #include "mozilla/StaticPrefs.h"
      31             : #include "mozilla/SyncRunnable.h"
      32             : #include "mozilla/SystemGroup.h"
      33             : #include "mozilla/Telemetry.h"
      34             : #include "mozilla/UniquePtrExtensions.h"
      35             : #include "mozilla/URLPreloader.h"
      36             : #include "mozilla/Variant.h"
      37             : #include "mozilla/Vector.h"
      38             : #include "nsAppDirectoryServiceDefs.h"
      39             : #include "nsAutoPtr.h"
      40             : #include "nsCategoryManagerUtils.h"
      41             : #include "nsClassHashtable.h"
      42             : #include "nsCOMArray.h"
      43             : #include "nsCOMPtr.h"
      44             : #include "nsCRT.h"
      45             : #include "nsDataHashtable.h"
      46             : #include "nsDirectoryServiceDefs.h"
      47             : #include "nsHashKeys.h"
      48             : #include "nsICategoryManager.h"
      49             : #include "nsIConsoleService.h"
      50             : #include "nsIDirectoryService.h"
      51             : #include "nsIFile.h"
      52             : #include "nsIInputStream.h"
      53             : #include "nsIMemoryReporter.h"
      54             : #include "nsIObserver.h"
      55             : #include "nsIObserverService.h"
      56             : #include "nsIOutputStream.h"
      57             : #include "nsIPrefBranch.h"
      58             : #include "nsIPrefLocalizedString.h"
      59             : #include "nsIRelativeFilePref.h"
      60             : #include "nsISafeOutputStream.h"
      61             : #include "nsISimpleEnumerator.h"
      62             : #include "nsIStringBundle.h"
      63             : #include "nsIStringEnumerator.h"
      64             : #include "nsISupportsImpl.h"
      65             : #include "nsISupportsPrimitives.h"
      66             : #include "nsIZipReader.h"
      67             : #include "nsNetUtil.h"
      68             : #include "nsPrintfCString.h"
      69             : #include "nsQuickSort.h"
      70             : #include "nsReadableUtils.h"
      71             : #include "nsRefPtrHashtable.h"
      72             : #include "nsString.h"
      73             : #include "nsTArray.h"
      74             : #include "nsThreadUtils.h"
      75             : #include "nsUTF8Utils.h"
      76             : #include "nsWeakReference.h"
      77             : #include "nsXPCOMCID.h"
      78             : #include "nsXPCOM.h"
      79             : #include "nsXULAppAPI.h"
      80             : #include "nsZipArchive.h"
      81             : #include "plbase64.h"
      82             : #include "PLDHashTable.h"
      83             : #include "plstr.h"
      84             : #include "prlink.h"
      85             : 
      86             : #ifdef XP_WIN
      87             : #include "windows.h"
      88             : #endif
      89             : 
      90             : using namespace mozilla;
      91             : 
      92             : #ifdef DEBUG
      93             : 
      94             : #define ENSURE_PARENT_PROCESS(func, pref)                                      \
      95             :   do {                                                                         \
      96             :     if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {                                \
      97             :       nsPrintfCString msg(                                                     \
      98             :         "ENSURE_PARENT_PROCESS: called %s on %s in a non-parent process",      \
      99             :         func,                                                                  \
     100             :         pref);                                                                 \
     101             :       NS_ERROR(msg.get());                                                     \
     102             :       return NS_ERROR_NOT_AVAILABLE;                                           \
     103             :     }                                                                          \
     104             :   } while (0)
     105             : 
     106             : #else // DEBUG
     107             : 
     108             : #define ENSURE_PARENT_PROCESS(func, pref)                                      \
     109             :   if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {                                  \
     110             :     return NS_ERROR_NOT_AVAILABLE;                                             \
     111             :   }
     112             : 
     113             : #endif // DEBUG
     114             : 
     115             : //===========================================================================
     116             : // Low-level types and operations
     117             : //===========================================================================
     118             : 
     119             : typedef nsTArray<nsCString> PrefSaveData;
     120             : 
     121             : // 1 MB should be enough for everyone.
     122             : static const uint32_t MAX_PREF_LENGTH = 1 * 1024 * 1024;
     123             : // Actually, 4kb should be enough for everyone.
     124             : static const uint32_t MAX_ADVISABLE_PREF_LENGTH = 4 * 1024;
     125             : 
     126             : // Keep this in sync with PrefType in parser/src/lib.rs.
     127             : enum class PrefType : uint8_t
     128             : {
     129             :   None = 0, // only used when neither the default nor user value is set
     130             :   String = 1,
     131             :   Int = 2,
     132             :   Bool = 3,
     133             : };
     134             : 
     135             : // This is used for pref names and string pref values. We encode the string
     136             : // length, then a '/', then the string chars. This encoding means there are no
     137             : // special chars that are forbidden or require escaping.
     138             : static void
     139           0 : SerializeAndAppendString(const char* aChars, nsCString& aStr)
     140             : {
     141           0 :   aStr.AppendInt(uint32_t(strlen(aChars)));
     142           0 :   aStr.Append('/');
     143           0 :   aStr.Append(aChars);
     144           0 : }
     145             : 
     146             : static char*
     147           0 : DeserializeString(char* aChars, nsCString& aStr)
     148             : {
     149           0 :   char* p = aChars;
     150           0 :   uint32_t length = strtol(p, &p, 10);
     151           0 :   MOZ_ASSERT(p[0] == '/');
     152           0 :   p++; // move past the '/'
     153           0 :   aStr.Assign(p, length);
     154           0 :   p += length; // move past the string itself
     155           0 :   return p;
     156             : }
     157             : 
     158             : // Keep this in sync with PrefValue in prefs_parser/src/lib.rs.
     159             : union PrefValue {
     160             :   const char* mStringVal;
     161             :   int32_t mIntVal;
     162             :   bool mBoolVal;
     163             : 
     164           0 :   bool Equals(PrefType aType, PrefValue aValue)
     165             :   {
     166           0 :     switch (aType) {
     167             :       case PrefType::String: {
     168           0 :         if (mStringVal && aValue.mStringVal) {
     169           0 :           return strcmp(mStringVal, aValue.mStringVal) == 0;
     170             :         }
     171           0 :         if (!mStringVal && !aValue.mStringVal) {
     172             :           return true;
     173             :         }
     174           0 :         return false;
     175             :       }
     176             : 
     177             :       case PrefType::Int:
     178           0 :         return mIntVal == aValue.mIntVal;
     179             : 
     180             :       case PrefType::Bool:
     181           0 :         return mBoolVal == aValue.mBoolVal;
     182             : 
     183             :       default:
     184           0 :         MOZ_CRASH("Unhandled enum value");
     185             :     }
     186             :   }
     187             : 
     188           0 :   void Init(PrefType aNewType, PrefValue aNewValue)
     189             :   {
     190           0 :     if (aNewType == PrefType::String) {
     191           0 :       MOZ_ASSERT(aNewValue.mStringVal);
     192           0 :       aNewValue.mStringVal = moz_xstrdup(aNewValue.mStringVal);
     193             :     }
     194           0 :     *this = aNewValue;
     195           0 :   }
     196             : 
     197           0 :   void Clear(PrefType aType)
     198             :   {
     199           0 :     if (aType == PrefType::String) {
     200           0 :       free(const_cast<char*>(mStringVal));
     201             :     }
     202             : 
     203             :     // Zero the entire value (regardless of type) via mStringVal.
     204           0 :     mStringVal = nullptr;
     205           0 :   }
     206             : 
     207           0 :   void Replace(bool aHasValue, PrefType aOldType, PrefType aNewType, PrefValue aNewValue)
     208             :   {
     209           0 :     if (aHasValue) {
     210           0 :       Clear(aOldType);
     211             :     }
     212           0 :     Init(aNewType, aNewValue);
     213           0 :   }
     214             : 
     215           0 :   void ToDomPrefValue(PrefType aType, dom::PrefValue* aDomValue)
     216             :   {
     217           0 :     switch (aType) {
     218             :       case PrefType::String:
     219           0 :         *aDomValue = nsDependentCString(mStringVal);
     220           0 :         return;
     221             : 
     222             :       case PrefType::Int:
     223           0 :         *aDomValue = mIntVal;
     224           0 :         return;
     225             : 
     226             :       case PrefType::Bool:
     227           0 :         *aDomValue = mBoolVal;
     228           0 :         return;
     229             : 
     230             :       default:
     231           0 :         MOZ_CRASH();
     232             :     }
     233             :   }
     234             : 
     235           0 :   PrefType FromDomPrefValue(const dom::PrefValue& aDomValue)
     236             :   {
     237           0 :     switch (aDomValue.type()) {
     238             :       case dom::PrefValue::TnsCString:
     239           0 :         mStringVal = aDomValue.get_nsCString().get();
     240           0 :         return PrefType::String;
     241             : 
     242             :       case dom::PrefValue::Tint32_t:
     243           0 :         mIntVal = aDomValue.get_int32_t();
     244           0 :         return PrefType::Int;
     245             : 
     246             :       case dom::PrefValue::Tbool:
     247           0 :         mBoolVal = aDomValue.get_bool();
     248           0 :         return PrefType::Bool;
     249             : 
     250             :       default:
     251           0 :         MOZ_CRASH();
     252             :     }
     253             :   }
     254             : 
     255           0 :   void SerializeAndAppend(PrefType aType, nsCString& aStr)
     256             :   {
     257           0 :     switch (aType) {
     258             :       case PrefType::Bool:
     259           0 :         aStr.Append(mBoolVal ? 'T' : 'F');
     260           0 :         break;
     261             : 
     262             :       case PrefType::Int:
     263           0 :         aStr.AppendInt(mIntVal);
     264           0 :         break;
     265             : 
     266             :       case PrefType::String: {
     267           0 :         SerializeAndAppendString(mStringVal, aStr);
     268           0 :         break;
     269             :       }
     270             : 
     271             :       case PrefType::None:
     272             :       default:
     273           0 :         MOZ_CRASH();
     274             :     }
     275           0 :   }
     276             : 
     277           0 :   static char* Deserialize(PrefType aType,
     278             :                            char* aStr,
     279             :                            dom::MaybePrefValue* aDomValue)
     280             :   {
     281           0 :     char* p = aStr;
     282             : 
     283           0 :     switch (aType) {
     284             :       case PrefType::Bool:
     285           0 :         if (*p == 'T') {
     286           0 :           *aDomValue = true;
     287           0 :         } else if (*p == 'F') {
     288           0 :           *aDomValue = false;
     289             :         } else {
     290           0 :           *aDomValue = false;
     291           0 :           NS_ERROR("bad bool pref value");
     292             :         }
     293           0 :         p++;
     294           0 :         return p;
     295             : 
     296             :       case PrefType::Int: {
     297           0 :         *aDomValue = int32_t(strtol(p, &p, 10));
     298           0 :         return p;
     299             :       }
     300             : 
     301             :       case PrefType::String: {
     302           0 :         nsCString str;
     303           0 :         p = DeserializeString(p, str);
     304           0 :         *aDomValue = str;
     305           0 :         return p;
     306             :       }
     307             : 
     308             :       default:
     309           0 :         MOZ_CRASH();
     310             :     }
     311             :   }
     312             : };
     313             : 
     314             : #ifdef DEBUG
     315             : const char*
     316           0 : PrefTypeToString(PrefType aType)
     317             : {
     318           0 :   switch (aType) {
     319             :     case PrefType::None:
     320             :       return "none";
     321             :     case PrefType::String:
     322           0 :       return "string";
     323             :     case PrefType::Int:
     324           0 :       return "int";
     325             :     case PrefType::Bool:
     326           0 :       return "bool";
     327             :     default:
     328           0 :       MOZ_CRASH("Unhandled enum value");
     329             :   }
     330             : }
     331             : #endif
     332             : 
     333             : // Assign to aResult a quoted, escaped copy of aOriginal.
     334             : static void
     335           0 : StrEscape(const char* aOriginal, nsCString& aResult)
     336             : {
     337           0 :   if (aOriginal == nullptr) {
     338           0 :     aResult.AssignLiteral("\"\"");
     339           0 :     return;
     340             :   }
     341             : 
     342             :   // JavaScript does not allow quotes, slashes, or line terminators inside
     343             :   // strings so we must escape them. ECMAScript defines four line terminators,
     344             :   // but we're only worrying about \r and \n here.  We currently feed our pref
     345             :   // script to the JS interpreter as Latin-1 so  we won't encounter \u2028
     346             :   // (line separator) or \u2029 (paragraph separator).
     347             :   //
     348             :   // WARNING: There are hints that we may be moving to storing prefs as utf8.
     349             :   // If we ever feed them to the JS compiler as UTF8 then we'll have to worry
     350             :   // about the multibyte sequences that would be interpreted as \u2028 and
     351             :   // \u2029.
     352             :   const char* p;
     353             : 
     354           0 :   aResult.Assign('"');
     355             : 
     356             :   // Paranoid worst case all slashes will free quickly.
     357           0 :   for (p = aOriginal; *p; ++p) {
     358           0 :     switch (*p) {
     359             :       case '\n':
     360           0 :         aResult.AppendLiteral("\\n");
     361           0 :         break;
     362             : 
     363             :       case '\r':
     364           0 :         aResult.AppendLiteral("\\r");
     365           0 :         break;
     366             : 
     367             :       case '\\':
     368           0 :         aResult.AppendLiteral("\\\\");
     369           0 :         break;
     370             : 
     371             :       case '\"':
     372           0 :         aResult.AppendLiteral("\\\"");
     373           0 :         break;
     374             : 
     375             :       default:
     376           0 :         aResult.Append(*p);
     377           0 :         break;
     378             :     }
     379             :   }
     380             : 
     381           0 :   aResult.Append('"');
     382             : }
     383             : 
     384             : namespace mozilla {
     385             : struct PrefsSizes
     386             : {
     387           0 :   PrefsSizes()
     388           0 :     : mHashTable(0)
     389             :     , mPrefValues(0)
     390             :     , mStringValues(0)
     391             :     , mCacheData(0)
     392             :     , mRootBranches(0)
     393             :     , mPrefNameArena(0)
     394             :     , mCallbacksObjects(0)
     395             :     , mCallbacksDomains(0)
     396           0 :     , mMisc(0)
     397             :   {
     398           0 :   }
     399             : 
     400             :   size_t mHashTable;
     401             :   size_t mPrefValues;
     402             :   size_t mStringValues;
     403             :   size_t mCacheData;
     404             :   size_t mRootBranches;
     405             :   size_t mPrefNameArena;
     406             :   size_t mCallbacksObjects;
     407             :   size_t mCallbacksDomains;
     408             :   size_t mMisc;
     409             : };
     410             : }
     411             : 
     412           0 : static ArenaAllocator<8192, 1> gPrefNameArena;
     413             : 
     414             : class Pref
     415             : {
     416             : public:
     417           0 :   explicit Pref(const char* aName)
     418           0 :     : mName(ArenaStrdup(aName, gPrefNameArena))
     419             :     , mType(static_cast<uint32_t>(PrefType::None))
     420             :     , mIsSticky(false)
     421             :     , mIsLocked(false)
     422             :     , mHasDefaultValue(false)
     423             :     , mHasUserValue(false)
     424             :     , mHasChangedSinceInit(false)
     425             :     , mDefaultValue()
     426           0 :     , mUserValue()
     427             :   {
     428           0 :   }
     429             : 
     430           0 :   ~Pref()
     431           0 :   {
     432             :     // There's no need to free mName because it's allocated in memory owned by
     433             :     // gPrefNameArena.
     434             : 
     435           0 :     mDefaultValue.Clear(Type());
     436           0 :     mUserValue.Clear(Type());
     437           0 :   }
     438             : 
     439             :   const char* Name() { return mName; }
     440             : 
     441             :   // Types.
     442             : 
     443           0 :   PrefType Type() const { return static_cast<PrefType>(mType); }
     444           0 :   void SetType(PrefType aType) { mType = static_cast<uint32_t>(aType); }
     445             : 
     446           0 :   bool IsType(PrefType aType) const { return Type() == aType; }
     447           0 :   bool IsTypeNone() const { return IsType(PrefType::None); }
     448           0 :   bool IsTypeString() const { return IsType(PrefType::String); }
     449           0 :   bool IsTypeInt() const { return IsType(PrefType::Int); }
     450           0 :   bool IsTypeBool() const { return IsType(PrefType::Bool); }
     451             : 
     452             :   // Other properties.
     453             : 
     454           0 :   bool IsLocked() const { return mIsLocked; }
     455             :   void SetIsLocked(bool aValue)
     456             :   {
     457           0 :     mIsLocked = aValue;
     458           0 :     mHasChangedSinceInit = true;
     459             :   }
     460             : 
     461           0 :   bool HasDefaultValue() const { return mHasDefaultValue; }
     462           0 :   bool HasUserValue() const { return mHasUserValue; }
     463             : 
     464             :   // When a content process is created we could tell it about every pref. But
     465             :   // the content process also initializes prefs from file, so we save a lot of
     466             :   // IPC if we only tell it about prefs that have changed since initialization.
     467             :   //
     468             :   // Specifically, we send a pref if any of the following conditions are met.
     469             :   //
     470             :   // - If the pref has changed in any way (default value, user value, or other
     471             :   //   attribute, such as whether it is locked) since being initialized from
     472             :   //   file.
     473             :   //
     474             :   // - If the pref has a user value. (User values are more complicated than
     475             :   //   default values, because they can be loaded from file after
     476             :   //   initialization with Preferences::ReadUserPrefsFromFile(), so we are
     477             :   //   conservative with them.)
     478             :   //
     479             :   // In other words, prefs that only have a default value and haven't changed
     480             :   // need not be sent. One could do better with effort, but it's ok to be
     481             :   // conservative and this still greatly reduces the number of prefs sent.
     482             :   //
     483             :   // Note: This function is only useful in the parent process.
     484           0 :   bool MustSendToContentProcesses() const
     485             :   {
     486           0 :     MOZ_ASSERT(XRE_IsParentProcess());
     487           0 :     return mHasUserValue || mHasChangedSinceInit;
     488             :   }
     489             : 
     490             :   // Other operations.
     491             : 
     492           0 :   bool MatchEntry(const char* aPrefName)
     493             :   {
     494           0 :     if (!mName || !aPrefName) {
     495             :       return false;
     496             :     }
     497             : 
     498           0 :     return strcmp(mName, aPrefName) == 0;
     499             :   }
     500             : 
     501           0 :   nsresult GetBoolValue(PrefValueKind aKind, bool* aResult)
     502             :   {
     503           0 :     if (!IsTypeBool()) {
     504             :       return NS_ERROR_UNEXPECTED;
     505             :     }
     506             : 
     507           0 :     if (aKind == PrefValueKind::Default || IsLocked() || !mHasUserValue) {
     508             :       // Do we have a default?
     509           0 :       if (!mHasDefaultValue) {
     510             :         return NS_ERROR_UNEXPECTED;
     511             :       }
     512           0 :       *aResult = mDefaultValue.mBoolVal;
     513             :     } else {
     514           0 :       *aResult = mUserValue.mBoolVal;
     515             :     }
     516             : 
     517             :     return NS_OK;
     518             :   }
     519             : 
     520           0 :   nsresult GetIntValue(PrefValueKind aKind, int32_t* aResult)
     521             :   {
     522           0 :     if (!IsTypeInt()) {
     523             :       return NS_ERROR_UNEXPECTED;
     524             :     }
     525             : 
     526           0 :     if (aKind == PrefValueKind::Default || IsLocked() || !mHasUserValue) {
     527             :       // Do we have a default?
     528           0 :       if (!mHasDefaultValue) {
     529             :         return NS_ERROR_UNEXPECTED;
     530             :       }
     531           0 :       *aResult = mDefaultValue.mIntVal;
     532             :     } else {
     533           0 :       *aResult = mUserValue.mIntVal;
     534             :     }
     535             : 
     536             :     return NS_OK;
     537             :   }
     538             : 
     539           0 :   nsresult GetCStringValue(PrefValueKind aKind, nsACString& aResult)
     540             :   {
     541           0 :     if (!IsTypeString()) {
     542             :       return NS_ERROR_UNEXPECTED;
     543             :     }
     544             : 
     545           0 :     if (aKind == PrefValueKind::Default || IsLocked() || !mHasUserValue) {
     546             :       // Do we have a default?
     547           0 :       if (!mHasDefaultValue) {
     548             :         return NS_ERROR_UNEXPECTED;
     549             :       }
     550           0 :       MOZ_ASSERT(mDefaultValue.mStringVal);
     551           0 :       aResult = mDefaultValue.mStringVal;
     552             :     } else {
     553           0 :       MOZ_ASSERT(mUserValue.mStringVal);
     554           0 :       aResult = mUserValue.mStringVal;
     555             :     }
     556             : 
     557             :     return NS_OK;
     558             :   }
     559             : 
     560           0 :   void ToDomPref(dom::Pref* aDomPref)
     561             :   {
     562           0 :     MOZ_ASSERT(XRE_IsParentProcess());
     563             : 
     564           0 :     aDomPref->name() = mName;
     565             : 
     566           0 :     aDomPref->isLocked() = mIsLocked;
     567             : 
     568           0 :     if (mHasDefaultValue) {
     569           0 :       aDomPref->defaultValue() = dom::PrefValue();
     570           0 :       mDefaultValue.ToDomPrefValue(Type(),
     571           0 :                                    &aDomPref->defaultValue().get_PrefValue());
     572             :     } else {
     573           0 :       aDomPref->defaultValue() = null_t();
     574             :     }
     575             : 
     576           0 :     if (mHasUserValue) {
     577           0 :       aDomPref->userValue() = dom::PrefValue();
     578           0 :       mUserValue.ToDomPrefValue(Type(), &aDomPref->userValue().get_PrefValue());
     579             :     } else {
     580           0 :       aDomPref->userValue() = null_t();
     581             :     }
     582             : 
     583           0 :     MOZ_ASSERT(aDomPref->defaultValue().type() ==
     584             :                  dom::MaybePrefValue::Tnull_t ||
     585             :                aDomPref->userValue().type() == dom::MaybePrefValue::Tnull_t ||
     586             :                (aDomPref->defaultValue().get_PrefValue().type() ==
     587             :                 aDomPref->userValue().get_PrefValue().type()));
     588           0 :   }
     589             : 
     590           0 :   void FromDomPref(const dom::Pref& aDomPref, bool* aValueChanged)
     591             :   {
     592           0 :     MOZ_ASSERT(!XRE_IsParentProcess());
     593           0 :     MOZ_ASSERT(strcmp(mName, aDomPref.name().get()) == 0);
     594             : 
     595           0 :     mIsLocked = aDomPref.isLocked();
     596             : 
     597           0 :     const dom::MaybePrefValue& defaultValue = aDomPref.defaultValue();
     598           0 :     bool defaultValueChanged = false;
     599           0 :     if (defaultValue.type() == dom::MaybePrefValue::TPrefValue) {
     600             :       PrefValue value;
     601           0 :       PrefType type = value.FromDomPrefValue(defaultValue.get_PrefValue());
     602           0 :       if (!ValueMatches(PrefValueKind::Default, type, value)) {
     603             :         // Type() is PrefType::None if it's a newly added pref. This is ok.
     604           0 :         mDefaultValue.Replace(mHasDefaultValue, Type(), type, value);
     605           0 :         SetType(type);
     606           0 :         mHasDefaultValue = true;
     607           0 :         defaultValueChanged = true;
     608             :       }
     609             :     }
     610             :     // Note: we never clear a default value.
     611             : 
     612           0 :     const dom::MaybePrefValue& userValue = aDomPref.userValue();
     613           0 :     bool userValueChanged = false;
     614           0 :     if (userValue.type() == dom::MaybePrefValue::TPrefValue) {
     615             :       PrefValue value;
     616           0 :       PrefType type = value.FromDomPrefValue(userValue.get_PrefValue());
     617           0 :       if (!ValueMatches(PrefValueKind::User, type, value)) {
     618             :         // Type() is PrefType::None if it's a newly added pref. This is ok.
     619           0 :         mUserValue.Replace(mHasUserValue, Type(), type, value);
     620           0 :         SetType(type);
     621           0 :         mHasUserValue = true;
     622           0 :         userValueChanged = true;
     623             :       }
     624           0 :     } else if (mHasUserValue) {
     625           0 :       ClearUserValue();
     626           0 :       userValueChanged = true;
     627             :     }
     628             : 
     629           0 :     mHasChangedSinceInit = true;
     630             : 
     631           0 :     if (userValueChanged || (defaultValueChanged && !mHasUserValue)) {
     632           0 :       *aValueChanged = true;
     633             :     }
     634           0 :   }
     635             : 
     636           0 :   bool HasAdvisablySizedValues()
     637             :   {
     638           0 :     MOZ_ASSERT(XRE_IsParentProcess());
     639             : 
     640           0 :     if (!IsTypeString()) {
     641             :       return true;
     642             :     }
     643             : 
     644             :     const char* stringVal;
     645           0 :     if (mHasDefaultValue) {
     646           0 :       stringVal = mDefaultValue.mStringVal;
     647           0 :       if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
     648             :         return false;
     649             :       }
     650             :     }
     651             : 
     652           0 :     if (mHasUserValue) {
     653           0 :       stringVal = mUserValue.mStringVal;
     654           0 :       if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
     655             :         return false;
     656             :       }
     657             :     }
     658             : 
     659           0 :     return true;
     660             :   }
     661             : 
     662             : private:
     663           0 :   bool ValueMatches(PrefValueKind aKind, PrefType aType, PrefValue aValue)
     664             :   {
     665           0 :     return IsType(aType) &&
     666             :            (aKind == PrefValueKind::Default
     667           0 :               ? mHasDefaultValue && mDefaultValue.Equals(aType, aValue)
     668           0 :               : mHasUserValue && mUserValue.Equals(aType, aValue));
     669             :   }
     670             : 
     671             : public:
     672           0 :   void ClearUserValue()
     673             :   {
     674           0 :     mUserValue.Clear(Type());
     675           0 :     mHasUserValue = false;
     676           0 :     mHasChangedSinceInit = true;
     677           0 :   }
     678             : 
     679           0 :   nsresult SetDefaultValue(PrefType aType,
     680             :                            PrefValue aValue,
     681             :                            bool aIsSticky,
     682             :                            bool aIsLocked,
     683             :                            bool aFromInit,
     684             :                            bool* aValueChanged)
     685             :   {
     686             :     // Types must always match when setting the default value.
     687           0 :     if (!IsType(aType)) {
     688             :       return NS_ERROR_UNEXPECTED;
     689             :     }
     690             : 
     691             :     // Should we set the default value? Only if the pref is not locked, and
     692             :     // doing so would change the default value.
     693           0 :     if (!IsLocked()) {
     694           0 :       if (aIsLocked) {
     695             :         SetIsLocked(true);
     696             :       }
     697           0 :       if (!ValueMatches(PrefValueKind::Default, aType, aValue)) {
     698           0 :         mDefaultValue.Replace(mHasDefaultValue, Type(), aType, aValue);
     699           0 :         mHasDefaultValue = true;
     700           0 :         if (!aFromInit) {
     701           0 :           mHasChangedSinceInit = true;
     702             :         }
     703           0 :         if (aIsSticky) {
     704           0 :           mIsSticky = true;
     705             :         }
     706           0 :         if (!mHasUserValue) {
     707           0 :           *aValueChanged = true;
     708             :         }
     709             :         // What if we change the default to be the same as the user value?
     710             :         // Should we clear the user value? Currently we don't.
     711             :       }
     712             :     }
     713             :     return NS_OK;
     714             :   }
     715             : 
     716           0 :   nsresult SetUserValue(PrefType aType,
     717             :                         PrefValue aValue,
     718             :                         bool aFromInit,
     719             :                         bool* aValueChanged)
     720             :   {
     721             :     // If we have a default value, types must match when setting the user
     722             :     // value.
     723           0 :     if (mHasDefaultValue && !IsType(aType)) {
     724             :       return NS_ERROR_UNEXPECTED;
     725             :     }
     726             : 
     727             :     // Should we clear the user value, if present? Only if the new user value
     728             :     // matches the default value, and the pref isn't sticky, and we aren't
     729             :     // force-setting it during initialization.
     730           0 :     if (ValueMatches(PrefValueKind::Default, aType, aValue) && !mIsSticky &&
     731             :         !aFromInit) {
     732           0 :       if (mHasUserValue) {
     733           0 :         ClearUserValue();
     734           0 :         if (!IsLocked()) {
     735           0 :           *aValueChanged = true;
     736             :         }
     737             :       }
     738             : 
     739             :       // Otherwise, should we set the user value? Only if doing so would
     740             :       // change the user value.
     741           0 :     } else if (!ValueMatches(PrefValueKind::User, aType, aValue)) {
     742           0 :       mUserValue.Replace(mHasUserValue, Type(), aType, aValue);
     743           0 :       SetType(aType); // needed because we may have changed the type
     744           0 :       mHasUserValue = true;
     745           0 :       if (!aFromInit) {
     746           0 :         mHasChangedSinceInit = true;
     747             :       }
     748           0 :       if (!IsLocked()) {
     749           0 :         *aValueChanged = true;
     750             :       }
     751             :     }
     752             :     return NS_OK;
     753             :   }
     754             : 
     755             :   // Returns false if this pref doesn't have a user value worth saving.
     756           0 :   bool UserValueToStringForSaving(nsCString& aStr)
     757             :   {
     758             :     // Should we save the user value, if present? Only if it does not match the
     759             :     // default value, or it is sticky.
     760           0 :     if (mHasUserValue &&
     761           0 :         (!ValueMatches(PrefValueKind::Default, Type(), mUserValue) ||
     762           0 :          mIsSticky)) {
     763           0 :       if (IsTypeString()) {
     764           0 :         StrEscape(mUserValue.mStringVal, aStr);
     765             : 
     766           0 :       } else if (IsTypeInt()) {
     767           0 :         aStr.AppendInt(mUserValue.mIntVal);
     768             : 
     769           0 :       } else if (IsTypeBool()) {
     770           0 :         aStr = mUserValue.mBoolVal ? "true" : "false";
     771             :       }
     772             :       return true;
     773             :     }
     774             : 
     775             :     // Do not save default prefs that haven't changed.
     776             :     return false;
     777             :   }
     778             : 
     779             :   // Prefs are serialized in a manner that mirrors dom::Pref. The two should be
     780             :   // kept in sync. E.g. if something is added to one it should also be added to
     781             :   // the other. (It would be nice to be able to use the code generated from
     782             :   // IPDL for serializing dom::Pref here instead of writing by hand this
     783             :   // serialization/deserialization. Unfortunately, that generated code is
     784             :   // difficult to use directly, outside of the IPDL IPC code.)
     785             :   //
     786             :   // The grammar for the serialized prefs has the following form.
     787             :   //
     788             :   // <pref>         = <type> <locked> ':' <name> ':' <value>? ':' <value>? '\n'
     789             :   // <type>         = 'B' | 'I' | 'S'
     790             :   // <locked>       = 'L' | '-'
     791             :   // <name>         = <string-value>
     792             :   // <value>        = <bool-value> | <int-value> | <string-value>
     793             :   // <bool-value>   = 'T' | 'F'
     794             :   // <int-value>    = an integer literal accepted by strtol()
     795             :   // <string-value> = <int-value> '/' <chars>
     796             :   // <chars>        = any char sequence of length dictated by the preceding
     797             :   //                  <int-value>.
     798             :   //
     799             :   // No whitespace is tolerated between tokens. <type> must match the types of
     800             :   // the values.
     801             :   //
     802             :   // The serialization is text-based, rather than binary, for the following
     803             :   // reasons.
     804             :   //
     805             :   // - The size difference wouldn't be much different between text-based and
     806             :   //   binary. Most of the space is for strings (pref names and string pref
     807             :   //   values), which would be the same in both styles. And other differences
     808             :   //   would be minimal, e.g. small integers are shorter in text but long
     809             :   //   integers are longer in text.
     810             :   //
     811             :   // - Likewise, speed differences should be negligible.
     812             :   //
     813             :   // - It's much easier to debug a text-based serialization. E.g. you can
     814             :   //   print it and inspect it easily in a debugger.
     815             :   //
     816             :   // Examples of unlocked boolean prefs:
     817             :   // - "B-:8/my.bool1:F:T\n"
     818             :   // - "B-:8/my.bool2:F:\n"
     819             :   // - "B-:8/my.bool3::T\n"
     820             :   //
     821             :   // Examples of locked integer prefs:
     822             :   // - "IL:7/my.int1:0:1\n"
     823             :   // - "IL:7/my.int2:123:\n"
     824             :   // - "IL:7/my.int3::-99\n"
     825             :   //
     826             :   // Examples of unlocked string prefs:
     827             :   // - "S-:10/my.string1:3/abc:4/wxyz\n"
     828             :   // - "S-:10/my.string2:5/1.234:\n"
     829             :   // - "S-:10/my.string3::7/string!\n"
     830             : 
     831           0 :   void SerializeAndAppend(nsCString& aStr)
     832             :   {
     833           0 :     switch (Type()) {
     834             :       case PrefType::Bool:
     835           0 :         aStr.Append('B');
     836           0 :         break;
     837             : 
     838             :       case PrefType::Int:
     839           0 :         aStr.Append('I');
     840           0 :         break;
     841             : 
     842             :       case PrefType::String: {
     843           0 :         aStr.Append('S');
     844           0 :         break;
     845             :       }
     846             : 
     847             :       case PrefType::None:
     848             :       default:
     849           0 :         MOZ_CRASH();
     850             :     }
     851             : 
     852           0 :     aStr.Append(mIsLocked ? 'L' : '-');
     853           0 :     aStr.Append(':');
     854             : 
     855           0 :     SerializeAndAppendString(mName, aStr);
     856           0 :     aStr.Append(':');
     857             : 
     858           0 :     if (mHasDefaultValue) {
     859           0 :       mDefaultValue.SerializeAndAppend(Type(), aStr);
     860             :     }
     861           0 :     aStr.Append(':');
     862             : 
     863           0 :     if (mHasUserValue) {
     864           0 :       mUserValue.SerializeAndAppend(Type(), aStr);
     865             :     }
     866           0 :     aStr.Append('\n');
     867           0 :   }
     868             : 
     869           0 :   static char* Deserialize(char* aStr, dom::Pref* aDomPref)
     870             :   {
     871           0 :     char* p = aStr;
     872             : 
     873             :     // The type.
     874             :     PrefType type;
     875           0 :     if (*p == 'B') {
     876             :       type = PrefType::Bool;
     877           0 :     } else if (*p == 'I') {
     878             :       type = PrefType::Int;
     879           0 :     } else if (*p == 'S') {
     880             :       type = PrefType::String;
     881             :     } else {
     882           0 :       NS_ERROR("bad pref type");
     883           0 :       type = PrefType::None;
     884             :     }
     885           0 :     p++; // move past the type char
     886             : 
     887             :     // Locked?
     888             :     bool isLocked;
     889           0 :     if (*p == 'L') {
     890           0 :       isLocked = true;
     891           0 :     } else if (*p == '-') {
     892           0 :       isLocked = false;
     893             :     } else {
     894           0 :       NS_ERROR("bad pref locked status");
     895           0 :       isLocked = false;
     896             :     }
     897           0 :     p++; // move past the isLocked char
     898             : 
     899           0 :     MOZ_ASSERT(*p == ':');
     900           0 :     p++; // move past the ':'
     901             : 
     902             :     // The pref name.
     903           0 :     nsCString name;
     904           0 :     p = DeserializeString(p, name);
     905             : 
     906           0 :     MOZ_ASSERT(*p == ':');
     907           0 :     p++; // move past the ':' preceding the default value
     908             : 
     909           0 :     dom::MaybePrefValue maybeDefaultValue;
     910           0 :     if (*p != ':') {
     911           0 :       dom::PrefValue defaultValue;
     912           0 :       p = PrefValue::Deserialize(type, p, &maybeDefaultValue);
     913             :     }
     914             : 
     915           0 :     MOZ_ASSERT(*p == ':');
     916           0 :     p++; // move past the ':' between the default and user values
     917             : 
     918           0 :     dom::MaybePrefValue maybeUserValue;
     919           0 :     if (*p != '\n') {
     920           0 :       dom::PrefValue userValue;
     921           0 :       p = PrefValue::Deserialize(type, p, &maybeUserValue);
     922             :     }
     923             : 
     924           0 :     MOZ_ASSERT(*p == '\n');
     925           0 :     p++; // move past the '\n' following the user value
     926             : 
     927           0 :     *aDomPref = dom::Pref(name, isLocked, maybeDefaultValue, maybeUserValue);
     928             : 
     929           0 :     return p;
     930             :   }
     931             : 
     932           0 :   void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, PrefsSizes& aSizes)
     933             :   {
     934             :     // Note: mName is allocated in gPrefNameArena, measured elsewhere.
     935           0 :     aSizes.mPrefValues += aMallocSizeOf(this);
     936           0 :     if (IsTypeString()) {
     937           0 :       if (mHasDefaultValue) {
     938           0 :         aSizes.mStringValues += aMallocSizeOf(mDefaultValue.mStringVal);
     939             :       }
     940           0 :       if (mHasUserValue) {
     941           0 :         aSizes.mStringValues += aMallocSizeOf(mUserValue.mStringVal);
     942             :       }
     943             :     }
     944           0 :   }
     945             : 
     946             : private:
     947             :   const char* mName; // allocated in gPrefNameArena
     948             : 
     949             :   uint32_t mType : 2;
     950             :   uint32_t mIsSticky : 1;
     951             :   uint32_t mIsLocked : 1;
     952             :   uint32_t mHasDefaultValue : 1;
     953             :   uint32_t mHasUserValue : 1;
     954             :   uint32_t mHasChangedSinceInit : 1;
     955             : 
     956             :   PrefValue mDefaultValue;
     957             :   PrefValue mUserValue;
     958             : };
     959             : 
     960             : class PrefEntry : public PLDHashEntryHdr
     961             : {
     962             : public:
     963             : #ifdef DEBUG
     964             :   // This field is before mPref to minimize sizeof(PrefEntry) on 64-bit.
     965             :   uint32_t mAccessCount;
     966             : #endif
     967             :   Pref* mPref; // Note: this is never null in a live entry.
     968             : 
     969           0 :   static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey)
     970             :   {
     971           0 :     auto entry = static_cast<const PrefEntry*>(aEntry);
     972           0 :     auto prefName = static_cast<const char*>(aKey);
     973             : 
     974           0 :     return entry->mPref->MatchEntry(prefName);
     975             :   }
     976             : 
     977           0 :   static void InitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
     978             :   {
     979           0 :     auto entry = static_cast<PrefEntry*>(aEntry);
     980           0 :     auto prefName = static_cast<const char*>(aKey);
     981             : 
     982             : #ifdef DEBUG
     983           0 :     entry->mAccessCount = 0;
     984             : #endif
     985           0 :     entry->mPref = new Pref(prefName);
     986           0 :   }
     987             : 
     988           0 :   static void ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
     989             :   {
     990           0 :     auto entry = static_cast<PrefEntry*>(aEntry);
     991             : 
     992           0 :     delete entry->mPref;
     993           0 :     entry->mPref = nullptr;
     994           0 :   }
     995             : };
     996             : 
     997           0 : class CallbackNode
     998             : {
     999             : public:
    1000             :   CallbackNode(const char* aDomain,
    1001             :                PrefChangedFunc aFunc,
    1002             :                void* aData,
    1003             :                Preferences::MatchKind aMatchKind)
    1004           0 :     : mDomain(moz_xstrdup(aDomain))
    1005             :     , mFunc(aFunc)
    1006             :     , mData(aData)
    1007           0 :     , mNextAndMatchKind(aMatchKind)
    1008             :   {
    1009             :   }
    1010             : 
    1011             :   // mDomain is a UniquePtr<>, so any uses of Domain() should only be temporary
    1012             :   // borrows.
    1013           0 :   const char* Domain() const { return mDomain.get(); }
    1014             : 
    1015             :   PrefChangedFunc Func() const { return mFunc; }
    1016           0 :   void ClearFunc() { mFunc = nullptr; }
    1017             : 
    1018             :   void* Data() const { return mData; }
    1019             : 
    1020             :   Preferences::MatchKind MatchKind() const
    1021             :   {
    1022           0 :     return static_cast<Preferences::MatchKind>(mNextAndMatchKind &
    1023           0 :                                                kMatchKindMask);
    1024             :   }
    1025             : 
    1026             :   CallbackNode* Next() const
    1027             :   {
    1028           0 :     return reinterpret_cast<CallbackNode*>(mNextAndMatchKind & kNextMask);
    1029             :   }
    1030             : 
    1031           0 :   void SetNext(CallbackNode* aNext)
    1032             :   {
    1033           0 :     uintptr_t matchKind = mNextAndMatchKind & kMatchKindMask;
    1034           0 :     mNextAndMatchKind = reinterpret_cast<uintptr_t>(aNext);
    1035           0 :     MOZ_ASSERT((mNextAndMatchKind & kMatchKindMask) == 0);
    1036           0 :     mNextAndMatchKind |= matchKind;
    1037           0 :   }
    1038             : 
    1039           0 :   void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, PrefsSizes& aSizes)
    1040             :   {
    1041           0 :     aSizes.mCallbacksObjects += aMallocSizeOf(this);
    1042           0 :     aSizes.mCallbacksDomains += aMallocSizeOf(mDomain.get());
    1043           0 :   }
    1044             : 
    1045             : private:
    1046             :   static const uintptr_t kMatchKindMask = uintptr_t(0x1);
    1047             :   static const uintptr_t kNextMask = ~kMatchKindMask;
    1048             : 
    1049             :   UniqueFreePtr<const char> mDomain;
    1050             : 
    1051             :   // If someone attempts to remove the node from the callback list while
    1052             :   // NotifyCallbacks() is running, |func| is set to nullptr. Such nodes will
    1053             :   // be removed at the end of NotifyCallbacks().
    1054             :   PrefChangedFunc mFunc;
    1055             :   void* mData;
    1056             : 
    1057             :   // Conceptually this is two fields:
    1058             :   // - CallbackNode* mNext;
    1059             :   // - Preferences::MatchKind mMatchKind;
    1060             :   // They are combined into a tagged pointer to save memory.
    1061             :   uintptr_t mNextAndMatchKind;
    1062             : };
    1063             : 
    1064             : static PLDHashTable* gHashTable;
    1065             : 
    1066             : // The callback list contains all the priority callbacks followed by the
    1067             : // non-priority callbacks. gLastPriorityNode records where the first part ends.
    1068             : static CallbackNode* gFirstCallback = nullptr;
    1069             : static CallbackNode* gLastPriorityNode = nullptr;
    1070             : 
    1071             : // These are only used during the call to NotifyCallbacks().
    1072             : static bool gCallbacksInProgress = false;
    1073             : static bool gShouldCleanupDeadNodes = false;
    1074             : 
    1075             : static PLDHashTableOps pref_HashTableOps = {
    1076             :   PLDHashTable::HashStringKey, PrefEntry::MatchEntry,
    1077             :   PLDHashTable::MoveEntryStub, PrefEntry::ClearEntry,
    1078             :   PrefEntry::InitEntry,
    1079             : };
    1080             : 
    1081             : static Pref*
    1082             : pref_HashTableLookup(const char* aPrefName);
    1083             : 
    1084             : static void
    1085             : NotifyCallbacks(const char* aPrefName);
    1086             : 
    1087             : #define PREF_HASHTABLE_INITIAL_LENGTH 1024
    1088             : 
    1089             : static PrefSaveData
    1090           0 : pref_savePrefs()
    1091             : {
    1092           0 :   MOZ_ASSERT(NS_IsMainThread());
    1093             : 
    1094           0 :   PrefSaveData savedPrefs(gHashTable->EntryCount());
    1095             : 
    1096           0 :   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
    1097           0 :     Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
    1098             : 
    1099           0 :     nsAutoCString prefValueStr;
    1100           0 :     if (!pref->UserValueToStringForSaving(prefValueStr)) {
    1101           0 :       continue;
    1102             :     }
    1103             : 
    1104           0 :     nsAutoCString prefNameStr;
    1105           0 :     StrEscape(pref->Name(), prefNameStr);
    1106             : 
    1107             :     nsPrintfCString str(
    1108           0 :       "user_pref(%s, %s);", prefNameStr.get(), prefValueStr.get());
    1109             : 
    1110           0 :     savedPrefs.AppendElement(str);
    1111             :   }
    1112             : 
    1113           0 :   return savedPrefs;
    1114             : }
    1115             : 
    1116             : #ifdef DEBUG
    1117             : 
    1118             : // Note that this never changes in the parent process, and is only read in
    1119             : // content processes.
    1120             : static bool gContentProcessPrefsAreInited = false;
    1121             : 
    1122             : #endif // DEBUG
    1123             : 
    1124             : static PrefEntry*
    1125           0 : pref_HashTableLookupInner(const char* aPrefName)
    1126             : {
    1127           0 :   MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
    1128             : 
    1129           0 :   MOZ_ASSERT_IF(!XRE_IsParentProcess(), gContentProcessPrefsAreInited);
    1130             : 
    1131           0 :   return static_cast<PrefEntry*>(gHashTable->Search(aPrefName));
    1132             : }
    1133             : 
    1134             : static Pref*
    1135           0 : pref_HashTableLookup(const char* aPrefName)
    1136             : {
    1137           0 :   PrefEntry* entry = pref_HashTableLookupInner(aPrefName);
    1138           0 :   if (!entry) {
    1139             :     return nullptr;
    1140             :   }
    1141             : 
    1142             : #ifdef DEBUG
    1143           0 :   entry->mAccessCount += 1;
    1144             : #endif
    1145             : 
    1146           0 :   return entry->mPref;
    1147             : }
    1148             : 
    1149             : static nsresult
    1150           0 : pref_SetPref(const char* aPrefName,
    1151             :              PrefType aType,
    1152             :              PrefValueKind aKind,
    1153             :              PrefValue aValue,
    1154             :              bool aIsSticky,
    1155             :              bool aIsLocked,
    1156             :              bool aFromInit)
    1157             : {
    1158           0 :   MOZ_ASSERT(NS_IsMainThread());
    1159             : 
    1160           0 :   if (!gHashTable) {
    1161             :     return NS_ERROR_OUT_OF_MEMORY;
    1162             :   }
    1163             : 
    1164           0 :   auto entry = static_cast<PrefEntry*>(gHashTable->Add(aPrefName, fallible));
    1165           0 :   if (!entry) {
    1166             :     return NS_ERROR_OUT_OF_MEMORY;
    1167             :   }
    1168             : 
    1169           0 :   Pref* pref = entry->mPref;
    1170           0 :   if (pref->IsTypeNone()) {
    1171             :     // New entry. Set the type.
    1172             :     pref->SetType(aType);
    1173             :   }
    1174             : 
    1175           0 :   bool valueChanged = false;
    1176             :   nsresult rv;
    1177           0 :   if (aKind == PrefValueKind::Default) {
    1178           0 :     rv = pref->SetDefaultValue(
    1179           0 :       aType, aValue, aIsSticky, aIsLocked, aFromInit, &valueChanged);
    1180             :   } else {
    1181           0 :     MOZ_ASSERT(!aIsLocked); // `locked` is disallowed in user pref files
    1182           0 :     rv = pref->SetUserValue(aType, aValue, aFromInit, &valueChanged);
    1183             :   }
    1184           0 :   if (NS_FAILED(rv)) {
    1185           0 :     NS_WARNING(
    1186             :       nsPrintfCString(
    1187             :         "Rejected attempt to change type of pref %s's %s value from %s to %s",
    1188             :         aPrefName,
    1189             :         (aKind == PrefValueKind::Default) ? "default" : "user",
    1190             :         PrefTypeToString(pref->Type()),
    1191             :         PrefTypeToString(aType))
    1192           0 :         .get());
    1193             : 
    1194           0 :     return rv;
    1195             :   }
    1196             : 
    1197           0 :   if (valueChanged) {
    1198           0 :     if (aKind == PrefValueKind::User && XRE_IsParentProcess()) {
    1199           0 :       Preferences::HandleDirty();
    1200             :     }
    1201           0 :     NotifyCallbacks(aPrefName);
    1202             :   }
    1203             : 
    1204             :   return NS_OK;
    1205             : }
    1206             : 
    1207             : // Removes |node| from callback list. Returns the node after the deleted one.
    1208             : static CallbackNode*
    1209           0 : pref_RemoveCallbackNode(CallbackNode* aNode, CallbackNode* aPrevNode)
    1210             : {
    1211           0 :   MOZ_ASSERT(!aPrevNode || aPrevNode->Next() == aNode);
    1212           0 :   MOZ_ASSERT(aPrevNode || gFirstCallback == aNode);
    1213           0 :   MOZ_ASSERT(!gCallbacksInProgress);
    1214             : 
    1215           0 :   CallbackNode* next_node = aNode->Next();
    1216           0 :   if (aPrevNode) {
    1217           0 :     aPrevNode->SetNext(next_node);
    1218             :   } else {
    1219           0 :     gFirstCallback = next_node;
    1220             :   }
    1221           0 :   if (gLastPriorityNode == aNode) {
    1222           0 :     gLastPriorityNode = aPrevNode;
    1223             :   }
    1224           0 :   delete aNode;
    1225           0 :   return next_node;
    1226             : }
    1227             : 
    1228             : static void
    1229           0 : NotifyCallbacks(const char* aPrefName)
    1230             : {
    1231           0 :   bool reentered = gCallbacksInProgress;
    1232             : 
    1233             :   // Nodes must not be deleted while gCallbacksInProgress is true.
    1234             :   // Nodes that need to be deleted are marked for deletion by nulling
    1235             :   // out the |func| pointer. We release them at the end of this function
    1236             :   // if we haven't reentered.
    1237           0 :   gCallbacksInProgress = true;
    1238             : 
    1239           0 :   for (CallbackNode* node = gFirstCallback; node; node = node->Next()) {
    1240           0 :     if (node->Func()) {
    1241             :       bool matches =
    1242           0 :         node->MatchKind() == Preferences::ExactMatch
    1243           0 :           ? strcmp(node->Domain(), aPrefName) == 0
    1244           0 :           : strncmp(node->Domain(), aPrefName, strlen(node->Domain())) == 0;
    1245           0 :       if (matches) {
    1246           0 :         (node->Func())(aPrefName, node->Data());
    1247             :       }
    1248             :     }
    1249             :   }
    1250             : 
    1251           0 :   gCallbacksInProgress = reentered;
    1252             : 
    1253           0 :   if (gShouldCleanupDeadNodes && !gCallbacksInProgress) {
    1254           0 :     CallbackNode* prev_node = nullptr;
    1255           0 :     CallbackNode* node = gFirstCallback;
    1256             : 
    1257           0 :     while (node) {
    1258           0 :       if (!node->Func()) {
    1259           0 :         node = pref_RemoveCallbackNode(node, prev_node);
    1260             :       } else {
    1261           0 :         prev_node = node;
    1262           0 :         node = node->Next();
    1263             :       }
    1264             :     }
    1265           0 :     gShouldCleanupDeadNodes = false;
    1266             :   }
    1267           0 : }
    1268             : 
    1269             : //===========================================================================
    1270             : // Prefs parsing
    1271             : //===========================================================================
    1272             : 
    1273             : struct TelemetryLoadData
    1274             : {
    1275             :   uint32_t mFileLoadSize_B;
    1276             :   uint32_t mFileLoadNumPrefs;
    1277             :   uint32_t mFileLoadTime_us;
    1278             : };
    1279             : 
    1280             : static nsDataHashtable<nsCStringHashKey, TelemetryLoadData>* gTelemetryLoadData;
    1281             : 
    1282             : extern "C" {
    1283             : 
    1284             : // Keep this in sync with PrefFn in prefs_parser/src/lib.rs.
    1285             : typedef void (*PrefsParserPrefFn)(const char* aPrefName,
    1286             :                                   PrefType aType,
    1287             :                                   PrefValueKind aKind,
    1288             :                                   PrefValue aValue,
    1289             :                                   bool aIsSticky,
    1290             :                                   bool aIsLocked);
    1291             : 
    1292             : // Keep this in sync with ErrorFn in prefs_parser/src/lib.rs.
    1293             : //
    1294             : // `aMsg` is just a borrow of the string, and must be copied if it is used
    1295             : // outside the lifetime of the prefs_parser_parse() call.
    1296             : typedef void (*PrefsParserErrorFn)(const char* aMsg);
    1297             : 
    1298             : // Keep this in sync with prefs_parser_parse() in prefs_parser/src/lib.rs.
    1299             : bool
    1300             : prefs_parser_parse(const char* aPath,
    1301             :                    PrefValueKind aKind,
    1302             :                    const char* aBuf,
    1303             :                    size_t aLen,
    1304             :                    PrefsParserPrefFn aPrefFn,
    1305             :                    PrefsParserErrorFn aErrorFn);
    1306             : }
    1307             : 
    1308             : class Parser
    1309             : {
    1310             : public:
    1311             :   Parser() = default;
    1312             :   ~Parser() = default;
    1313             : 
    1314           0 :   bool Parse(const nsCString& aName,
    1315             :              PrefValueKind aKind,
    1316             :              const char* aPath,
    1317             :              const TimeStamp& aStartTime,
    1318             :              const nsCString& aBuf)
    1319             :   {
    1320           0 :     sNumPrefs = 0;
    1321           0 :     bool ok = prefs_parser_parse(
    1322           0 :       aPath, aKind, aBuf.get(), aBuf.Length(), HandlePref, HandleError);
    1323           0 :     if (!ok) {
    1324             :       return false;
    1325             :     }
    1326             : 
    1327           0 :     uint32_t loadTime_us = (TimeStamp::Now() - aStartTime).ToMicroseconds();
    1328             : 
    1329             :     // Most prefs files are read before telemetry initializes, so we have to
    1330             :     // save these measurements now and send them to telemetry later.
    1331           0 :     TelemetryLoadData loadData = { uint32_t(aBuf.Length()),
    1332             :                                    sNumPrefs,
    1333           0 :                                    loadTime_us };
    1334           0 :     gTelemetryLoadData->Put(aName, loadData);
    1335             : 
    1336             :     return true;
    1337             :   }
    1338             : 
    1339             : private:
    1340           0 :   static void HandlePref(const char* aPrefName,
    1341             :                          PrefType aType,
    1342             :                          PrefValueKind aKind,
    1343             :                          PrefValue aValue,
    1344             :                          bool aIsSticky,
    1345             :                          bool aIsLocked)
    1346             :   {
    1347           0 :     sNumPrefs++;
    1348           0 :     pref_SetPref(aPrefName,
    1349             :                  aType,
    1350             :                  aKind,
    1351             :                  aValue,
    1352             :                  aIsSticky,
    1353             :                  aIsLocked,
    1354           0 :                  /* fromInit */ true);
    1355           0 :   }
    1356             : 
    1357           0 :   static void HandleError(const char* aMsg)
    1358             :   {
    1359             :     nsresult rv;
    1360             :     nsCOMPtr<nsIConsoleService> console =
    1361           0 :       do_GetService("@mozilla.org/consoleservice;1", &rv);
    1362           0 :     if (NS_SUCCEEDED(rv)) {
    1363           0 :       console->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
    1364             :     }
    1365             : #ifdef DEBUG
    1366           0 :     NS_ERROR(aMsg);
    1367             : #else
    1368             :     printf_stderr("%s\n", aMsg);
    1369             : #endif
    1370           0 :   }
    1371             : 
    1372             :   // This is static so that HandlePref() can increment it easily. This is ok
    1373             :   // because prefs files are read one at a time.
    1374             :   static uint32_t sNumPrefs;
    1375             : };
    1376             : 
    1377             : uint32_t Parser::sNumPrefs = 0;
    1378             : 
    1379             : // The following code is test code for the gtest.
    1380             : 
    1381             : static void
    1382           0 : TestParseErrorHandlePref(const char* aPrefName,
    1383             :                          PrefType aType,
    1384             :                          PrefValueKind aKind,
    1385             :                          PrefValue aValue,
    1386             :                          bool aIsSticky,
    1387             :                          bool aIsLocked)
    1388             : {
    1389           0 : }
    1390             : 
    1391           0 : static nsCString gTestParseErrorMsgs;
    1392             : 
    1393             : static void
    1394           0 : TestParseErrorHandleError(const char* aMsg)
    1395             : {
    1396           0 :   gTestParseErrorMsgs.Append(aMsg);
    1397           0 :   gTestParseErrorMsgs.Append('\n');
    1398           0 : }
    1399             : 
    1400             : // Keep this in sync with the declaration in test/gtest/Parser.cpp.
    1401             : void
    1402           0 : TestParseError(PrefValueKind aKind, const char* aText, nsCString& aErrorMsg)
    1403             : {
    1404           0 :   prefs_parser_parse("test",
    1405             :                      aKind,
    1406             :                      aText,
    1407             :                      strlen(aText),
    1408             :                      TestParseErrorHandlePref,
    1409           0 :                      TestParseErrorHandleError);
    1410             : 
    1411             :   // Copy the error messages into the outparam, then clear them from
    1412             :   // gTestParseErrorMsgs.
    1413           0 :   aErrorMsg.Assign(gTestParseErrorMsgs);
    1414           0 :   gTestParseErrorMsgs.Truncate();
    1415           0 : }
    1416             : 
    1417             : void
    1418           0 : SendTelemetryLoadData()
    1419             : {
    1420           0 :   for (auto iter = gTelemetryLoadData->Iter(); !iter.Done(); iter.Next()) {
    1421           0 :     const nsCString& filename = PromiseFlatCString(iter.Key());
    1422           0 :     const TelemetryLoadData& data = iter.Data();
    1423             :     Telemetry::Accumulate(
    1424           0 :       Telemetry::PREFERENCES_FILE_LOAD_SIZE_B, filename, data.mFileLoadSize_B);
    1425             :     Telemetry::Accumulate(Telemetry::PREFERENCES_FILE_LOAD_NUM_PREFS,
    1426             :                           filename,
    1427           0 :                           data.mFileLoadNumPrefs);
    1428             :     Telemetry::Accumulate(Telemetry::PREFERENCES_FILE_LOAD_TIME_US,
    1429             :                           filename,
    1430           0 :                           data.mFileLoadTime_us);
    1431             :   }
    1432             : 
    1433           0 :   gTelemetryLoadData->Clear();
    1434           0 : }
    1435             : 
    1436             : //===========================================================================
    1437             : // nsPrefBranch et al.
    1438             : //===========================================================================
    1439             : 
    1440             : namespace mozilla {
    1441             : class PreferenceServiceReporter;
    1442             : } // namespace mozilla
    1443             : 
    1444             : class PrefCallback : public PLDHashEntryHdr
    1445             : {
    1446             :   friend class mozilla::PreferenceServiceReporter;
    1447             : 
    1448             : public:
    1449             :   typedef PrefCallback* KeyType;
    1450             :   typedef const PrefCallback* KeyTypePointer;
    1451             : 
    1452             :   static const PrefCallback* KeyToPointer(PrefCallback* aKey) { return aKey; }
    1453             : 
    1454           0 :   static PLDHashNumber HashKey(const PrefCallback* aKey)
    1455             :   {
    1456           0 :     uint32_t hash = mozilla::HashString(aKey->mDomain);
    1457           0 :     return mozilla::AddToHash(hash, aKey->mCanonical);
    1458             :   }
    1459             : 
    1460             : public:
    1461             :   // Create a PrefCallback with a strong reference to its observer.
    1462           0 :   PrefCallback(const char* aDomain,
    1463             :                nsIObserver* aObserver,
    1464             :                nsPrefBranch* aBranch)
    1465           0 :     : mDomain(aDomain)
    1466             :     , mBranch(aBranch)
    1467             :     , mWeakRef(nullptr)
    1468           0 :     , mStrongRef(aObserver)
    1469             :   {
    1470           0 :     MOZ_COUNT_CTOR(PrefCallback);
    1471           0 :     nsCOMPtr<nsISupports> canonical = do_QueryInterface(aObserver);
    1472           0 :     mCanonical = canonical;
    1473           0 :   }
    1474             : 
    1475             :   // Create a PrefCallback with a weak reference to its observer.
    1476           0 :   PrefCallback(const char* aDomain,
    1477             :                nsISupportsWeakReference* aObserver,
    1478             :                nsPrefBranch* aBranch)
    1479           0 :     : mDomain(aDomain)
    1480             :     , mBranch(aBranch)
    1481           0 :     , mWeakRef(do_GetWeakReference(aObserver))
    1482           0 :     , mStrongRef(nullptr)
    1483             :   {
    1484           0 :     MOZ_COUNT_CTOR(PrefCallback);
    1485           0 :     nsCOMPtr<nsISupports> canonical = do_QueryInterface(aObserver);
    1486           0 :     mCanonical = canonical;
    1487           0 :   }
    1488             : 
    1489             :   // Copy constructor needs to be explicit or the linker complains.
    1490           0 :   explicit PrefCallback(const PrefCallback*& aCopy)
    1491           0 :     : mDomain(aCopy->mDomain)
    1492           0 :     , mBranch(aCopy->mBranch)
    1493           0 :     , mWeakRef(aCopy->mWeakRef)
    1494           0 :     , mStrongRef(aCopy->mStrongRef)
    1495           0 :     , mCanonical(aCopy->mCanonical)
    1496             :   {
    1497           0 :     MOZ_COUNT_CTOR(PrefCallback);
    1498           0 :   }
    1499             : 
    1500           0 :   ~PrefCallback() { MOZ_COUNT_DTOR(PrefCallback); }
    1501             : 
    1502           0 :   bool KeyEquals(const PrefCallback* aKey) const
    1503             :   {
    1504             :     // We want to be able to look up a weakly-referencing PrefCallback after
    1505             :     // its observer has died so we can remove it from the table. Once the
    1506             :     // callback's observer dies, its canonical pointer is stale -- in
    1507             :     // particular, we may have allocated a new observer in the same spot in
    1508             :     // memory! So we can't just compare canonical pointers to determine whether
    1509             :     // aKey refers to the same observer as this.
    1510             :     //
    1511             :     // Our workaround is based on the way we use this hashtable: When we ask
    1512             :     // the hashtable to remove a PrefCallback whose weak reference has expired,
    1513             :     // we use as the key for removal the same object as was inserted into the
    1514             :     // hashtable. Thus we can say that if one of the keys' weak references has
    1515             :     // expired, the two keys are equal iff they're the same object.
    1516             : 
    1517           0 :     if (IsExpired() || aKey->IsExpired()) {
    1518           0 :       return this == aKey;
    1519             :     }
    1520             : 
    1521           0 :     if (mCanonical != aKey->mCanonical) {
    1522             :       return false;
    1523             :     }
    1524             : 
    1525           0 :     return mDomain.Equals(aKey->mDomain);
    1526             :   }
    1527             : 
    1528             :   PrefCallback* GetKey() const { return const_cast<PrefCallback*>(this); }
    1529             : 
    1530             :   // Get a reference to the callback's observer, or null if the observer was
    1531             :   // weakly referenced and has been destroyed.
    1532           0 :   already_AddRefed<nsIObserver> GetObserver() const
    1533             :   {
    1534           0 :     if (!IsWeak()) {
    1535           0 :       nsCOMPtr<nsIObserver> copy = mStrongRef;
    1536           0 :       return copy.forget();
    1537             :     }
    1538             : 
    1539           0 :     nsCOMPtr<nsIObserver> observer = do_QueryReferent(mWeakRef);
    1540           0 :     return observer.forget();
    1541             :   }
    1542             : 
    1543             :   const nsCString& GetDomain() const { return mDomain; }
    1544             : 
    1545             :   nsPrefBranch* GetPrefBranch() const { return mBranch; }
    1546             : 
    1547             :   // Has this callback's weak reference died?
    1548           0 :   bool IsExpired() const
    1549             :   {
    1550           0 :     if (!IsWeak())
    1551             :       return false;
    1552             : 
    1553           0 :     nsCOMPtr<nsIObserver> observer(do_QueryReferent(mWeakRef));
    1554           0 :     return !observer;
    1555             :   }
    1556             : 
    1557           0 :   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
    1558             :   {
    1559           0 :     size_t n = aMallocSizeOf(this);
    1560           0 :     n += mDomain.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
    1561             : 
    1562             :     // All the other fields are non-owning pointers, so we don't measure them.
    1563             : 
    1564           0 :     return n;
    1565             :   }
    1566             : 
    1567             :   enum
    1568             :   {
    1569             :     ALLOW_MEMMOVE = true
    1570             :   };
    1571             : 
    1572             : private:
    1573             :   nsCString mDomain;
    1574             :   nsPrefBranch* mBranch;
    1575             : 
    1576             :   // Exactly one of mWeakRef and mStrongRef should be non-null.
    1577             :   nsWeakPtr mWeakRef;
    1578             :   nsCOMPtr<nsIObserver> mStrongRef;
    1579             : 
    1580             :   // We need a canonical nsISupports pointer, per bug 578392.
    1581             :   nsISupports* mCanonical;
    1582             : 
    1583           0 :   bool IsWeak() const { return !!mWeakRef; }
    1584             : };
    1585             : 
    1586             : class nsPrefBranch final
    1587             :   : public nsIPrefBranch
    1588             :   , public nsIObserver
    1589             :   , public nsSupportsWeakReference
    1590             : {
    1591             :   friend class mozilla::PreferenceServiceReporter;
    1592             : 
    1593             : public:
    1594             :   NS_DECL_ISUPPORTS
    1595             :   NS_DECL_NSIPREFBRANCH
    1596             :   NS_DECL_NSIOBSERVER
    1597             : 
    1598             :   nsPrefBranch(const char* aPrefRoot, PrefValueKind aKind);
    1599             :   nsPrefBranch() = delete;
    1600             : 
    1601             :   static void NotifyObserver(const char* aNewpref, void* aData);
    1602             : 
    1603             :   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
    1604             : 
    1605             : private:
    1606             :   // Helper class for either returning a raw cstring or nsCString.
    1607             :   typedef mozilla::Variant<const char*, const nsCString> PrefNameBase;
    1608           0 :   class PrefName : public PrefNameBase
    1609             :   {
    1610             :   public:
    1611             :     explicit PrefName(const char* aName)
    1612           0 :       : PrefNameBase(aName)
    1613             :     {
    1614             :     }
    1615             :     explicit PrefName(const nsCString& aName)
    1616           0 :       : PrefNameBase(aName)
    1617             :     {
    1618             :     }
    1619             : 
    1620             :     // Use default move constructors, disallow copy constructors.
    1621             :     PrefName(PrefName&& aOther) = default;
    1622             :     PrefName& operator=(PrefName&& aOther) = default;
    1623             :     PrefName(const PrefName&) = delete;
    1624             :     PrefName& operator=(const PrefName&) = delete;
    1625             : 
    1626             :     struct PtrMatcher
    1627             :     {
    1628             :       static const char* match(const char* aVal) { return aVal; }
    1629           0 :       static const char* match(const nsCString& aVal) { return aVal.get(); }
    1630             :     };
    1631             : 
    1632             :     struct LenMatcher
    1633             :     {
    1634           0 :       static size_t match(const char* aVal) { return strlen(aVal); }
    1635           0 :       static size_t match(const nsCString& aVal) { return aVal.Length(); }
    1636             :     };
    1637             : 
    1638             :     const char* get() const
    1639             :     {
    1640             :       static PtrMatcher m;
    1641           0 :       return match(m);
    1642             :     }
    1643             : 
    1644             :     size_t Length() const
    1645             :     {
    1646             :       static LenMatcher m;
    1647           0 :       return match(m);
    1648             :     }
    1649             :   };
    1650             : 
    1651             :   virtual ~nsPrefBranch();
    1652             : 
    1653           0 :   int32_t GetRootLength() const { return mPrefRoot.Length(); }
    1654             : 
    1655             :   nsresult GetDefaultFromPropertiesFile(const char* aPrefName,
    1656             :                                         nsAString& aReturn);
    1657             : 
    1658             :   // As SetCharPref, but without any check on the length of |aValue|.
    1659             :   nsresult SetCharPrefNoLengthCheck(const char* aPrefName,
    1660             :                                     const nsACString& aValue);
    1661             : 
    1662             :   // Reject strings that are more than 1Mb, warn if strings are more than 16kb.
    1663             :   nsresult CheckSanityOfStringLength(const char* aPrefName,
    1664             :                                      const nsAString& aValue);
    1665             :   nsresult CheckSanityOfStringLength(const char* aPrefName,
    1666             :                                      const nsACString& aValue);
    1667             :   nsresult CheckSanityOfStringLength(const char* aPrefName,
    1668             :                                      const uint32_t aLength);
    1669             : 
    1670             :   void RemoveExpiredCallback(PrefCallback* aCallback);
    1671             : 
    1672             :   PrefName GetPrefName(const char* aPrefName) const;
    1673             : 
    1674             :   void FreeObserverList(void);
    1675             : 
    1676             :   const nsCString mPrefRoot;
    1677             :   PrefValueKind mKind;
    1678             : 
    1679             :   bool mFreeingObserverList;
    1680             :   nsClassHashtable<PrefCallback, PrefCallback> mObservers;
    1681             : };
    1682             : 
    1683           0 : class nsPrefLocalizedString final : public nsIPrefLocalizedString
    1684             : {
    1685             : public:
    1686             :   nsPrefLocalizedString();
    1687             : 
    1688             :   NS_DECL_ISUPPORTS
    1689           0 :   NS_FORWARD_NSISUPPORTSPRIMITIVE(mUnicodeString->)
    1690           0 :   NS_FORWARD_NSISUPPORTSSTRING(mUnicodeString->)
    1691             : 
    1692             :   nsresult Init();
    1693             : 
    1694             : private:
    1695             :   virtual ~nsPrefLocalizedString();
    1696             : 
    1697             :   nsCOMPtr<nsISupportsString> mUnicodeString;
    1698             : };
    1699             : 
    1700           0 : class nsRelativeFilePref : public nsIRelativeFilePref
    1701             : {
    1702             : public:
    1703             :   NS_DECL_ISUPPORTS
    1704             :   NS_DECL_NSIRELATIVEFILEPREF
    1705             : 
    1706             :   nsRelativeFilePref();
    1707             : 
    1708             : private:
    1709             :   virtual ~nsRelativeFilePref();
    1710             : 
    1711             :   nsCOMPtr<nsIFile> mFile;
    1712             :   nsCString mRelativeToKey;
    1713             : };
    1714             : 
    1715             : //----------------------------------------------------------------------------
    1716             : // nsPrefBranch
    1717             : //----------------------------------------------------------------------------
    1718             : 
    1719           0 : nsPrefBranch::nsPrefBranch(const char* aPrefRoot, PrefValueKind aKind)
    1720             :   : mPrefRoot(aPrefRoot)
    1721             :   , mKind(aKind)
    1722             :   , mFreeingObserverList(false)
    1723           0 :   , mObservers()
    1724             : {
    1725             :   nsCOMPtr<nsIObserverService> observerService =
    1726           0 :     mozilla::services::GetObserverService();
    1727           0 :   if (observerService) {
    1728           0 :     ++mRefCnt; // must be > 0 when we call this, or we'll get deleted!
    1729             : 
    1730             :     // Add weakly so we don't have to clean up at shutdown.
    1731           0 :     observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
    1732           0 :     --mRefCnt;
    1733             :   }
    1734           0 : }
    1735             : 
    1736           0 : nsPrefBranch::~nsPrefBranch()
    1737             : {
    1738           0 :   FreeObserverList();
    1739             : 
    1740             :   nsCOMPtr<nsIObserverService> observerService =
    1741           0 :     mozilla::services::GetObserverService();
    1742           0 :   if (observerService) {
    1743           0 :     observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
    1744             :   }
    1745           0 : }
    1746             : 
    1747           0 : NS_IMPL_ISUPPORTS(nsPrefBranch,
    1748             :                   nsIPrefBranch,
    1749             :                   nsIObserver,
    1750             :                   nsISupportsWeakReference)
    1751             : 
    1752             : NS_IMETHODIMP
    1753           0 : nsPrefBranch::GetRoot(nsACString& aRoot)
    1754             : {
    1755           0 :   aRoot = mPrefRoot;
    1756           0 :   return NS_OK;
    1757             : }
    1758             : 
    1759             : NS_IMETHODIMP
    1760           0 : nsPrefBranch::GetPrefType(const char* aPrefName, int32_t* aRetVal)
    1761             : {
    1762           0 :   NS_ENSURE_ARG(aPrefName);
    1763             : 
    1764           0 :   const PrefName& prefName = GetPrefName(aPrefName);
    1765           0 :   *aRetVal = Preferences::GetType(prefName.get());
    1766           0 :   return NS_OK;
    1767             : }
    1768             : 
    1769             : NS_IMETHODIMP
    1770           0 : nsPrefBranch::GetBoolPrefWithDefault(const char* aPrefName,
    1771             :                                      bool aDefaultValue,
    1772             :                                      uint8_t aArgc,
    1773             :                                      bool* aRetVal)
    1774             : {
    1775           0 :   nsresult rv = GetBoolPref(aPrefName, aRetVal);
    1776           0 :   if (NS_FAILED(rv) && aArgc == 1) {
    1777           0 :     *aRetVal = aDefaultValue;
    1778           0 :     return NS_OK;
    1779             :   }
    1780             : 
    1781             :   return rv;
    1782             : }
    1783             : 
    1784             : NS_IMETHODIMP
    1785           0 : nsPrefBranch::GetBoolPref(const char* aPrefName, bool* aRetVal)
    1786             : {
    1787           0 :   NS_ENSURE_ARG(aPrefName);
    1788             : 
    1789           0 :   const PrefName& pref = GetPrefName(aPrefName);
    1790           0 :   return Preferences::GetBool(pref.get(), aRetVal, mKind);
    1791             : }
    1792             : 
    1793             : NS_IMETHODIMP
    1794           0 : nsPrefBranch::SetBoolPref(const char* aPrefName, bool aValue)
    1795             : {
    1796           0 :   NS_ENSURE_ARG(aPrefName);
    1797             : 
    1798           0 :   const PrefName& pref = GetPrefName(aPrefName);
    1799           0 :   return Preferences::SetBool(pref.get(), aValue, mKind);
    1800             : }
    1801             : 
    1802             : NS_IMETHODIMP
    1803           0 : nsPrefBranch::GetFloatPrefWithDefault(const char* aPrefName,
    1804             :                                       float aDefaultValue,
    1805             :                                       uint8_t aArgc,
    1806             :                                       float* aRetVal)
    1807             : {
    1808           0 :   nsresult rv = GetFloatPref(aPrefName, aRetVal);
    1809             : 
    1810           0 :   if (NS_FAILED(rv) && aArgc == 1) {
    1811           0 :     *aRetVal = aDefaultValue;
    1812           0 :     return NS_OK;
    1813             :   }
    1814             : 
    1815             :   return rv;
    1816             : }
    1817             : 
    1818             : NS_IMETHODIMP
    1819           0 : nsPrefBranch::GetFloatPref(const char* aPrefName, float* aRetVal)
    1820             : {
    1821           0 :   NS_ENSURE_ARG(aPrefName);
    1822             : 
    1823           0 :   nsAutoCString stringVal;
    1824           0 :   nsresult rv = GetCharPref(aPrefName, stringVal);
    1825           0 :   if (NS_SUCCEEDED(rv)) {
    1826           0 :     *aRetVal = stringVal.ToFloat(&rv);
    1827             :   }
    1828             : 
    1829           0 :   return rv;
    1830             : }
    1831             : 
    1832             : NS_IMETHODIMP
    1833           0 : nsPrefBranch::GetCharPrefWithDefault(const char* aPrefName,
    1834             :                                      const nsACString& aDefaultValue,
    1835             :                                      uint8_t aArgc,
    1836             :                                      nsACString& aRetVal)
    1837             : {
    1838           0 :   nsresult rv = GetCharPref(aPrefName, aRetVal);
    1839             : 
    1840           0 :   if (NS_FAILED(rv) && aArgc == 1) {
    1841           0 :     aRetVal = aDefaultValue;
    1842           0 :     return NS_OK;
    1843             :   }
    1844             : 
    1845             :   return rv;
    1846             : }
    1847             : 
    1848             : NS_IMETHODIMP
    1849           0 : nsPrefBranch::GetCharPref(const char* aPrefName, nsACString& aRetVal)
    1850             : {
    1851           0 :   NS_ENSURE_ARG(aPrefName);
    1852             : 
    1853           0 :   const PrefName& pref = GetPrefName(aPrefName);
    1854           0 :   return Preferences::GetCString(pref.get(), aRetVal, mKind);
    1855             : }
    1856             : 
    1857             : NS_IMETHODIMP
    1858           0 : nsPrefBranch::SetCharPref(const char* aPrefName, const nsACString& aValue)
    1859             : {
    1860           0 :   nsresult rv = CheckSanityOfStringLength(aPrefName, aValue);
    1861           0 :   if (NS_FAILED(rv)) {
    1862             :     return rv;
    1863             :   }
    1864           0 :   return SetCharPrefNoLengthCheck(aPrefName, aValue);
    1865             : }
    1866             : 
    1867             : nsresult
    1868           0 : nsPrefBranch::SetCharPrefNoLengthCheck(const char* aPrefName,
    1869             :                                        const nsACString& aValue)
    1870             : {
    1871           0 :   NS_ENSURE_ARG(aPrefName);
    1872             : 
    1873           0 :   const PrefName& pref = GetPrefName(aPrefName);
    1874           0 :   return Preferences::SetCString(pref.get(), aValue, mKind);
    1875             : }
    1876             : 
    1877             : NS_IMETHODIMP
    1878           0 : nsPrefBranch::GetStringPref(const char* aPrefName,
    1879             :                             const nsACString& aDefaultValue,
    1880             :                             uint8_t aArgc,
    1881             :                             nsACString& aRetVal)
    1882             : {
    1883           0 :   nsCString utf8String;
    1884           0 :   nsresult rv = GetCharPref(aPrefName, utf8String);
    1885           0 :   if (NS_SUCCEEDED(rv)) {
    1886           0 :     aRetVal = utf8String;
    1887           0 :     return rv;
    1888             :   }
    1889             : 
    1890           0 :   if (aArgc == 1) {
    1891           0 :     aRetVal = aDefaultValue;
    1892           0 :     return NS_OK;
    1893             :   }
    1894             : 
    1895             :   return rv;
    1896             : }
    1897             : 
    1898             : NS_IMETHODIMP
    1899           0 : nsPrefBranch::SetStringPref(const char* aPrefName, const nsACString& aValue)
    1900             : {
    1901           0 :   nsresult rv = CheckSanityOfStringLength(aPrefName, aValue);
    1902           0 :   if (NS_FAILED(rv)) {
    1903             :     return rv;
    1904             :   }
    1905             : 
    1906           0 :   return SetCharPrefNoLengthCheck(aPrefName, aValue);
    1907             : }
    1908             : 
    1909             : NS_IMETHODIMP
    1910           0 : nsPrefBranch::GetIntPrefWithDefault(const char* aPrefName,
    1911             :                                     int32_t aDefaultValue,
    1912             :                                     uint8_t aArgc,
    1913             :                                     int32_t* aRetVal)
    1914             : {
    1915           0 :   nsresult rv = GetIntPref(aPrefName, aRetVal);
    1916             : 
    1917           0 :   if (NS_FAILED(rv) && aArgc == 1) {
    1918           0 :     *aRetVal = aDefaultValue;
    1919           0 :     return NS_OK;
    1920             :   }
    1921             : 
    1922             :   return rv;
    1923             : }
    1924             : 
    1925             : NS_IMETHODIMP
    1926           0 : nsPrefBranch::GetIntPref(const char* aPrefName, int32_t* aRetVal)
    1927             : {
    1928           0 :   NS_ENSURE_ARG(aPrefName);
    1929           0 :   const PrefName& pref = GetPrefName(aPrefName);
    1930           0 :   return Preferences::GetInt(pref.get(), aRetVal, mKind);
    1931             : }
    1932             : 
    1933             : NS_IMETHODIMP
    1934           0 : nsPrefBranch::SetIntPref(const char* aPrefName, int32_t aValue)
    1935             : {
    1936           0 :   NS_ENSURE_ARG(aPrefName);
    1937             : 
    1938           0 :   const PrefName& pref = GetPrefName(aPrefName);
    1939           0 :   return Preferences::SetInt(pref.get(), aValue, mKind);
    1940             : }
    1941             : 
    1942             : NS_IMETHODIMP
    1943           0 : nsPrefBranch::GetComplexValue(const char* aPrefName,
    1944             :                               const nsIID& aType,
    1945             :                               void** aRetVal)
    1946             : {
    1947           0 :   NS_ENSURE_ARG(aPrefName);
    1948             : 
    1949             :   nsresult rv;
    1950           0 :   nsAutoCString utf8String;
    1951             : 
    1952             :   // We have to do this one first because it's different to all the rest.
    1953           0 :   if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
    1954             :     nsCOMPtr<nsIPrefLocalizedString> theString(
    1955           0 :       do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv));
    1956           0 :     if (NS_FAILED(rv)) {
    1957             :       return rv;
    1958             :     }
    1959             : 
    1960           0 :     const PrefName& pref = GetPrefName(aPrefName);
    1961           0 :     bool bNeedDefault = false;
    1962             : 
    1963           0 :     if (mKind == PrefValueKind::Default) {
    1964             :       bNeedDefault = true;
    1965             :     } else {
    1966             :       // if there is no user (or locked) value
    1967           0 :       if (!Preferences::HasUserValue(pref.get()) &&
    1968           0 :           !Preferences::IsLocked(pref.get())) {
    1969           0 :         bNeedDefault = true;
    1970             :       }
    1971             :     }
    1972             : 
    1973             :     // if we need to fetch the default value, do that instead, otherwise use the
    1974             :     // value we pulled in at the top of this function
    1975           0 :     if (bNeedDefault) {
    1976           0 :       nsAutoString utf16String;
    1977           0 :       rv = GetDefaultFromPropertiesFile(pref.get(), utf16String);
    1978           0 :       if (NS_SUCCEEDED(rv)) {
    1979           0 :         theString->SetData(utf16String);
    1980             :       }
    1981             :     } else {
    1982           0 :       rv = GetCharPref(aPrefName, utf8String);
    1983           0 :       if (NS_SUCCEEDED(rv)) {
    1984           0 :         theString->SetData(NS_ConvertUTF8toUTF16(utf8String));
    1985             :       }
    1986             :     }
    1987             : 
    1988           0 :     if (NS_SUCCEEDED(rv)) {
    1989           0 :       theString.forget(reinterpret_cast<nsIPrefLocalizedString**>(aRetVal));
    1990             :     }
    1991             : 
    1992           0 :     return rv;
    1993             :   }
    1994             : 
    1995             :   // if we can't get the pref, there's no point in being here
    1996           0 :   rv = GetCharPref(aPrefName, utf8String);
    1997           0 :   if (NS_FAILED(rv)) {
    1998             :     return rv;
    1999             :   }
    2000             : 
    2001           0 :   if (aType.Equals(NS_GET_IID(nsIFile))) {
    2002           0 :     ENSURE_PARENT_PROCESS("GetComplexValue(nsIFile)", aPrefName);
    2003             : 
    2004           0 :     nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
    2005             : 
    2006           0 :     if (NS_SUCCEEDED(rv)) {
    2007           0 :       rv = file->SetPersistentDescriptor(utf8String);
    2008           0 :       if (NS_SUCCEEDED(rv)) {
    2009           0 :         file.forget(reinterpret_cast<nsIFile**>(aRetVal));
    2010           0 :         return NS_OK;
    2011             :       }
    2012             :     }
    2013           0 :     return rv;
    2014             :   }
    2015             : 
    2016           0 :   if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
    2017           0 :     ENSURE_PARENT_PROCESS("GetComplexValue(nsIRelativeFilePref)", aPrefName);
    2018             : 
    2019           0 :     nsACString::const_iterator keyBegin, strEnd;
    2020           0 :     utf8String.BeginReading(keyBegin);
    2021           0 :     utf8String.EndReading(strEnd);
    2022             : 
    2023             :     // The pref has the format: [fromKey]a/b/c
    2024           0 :     if (*keyBegin++ != '[') {
    2025             :       return NS_ERROR_FAILURE;
    2026             :     }
    2027             : 
    2028           0 :     nsACString::const_iterator keyEnd(keyBegin);
    2029           0 :     if (!FindCharInReadable(']', keyEnd, strEnd)) {
    2030             :       return NS_ERROR_FAILURE;
    2031             :     }
    2032             : 
    2033           0 :     nsAutoCString key(Substring(keyBegin, keyEnd));
    2034             : 
    2035           0 :     nsCOMPtr<nsIFile> fromFile;
    2036             :     nsCOMPtr<nsIProperties> directoryService(
    2037           0 :       do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
    2038           0 :     if (NS_FAILED(rv)) {
    2039             :       return rv;
    2040             :     }
    2041             : 
    2042           0 :     rv = directoryService->Get(
    2043           0 :       key.get(), NS_GET_IID(nsIFile), getter_AddRefs(fromFile));
    2044           0 :     if (NS_FAILED(rv)) {
    2045             :       return rv;
    2046             :     }
    2047             : 
    2048           0 :     nsCOMPtr<nsIFile> theFile;
    2049           0 :     rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(theFile));
    2050           0 :     if (NS_FAILED(rv)) {
    2051             :       return rv;
    2052             :     }
    2053             : 
    2054           0 :     rv = theFile->SetRelativeDescriptor(fromFile, Substring(++keyEnd, strEnd));
    2055           0 :     if (NS_FAILED(rv)) {
    2056             :       return rv;
    2057             :     }
    2058             : 
    2059           0 :     nsCOMPtr<nsIRelativeFilePref> relativePref;
    2060           0 :     rv = NS_NewRelativeFilePref(theFile, key, getter_AddRefs(relativePref));
    2061           0 :     if (NS_FAILED(rv)) {
    2062             :       return rv;
    2063             :     }
    2064             : 
    2065           0 :     relativePref.forget(reinterpret_cast<nsIRelativeFilePref**>(aRetVal));
    2066           0 :     return NS_OK;
    2067             :   }
    2068             : 
    2069           0 :   NS_WARNING("nsPrefBranch::GetComplexValue - Unsupported interface type");
    2070           0 :   return NS_NOINTERFACE;
    2071             : }
    2072             : 
    2073             : nsresult
    2074           0 : nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
    2075             :                                         const nsAString& aValue)
    2076             : {
    2077           0 :   return CheckSanityOfStringLength(aPrefName, aValue.Length());
    2078             : }
    2079             : 
    2080             : nsresult
    2081           0 : nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
    2082             :                                         const nsACString& aValue)
    2083             : {
    2084           0 :   return CheckSanityOfStringLength(aPrefName, aValue.Length());
    2085             : }
    2086             : 
    2087             : nsresult
    2088           0 : nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
    2089             :                                         const uint32_t aLength)
    2090             : {
    2091           0 :   if (aLength > MAX_PREF_LENGTH) {
    2092             :     return NS_ERROR_ILLEGAL_VALUE;
    2093             :   }
    2094           0 :   if (aLength <= MAX_ADVISABLE_PREF_LENGTH) {
    2095             :     return NS_OK;
    2096             :   }
    2097             : 
    2098             :   nsresult rv;
    2099             :   nsCOMPtr<nsIConsoleService> console =
    2100           0 :     do_GetService("@mozilla.org/consoleservice;1", &rv);
    2101           0 :   if (NS_FAILED(rv)) {
    2102             :     return rv;
    2103             :   }
    2104             : 
    2105           0 :   nsAutoCString message(nsPrintfCString(
    2106             :     "Warning: attempting to write %d bytes to preference %s. This is bad "
    2107             :     "for general performance and memory usage. Such an amount of data "
    2108             :     "should rather be written to an external file. This preference will "
    2109             :     "not be sent to any content processes.",
    2110             :     aLength,
    2111           0 :     GetPrefName(aPrefName).get()));
    2112             : 
    2113           0 :   rv = console->LogStringMessage(NS_ConvertUTF8toUTF16(message).get());
    2114           0 :   if (NS_FAILED(rv)) {
    2115           0 :     return rv;
    2116             :   }
    2117             :   return NS_OK;
    2118             : }
    2119             : 
    2120             : NS_IMETHODIMP
    2121           0 : nsPrefBranch::SetComplexValue(const char* aPrefName,
    2122             :                               const nsIID& aType,
    2123             :                               nsISupports* aValue)
    2124             : {
    2125           0 :   ENSURE_PARENT_PROCESS("SetComplexValue", aPrefName);
    2126           0 :   NS_ENSURE_ARG(aPrefName);
    2127             : 
    2128           0 :   nsresult rv = NS_NOINTERFACE;
    2129             : 
    2130           0 :   if (aType.Equals(NS_GET_IID(nsIFile))) {
    2131           0 :     nsCOMPtr<nsIFile> file = do_QueryInterface(aValue);
    2132           0 :     if (!file) {
    2133             :       return NS_NOINTERFACE;
    2134             :     }
    2135             : 
    2136           0 :     nsAutoCString descriptorString;
    2137           0 :     rv = file->GetPersistentDescriptor(descriptorString);
    2138           0 :     if (NS_SUCCEEDED(rv)) {
    2139           0 :       rv = SetCharPrefNoLengthCheck(aPrefName, descriptorString);
    2140             :     }
    2141           0 :     return rv;
    2142             :   }
    2143             : 
    2144           0 :   if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
    2145           0 :     nsCOMPtr<nsIRelativeFilePref> relFilePref = do_QueryInterface(aValue);
    2146           0 :     if (!relFilePref) {
    2147             :       return NS_NOINTERFACE;
    2148             :     }
    2149             : 
    2150           0 :     nsCOMPtr<nsIFile> file;
    2151           0 :     relFilePref->GetFile(getter_AddRefs(file));
    2152           0 :     if (!file) {
    2153             :       return NS_NOINTERFACE;
    2154             :     }
    2155             : 
    2156           0 :     nsAutoCString relativeToKey;
    2157           0 :     (void)relFilePref->GetRelativeToKey(relativeToKey);
    2158             : 
    2159           0 :     nsCOMPtr<nsIFile> relativeToFile;
    2160             :     nsCOMPtr<nsIProperties> directoryService(
    2161           0 :       do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
    2162           0 :     if (NS_FAILED(rv)) {
    2163             :       return rv;
    2164             :     }
    2165             : 
    2166           0 :     rv = directoryService->Get(
    2167           0 :       relativeToKey.get(), NS_GET_IID(nsIFile), getter_AddRefs(relativeToFile));
    2168           0 :     if (NS_FAILED(rv)) {
    2169             :       return rv;
    2170             :     }
    2171             : 
    2172           0 :     nsAutoCString relDescriptor;
    2173           0 :     rv = file->GetRelativeDescriptor(relativeToFile, relDescriptor);
    2174           0 :     if (NS_FAILED(rv)) {
    2175             :       return rv;
    2176             :     }
    2177             : 
    2178           0 :     nsAutoCString descriptorString;
    2179           0 :     descriptorString.Append('[');
    2180           0 :     descriptorString.Append(relativeToKey);
    2181           0 :     descriptorString.Append(']');
    2182           0 :     descriptorString.Append(relDescriptor);
    2183           0 :     return SetCharPrefNoLengthCheck(aPrefName, descriptorString);
    2184             :   }
    2185             : 
    2186           0 :   if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
    2187           0 :     nsCOMPtr<nsISupportsString> theString = do_QueryInterface(aValue);
    2188             : 
    2189           0 :     if (theString) {
    2190           0 :       nsString wideString;
    2191             : 
    2192           0 :       rv = theString->GetData(wideString);
    2193           0 :       if (NS_SUCCEEDED(rv)) {
    2194             :         // Check sanity of string length before any lengthy conversion
    2195           0 :         rv = CheckSanityOfStringLength(aPrefName, wideString);
    2196           0 :         if (NS_FAILED(rv)) {
    2197           0 :           return rv;
    2198             :         }
    2199           0 :         rv = SetCharPrefNoLengthCheck(aPrefName,
    2200           0 :                                       NS_ConvertUTF16toUTF8(wideString));
    2201             :       }
    2202             :     }
    2203           0 :     return rv;
    2204             :   }
    2205             : 
    2206           0 :   NS_WARNING("nsPrefBranch::SetComplexValue - Unsupported interface type");
    2207           0 :   return NS_NOINTERFACE;
    2208             : }
    2209             : 
    2210             : NS_IMETHODIMP
    2211           0 : nsPrefBranch::ClearUserPref(const char* aPrefName)
    2212             : {
    2213           0 :   NS_ENSURE_ARG(aPrefName);
    2214             : 
    2215           0 :   const PrefName& pref = GetPrefName(aPrefName);
    2216           0 :   return Preferences::ClearUser(pref.get());
    2217             : }
    2218             : 
    2219             : NS_IMETHODIMP
    2220           0 : nsPrefBranch::PrefHasUserValue(const char* aPrefName, bool* aRetVal)
    2221             : {
    2222           0 :   NS_ENSURE_ARG_POINTER(aRetVal);
    2223           0 :   NS_ENSURE_ARG(aPrefName);
    2224             : 
    2225           0 :   const PrefName& pref = GetPrefName(aPrefName);
    2226           0 :   *aRetVal = Preferences::HasUserValue(pref.get());
    2227           0 :   return NS_OK;
    2228             : }
    2229             : 
    2230             : NS_IMETHODIMP
    2231           0 : nsPrefBranch::LockPref(const char* aPrefName)
    2232             : {
    2233           0 :   NS_ENSURE_ARG(aPrefName);
    2234             : 
    2235           0 :   const PrefName& pref = GetPrefName(aPrefName);
    2236           0 :   return Preferences::Lock(pref.get());
    2237             : }
    2238             : 
    2239             : NS_IMETHODIMP
    2240           0 : nsPrefBranch::PrefIsLocked(const char* aPrefName, bool* aRetVal)
    2241             : {
    2242           0 :   NS_ENSURE_ARG_POINTER(aRetVal);
    2243           0 :   NS_ENSURE_ARG(aPrefName);
    2244             : 
    2245           0 :   const PrefName& pref = GetPrefName(aPrefName);
    2246           0 :   *aRetVal = Preferences::IsLocked(pref.get());
    2247           0 :   return NS_OK;
    2248             : }
    2249             : 
    2250             : NS_IMETHODIMP
    2251           0 : nsPrefBranch::UnlockPref(const char* aPrefName)
    2252             : {
    2253           0 :   NS_ENSURE_ARG(aPrefName);
    2254             : 
    2255           0 :   const PrefName& pref = GetPrefName(aPrefName);
    2256           0 :   return Preferences::Unlock(pref.get());
    2257             : }
    2258             : 
    2259             : NS_IMETHODIMP
    2260           0 : nsPrefBranch::ResetBranch(const char* aStartingAt)
    2261             : {
    2262           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    2263             : }
    2264             : 
    2265             : NS_IMETHODIMP
    2266           0 : nsPrefBranch::DeleteBranch(const char* aStartingAt)
    2267             : {
    2268           0 :   ENSURE_PARENT_PROCESS("DeleteBranch", aStartingAt);
    2269           0 :   NS_ENSURE_ARG(aStartingAt);
    2270             : 
    2271           0 :   MOZ_ASSERT(NS_IsMainThread());
    2272             : 
    2273           0 :   if (!gHashTable) {
    2274             :     return NS_ERROR_NOT_INITIALIZED;
    2275             :   }
    2276             : 
    2277           0 :   const PrefName& pref = GetPrefName(aStartingAt);
    2278           0 :   nsAutoCString branchName(pref.get());
    2279             : 
    2280             :   // Add a trailing '.' if it doesn't already have one.
    2281           0 :   if (branchName.Length() > 1 &&
    2282           0 :       !StringEndsWith(branchName, NS_LITERAL_CSTRING("."))) {
    2283             :     branchName += '.';
    2284             :   }
    2285             : 
    2286             :   const nsACString& branchNameNoDot =
    2287           0 :     Substring(branchName, 0, branchName.Length() - 1);
    2288             : 
    2289           0 :   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
    2290           0 :     Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
    2291             : 
    2292             :     // The first disjunct matches branches: e.g. a branch name "foo.bar."
    2293             :     // matches a name "foo.bar.baz" (but it won't match "foo.barrel.baz").
    2294             :     // The second disjunct matches leaf nodes: e.g. a branch name "foo.bar."
    2295             :     // matches a name "foo.bar" (by ignoring the trailing '.').
    2296           0 :     nsDependentCString name(pref->Name());
    2297           0 :     if (StringBeginsWith(name, branchName) || name.Equals(branchNameNoDot)) {
    2298           0 :       iter.Remove();
    2299             :     }
    2300             :   }
    2301             : 
    2302           0 :   Preferences::HandleDirty();
    2303           0 :   return NS_OK;
    2304             : }
    2305             : 
    2306             : NS_IMETHODIMP
    2307           0 : nsPrefBranch::GetChildList(const char* aStartingAt,
    2308             :                            uint32_t* aCount,
    2309             :                            char*** aChildArray)
    2310             : {
    2311             :   char** outArray;
    2312             :   int32_t numPrefs;
    2313             :   int32_t dwIndex;
    2314           0 :   AutoTArray<nsCString, 32> prefArray;
    2315             : 
    2316           0 :   NS_ENSURE_ARG(aStartingAt);
    2317           0 :   NS_ENSURE_ARG_POINTER(aCount);
    2318           0 :   NS_ENSURE_ARG_POINTER(aChildArray);
    2319             : 
    2320           0 :   MOZ_ASSERT(NS_IsMainThread());
    2321             : 
    2322           0 :   *aChildArray = nullptr;
    2323           0 :   *aCount = 0;
    2324             : 
    2325             :   // This will contain a list of all the pref name strings. Allocated on the
    2326             :   // stack for speed.
    2327             : 
    2328           0 :   const PrefName& parent = GetPrefName(aStartingAt);
    2329           0 :   size_t parentLen = parent.Length();
    2330           0 :   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
    2331           0 :     Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
    2332           0 :     if (strncmp(pref->Name(), parent.get(), parentLen) == 0) {
    2333           0 :       prefArray.AppendElement(pref->Name());
    2334             :     }
    2335             :   }
    2336             : 
    2337             :   // Now that we've built up the list, run the callback on all the matching
    2338             :   // elements.
    2339           0 :   numPrefs = prefArray.Length();
    2340             : 
    2341           0 :   if (numPrefs) {
    2342           0 :     outArray = (char**)moz_xmalloc(numPrefs * sizeof(char*));
    2343             : 
    2344           0 :     for (dwIndex = 0; dwIndex < numPrefs; ++dwIndex) {
    2345             :       // we need to lop off mPrefRoot in case the user is planning to pass this
    2346             :       // back to us because if they do we are going to add mPrefRoot again.
    2347           0 :       const nsCString& element = prefArray[dwIndex];
    2348           0 :       outArray[dwIndex] =
    2349           0 :         (char*)nsMemory::Clone(element.get() + mPrefRoot.Length(),
    2350           0 :                                element.Length() - mPrefRoot.Length() + 1);
    2351             : 
    2352           0 :       if (!outArray[dwIndex]) {
    2353             :         // We ran out of memory... this is annoying.
    2354           0 :         NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(dwIndex, outArray);
    2355           0 :         return NS_ERROR_OUT_OF_MEMORY;
    2356             :       }
    2357             :     }
    2358           0 :     *aChildArray = outArray;
    2359             :   }
    2360           0 :   *aCount = numPrefs;
    2361             : 
    2362           0 :   return NS_OK;
    2363             : }
    2364             : 
    2365             : NS_IMETHODIMP
    2366           0 : nsPrefBranch::AddObserver(const char* aDomain,
    2367             :                           nsIObserver* aObserver,
    2368             :                           bool aHoldWeak)
    2369             : {
    2370             :   PrefCallback* pCallback;
    2371             : 
    2372           0 :   NS_ENSURE_ARG(aDomain);
    2373           0 :   NS_ENSURE_ARG(aObserver);
    2374             : 
    2375             :   // Hold a weak reference to the observer if so requested.
    2376           0 :   if (aHoldWeak) {
    2377             :     nsCOMPtr<nsISupportsWeakReference> weakRefFactory =
    2378           0 :       do_QueryInterface(aObserver);
    2379           0 :     if (!weakRefFactory) {
    2380             :       // The caller didn't give us a object that supports weak reference...
    2381             :       // tell them.
    2382           0 :       return NS_ERROR_INVALID_ARG;
    2383             :     }
    2384             : 
    2385             :     // Construct a PrefCallback with a weak reference to the observer.
    2386           0 :     pCallback = new PrefCallback(aDomain, weakRefFactory, this);
    2387             : 
    2388             :   } else {
    2389             :     // Construct a PrefCallback with a strong reference to the observer.
    2390           0 :     pCallback = new PrefCallback(aDomain, aObserver, this);
    2391             :   }
    2392             : 
    2393           0 :   auto p = mObservers.LookupForAdd(pCallback);
    2394           0 :   if (p) {
    2395           0 :     NS_WARNING("Ignoring duplicate observer.");
    2396           0 :     delete pCallback;
    2397             :     return NS_OK;
    2398             :   }
    2399             : 
    2400           0 :   p.OrInsert([&pCallback]() { return pCallback; });
    2401             : 
    2402             :   // We must pass a fully qualified preference name to the callback
    2403             :   // aDomain == nullptr is the only possible failure, and we trapped it with
    2404             :   // NS_ENSURE_ARG above.
    2405           0 :   const PrefName& pref = GetPrefName(aDomain);
    2406           0 :   Preferences::RegisterCallback(NotifyObserver,
    2407             :                                 pref.get(),
    2408             :                                 pCallback,
    2409             :                                 Preferences::PrefixMatch,
    2410           0 :                                 /* isPriority */ false);
    2411             : 
    2412           0 :   return NS_OK;
    2413             : }
    2414             : 
    2415             : NS_IMETHODIMP
    2416           0 : nsPrefBranch::RemoveObserver(const char* aDomain, nsIObserver* aObserver)
    2417             : {
    2418           0 :   NS_ENSURE_ARG(aDomain);
    2419           0 :   NS_ENSURE_ARG(aObserver);
    2420             : 
    2421           0 :   nsresult rv = NS_OK;
    2422             : 
    2423             :   // If we're in the middle of a call to FreeObserverList, don't process this
    2424             :   // RemoveObserver call -- the observer in question will be removed soon, if
    2425             :   // it hasn't been already.
    2426             :   //
    2427             :   // It's important that we don't touch mObservers in any way -- even a Get()
    2428             :   // which returns null might cause the hashtable to resize itself, which will
    2429             :   // break the iteration in FreeObserverList.
    2430           0 :   if (mFreeingObserverList) {
    2431             :     return NS_OK;
    2432             :   }
    2433             : 
    2434             :   // Remove the relevant PrefCallback from mObservers and get an owning pointer
    2435             :   // to it. Unregister the callback first, and then let the owning pointer go
    2436             :   // out of scope and destroy the callback.
    2437           0 :   PrefCallback key(aDomain, aObserver, this);
    2438           0 :   nsAutoPtr<PrefCallback> pCallback;
    2439           0 :   mObservers.Remove(&key, &pCallback);
    2440           0 :   if (pCallback) {
    2441             :     // aDomain == nullptr is the only possible failure, trapped above.
    2442           0 :     const PrefName& pref = GetPrefName(aDomain);
    2443           0 :     rv = Preferences::UnregisterCallback(
    2444           0 :       NotifyObserver, pref.get(), pCallback, Preferences::PrefixMatch);
    2445             :   }
    2446             : 
    2447             :   return rv;
    2448             : }
    2449             : 
    2450             : NS_IMETHODIMP
    2451           0 : nsPrefBranch::Observe(nsISupports* aSubject,
    2452             :                       const char* aTopic,
    2453             :                       const char16_t* aData)
    2454             : {
    2455             :   // Watch for xpcom shutdown and free our observers to eliminate any cyclic
    2456             :   // references.
    2457           0 :   if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    2458           0 :     FreeObserverList();
    2459             :   }
    2460           0 :   return NS_OK;
    2461             : }
    2462             : 
    2463             : /* static */ void
    2464           0 : nsPrefBranch::NotifyObserver(const char* aNewPref, void* aData)
    2465             : {
    2466           0 :   PrefCallback* pCallback = (PrefCallback*)aData;
    2467             : 
    2468           0 :   nsCOMPtr<nsIObserver> observer = pCallback->GetObserver();
    2469           0 :   if (!observer) {
    2470             :     // The observer has expired.  Let's remove this callback.
    2471           0 :     pCallback->GetPrefBranch()->RemoveExpiredCallback(pCallback);
    2472           0 :     return;
    2473             :   }
    2474             : 
    2475             :   // Remove any root this string may contain so as to not confuse the observer
    2476             :   // by passing them something other than what they passed us as a topic.
    2477           0 :   uint32_t len = pCallback->GetPrefBranch()->GetRootLength();
    2478           0 :   nsAutoCString suffix(aNewPref + len);
    2479             : 
    2480           0 :   observer->Observe(static_cast<nsIPrefBranch*>(pCallback->GetPrefBranch()),
    2481             :                     NS_PREFBRANCH_PREFCHANGE_TOPIC_ID,
    2482           0 :                     NS_ConvertASCIItoUTF16(suffix).get());
    2483             : }
    2484             : 
    2485             : size_t
    2486           0 : nsPrefBranch::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
    2487             : {
    2488           0 :   size_t n = aMallocSizeOf(this);
    2489             : 
    2490           0 :   n += mPrefRoot.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
    2491             : 
    2492           0 :   n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
    2493           0 :   for (auto iter = mObservers.ConstIter(); !iter.Done(); iter.Next()) {
    2494           0 :     const PrefCallback* data = iter.UserData();
    2495           0 :     n += data->SizeOfIncludingThis(aMallocSizeOf);
    2496             :   }
    2497             : 
    2498           0 :   return n;
    2499             : }
    2500             : 
    2501             : void
    2502           0 : nsPrefBranch::FreeObserverList()
    2503             : {
    2504             :   // We need to prevent anyone from modifying mObservers while we're iterating
    2505             :   // over it. In particular, some clients will call RemoveObserver() when
    2506             :   // they're removed and destructed via the iterator; we set
    2507             :   // mFreeingObserverList to keep those calls from touching mObservers.
    2508           0 :   mFreeingObserverList = true;
    2509           0 :   for (auto iter = mObservers.Iter(); !iter.Done(); iter.Next()) {
    2510           0 :     nsAutoPtr<PrefCallback>& callback = iter.Data();
    2511           0 :     nsPrefBranch* prefBranch = callback->GetPrefBranch();
    2512           0 :     const PrefName& pref = prefBranch->GetPrefName(callback->GetDomain().get());
    2513           0 :     Preferences::UnregisterCallback(nsPrefBranch::NotifyObserver,
    2514             :                                     pref.get(),
    2515             :                                     callback,
    2516           0 :                                     Preferences::PrefixMatch);
    2517           0 :     iter.Remove();
    2518             :   }
    2519           0 :   mFreeingObserverList = false;
    2520           0 : }
    2521             : 
    2522             : void
    2523           0 : nsPrefBranch::RemoveExpiredCallback(PrefCallback* aCallback)
    2524             : {
    2525           0 :   MOZ_ASSERT(aCallback->IsExpired());
    2526           0 :   mObservers.Remove(aCallback);
    2527           0 : }
    2528             : 
    2529             : nsresult
    2530           0 : nsPrefBranch::GetDefaultFromPropertiesFile(const char* aPrefName,
    2531             :                                            nsAString& aReturn)
    2532             : {
    2533             :   // The default value contains a URL to a .properties file.
    2534             : 
    2535           0 :   nsAutoCString propertyFileURL;
    2536             :   nsresult rv =
    2537           0 :     Preferences::GetCString(aPrefName, propertyFileURL, PrefValueKind::Default);
    2538           0 :   if (NS_FAILED(rv)) {
    2539             :     return rv;
    2540             :   }
    2541             : 
    2542             :   nsCOMPtr<nsIStringBundleService> bundleService =
    2543           0 :     mozilla::services::GetStringBundleService();
    2544           0 :   if (!bundleService) {
    2545             :     return NS_ERROR_FAILURE;
    2546             :   }
    2547             : 
    2548           0 :   nsCOMPtr<nsIStringBundle> bundle;
    2549             :   rv =
    2550           0 :     bundleService->CreateBundle(propertyFileURL.get(), getter_AddRefs(bundle));
    2551           0 :   if (NS_FAILED(rv)) {
    2552             :     return rv;
    2553             :   }
    2554             : 
    2555           0 :   return bundle->GetStringFromName(aPrefName, aReturn);
    2556             : }
    2557             : 
    2558             : nsPrefBranch::PrefName
    2559           0 : nsPrefBranch::GetPrefName(const char* aPrefName) const
    2560             : {
    2561           0 :   MOZ_ASSERT(aPrefName);
    2562             : 
    2563             :   // For speed, avoid strcpy if we can.
    2564           0 :   if (mPrefRoot.IsEmpty()) {
    2565           0 :     return PrefName(aPrefName);
    2566             :   }
    2567             : 
    2568           0 :   return PrefName(mPrefRoot + nsDependentCString(aPrefName));
    2569             : }
    2570             : 
    2571             : //----------------------------------------------------------------------------
    2572             : // nsPrefLocalizedString
    2573             : //----------------------------------------------------------------------------
    2574             : 
    2575             : nsPrefLocalizedString::nsPrefLocalizedString() = default;
    2576             : 
    2577             : nsPrefLocalizedString::~nsPrefLocalizedString() = default;
    2578             : 
    2579           0 : NS_IMPL_ISUPPORTS(nsPrefLocalizedString,
    2580             :                   nsIPrefLocalizedString,
    2581             :                   nsISupportsString)
    2582             : 
    2583             : nsresult
    2584           0 : nsPrefLocalizedString::Init()
    2585             : {
    2586             :   nsresult rv;
    2587           0 :   mUnicodeString = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
    2588             : 
    2589           0 :   return rv;
    2590             : }
    2591             : 
    2592             : //----------------------------------------------------------------------------
    2593             : // nsRelativeFilePref
    2594             : //----------------------------------------------------------------------------
    2595             : 
    2596           0 : NS_IMPL_ISUPPORTS(nsRelativeFilePref, nsIRelativeFilePref)
    2597             : 
    2598             : nsRelativeFilePref::nsRelativeFilePref() = default;
    2599             : 
    2600             : nsRelativeFilePref::~nsRelativeFilePref() = default;
    2601             : 
    2602             : NS_IMETHODIMP
    2603           0 : nsRelativeFilePref::GetFile(nsIFile** aFile)
    2604             : {
    2605           0 :   NS_ENSURE_ARG_POINTER(aFile);
    2606           0 :   *aFile = mFile;
    2607           0 :   NS_IF_ADDREF(*aFile);
    2608           0 :   return NS_OK;
    2609             : }
    2610             : 
    2611             : NS_IMETHODIMP
    2612           0 : nsRelativeFilePref::SetFile(nsIFile* aFile)
    2613             : {
    2614           0 :   mFile = aFile;
    2615           0 :   return NS_OK;
    2616             : }
    2617             : 
    2618             : NS_IMETHODIMP
    2619           0 : nsRelativeFilePref::GetRelativeToKey(nsACString& aRelativeToKey)
    2620             : {
    2621           0 :   aRelativeToKey.Assign(mRelativeToKey);
    2622           0 :   return NS_OK;
    2623             : }
    2624             : 
    2625             : NS_IMETHODIMP
    2626           0 : nsRelativeFilePref::SetRelativeToKey(const nsACString& aRelativeToKey)
    2627             : {
    2628           0 :   mRelativeToKey.Assign(aRelativeToKey);
    2629           0 :   return NS_OK;
    2630             : }
    2631             : 
    2632             : //===========================================================================
    2633             : // class Preferences and related things
    2634             : //===========================================================================
    2635             : 
    2636             : namespace mozilla {
    2637             : 
    2638             : #define INITIAL_PREF_FILES 10
    2639             : 
    2640             : static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
    2641             : 
    2642             : void
    2643           0 : Preferences::HandleDirty()
    2644             : {
    2645           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    2646             : 
    2647           0 :   if (!gHashTable || !sPreferences) {
    2648             :     return;
    2649             :   }
    2650             : 
    2651           0 :   if (sPreferences->mProfileShutdown) {
    2652           0 :     NS_WARNING("Setting user pref after profile shutdown.");
    2653           0 :     return;
    2654             :   }
    2655             : 
    2656           0 :   if (!sPreferences->mDirty) {
    2657           0 :     sPreferences->mDirty = true;
    2658             : 
    2659           0 :     if (sPreferences->mCurrentFile && sPreferences->AllowOffMainThreadSave() &&
    2660           0 :         !sPreferences->mSavePending) {
    2661           0 :       sPreferences->mSavePending = true;
    2662             :       static const int PREF_DELAY_MS = 500;
    2663           0 :       NS_DelayedDispatchToCurrentThread(
    2664           0 :         mozilla::NewRunnableMethod("Preferences::SavePrefFileAsynchronous",
    2665           0 :                                    sPreferences.get(),
    2666             :                                    &Preferences::SavePrefFileAsynchronous),
    2667           0 :         PREF_DELAY_MS);
    2668             :     }
    2669             :   }
    2670             : }
    2671             : 
    2672             : static nsresult
    2673             : openPrefFile(nsIFile* aFile, PrefValueKind aKind);
    2674             : 
    2675             : // clang-format off
    2676             : static const char kPrefFileHeader[] =
    2677             :   "// Mozilla User Preferences"
    2678             :   NS_LINEBREAK
    2679             :   NS_LINEBREAK
    2680             :   "// DO NOT EDIT THIS FILE."
    2681             :   NS_LINEBREAK
    2682             :   "//"
    2683             :   NS_LINEBREAK
    2684             :   "// If you make changes to this file while the application is running,"
    2685             :   NS_LINEBREAK
    2686             :   "// the changes will be overwritten when the application exits."
    2687             :   NS_LINEBREAK
    2688             :   "//"
    2689             :   NS_LINEBREAK
    2690             :   "// To change a preference value, you can either:"
    2691             :   NS_LINEBREAK
    2692             :   "// - modify it via the UI (e.g. via about:config in the browser); or"
    2693             :   NS_LINEBREAK
    2694             :   "// - set it within a user.js file in your profile."
    2695             :   NS_LINEBREAK
    2696             :   NS_LINEBREAK;
    2697             : // clang-format on
    2698             : 
    2699             : // Note: if sShutdown is true, sPreferences will be nullptr.
    2700           0 : StaticRefPtr<Preferences> Preferences::sPreferences;
    2701             : bool Preferences::sShutdown = false;
    2702             : 
    2703             : // This globally enables or disables OMT pref writing, both sync and async.
    2704             : static int32_t sAllowOMTPrefWrite = -1;
    2705             : 
    2706             : // Write the preference data to a file.
    2707             : class PreferencesWriter final
    2708             : {
    2709             : public:
    2710             :   PreferencesWriter() = default;
    2711             : 
    2712           0 :   static nsresult Write(nsIFile* aFile, PrefSaveData& aPrefs)
    2713             :   {
    2714           0 :     nsCOMPtr<nsIOutputStream> outStreamSink;
    2715           0 :     nsCOMPtr<nsIOutputStream> outStream;
    2716             :     uint32_t writeAmount;
    2717             :     nsresult rv;
    2718             : 
    2719             :     // Execute a "safe" save by saving through a tempfile.
    2720           0 :     rv = NS_NewSafeLocalFileOutputStream(
    2721           0 :       getter_AddRefs(outStreamSink), aFile, -1, 0600);
    2722           0 :     if (NS_FAILED(rv)) {
    2723             :       return rv;
    2724             :     }
    2725             : 
    2726           0 :     rv = NS_NewBufferedOutputStream(
    2727           0 :       getter_AddRefs(outStream), outStreamSink.forget(), 4096);
    2728           0 :     if (NS_FAILED(rv)) {
    2729             :       return rv;
    2730             :     }
    2731             : 
    2732             :     struct CharComparator
    2733             :     {
    2734             :       bool LessThan(const nsCString& aA, const nsCString& aB) const
    2735             :       {
    2736           0 :         return aA < aB;
    2737             :       }
    2738             : 
    2739             :       bool Equals(const nsCString& aA, const nsCString& aB) const
    2740             :       {
    2741           0 :         return aA == aB;
    2742             :       }
    2743             :     };
    2744             : 
    2745             :     // Sort the preferences to make a readable file on disk.
    2746           0 :     aPrefs.Sort(CharComparator());
    2747             : 
    2748             :     // Write out the file header.
    2749           0 :     outStream->Write(
    2750           0 :       kPrefFileHeader, sizeof(kPrefFileHeader) - 1, &writeAmount);
    2751             : 
    2752           0 :     for (nsCString& pref : aPrefs) {
    2753           0 :       outStream->Write(pref.get(), pref.Length(), &writeAmount);
    2754           0 :       outStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &writeAmount);
    2755             :     }
    2756             : 
    2757             :     // Tell the safe output stream to overwrite the real prefs file.
    2758             :     // (It'll abort if there were any errors during writing.)
    2759           0 :     nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outStream);
    2760           0 :     MOZ_ASSERT(safeStream, "expected a safe output stream!");
    2761           0 :     if (safeStream) {
    2762           0 :       rv = safeStream->Finish();
    2763             :     }
    2764             : 
    2765             : #ifdef DEBUG
    2766           0 :     if (NS_FAILED(rv)) {
    2767           0 :       NS_WARNING("failed to save prefs file! possible data loss");
    2768             :     }
    2769             : #endif
    2770             : 
    2771             :     return rv;
    2772             :   }
    2773             : 
    2774           0 :   static void Flush()
    2775             :   {
    2776             :     // This can be further optimized; instead of waiting for all of the writer
    2777             :     // thread to be available, we just have to wait for all the pending writes
    2778             :     // to be done.
    2779           0 :     if (!sPendingWriteData.compareExchange(nullptr, nullptr)) {
    2780           0 :       nsresult rv = NS_OK;
    2781             :       nsCOMPtr<nsIEventTarget> target =
    2782           0 :         do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
    2783           0 :       if (NS_SUCCEEDED(rv)) {
    2784           0 :         target->Dispatch(NS_NewRunnableFunction("Preferences_dummy", [] {}),
    2785           0 :                          nsIEventTarget::DISPATCH_SYNC);
    2786             :       }
    2787             :     }
    2788           0 :   }
    2789             : 
    2790             :   // This is the data that all of the runnables (see below) will attempt
    2791             :   // to write.  It will always have the most up to date version, or be
    2792             :   // null, if the up to date information has already been written out.
    2793             :   static Atomic<PrefSaveData*> sPendingWriteData;
    2794             : };
    2795             : 
    2796             : Atomic<PrefSaveData*> PreferencesWriter::sPendingWriteData(nullptr);
    2797             : 
    2798           0 : class PWRunnable : public Runnable
    2799             : {
    2800             : public:
    2801           0 :   explicit PWRunnable(nsIFile* aFile)
    2802           0 :     : Runnable("PWRunnable")
    2803           0 :     , mFile(aFile)
    2804             :   {
    2805           0 :   }
    2806             : 
    2807           0 :   NS_IMETHOD Run() override
    2808             :   {
    2809             :     // If we get a nullptr on the exchange, it means that somebody
    2810             :     // else has already processed the request, and we can just return.
    2811             :     mozilla::UniquePtr<PrefSaveData> prefs(
    2812           0 :       PreferencesWriter::sPendingWriteData.exchange(nullptr));
    2813           0 :     nsresult rv = NS_OK;
    2814           0 :     if (prefs) {
    2815           0 :       rv = PreferencesWriter::Write(mFile, *prefs);
    2816             : 
    2817             :       // Make a copy of these so we can have them in runnable lambda.
    2818             :       // nsIFile is only there so that we would never release the
    2819             :       // ref counted pointer off main thread.
    2820           0 :       nsresult rvCopy = rv;
    2821           0 :       nsCOMPtr<nsIFile> fileCopy(mFile);
    2822           0 :       SystemGroup::Dispatch(
    2823             :         TaskCategory::Other,
    2824           0 :         NS_NewRunnableFunction("Preferences::WriterRunnable",
    2825           0 :                                [fileCopy, rvCopy] {
    2826           0 :                                  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    2827           0 :                                  if (NS_FAILED(rvCopy)) {
    2828           0 :                                    Preferences::HandleDirty();
    2829             :                                  }
    2830           0 :                                }));
    2831             :     }
    2832           0 :     return rv;
    2833             :   }
    2834             : 
    2835             : protected:
    2836             :   nsCOMPtr<nsIFile> mFile;
    2837             : };
    2838             : 
    2839             : struct CacheData
    2840             : {
    2841             :   void* mCacheLocation;
    2842             :   union {
    2843             :     bool mDefaultValueBool;
    2844             :     int32_t mDefaultValueInt;
    2845             :     uint32_t mDefaultValueUint;
    2846             :     float mDefaultValueFloat;
    2847             :   };
    2848             : };
    2849             : 
    2850             : // gCacheDataDesc holds information about prefs startup. It's being used for
    2851             : // diagnosing prefs startup problems in bug 1276488.
    2852             : static const char* gCacheDataDesc = "untouched";
    2853             : 
    2854             : // gCacheData holds the CacheData objects used for VarCache prefs. It owns
    2855             : // those objects, and also is used to detect if multiple VarCaches get tied to
    2856             : // a single global variable.
    2857             : static nsTArray<nsAutoPtr<CacheData>>* gCacheData = nullptr;
    2858             : 
    2859             : #ifdef DEBUG
    2860             : static bool
    2861           0 : HaveExistingCacheFor(void* aPtr)
    2862             : {
    2863           0 :   MOZ_ASSERT(NS_IsMainThread());
    2864           0 :   if (gCacheData) {
    2865           0 :     for (size_t i = 0, count = gCacheData->Length(); i < count; ++i) {
    2866           0 :       if ((*gCacheData)[i]->mCacheLocation == aPtr) {
    2867             :         return true;
    2868             :       }
    2869             :     }
    2870             :   }
    2871             :   return false;
    2872             : }
    2873             : #endif
    2874             : 
    2875             : static void
    2876           0 : AssertNotAlreadyCached(const char* aPrefType, const char* aPref, void* aPtr)
    2877             : {
    2878             : #ifdef DEBUG
    2879           0 :   MOZ_ASSERT(aPtr);
    2880           0 :   if (HaveExistingCacheFor(aPtr)) {
    2881             :     fprintf_stderr(
    2882             :       stderr,
    2883             :       "Attempt to add a %s pref cache for preference '%s' at address '%p'"
    2884             :       "was made. However, a pref was already cached at this address.\n",
    2885             :       aPrefType,
    2886             :       aPref,
    2887           0 :       aPtr);
    2888           0 :     MOZ_ASSERT(false,
    2889             :                "Should not have an existing pref cache for this address");
    2890             :   }
    2891             : #endif
    2892           0 : }
    2893             : 
    2894             : // Although this is a member of Preferences, it measures sPreferences and
    2895             : // several other global structures.
    2896             : /* static */ void
    2897           0 : Preferences::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
    2898             :                                     PrefsSizes& aSizes)
    2899             : {
    2900           0 :   if (!sPreferences) {
    2901             :     return;
    2902             :   }
    2903             : 
    2904           0 :   aSizes.mMisc += aMallocSizeOf(sPreferences.get());
    2905             : 
    2906           0 :   aSizes.mRootBranches +=
    2907           0 :     static_cast<nsPrefBranch*>(sPreferences->mRootBranch.get())
    2908           0 :       ->SizeOfIncludingThis(aMallocSizeOf) +
    2909           0 :     static_cast<nsPrefBranch*>(sPreferences->mDefaultRootBranch.get())
    2910           0 :       ->SizeOfIncludingThis(aMallocSizeOf);
    2911             : }
    2912             : 
    2913           0 : class PreferenceServiceReporter final : public nsIMemoryReporter
    2914             : {
    2915           0 :   ~PreferenceServiceReporter() {}
    2916             : 
    2917             : public:
    2918             :   NS_DECL_ISUPPORTS
    2919             :   NS_DECL_NSIMEMORYREPORTER
    2920             : 
    2921             : protected:
    2922             :   static const uint32_t kSuspectReferentCount = 1000;
    2923             : };
    2924             : 
    2925           0 : NS_IMPL_ISUPPORTS(PreferenceServiceReporter, nsIMemoryReporter)
    2926             : 
    2927           0 : MOZ_DEFINE_MALLOC_SIZE_OF(PreferenceServiceMallocSizeOf)
    2928             : 
    2929             : NS_IMETHODIMP
    2930           0 : PreferenceServiceReporter::CollectReports(
    2931             :   nsIHandleReportCallback* aHandleReport,
    2932             :   nsISupports* aData,
    2933             :   bool aAnonymize)
    2934             : {
    2935           0 :   MOZ_ASSERT(NS_IsMainThread());
    2936             : 
    2937           0 :   MallocSizeOf mallocSizeOf = PreferenceServiceMallocSizeOf;
    2938           0 :   PrefsSizes sizes;
    2939             : 
    2940           0 :   Preferences::AddSizeOfIncludingThis(mallocSizeOf, sizes);
    2941             : 
    2942           0 :   if (gHashTable) {
    2943           0 :     sizes.mHashTable += gHashTable->ShallowSizeOfIncludingThis(mallocSizeOf);
    2944           0 :     for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
    2945           0 :       Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
    2946           0 :       pref->AddSizeOfIncludingThis(mallocSizeOf, sizes);
    2947             :     }
    2948             :   }
    2949             : 
    2950           0 :   if (gCacheData) {
    2951           0 :     sizes.mCacheData += gCacheData->ShallowSizeOfIncludingThis(mallocSizeOf);
    2952           0 :     for (uint32_t i = 0, count = gCacheData->Length(); i < count; ++i) {
    2953           0 :       sizes.mCacheData += mallocSizeOf((*gCacheData)[i]);
    2954             :     }
    2955             :   }
    2956             : 
    2957           0 :   sizes.mPrefNameArena += gPrefNameArena.SizeOfExcludingThis(mallocSizeOf);
    2958             : 
    2959           0 :   for (CallbackNode* node = gFirstCallback; node; node = node->Next()) {
    2960           0 :     node->AddSizeOfIncludingThis(mallocSizeOf, sizes);
    2961             :   }
    2962             : 
    2963           0 :   MOZ_COLLECT_REPORT("explicit/preferences/hash-table",
    2964             :                      KIND_HEAP,
    2965             :                      UNITS_BYTES,
    2966             :                      sizes.mHashTable,
    2967           0 :                      "Memory used by libpref's hash table.");
    2968             : 
    2969           0 :   MOZ_COLLECT_REPORT("explicit/preferences/pref-values",
    2970             :                      KIND_HEAP,
    2971             :                      UNITS_BYTES,
    2972             :                      sizes.mPrefValues,
    2973           0 :                      "Memory used by PrefValues hanging off the hash table.");
    2974             : 
    2975           0 :   MOZ_COLLECT_REPORT("explicit/preferences/string-values",
    2976             :                      KIND_HEAP,
    2977             :                      UNITS_BYTES,
    2978             :                      sizes.mStringValues,
    2979           0 :                      "Memory used by libpref's string pref values.");
    2980             : 
    2981           0 :   MOZ_COLLECT_REPORT("explicit/preferences/cache-data",
    2982             :                      KIND_HEAP,
    2983             :                      UNITS_BYTES,
    2984             :                      sizes.mCacheData,
    2985           0 :                      "Memory used by libpref's VarCaches.");
    2986             : 
    2987           0 :   MOZ_COLLECT_REPORT("explicit/preferences/root-branches",
    2988             :                      KIND_HEAP,
    2989             :                      UNITS_BYTES,
    2990             :                      sizes.mRootBranches,
    2991           0 :                      "Memory used by libpref's root branches.");
    2992             : 
    2993           0 :   MOZ_COLLECT_REPORT("explicit/preferences/pref-name-arena",
    2994             :                      KIND_HEAP,
    2995             :                      UNITS_BYTES,
    2996             :                      sizes.mPrefNameArena,
    2997           0 :                      "Memory used by libpref's arena for pref names.");
    2998             : 
    2999           0 :   MOZ_COLLECT_REPORT("explicit/preferences/callbacks/objects",
    3000             :                      KIND_HEAP,
    3001             :                      UNITS_BYTES,
    3002             :                      sizes.mCallbacksObjects,
    3003           0 :                      "Memory used by pref callback objects.");
    3004             : 
    3005           0 :   MOZ_COLLECT_REPORT("explicit/preferences/callbacks/domains",
    3006             :                      KIND_HEAP,
    3007             :                      UNITS_BYTES,
    3008             :                      sizes.mCallbacksDomains,
    3009             :                      "Memory used by pref callback domains (pref names and "
    3010           0 :                      "prefixes).");
    3011             : 
    3012           0 :   MOZ_COLLECT_REPORT("explicit/preferences/misc",
    3013             :                      KIND_HEAP,
    3014             :                      UNITS_BYTES,
    3015             :                      sizes.mMisc,
    3016           0 :                      "Miscellaneous memory used by libpref.");
    3017             : 
    3018             :   nsPrefBranch* rootBranch =
    3019           0 :     static_cast<nsPrefBranch*>(Preferences::GetRootBranch());
    3020           0 :   if (!rootBranch) {
    3021             :     return NS_OK;
    3022             :   }
    3023             : 
    3024           0 :   size_t numStrong = 0;
    3025           0 :   size_t numWeakAlive = 0;
    3026           0 :   size_t numWeakDead = 0;
    3027           0 :   nsTArray<nsCString> suspectPreferences;
    3028             :   // Count of the number of referents for each preference.
    3029           0 :   nsDataHashtable<nsCStringHashKey, uint32_t> prefCounter;
    3030             : 
    3031           0 :   for (auto iter = rootBranch->mObservers.Iter(); !iter.Done(); iter.Next()) {
    3032           0 :     nsAutoPtr<PrefCallback>& callback = iter.Data();
    3033           0 :     nsPrefBranch* prefBranch = callback->GetPrefBranch();
    3034           0 :     const auto& pref = prefBranch->GetPrefName(callback->GetDomain().get());
    3035             : 
    3036           0 :     if (callback->IsWeak()) {
    3037           0 :       nsCOMPtr<nsIObserver> callbackRef = do_QueryReferent(callback->mWeakRef);
    3038           0 :       if (callbackRef) {
    3039           0 :         numWeakAlive++;
    3040             :       } else {
    3041           0 :         numWeakDead++;
    3042             :       }
    3043             :     } else {
    3044           0 :       numStrong++;
    3045             :     }
    3046             : 
    3047           0 :     nsDependentCString prefString(pref.get());
    3048           0 :     uint32_t oldCount = 0;
    3049           0 :     prefCounter.Get(prefString, &oldCount);
    3050           0 :     uint32_t currentCount = oldCount + 1;
    3051           0 :     prefCounter.Put(prefString, currentCount);
    3052             : 
    3053             :     // Keep track of preferences that have a suspiciously large number of
    3054             :     // referents (a symptom of a leak).
    3055           0 :     if (currentCount == kSuspectReferentCount) {
    3056           0 :       suspectPreferences.AppendElement(prefString);
    3057             :     }
    3058             :   }
    3059             : 
    3060           0 :   for (uint32_t i = 0; i < suspectPreferences.Length(); i++) {
    3061           0 :     nsCString& suspect = suspectPreferences[i];
    3062           0 :     uint32_t totalReferentCount = 0;
    3063           0 :     prefCounter.Get(suspect, &totalReferentCount);
    3064             : 
    3065             :     nsPrintfCString suspectPath("preference-service-suspect/"
    3066             :                                 "referent(pref=%s)",
    3067           0 :                                 suspect.get());
    3068             : 
    3069           0 :     aHandleReport->Callback(
    3070           0 :       /* process = */ EmptyCString(),
    3071             :       suspectPath,
    3072             :       KIND_OTHER,
    3073             :       UNITS_COUNT,
    3074             :       totalReferentCount,
    3075           0 :       NS_LITERAL_CSTRING(
    3076             :         "A preference with a suspiciously large number referents (symptom of a "
    3077             :         "leak)."),
    3078           0 :       aData);
    3079             :   }
    3080             : 
    3081           0 :   MOZ_COLLECT_REPORT(
    3082             :     "preference-service/referent/strong",
    3083             :     KIND_OTHER,
    3084             :     UNITS_COUNT,
    3085             :     numStrong,
    3086           0 :     "The number of strong referents held by the preference service.");
    3087             : 
    3088           0 :   MOZ_COLLECT_REPORT(
    3089             :     "preference-service/referent/weak/alive",
    3090             :     KIND_OTHER,
    3091             :     UNITS_COUNT,
    3092             :     numWeakAlive,
    3093             :     "The number of weak referents held by the preference service that are "
    3094           0 :     "still alive.");
    3095             : 
    3096           0 :   MOZ_COLLECT_REPORT(
    3097             :     "preference-service/referent/weak/dead",
    3098             :     KIND_OTHER,
    3099             :     UNITS_COUNT,
    3100             :     numWeakDead,
    3101             :     "The number of weak referents held by the preference service that are "
    3102           0 :     "dead.");
    3103             : 
    3104           0 :   return NS_OK;
    3105             : }
    3106             : 
    3107             : namespace {
    3108             : 
    3109           0 : class AddPreferencesMemoryReporterRunnable : public Runnable
    3110             : {
    3111             : public:
    3112           0 :   AddPreferencesMemoryReporterRunnable()
    3113           0 :     : Runnable("AddPreferencesMemoryReporterRunnable")
    3114             :   {
    3115           0 :   }
    3116             : 
    3117           0 :   NS_IMETHOD Run() override
    3118             :   {
    3119           0 :     return RegisterStrongMemoryReporter(new PreferenceServiceReporter());
    3120             :   }
    3121             : };
    3122             : 
    3123             : } // namespace
    3124             : 
    3125             : // A list of changed prefs sent from the parent via shared memory.
    3126             : static InfallibleTArray<dom::Pref>* gChangedDomPrefs;
    3127             : 
    3128             : static const char kTelemetryPref[] = "toolkit.telemetry.enabled";
    3129             : static const char kChannelPref[] = "app.update.channel";
    3130             : 
    3131             : #ifdef MOZ_WIDGET_ANDROID
    3132             : 
    3133             : static Maybe<bool>
    3134             : TelemetryPrefValue()
    3135             : {
    3136             :   // Leave it unchanged if it's already set.
    3137             :   // XXX: how could it already be set?
    3138             :   if (Preferences::GetType(kTelemetryPref) != nsIPrefBranch::PREF_INVALID) {
    3139             :     return Nothing();
    3140             :   }
    3141             : 
    3142             :     // Determine the correct default for toolkit.telemetry.enabled. If this
    3143             :     // build has MOZ_TELEMETRY_ON_BY_DEFAULT *or* we're on the beta channel,
    3144             :     // telemetry is on by default, otherwise not. This is necessary so that
    3145             :     // beta users who are testing final release builds don't flipflop defaults.
    3146             : #ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
    3147             :   return Some(true);
    3148             : #else
    3149             :   nsAutoCString channelPrefValue;
    3150             :   Unused << Preferences::GetCString(
    3151             :     kChannelPref, channelPrefValue, PrefValueKind::Default);
    3152             :   return Some(channelPrefValue.EqualsLiteral("beta"));
    3153             : #endif
    3154             : }
    3155             : 
    3156             : /* static */ void
    3157             : Preferences::SetupTelemetryPref()
    3158             : {
    3159             :   MOZ_ASSERT(XRE_IsParentProcess());
    3160             : 
    3161             :   Maybe<bool> telemetryPrefValue = TelemetryPrefValue();
    3162             :   if (telemetryPrefValue.isSome()) {
    3163             :     Preferences::SetBool(
    3164             :       kTelemetryPref, *telemetryPrefValue, PrefValueKind::Default);
    3165             :   }
    3166             : }
    3167             : 
    3168             : #else // !MOZ_WIDGET_ANDROID
    3169             : 
    3170             : static bool
    3171           0 : TelemetryPrefValue()
    3172             : {
    3173             :   // For platforms with Unified Telemetry (here meaning not-Android),
    3174             :   // toolkit.telemetry.enabled determines whether we send "extended" data.
    3175             :   // We only want extended data from pre-release channels due to size.
    3176             : 
    3177           0 :   NS_NAMED_LITERAL_CSTRING(channel, NS_STRINGIFY(MOZ_UPDATE_CHANNEL));
    3178             : 
    3179             :   // Easy cases: Nightly, Aurora, Beta.
    3180           0 :   if (channel.EqualsLiteral("nightly") || channel.EqualsLiteral("aurora") ||
    3181           0 :       channel.EqualsLiteral("beta")) {
    3182             :     return true;
    3183             :   }
    3184             : 
    3185             : #ifndef MOZILLA_OFFICIAL
    3186             :   // Local developer builds: non-official builds on the "default" channel.
    3187             :   if (channel.EqualsLiteral("default")) {
    3188             :     return true;
    3189             :   }
    3190             : #endif
    3191             : 
    3192             :   // Release Candidate builds: builds that think they are release builds, but
    3193             :   // are shipped to beta users.
    3194           0 :   if (channel.EqualsLiteral("release")) {
    3195           0 :     nsAutoCString channelPrefValue;
    3196           0 :     Unused << Preferences::GetCString(
    3197             :       kChannelPref, channelPrefValue, PrefValueKind::Default);
    3198           0 :     if (channelPrefValue.EqualsLiteral("beta")) {
    3199           0 :       return true;
    3200             :     }
    3201             :   }
    3202             : 
    3203             :   return false;
    3204             : }
    3205             : 
    3206             : /* static */ void
    3207           0 : Preferences::SetupTelemetryPref()
    3208             : {
    3209           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    3210             : 
    3211           0 :   Preferences::SetBool(
    3212           0 :     kTelemetryPref, TelemetryPrefValue(), PrefValueKind::Default);
    3213           0 :   Preferences::Lock(kTelemetryPref);
    3214           0 : }
    3215             : 
    3216             : static void
    3217           0 : CheckTelemetryPref()
    3218             : {
    3219           0 :   MOZ_ASSERT(!XRE_IsParentProcess());
    3220             : 
    3221             :   // Make sure the children got passed the right telemetry pref details.
    3222           0 :   DebugOnly<bool> value;
    3223           0 :   MOZ_ASSERT(NS_SUCCEEDED(Preferences::GetBool(kTelemetryPref, &value)) &&
    3224             :              value == TelemetryPrefValue());
    3225           0 :   MOZ_ASSERT(Preferences::IsLocked(kTelemetryPref));
    3226           0 : }
    3227             : 
    3228             : #endif // MOZ_WIDGET_ANDROID
    3229             : 
    3230             : /* static */ already_AddRefed<Preferences>
    3231           0 : Preferences::GetInstanceForService()
    3232             : {
    3233           0 :   if (sPreferences) {
    3234           0 :     return do_AddRef(sPreferences);
    3235             :   }
    3236             : 
    3237           0 :   if (sShutdown) {
    3238           0 :     gCacheDataDesc = "shutting down in GetInstanceForService()";
    3239             :     return nullptr;
    3240             :   }
    3241             : 
    3242           0 :   sPreferences = new Preferences();
    3243             : 
    3244           0 :   MOZ_ASSERT(!gHashTable);
    3245           0 :   gHashTable = new PLDHashTable(
    3246           0 :     &pref_HashTableOps, sizeof(PrefEntry), PREF_HASHTABLE_INITIAL_LENGTH);
    3247             : 
    3248           0 :   gTelemetryLoadData =
    3249           0 :     new nsDataHashtable<nsCStringHashKey, TelemetryLoadData>();
    3250             : 
    3251           0 :   gCacheData = new nsTArray<nsAutoPtr<CacheData>>();
    3252           0 :   gCacheDataDesc = "set by GetInstanceForService() (1)";
    3253             : 
    3254           0 :   Result<Ok, const char*> res = InitInitialObjects(/* isStartup */ true);
    3255           0 :   if (res.isErr()) {
    3256           0 :     sPreferences = nullptr;
    3257           0 :     gCacheDataDesc = res.unwrapErr();
    3258             :     return nullptr;
    3259             :   }
    3260             : 
    3261           0 :   if (!XRE_IsParentProcess()) {
    3262           0 :     MOZ_ASSERT(gChangedDomPrefs);
    3263           0 :     for (unsigned int i = 0; i < gChangedDomPrefs->Length(); i++) {
    3264           0 :       Preferences::SetPreference(gChangedDomPrefs->ElementAt(i));
    3265             :     }
    3266           0 :     delete gChangedDomPrefs;
    3267           0 :     gChangedDomPrefs = nullptr;
    3268             : 
    3269             : #ifndef MOZ_WIDGET_ANDROID
    3270           0 :     CheckTelemetryPref();
    3271             : #endif
    3272             : 
    3273             :   } else {
    3274             :     // Check if there is a deployment configuration file. If so, set up the
    3275             :     // pref config machinery, which will actually read the file.
    3276           0 :     nsAutoCString lockFileName;
    3277             :     nsresult rv = Preferences::GetCString(
    3278           0 :       "general.config.filename", lockFileName, PrefValueKind::User);
    3279           0 :     if (NS_SUCCEEDED(rv)) {
    3280             :       NS_CreateServicesFromCategory(
    3281             :         "pref-config-startup",
    3282           0 :         static_cast<nsISupports*>(static_cast<void*>(sPreferences)),
    3283           0 :         "pref-config-startup");
    3284             :     }
    3285             : 
    3286             :     nsCOMPtr<nsIObserverService> observerService =
    3287           0 :       mozilla::services::GetObserverService();
    3288           0 :     if (!observerService) {
    3289           0 :       sPreferences = nullptr;
    3290           0 :       gCacheDataDesc = "GetObserverService() failed (1)";
    3291           0 :       return nullptr;
    3292             :     }
    3293             : 
    3294           0 :     observerService->AddObserver(
    3295           0 :       sPreferences, "profile-before-change-telemetry", true);
    3296             :     rv =
    3297           0 :       observerService->AddObserver(sPreferences, "profile-before-change", true);
    3298             : 
    3299           0 :     observerService->AddObserver(
    3300           0 :       sPreferences, "suspend_process_notification", true);
    3301             : 
    3302           0 :     if (NS_FAILED(rv)) {
    3303           0 :       sPreferences = nullptr;
    3304           0 :       gCacheDataDesc = "AddObserver(\"profile-before-change\") failed";
    3305             :       return nullptr;
    3306             :     }
    3307             :   }
    3308             : 
    3309           0 :   gCacheDataDesc = "set by GetInstanceForService() (2)";
    3310             : 
    3311             :   // Preferences::GetInstanceForService() can be called from GetService(), and
    3312             :   // RegisterStrongMemoryReporter calls GetService(nsIMemoryReporter).  To
    3313             :   // avoid a potential recursive GetService() call, we can't register the
    3314             :   // memory reporter here; instead, do it off a runnable.
    3315             :   RefPtr<AddPreferencesMemoryReporterRunnable> runnable =
    3316           0 :     new AddPreferencesMemoryReporterRunnable();
    3317           0 :   NS_DispatchToMainThread(runnable);
    3318             : 
    3319           0 :   return do_AddRef(sPreferences);
    3320             : }
    3321             : 
    3322             : /* static */ bool
    3323           0 : Preferences::IsServiceAvailable()
    3324             : {
    3325           0 :   return !!sPreferences;
    3326             : }
    3327             : 
    3328             : /* static */ bool
    3329           0 : Preferences::InitStaticMembers()
    3330             : {
    3331           0 :   MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
    3332             : 
    3333           0 :   if (MOZ_LIKELY(sPreferences)) {
    3334             :     return true;
    3335             :   }
    3336             : 
    3337           0 :   if (!sShutdown) {
    3338           0 :     MOZ_ASSERT(NS_IsMainThread());
    3339             :     nsCOMPtr<nsIPrefService> prefService =
    3340           0 :       do_GetService(NS_PREFSERVICE_CONTRACTID);
    3341             :   }
    3342             : 
    3343           0 :   return sPreferences != nullptr;
    3344             : }
    3345             : 
    3346             : /* static */ void
    3347           0 : Preferences::Shutdown()
    3348             : {
    3349           0 :   if (!sShutdown) {
    3350           0 :     sShutdown = true; // Don't create the singleton instance after here.
    3351             :     sPreferences = nullptr;
    3352             :   }
    3353           0 : }
    3354             : 
    3355           0 : Preferences::Preferences()
    3356           0 :   : mRootBranch(new nsPrefBranch("", PrefValueKind::User))
    3357           0 :   , mDefaultRootBranch(new nsPrefBranch("", PrefValueKind::Default))
    3358             : {
    3359           0 : }
    3360             : 
    3361           0 : Preferences::~Preferences()
    3362             : {
    3363           0 :   MOZ_ASSERT(!sPreferences);
    3364             : 
    3365           0 :   delete gCacheData;
    3366           0 :   gCacheData = nullptr;
    3367             : 
    3368           0 :   MOZ_ASSERT(!gCallbacksInProgress);
    3369             : 
    3370           0 :   CallbackNode* node = gFirstCallback;
    3371           0 :   while (node) {
    3372           0 :     CallbackNode* next_node = node->Next();
    3373           0 :     delete node;
    3374           0 :     node = next_node;
    3375             :   }
    3376           0 :   gLastPriorityNode = gFirstCallback = nullptr;
    3377             : 
    3378           0 :   delete gHashTable;
    3379           0 :   gHashTable = nullptr;
    3380             : 
    3381           0 :   delete gTelemetryLoadData;
    3382           0 :   gTelemetryLoadData = nullptr;
    3383             : 
    3384           0 :   gPrefNameArena.Clear();
    3385           0 : }
    3386             : 
    3387           0 : NS_IMPL_ISUPPORTS(Preferences,
    3388             :                   nsIPrefService,
    3389             :                   nsIObserver,
    3390             :                   nsIPrefBranch,
    3391             :                   nsISupportsWeakReference)
    3392             : 
    3393             : /* static */ void
    3394           0 : Preferences::SerializePreferences(nsCString& aStr)
    3395             : {
    3396           0 :   MOZ_RELEASE_ASSERT(InitStaticMembers());
    3397             : 
    3398           0 :   aStr.Truncate();
    3399             : 
    3400           0 :   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
    3401           0 :     Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
    3402           0 :     if (pref->MustSendToContentProcesses() && pref->HasAdvisablySizedValues()) {
    3403           0 :       pref->SerializeAndAppend(aStr);
    3404             :     }
    3405             :   }
    3406             : 
    3407           0 :   aStr.Append('\0');
    3408           0 : }
    3409             : 
    3410             : /* static */ void
    3411           0 : Preferences::DeserializePreferences(char* aStr, size_t aPrefsLen)
    3412             : {
    3413           0 :   MOZ_ASSERT(!XRE_IsParentProcess());
    3414             : 
    3415           0 :   MOZ_ASSERT(!gChangedDomPrefs);
    3416           0 :   gChangedDomPrefs = new InfallibleTArray<dom::Pref>();
    3417             : 
    3418           0 :   char* p = aStr;
    3419           0 :   while (*p != '\0') {
    3420           0 :     dom::Pref pref;
    3421           0 :     p = Pref::Deserialize(p, &pref);
    3422           0 :     gChangedDomPrefs->AppendElement(pref);
    3423             :   }
    3424             : 
    3425             :   // We finished parsing on a '\0'. That should be the last char in the shared
    3426             :   // memory. (aPrefsLen includes the '\0'.)
    3427           0 :   MOZ_ASSERT(p == aStr + aPrefsLen - 1);
    3428             : 
    3429             : #ifdef DEBUG
    3430           0 :   MOZ_ASSERT(!gContentProcessPrefsAreInited);
    3431           0 :   gContentProcessPrefsAreInited = true;
    3432             : #endif
    3433           0 : }
    3434             : 
    3435             : /* static */ void
    3436           0 : Preferences::InitializeUserPrefs()
    3437             : {
    3438           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    3439           0 :   MOZ_ASSERT(!sPreferences->mCurrentFile, "Should only initialize prefs once");
    3440             : 
    3441             :   // Prefs which are set before we initialize the profile are silently
    3442             :   // discarded. This is stupid, but there are various tests which depend on
    3443             :   // this behavior.
    3444           0 :   sPreferences->ResetUserPrefs();
    3445             : 
    3446           0 :   nsCOMPtr<nsIFile> prefsFile = sPreferences->ReadSavedPrefs();
    3447           0 :   sPreferences->ReadUserOverridePrefs();
    3448             : 
    3449           0 :   sPreferences->mDirty = false;
    3450             : 
    3451             :   // Don't set mCurrentFile until we're done so that dirty flags work properly.
    3452           0 :   sPreferences->mCurrentFile = prefsFile.forget();
    3453             : 
    3454           0 :   sPreferences->NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID);
    3455             : 
    3456             :   // At this point all the prefs files have been read and telemetry has been
    3457             :   // initialized. Send all the file load measurements to telemetry.
    3458           0 :   SendTelemetryLoadData();
    3459           0 : }
    3460             : 
    3461             : NS_IMETHODIMP
    3462           0 : Preferences::Observe(nsISupports* aSubject,
    3463             :                      const char* aTopic,
    3464             :                      const char16_t* someData)
    3465             : {
    3466           0 :   if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
    3467             :     return NS_ERROR_NOT_AVAILABLE;
    3468             :   }
    3469             : 
    3470           0 :   nsresult rv = NS_OK;
    3471             : 
    3472           0 :   if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
    3473             :     // Normally prefs aren't written after this point, and so we kick off
    3474             :     // an asynchronous pref save so that I/O can be done in parallel with
    3475             :     // other shutdown.
    3476           0 :     if (AllowOffMainThreadSave()) {
    3477           0 :       SavePrefFile(nullptr);
    3478             :     }
    3479             : 
    3480           0 :   } else if (!nsCRT::strcmp(aTopic, "profile-before-change-telemetry")) {
    3481             :     // It's possible that a profile-before-change observer after ours
    3482             :     // set a pref. A blocking save here re-saves if necessary and also waits
    3483             :     // for any pending saves to complete.
    3484           0 :     SavePrefFileBlocking();
    3485           0 :     MOZ_ASSERT(!mDirty, "Preferences should not be dirty");
    3486           0 :     mProfileShutdown = true;
    3487             : 
    3488           0 :   } else if (!nsCRT::strcmp(aTopic, "reload-default-prefs")) {
    3489             :     // Reload the default prefs from file.
    3490           0 :     Unused << InitInitialObjects(/* isStartup */ false);
    3491             : 
    3492           0 :   } else if (!nsCRT::strcmp(aTopic, "suspend_process_notification")) {
    3493             :     // Our process is being suspended. The OS may wake our process later,
    3494             :     // or it may kill the process. In case our process is going to be killed
    3495             :     // from the suspended state, we save preferences before suspending.
    3496           0 :     rv = SavePrefFileBlocking();
    3497             :   }
    3498             : 
    3499             :   return rv;
    3500             : }
    3501             : 
    3502             : NS_IMETHODIMP
    3503           0 : Preferences::ReadDefaultPrefsFromFile(nsIFile* aFile)
    3504             : {
    3505           0 :   ENSURE_PARENT_PROCESS("Preferences::ReadDefaultPrefsFromFile", "all prefs");
    3506             : 
    3507           0 :   if (!aFile) {
    3508           0 :     NS_ERROR("ReadDefaultPrefsFromFile requires a parameter");
    3509           0 :     return NS_ERROR_INVALID_ARG;
    3510             :   }
    3511             : 
    3512           0 :   return openPrefFile(aFile, PrefValueKind::Default);
    3513             : }
    3514             : 
    3515             : NS_IMETHODIMP
    3516           0 : Preferences::ReadUserPrefsFromFile(nsIFile* aFile)
    3517             : {
    3518           0 :   ENSURE_PARENT_PROCESS("Preferences::ReadUserPrefsFromFile", "all prefs");
    3519             : 
    3520           0 :   if (!aFile) {
    3521           0 :     NS_ERROR("ReadUserPrefsFromFile requires a parameter");
    3522           0 :     return NS_ERROR_INVALID_ARG;
    3523             :   }
    3524             : 
    3525           0 :   return openPrefFile(aFile, PrefValueKind::User);
    3526             : }
    3527             : 
    3528             : NS_IMETHODIMP
    3529           0 : Preferences::ResetPrefs()
    3530             : {
    3531           0 :   ENSURE_PARENT_PROCESS("Preferences::ResetPrefs", "all prefs");
    3532             : 
    3533           0 :   gHashTable->ClearAndPrepareForLength(PREF_HASHTABLE_INITIAL_LENGTH);
    3534           0 :   gPrefNameArena.Clear();
    3535             : 
    3536           0 :   return InitInitialObjects(/* isStartup */ false).isOk() ? NS_OK
    3537           0 :                                                           : NS_ERROR_FAILURE;
    3538             : }
    3539             : 
    3540             : NS_IMETHODIMP
    3541           0 : Preferences::ResetUserPrefs()
    3542             : {
    3543           0 :   ENSURE_PARENT_PROCESS("Preferences::ResetUserPrefs", "all prefs");
    3544           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    3545           0 :   MOZ_ASSERT(NS_IsMainThread());
    3546             : 
    3547           0 :   Vector<const char*> prefNames;
    3548           0 :   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
    3549           0 :     Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
    3550             : 
    3551           0 :     if (pref->HasUserValue()) {
    3552           0 :       if (!prefNames.append(pref->Name())) {
    3553           0 :         return NS_ERROR_OUT_OF_MEMORY;
    3554             :       }
    3555             : 
    3556           0 :       pref->ClearUserValue();
    3557           0 :       if (!pref->HasDefaultValue()) {
    3558           0 :         iter.Remove();
    3559             :       }
    3560             :     }
    3561             :   }
    3562             : 
    3563           0 :   for (const char* prefName : prefNames) {
    3564           0 :     NotifyCallbacks(prefName);
    3565             :   }
    3566             : 
    3567           0 :   Preferences::HandleDirty();
    3568           0 :   return NS_OK;
    3569             : }
    3570             : 
    3571             : bool
    3572           0 : Preferences::AllowOffMainThreadSave()
    3573             : {
    3574             :   // Put in a preference that allows us to disable off main thread preference
    3575             :   // file save.
    3576           0 :   if (sAllowOMTPrefWrite < 0) {
    3577           0 :     bool value = false;
    3578           0 :     Preferences::GetBool("preferences.allow.omt-write", &value);
    3579           0 :     sAllowOMTPrefWrite = value ? 1 : 0;
    3580             :   }
    3581             : 
    3582           0 :   return !!sAllowOMTPrefWrite;
    3583             : }
    3584             : 
    3585             : nsresult
    3586           0 : Preferences::SavePrefFileBlocking()
    3587             : {
    3588           0 :   if (mDirty) {
    3589           0 :     return SavePrefFileInternal(nullptr, SaveMethod::Blocking);
    3590             :   }
    3591             : 
    3592             :   // If we weren't dirty to start, SavePrefFileInternal will early exit so
    3593             :   // there is no guarantee that we don't have oustanding async saves in the
    3594             :   // pipe. Since the contract of SavePrefFileOnMainThread is that the file on
    3595             :   // disk matches the preferences, we have to make sure those requests are
    3596             :   // completed.
    3597             : 
    3598           0 :   if (AllowOffMainThreadSave()) {
    3599           0 :     PreferencesWriter::Flush();
    3600             :   }
    3601             : 
    3602             :   return NS_OK;
    3603             : }
    3604             : 
    3605             : nsresult
    3606           0 : Preferences::SavePrefFileAsynchronous()
    3607             : {
    3608           0 :   return SavePrefFileInternal(nullptr, SaveMethod::Asynchronous);
    3609             : }
    3610             : 
    3611             : NS_IMETHODIMP
    3612           0 : Preferences::SavePrefFile(nsIFile* aFile)
    3613             : {
    3614             :   // This is the method accessible from service API. Make it off main thread.
    3615           0 :   return SavePrefFileInternal(aFile, SaveMethod::Asynchronous);
    3616             : }
    3617             : 
    3618             : /* static */ void
    3619           0 : Preferences::SetPreference(const dom::Pref& aDomPref)
    3620             : {
    3621           0 :   MOZ_ASSERT(!XRE_IsParentProcess());
    3622           0 :   NS_ENSURE_TRUE(InitStaticMembers(), (void)0);
    3623             : 
    3624           0 :   const char* prefName = aDomPref.name().get();
    3625             : 
    3626           0 :   auto entry = static_cast<PrefEntry*>(gHashTable->Add(prefName, fallible));
    3627           0 :   if (!entry) {
    3628             :     return;
    3629             :   }
    3630             : 
    3631           0 :   Pref* pref = entry->mPref;
    3632             : 
    3633           0 :   bool valueChanged = false;
    3634           0 :   pref->FromDomPref(aDomPref, &valueChanged);
    3635             : 
    3636             :   // When the parent process clears a pref's user value we get a DomPref here
    3637             :   // with no default value and no user value. There are two possibilities.
    3638             :   //
    3639             :   // - There was an existing pref with only a user value. FromDomPref() will
    3640             :   //   have just cleared that user value, so the pref can be removed.
    3641             :   //
    3642             :   // - There was no existing pref. FromDomPref() will have done nothing, and
    3643             :   //   `pref` will be valueless. We will end up adding and removing the value
    3644             :   //   needlessly, but that's ok because this case is rare.
    3645             :   //
    3646           0 :   if (!pref->HasDefaultValue() && !pref->HasUserValue()) {
    3647           0 :     gHashTable->RemoveEntry(entry);
    3648             :   }
    3649             : 
    3650             :   // Note: we don't have to worry about HandleDirty() because we are setting
    3651             :   // prefs in the content process that have come from the parent process.
    3652             : 
    3653           0 :   if (valueChanged) {
    3654           0 :     NotifyCallbacks(prefName);
    3655             :   }
    3656             : }
    3657             : 
    3658             : /* static */ void
    3659           0 : Preferences::GetPreference(dom::Pref* aDomPref)
    3660             : {
    3661           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    3662             : 
    3663           0 :   Pref* pref = pref_HashTableLookup(aDomPref->name().get());
    3664           0 :   if (pref && pref->HasAdvisablySizedValues()) {
    3665           0 :     pref->ToDomPref(aDomPref);
    3666             :   }
    3667           0 : }
    3668             : 
    3669             : #ifdef DEBUG
    3670             : bool
    3671           0 : Preferences::ArePrefsInitedInContentProcess()
    3672             : {
    3673           0 :   MOZ_ASSERT(!XRE_IsParentProcess());
    3674           0 :   return gContentProcessPrefsAreInited;
    3675             : }
    3676             : #endif
    3677             : 
    3678             : NS_IMETHODIMP
    3679           0 : Preferences::GetBranch(const char* aPrefRoot, nsIPrefBranch** aRetVal)
    3680             : {
    3681           0 :   if ((nullptr != aPrefRoot) && (*aPrefRoot != '\0')) {
    3682             :     // TODO: Cache this stuff and allow consumers to share branches (hold weak
    3683             :     // references, I think).
    3684             :     RefPtr<nsPrefBranch> prefBranch =
    3685           0 :       new nsPrefBranch(aPrefRoot, PrefValueKind::User);
    3686           0 :     prefBranch.forget(aRetVal);
    3687             :   } else {
    3688             :     // Special case: caching the default root.
    3689           0 :     nsCOMPtr<nsIPrefBranch> root(sPreferences->mRootBranch);
    3690           0 :     root.forget(aRetVal);
    3691             :   }
    3692             : 
    3693           0 :   return NS_OK;
    3694             : }
    3695             : 
    3696             : NS_IMETHODIMP
    3697           0 : Preferences::GetDefaultBranch(const char* aPrefRoot, nsIPrefBranch** aRetVal)
    3698             : {
    3699           0 :   if (!aPrefRoot || !aPrefRoot[0]) {
    3700           0 :     nsCOMPtr<nsIPrefBranch> root(sPreferences->mDefaultRootBranch);
    3701           0 :     root.forget(aRetVal);
    3702             :     return NS_OK;
    3703             :   }
    3704             : 
    3705             :   // TODO: Cache this stuff and allow consumers to share branches (hold weak
    3706             :   // references, I think).
    3707             :   RefPtr<nsPrefBranch> prefBranch =
    3708           0 :     new nsPrefBranch(aPrefRoot, PrefValueKind::Default);
    3709           0 :   if (!prefBranch) {
    3710             :     return NS_ERROR_OUT_OF_MEMORY;
    3711             :   }
    3712             : 
    3713           0 :   prefBranch.forget(aRetVal);
    3714           0 :   return NS_OK;
    3715             : }
    3716             : 
    3717             : NS_IMETHODIMP
    3718           0 : Preferences::ReadStats(nsIPrefStatsCallback* aCallback)
    3719             : {
    3720             : #ifdef DEBUG
    3721           0 :   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
    3722           0 :     PrefEntry* entry = static_cast<PrefEntry*>(iter.Get());
    3723           0 :     aCallback->Visit(entry->mPref->Name(), entry->mAccessCount);
    3724             :   }
    3725             : 
    3726           0 :   return NS_OK;
    3727             : #else
    3728             :   return NS_ERROR_NOT_IMPLEMENTED;
    3729             : #endif
    3730             : }
    3731             : 
    3732             : NS_IMETHODIMP
    3733           0 : Preferences::ResetStats()
    3734             : {
    3735             : #ifdef DEBUG
    3736           0 :   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
    3737           0 :     static_cast<PrefEntry*>(iter.Get())->mAccessCount = 0;
    3738             :   }
    3739           0 :   return NS_OK;
    3740             : #else
    3741             :   return NS_ERROR_NOT_IMPLEMENTED;
    3742             : #endif
    3743             : }
    3744             : 
    3745             : NS_IMETHODIMP
    3746           0 : Preferences::GetDirty(bool* aRetVal)
    3747             : {
    3748           0 :   *aRetVal = mDirty;
    3749           0 :   return NS_OK;
    3750             : }
    3751             : 
    3752             : nsresult
    3753           0 : Preferences::NotifyServiceObservers(const char* aTopic)
    3754             : {
    3755             :   nsCOMPtr<nsIObserverService> observerService =
    3756           0 :     mozilla::services::GetObserverService();
    3757           0 :   if (!observerService) {
    3758             :     return NS_ERROR_FAILURE;
    3759             :   }
    3760             : 
    3761           0 :   auto subject = static_cast<nsIPrefService*>(this);
    3762           0 :   observerService->NotifyObservers(subject, aTopic, nullptr);
    3763             : 
    3764           0 :   return NS_OK;
    3765             : }
    3766             : 
    3767             : already_AddRefed<nsIFile>
    3768           0 : Preferences::ReadSavedPrefs()
    3769             : {
    3770           0 :   nsCOMPtr<nsIFile> file;
    3771             :   nsresult rv =
    3772           0 :     NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE, getter_AddRefs(file));
    3773           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3774             :     return nullptr;
    3775             :   }
    3776             : 
    3777           0 :   rv = openPrefFile(file, PrefValueKind::User);
    3778           0 :   if (rv == NS_ERROR_FILE_NOT_FOUND) {
    3779             :     // This is a normal case for new users.
    3780             :     Telemetry::ScalarSet(
    3781           0 :       Telemetry::ScalarID::PREFERENCES_CREATED_NEW_USER_PREFS_FILE, true);
    3782           0 :     rv = NS_OK;
    3783           0 :   } else if (NS_FAILED(rv)) {
    3784             :     // Save a backup copy of the current (invalid) prefs file, since all prefs
    3785             :     // from the error line to the end of the file will be lost (bug 361102).
    3786             :     // TODO we should notify the user about it (bug 523725).
    3787             :     Telemetry::ScalarSet(
    3788           0 :       Telemetry::ScalarID::PREFERENCES_PREFS_FILE_WAS_INVALID, true);
    3789           0 :     MakeBackupPrefFile(file);
    3790             :   }
    3791             : 
    3792           0 :   return file.forget();
    3793             : }
    3794             : 
    3795             : void
    3796           0 : Preferences::ReadUserOverridePrefs()
    3797             : {
    3798           0 :   nsCOMPtr<nsIFile> aFile;
    3799             :   nsresult rv =
    3800           0 :     NS_GetSpecialDirectory(NS_APP_PREFS_50_DIR, getter_AddRefs(aFile));
    3801           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3802           0 :     return;
    3803             :   }
    3804             : 
    3805           0 :   aFile->AppendNative(NS_LITERAL_CSTRING("user.js"));
    3806           0 :   rv = openPrefFile(aFile, PrefValueKind::User);
    3807           0 :   if (rv != NS_ERROR_FILE_NOT_FOUND) {
    3808             :     // If the file exists and was at least partially read, record that in
    3809             :     // telemetry as it may be a sign of pref injection.
    3810           0 :     Telemetry::ScalarSet(Telemetry::ScalarID::PREFERENCES_READ_USER_JS, true);
    3811             :   }
    3812             : }
    3813             : 
    3814             : nsresult
    3815           0 : Preferences::MakeBackupPrefFile(nsIFile* aFile)
    3816             : {
    3817             :   // Example: this copies "prefs.js" to "Invalidprefs.js" in the same directory.
    3818             :   // "Invalidprefs.js" is removed if it exists, prior to making the copy.
    3819           0 :   nsAutoString newFilename;
    3820           0 :   nsresult rv = aFile->GetLeafName(newFilename);
    3821           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3822             : 
    3823           0 :   newFilename.InsertLiteral(u"Invalid", 0);
    3824           0 :   nsCOMPtr<nsIFile> newFile;
    3825           0 :   rv = aFile->GetParent(getter_AddRefs(newFile));
    3826           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3827             : 
    3828           0 :   rv = newFile->Append(newFilename);
    3829           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3830             : 
    3831           0 :   bool exists = false;
    3832           0 :   newFile->Exists(&exists);
    3833           0 :   if (exists) {
    3834           0 :     rv = newFile->Remove(false);
    3835           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3836             :   }
    3837             : 
    3838           0 :   rv = aFile->CopyTo(nullptr, newFilename);
    3839           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3840             : 
    3841             :   return rv;
    3842             : }
    3843             : 
    3844             : nsresult
    3845           0 : Preferences::SavePrefFileInternal(nsIFile* aFile, SaveMethod aSaveMethod)
    3846             : {
    3847           0 :   ENSURE_PARENT_PROCESS("Preferences::SavePrefFileInternal", "all prefs");
    3848             : 
    3849             :   // We allow different behavior here when aFile argument is not null, but it
    3850             :   // happens to be the same as the current file.  It is not clear that we
    3851             :   // should, but it does give us a "force" save on the unmodified pref file
    3852             :   // (see the original bug 160377 when we added this.)
    3853             : 
    3854           0 :   if (nullptr == aFile) {
    3855           0 :     mSavePending = false;
    3856             : 
    3857             :     // Off main thread writing only if allowed.
    3858           0 :     if (!AllowOffMainThreadSave()) {
    3859           0 :       aSaveMethod = SaveMethod::Blocking;
    3860             :     }
    3861             : 
    3862             :     // The mDirty flag tells us if we should write to mCurrentFile. We only
    3863             :     // check this flag when the caller wants to write to the default.
    3864           0 :     if (!mDirty) {
    3865             :       return NS_OK;
    3866             :     }
    3867             : 
    3868             :     // Check for profile shutdown after mDirty because the runnables from
    3869             :     // HandleDirty() can still be pending.
    3870           0 :     if (mProfileShutdown) {
    3871           0 :       NS_WARNING("Cannot save pref file after profile shutdown.");
    3872           0 :       return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
    3873             :     }
    3874             : 
    3875             :     // It's possible that we never got a prefs file.
    3876           0 :     nsresult rv = NS_OK;
    3877           0 :     if (mCurrentFile) {
    3878           0 :       rv = WritePrefFile(mCurrentFile, aSaveMethod);
    3879             :     }
    3880             : 
    3881             :     // If we succeeded writing to mCurrentFile, reset the dirty flag.
    3882           0 :     if (NS_SUCCEEDED(rv)) {
    3883           0 :       mDirty = false;
    3884             :     }
    3885             :     return rv;
    3886             : 
    3887             :   } else {
    3888             :     // We only allow off main thread writes on mCurrentFile.
    3889           0 :     return WritePrefFile(aFile, SaveMethod::Blocking);
    3890             :   }
    3891             : }
    3892             : 
    3893             : nsresult
    3894           0 : Preferences::WritePrefFile(nsIFile* aFile, SaveMethod aSaveMethod)
    3895             : {
    3896           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    3897             : 
    3898           0 :   if (!gHashTable) {
    3899             :     return NS_ERROR_NOT_INITIALIZED;
    3900             :   }
    3901             : 
    3902           0 :   AUTO_PROFILER_LABEL("Preferences::WritePrefFile", OTHER);
    3903             : 
    3904           0 :   if (AllowOffMainThreadSave()) {
    3905             : 
    3906           0 :     nsresult rv = NS_OK;
    3907             :     mozilla::UniquePtr<PrefSaveData> prefs =
    3908           0 :       MakeUnique<PrefSaveData>(pref_savePrefs());
    3909             : 
    3910             :     // Put the newly constructed preference data into sPendingWriteData
    3911             :     // for the next request to pick up
    3912           0 :     prefs.reset(PreferencesWriter::sPendingWriteData.exchange(prefs.release()));
    3913           0 :     if (prefs) {
    3914             :       // There was a previous request that hasn't been processed,
    3915             :       // and this is the data it had.
    3916           0 :       return rv;
    3917             :     }
    3918             : 
    3919             :     // There were no previous requests. Dispatch one since sPendingWriteData has
    3920             :     // the up to date information.
    3921             :     nsCOMPtr<nsIEventTarget> target =
    3922           0 :       do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
    3923           0 :     if (NS_SUCCEEDED(rv)) {
    3924           0 :       bool async = aSaveMethod == SaveMethod::Asynchronous;
    3925           0 :       if (async) {
    3926           0 :         rv = target->Dispatch(new PWRunnable(aFile),
    3927             :                               nsIEventTarget::DISPATCH_NORMAL);
    3928             :       } else {
    3929             :         // Note that we don't get the nsresult return value here.
    3930           0 :         SyncRunnable::DispatchToThread(target, new PWRunnable(aFile), true);
    3931             :       }
    3932           0 :       return rv;
    3933             :     }
    3934             : 
    3935             :     // If we can't get the thread for writing, for whatever reason, do the main
    3936             :     // thread write after making some noise.
    3937           0 :     MOZ_ASSERT(false, "failed to get the target thread for OMT pref write");
    3938             :   }
    3939             : 
    3940             :   // This will do a main thread write. It is safe to do it this way because
    3941             :   // AllowOffMainThreadSave() returns a consistent value for the lifetime of
    3942             :   // the parent process.
    3943           0 :   PrefSaveData prefsData = pref_savePrefs();
    3944           0 :   return PreferencesWriter::Write(aFile, prefsData);
    3945             : }
    3946             : 
    3947             : static nsresult
    3948           0 : openPrefFile(nsIFile* aFile, PrefValueKind aKind)
    3949             : {
    3950           0 :   TimeStamp startTime = TimeStamp::Now();
    3951             : 
    3952           0 :   nsCString data;
    3953           0 :   MOZ_TRY_VAR(data, URLPreloader::ReadFile(aFile));
    3954             : 
    3955           0 :   nsAutoString filenameUtf16;
    3956           0 :   aFile->GetLeafName(filenameUtf16);
    3957           0 :   NS_ConvertUTF16toUTF8 filename(filenameUtf16);
    3958             : 
    3959           0 :   nsAutoString path;
    3960           0 :   aFile->GetPath(path);
    3961             : 
    3962             :   Parser parser;
    3963           0 :   if (!parser.Parse(
    3964           0 :         filename, aKind, NS_ConvertUTF16toUTF8(path).get(), startTime, data)) {
    3965             :     return NS_ERROR_FILE_CORRUPTED;
    3966             :   }
    3967             : 
    3968           0 :   return NS_OK;
    3969             : }
    3970             : 
    3971             : static int
    3972           0 : pref_CompareFileNames(nsIFile* aFile1, nsIFile* aFile2, void* /* unused */)
    3973             : {
    3974           0 :   nsAutoCString filename1, filename2;
    3975           0 :   aFile1->GetNativeLeafName(filename1);
    3976           0 :   aFile2->GetNativeLeafName(filename2);
    3977             : 
    3978           0 :   return Compare(filename2, filename1);
    3979             : }
    3980             : 
    3981             : // Load default pref files from a directory. The files in the directory are
    3982             : // sorted reverse-alphabetically; a set of "special file names" may be
    3983             : // specified which are loaded after all the others.
    3984             : static nsresult
    3985           0 : pref_LoadPrefsInDir(nsIFile* aDir,
    3986             :                     char const* const* aSpecialFiles,
    3987             :                     uint32_t aSpecialFilesCount)
    3988             : {
    3989             :   nsresult rv, rv2;
    3990             : 
    3991           0 :   nsCOMPtr<nsIDirectoryEnumerator> dirIterator;
    3992             : 
    3993             :   // This may fail in some normal cases, such as embedders who do not use a
    3994             :   // GRE.
    3995           0 :   rv = aDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
    3996           0 :   if (NS_FAILED(rv)) {
    3997             :     // If the directory doesn't exist, then we have no reason to complain. We
    3998             :     // loaded everything (and nothing) successfully.
    3999           0 :     if (rv == NS_ERROR_FILE_NOT_FOUND ||
    4000           0 :         rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
    4001           0 :       rv = NS_OK;
    4002             :     }
    4003             :     return rv;
    4004             :   }
    4005             : 
    4006           0 :   nsCOMArray<nsIFile> prefFiles(INITIAL_PREF_FILES);
    4007           0 :   nsCOMArray<nsIFile> specialFiles(aSpecialFilesCount);
    4008           0 :   nsCOMPtr<nsIFile> prefFile;
    4009             : 
    4010           0 :   while (NS_SUCCEEDED(dirIterator->GetNextFile(getter_AddRefs(prefFile))) &&
    4011           0 :          prefFile) {
    4012           0 :     nsAutoCString leafName;
    4013           0 :     prefFile->GetNativeLeafName(leafName);
    4014           0 :     MOZ_ASSERT(
    4015             :       !leafName.IsEmpty(),
    4016             :       "Failure in default prefs: directory enumerator returned empty file?");
    4017             : 
    4018             :     // Skip non-js files.
    4019           0 :     if (StringEndsWith(leafName,
    4020           0 :                        NS_LITERAL_CSTRING(".js"),
    4021           0 :                        nsCaseInsensitiveCStringComparator())) {
    4022             :       bool shouldParse = true;
    4023             : 
    4024             :       // Separate out special files.
    4025           0 :       for (uint32_t i = 0; i < aSpecialFilesCount; ++i) {
    4026           0 :         if (leafName.Equals(nsDependentCString(aSpecialFiles[i]))) {
    4027           0 :           shouldParse = false;
    4028             :           // Special files should be processed in order. We put them into the
    4029             :           // array by index, which can make the array sparse.
    4030           0 :           specialFiles.ReplaceObjectAt(prefFile, i);
    4031             :         }
    4032             :       }
    4033             : 
    4034           0 :       if (shouldParse) {
    4035           0 :         prefFiles.AppendObject(prefFile);
    4036             :       }
    4037             :     }
    4038             :   }
    4039             : 
    4040           0 :   if (prefFiles.Count() + specialFiles.Count() == 0) {
    4041           0 :     NS_WARNING("No default pref files found.");
    4042           0 :     if (NS_SUCCEEDED(rv)) {
    4043           0 :       rv = NS_SUCCESS_FILE_DIRECTORY_EMPTY;
    4044             :     }
    4045             :     return rv;
    4046             :   }
    4047             : 
    4048           0 :   prefFiles.Sort(pref_CompareFileNames, nullptr);
    4049             : 
    4050           0 :   uint32_t arrayCount = prefFiles.Count();
    4051             :   uint32_t i;
    4052           0 :   for (i = 0; i < arrayCount; ++i) {
    4053           0 :     rv2 = openPrefFile(prefFiles[i], PrefValueKind::Default);
    4054           0 :     if (NS_FAILED(rv2)) {
    4055           0 :       NS_ERROR("Default pref file not parsed successfully.");
    4056           0 :       rv = rv2;
    4057             :     }
    4058             :   }
    4059             : 
    4060           0 :   arrayCount = specialFiles.Count();
    4061           0 :   for (i = 0; i < arrayCount; ++i) {
    4062             :     // This may be a sparse array; test before parsing.
    4063           0 :     nsIFile* file = specialFiles[i];
    4064           0 :     if (file) {
    4065           0 :       rv2 = openPrefFile(file, PrefValueKind::Default);
    4066           0 :       if (NS_FAILED(rv2)) {
    4067           0 :         NS_ERROR("Special default pref file not parsed successfully.");
    4068           0 :         rv = rv2;
    4069             :       }
    4070             :     }
    4071             :   }
    4072             : 
    4073             :   return rv;
    4074             : }
    4075             : 
    4076             : static nsresult
    4077           0 : pref_ReadPrefFromJar(nsZipArchive* aJarReader, const char* aName)
    4078             : {
    4079           0 :   TimeStamp startTime = TimeStamp::Now();
    4080             : 
    4081           0 :   nsCString manifest;
    4082           0 :   MOZ_TRY_VAR(manifest,
    4083             :               URLPreloader::ReadZip(aJarReader, nsDependentCString(aName)));
    4084             : 
    4085             :   Parser parser;
    4086           0 :   if (!parser.Parse(nsDependentCString(aName),
    4087             :                     PrefValueKind::Default,
    4088             :                     aName,
    4089             :                     startTime,
    4090             :                     manifest)) {
    4091             :     return NS_ERROR_FILE_CORRUPTED;
    4092             :   }
    4093             : 
    4094           0 :   return NS_OK;
    4095             : }
    4096             : 
    4097             : // Initialize default preference JavaScript buffers from appropriate TEXT
    4098             : // resources.
    4099             : /* static */ Result<Ok, const char*>
    4100           0 : Preferences::InitInitialObjects(bool aIsStartup)
    4101             : {
    4102             :   // Initialize static prefs before prefs from data files so that the latter
    4103             :   // will override the former.
    4104           0 :   StaticPrefs::InitAll(aIsStartup);
    4105             : 
    4106             :   // In the omni.jar case, we load the following prefs:
    4107             :   // - jar:$gre/omni.jar!/greprefs.js
    4108             :   // - jar:$gre/omni.jar!/defaults/pref/*.js
    4109             :   //
    4110             :   // In the non-omni.jar case, we load:
    4111             :   // - $gre/greprefs.js
    4112             :   //
    4113             :   // In both cases, we also load:
    4114             :   // - $gre/defaults/pref/*.js
    4115             :   //
    4116             :   // This is kept for bug 591866 (channel-prefs.js should not be in omni.jar)
    4117             :   // in the `$app == $gre` case; we load all files instead of channel-prefs.js
    4118             :   // only to have the same behaviour as `$app != $gre`, where this is required
    4119             :   // as a supported location for GRE preferences.
    4120             :   //
    4121             :   // When `$app != $gre`, we additionally load, in the omni.jar case:
    4122             :   // - jar:$app/omni.jar!/defaults/preferences/*.js
    4123             :   // - $app/defaults/preferences/*.js
    4124             :   //
    4125             :   // and in the non-omni.jar case:
    4126             :   // - $app/defaults/preferences/*.js
    4127             :   //
    4128             :   // When `$app == $gre`, we additionally load, in the omni.jar case:
    4129             :   // - jar:$gre/omni.jar!/defaults/preferences/*.js
    4130             :   //
    4131             :   // Thus, in the omni.jar case, we always load app-specific default
    4132             :   // preferences from omni.jar, whether or not `$app == $gre`.
    4133             : 
    4134             :   nsresult rv;
    4135             :   nsZipFind* findPtr;
    4136           0 :   nsAutoPtr<nsZipFind> find;
    4137           0 :   nsTArray<nsCString> prefEntries;
    4138             :   const char* entryName;
    4139             :   uint16_t entryNameLen;
    4140             : 
    4141             :   RefPtr<nsZipArchive> jarReader =
    4142           0 :     mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
    4143           0 :   if (jarReader) {
    4144             :     // Load jar:$gre/omni.jar!/greprefs.js.
    4145           0 :     rv = pref_ReadPrefFromJar(jarReader, "greprefs.js");
    4146           0 :     NS_ENSURE_SUCCESS(rv, Err("pref_ReadPrefFromJar() failed"));
    4147             : 
    4148             :     // Load jar:$gre/omni.jar!/defaults/pref/*.js.
    4149           0 :     rv = jarReader->FindInit("defaults/pref/*.js$", &findPtr);
    4150           0 :     NS_ENSURE_SUCCESS(rv, Err("jarReader->FindInit() failed"));
    4151             : 
    4152           0 :     find = findPtr;
    4153           0 :     while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
    4154           0 :       prefEntries.AppendElement(Substring(entryName, entryNameLen));
    4155             :     }
    4156             : 
    4157           0 :     prefEntries.Sort();
    4158           0 :     for (uint32_t i = prefEntries.Length(); i--;) {
    4159           0 :       rv = pref_ReadPrefFromJar(jarReader, prefEntries[i].get());
    4160           0 :       if (NS_FAILED(rv)) {
    4161           0 :         NS_WARNING("Error parsing preferences.");
    4162             :       }
    4163             :     }
    4164             : 
    4165             :   } else {
    4166             :     // Load $gre/greprefs.js.
    4167           0 :     nsCOMPtr<nsIFile> greprefsFile;
    4168           0 :     rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greprefsFile));
    4169           0 :     NS_ENSURE_SUCCESS(rv, Err("NS_GetSpecialDirectory(NS_GRE_DIR) failed"));
    4170             : 
    4171           0 :     rv = greprefsFile->AppendNative(NS_LITERAL_CSTRING("greprefs.js"));
    4172           0 :     NS_ENSURE_SUCCESS(rv, Err("greprefsFile->AppendNative() failed"));
    4173             : 
    4174           0 :     rv = openPrefFile(greprefsFile, PrefValueKind::Default);
    4175           0 :     if (NS_FAILED(rv)) {
    4176             :       NS_WARNING("Error parsing GRE default preferences. Is this an old-style "
    4177           0 :                  "embedding app?");
    4178             :     }
    4179             :   }
    4180             : 
    4181             :   // Load $gre/defaults/pref/*.js.
    4182           0 :   nsCOMPtr<nsIFile> defaultPrefDir;
    4183           0 :   rv = NS_GetSpecialDirectory(NS_APP_PREF_DEFAULTS_50_DIR,
    4184           0 :                               getter_AddRefs(defaultPrefDir));
    4185           0 :   NS_ENSURE_SUCCESS(
    4186             :     rv, Err("NS_GetSpecialDirectory(NS_APP_PREF_DEFAULTS_50_DIR) failed"));
    4187             : 
    4188             :   // These pref file names should not be used: we process them after all other
    4189             :   // application pref files for backwards compatibility.
    4190             :   static const char* specialFiles[] = {
    4191             : #if defined(XP_MACOSX)
    4192             :     "macprefs.js"
    4193             : #elif defined(XP_WIN)
    4194             :     "winpref.js"
    4195             : #elif defined(XP_UNIX)
    4196             :     "unix.js"
    4197             : #if defined(_AIX)
    4198             :     ,
    4199             :     "aix.js"
    4200             : #endif
    4201             : #elif defined(XP_BEOS)
    4202             :     "beos.js"
    4203             : #endif
    4204             :   };
    4205             : 
    4206           0 :   rv = pref_LoadPrefsInDir(
    4207             :     defaultPrefDir, specialFiles, ArrayLength(specialFiles));
    4208           0 :   if (NS_FAILED(rv)) {
    4209           0 :     NS_WARNING("Error parsing application default preferences.");
    4210             :   }
    4211             : 
    4212             :   // Load jar:$app/omni.jar!/defaults/preferences/*.js
    4213             :   // or jar:$gre/omni.jar!/defaults/preferences/*.js.
    4214             :   RefPtr<nsZipArchive> appJarReader =
    4215           0 :     mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
    4216             : 
    4217             :   // GetReader(mozilla::Omnijar::APP) returns null when `$app == $gre`, in
    4218             :   // which case we look for app-specific default preferences in $gre.
    4219           0 :   if (!appJarReader) {
    4220           0 :     appJarReader = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
    4221             :   }
    4222             : 
    4223           0 :   if (appJarReader) {
    4224           0 :     rv = appJarReader->FindInit("defaults/preferences/*.js$", &findPtr);
    4225           0 :     NS_ENSURE_SUCCESS(rv, Err("appJarReader->FindInit() failed"));
    4226           0 :     find = findPtr;
    4227           0 :     prefEntries.Clear();
    4228           0 :     while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
    4229           0 :       prefEntries.AppendElement(Substring(entryName, entryNameLen));
    4230             :     }
    4231           0 :     prefEntries.Sort();
    4232           0 :     for (uint32_t i = prefEntries.Length(); i--;) {
    4233           0 :       rv = pref_ReadPrefFromJar(appJarReader, prefEntries[i].get());
    4234           0 :       if (NS_FAILED(rv)) {
    4235           0 :         NS_WARNING("Error parsing preferences.");
    4236             :       }
    4237             :     }
    4238             :   }
    4239             : 
    4240             :   nsCOMPtr<nsIProperties> dirSvc(
    4241           0 :     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
    4242           0 :   NS_ENSURE_SUCCESS(
    4243             :     rv, Err("do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID) failed"));
    4244             : 
    4245           0 :   nsCOMPtr<nsISimpleEnumerator> list;
    4246           0 :   dirSvc->Get(NS_APP_PREFS_DEFAULTS_DIR_LIST,
    4247             :               NS_GET_IID(nsISimpleEnumerator),
    4248           0 :               getter_AddRefs(list));
    4249           0 :   if (list) {
    4250             :     bool hasMore;
    4251           0 :     while (NS_SUCCEEDED(list->HasMoreElements(&hasMore)) && hasMore) {
    4252           0 :       nsCOMPtr<nsISupports> elem;
    4253           0 :       list->GetNext(getter_AddRefs(elem));
    4254           0 :       if (!elem) {
    4255           0 :         continue;
    4256             :       }
    4257             : 
    4258           0 :       nsCOMPtr<nsIFile> path = do_QueryInterface(elem);
    4259           0 :       if (!path) {
    4260           0 :         continue;
    4261             :       }
    4262             : 
    4263             :       // Do we care if a file provided by this process fails to load?
    4264           0 :       pref_LoadPrefsInDir(path, nullptr, 0);
    4265             :     }
    4266             :   }
    4267             : 
    4268           0 :   if (XRE_IsParentProcess()) {
    4269           0 :     SetupTelemetryPref();
    4270             :   }
    4271             : 
    4272             :   NS_CreateServicesFromCategory(NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID,
    4273             :                                 nullptr,
    4274           0 :                                 NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID);
    4275             : 
    4276             :   nsCOMPtr<nsIObserverService> observerService =
    4277           0 :     mozilla::services::GetObserverService();
    4278           0 :   NS_ENSURE_SUCCESS(rv, Err("GetObserverService() failed (2)"));
    4279             : 
    4280           0 :   observerService->NotifyObservers(
    4281           0 :     nullptr, NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID, nullptr);
    4282             : 
    4283           0 :   return Ok();
    4284             : }
    4285             : 
    4286             : /* static */ nsresult
    4287           0 : Preferences::GetBool(const char* aPrefName, bool* aResult, PrefValueKind aKind)
    4288             : {
    4289           0 :   MOZ_ASSERT(aResult);
    4290           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4291             : 
    4292           0 :   Pref* pref = pref_HashTableLookup(aPrefName);
    4293           0 :   return pref ? pref->GetBoolValue(aKind, aResult) : NS_ERROR_UNEXPECTED;
    4294             : }
    4295             : 
    4296             : /* static */ nsresult
    4297           0 : Preferences::GetInt(const char* aPrefName,
    4298             :                     int32_t* aResult,
    4299             :                     PrefValueKind aKind)
    4300             : {
    4301           0 :   MOZ_ASSERT(aResult);
    4302           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4303             : 
    4304           0 :   Pref* pref = pref_HashTableLookup(aPrefName);
    4305           0 :   return pref ? pref->GetIntValue(aKind, aResult) : NS_ERROR_UNEXPECTED;
    4306             : }
    4307             : 
    4308             : /* static */ nsresult
    4309           0 : Preferences::GetFloat(const char* aPrefName,
    4310             :                       float* aResult,
    4311             :                       PrefValueKind aKind)
    4312             : {
    4313           0 :   MOZ_ASSERT(aResult);
    4314             : 
    4315           0 :   nsAutoCString result;
    4316           0 :   nsresult rv = Preferences::GetCString(aPrefName, result, aKind);
    4317           0 :   if (NS_SUCCEEDED(rv)) {
    4318           0 :     *aResult = result.ToFloat(&rv);
    4319             :   }
    4320           0 :   return rv;
    4321             : }
    4322             : 
    4323             : /* static */ nsresult
    4324           0 : Preferences::GetCString(const char* aPrefName,
    4325             :                         nsACString& aResult,
    4326             :                         PrefValueKind aKind)
    4327             : {
    4328           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4329             : 
    4330           0 :   aResult.SetIsVoid(true);
    4331             : 
    4332           0 :   Pref* pref = pref_HashTableLookup(aPrefName);
    4333           0 :   return pref ? pref->GetCStringValue(aKind, aResult) : NS_ERROR_UNEXPECTED;
    4334             : }
    4335             : 
    4336             : /* static */ nsresult
    4337           0 : Preferences::GetString(const char* aPrefName,
    4338             :                        nsAString& aResult,
    4339             :                        PrefValueKind aKind)
    4340             : {
    4341           0 :   nsAutoCString result;
    4342           0 :   nsresult rv = Preferences::GetCString(aPrefName, result, aKind);
    4343           0 :   if (NS_SUCCEEDED(rv)) {
    4344           0 :     CopyUTF8toUTF16(result, aResult);
    4345             :   }
    4346           0 :   return rv;
    4347             : }
    4348             : 
    4349             : /* static */ nsresult
    4350           0 : Preferences::GetLocalizedCString(const char* aPrefName,
    4351             :                                  nsACString& aResult,
    4352             :                                  PrefValueKind aKind)
    4353             : {
    4354           0 :   nsAutoString result;
    4355           0 :   nsresult rv = GetLocalizedString(aPrefName, result, aKind);
    4356           0 :   if (NS_SUCCEEDED(rv)) {
    4357           0 :     CopyUTF16toUTF8(result, aResult);
    4358             :   }
    4359           0 :   return rv;
    4360             : }
    4361             : 
    4362             : /* static */ nsresult
    4363           0 : Preferences::GetLocalizedString(const char* aPrefName,
    4364             :                                 nsAString& aResult,
    4365             :                                 PrefValueKind aKind)
    4366             : {
    4367           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4368           0 :   nsCOMPtr<nsIPrefLocalizedString> prefLocalString;
    4369             :   nsresult rv =
    4370           0 :     GetRootBranch(aKind)->GetComplexValue(aPrefName,
    4371             :                                           NS_GET_IID(nsIPrefLocalizedString),
    4372           0 :                                           getter_AddRefs(prefLocalString));
    4373           0 :   if (NS_SUCCEEDED(rv)) {
    4374           0 :     MOZ_ASSERT(prefLocalString, "Succeeded but the result is NULL");
    4375           0 :     prefLocalString->GetData(aResult);
    4376             :   }
    4377             :   return rv;
    4378             : }
    4379             : 
    4380             : /* static */ nsresult
    4381           0 : Preferences::GetComplex(const char* aPrefName,
    4382             :                         const nsIID& aType,
    4383             :                         void** aResult,
    4384             :                         PrefValueKind aKind)
    4385             : {
    4386           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4387           0 :   return GetRootBranch(aKind)->GetComplexValue(aPrefName, aType, aResult);
    4388             : }
    4389             : 
    4390             : /* static */ nsresult
    4391           0 : Preferences::SetCString(const char* aPrefName,
    4392             :                         const nsACString& aValue,
    4393             :                         PrefValueKind aKind)
    4394             : {
    4395           0 :   ENSURE_PARENT_PROCESS("SetCString", aPrefName);
    4396           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4397             : 
    4398           0 :   if (aValue.Length() > MAX_PREF_LENGTH) {
    4399             :     return NS_ERROR_ILLEGAL_VALUE;
    4400             :   }
    4401             : 
    4402             :   // It's ok to stash a pointer to the temporary PromiseFlatCString's chars in
    4403             :   // pref because pref_SetPref() duplicates those chars.
    4404             :   PrefValue prefValue;
    4405           0 :   const nsCString& flat = PromiseFlatCString(aValue);
    4406           0 :   prefValue.mStringVal = flat.get();
    4407             :   return pref_SetPref(aPrefName,
    4408             :                       PrefType::String,
    4409             :                       aKind,
    4410             :                       prefValue,
    4411             :                       /* isSticky */ false,
    4412             :                       /* isLocked */ false,
    4413           0 :                       /* fromInit */ false);
    4414             : }
    4415             : 
    4416             : /* static */ nsresult
    4417           0 : Preferences::SetBool(const char* aPrefName, bool aValue, PrefValueKind aKind)
    4418             : {
    4419           0 :   ENSURE_PARENT_PROCESS("SetBool", aPrefName);
    4420           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4421             : 
    4422             :   PrefValue prefValue;
    4423           0 :   prefValue.mBoolVal = aValue;
    4424             :   return pref_SetPref(aPrefName,
    4425             :                       PrefType::Bool,
    4426             :                       aKind,
    4427             :                       prefValue,
    4428             :                       /* isSticky */ false,
    4429             :                       /* isLocked */ false,
    4430           0 :                       /* fromInit */ false);
    4431             : }
    4432             : 
    4433             : /* static */ nsresult
    4434           0 : Preferences::SetInt(const char* aPrefName, int32_t aValue, PrefValueKind aKind)
    4435             : {
    4436           0 :   ENSURE_PARENT_PROCESS("SetInt", aPrefName);
    4437           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4438             : 
    4439             :   PrefValue prefValue;
    4440           0 :   prefValue.mIntVal = aValue;
    4441             :   return pref_SetPref(aPrefName,
    4442             :                       PrefType::Int,
    4443             :                       aKind,
    4444             :                       prefValue,
    4445             :                       /* isSticky */ false,
    4446             :                       /* isLocked */ false,
    4447           0 :                       /* fromInit */ false);
    4448             : }
    4449             : 
    4450             : /* static */ nsresult
    4451           0 : Preferences::SetComplex(const char* aPrefName,
    4452             :                         const nsIID& aType,
    4453             :                         nsISupports* aValue,
    4454             :                         PrefValueKind aKind)
    4455             : {
    4456           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4457           0 :   return GetRootBranch(aKind)->SetComplexValue(aPrefName, aType, aValue);
    4458             : }
    4459             : 
    4460             : /* static */ nsresult
    4461           0 : Preferences::Lock(const char* aPrefName)
    4462             : {
    4463           0 :   ENSURE_PARENT_PROCESS("Lock", aPrefName);
    4464           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4465             : 
    4466           0 :   Pref* pref = pref_HashTableLookup(aPrefName);
    4467           0 :   if (!pref) {
    4468             :     return NS_ERROR_UNEXPECTED;
    4469             :   }
    4470             : 
    4471           0 :   if (!pref->IsLocked()) {
    4472           0 :     pref->SetIsLocked(true);
    4473           0 :     NotifyCallbacks(aPrefName);
    4474             :   }
    4475             : 
    4476             :   return NS_OK;
    4477             : }
    4478             : 
    4479             : /* static */ nsresult
    4480           0 : Preferences::Unlock(const char* aPrefName)
    4481             : {
    4482           0 :   ENSURE_PARENT_PROCESS("Unlock", aPrefName);
    4483           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4484             : 
    4485           0 :   Pref* pref = pref_HashTableLookup(aPrefName);
    4486           0 :   if (!pref) {
    4487             :     return NS_ERROR_UNEXPECTED;
    4488             :   }
    4489             : 
    4490           0 :   if (pref->IsLocked()) {
    4491           0 :     pref->SetIsLocked(false);
    4492           0 :     NotifyCallbacks(aPrefName);
    4493             :   }
    4494             : 
    4495             :   return NS_OK;
    4496             : }
    4497             : 
    4498             : /* static */ bool
    4499           0 : Preferences::IsLocked(const char* aPrefName)
    4500             : {
    4501           0 :   NS_ENSURE_TRUE(InitStaticMembers(), false);
    4502             : 
    4503           0 :   Pref* pref = pref_HashTableLookup(aPrefName);
    4504           0 :   return pref && pref->IsLocked();
    4505             : }
    4506             : 
    4507             : /* static */ nsresult
    4508           0 : Preferences::ClearUser(const char* aPrefName)
    4509             : {
    4510           0 :   ENSURE_PARENT_PROCESS("ClearUser", aPrefName);
    4511           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4512             : 
    4513           0 :   PrefEntry* entry = pref_HashTableLookupInner(aPrefName);
    4514             :   Pref* pref;
    4515           0 :   if (entry && (pref = entry->mPref) && pref->HasUserValue()) {
    4516           0 :     pref->ClearUserValue();
    4517             : 
    4518           0 :     if (!pref->HasDefaultValue()) {
    4519           0 :       gHashTable->RemoveEntry(entry);
    4520             :     }
    4521             : 
    4522           0 :     NotifyCallbacks(aPrefName);
    4523           0 :     Preferences::HandleDirty();
    4524             :   }
    4525             :   return NS_OK;
    4526             : }
    4527             : 
    4528             : /* static */ bool
    4529           0 : Preferences::HasUserValue(const char* aPrefName)
    4530             : {
    4531           0 :   NS_ENSURE_TRUE(InitStaticMembers(), false);
    4532             : 
    4533           0 :   Pref* pref = pref_HashTableLookup(aPrefName);
    4534           0 :   return pref && pref->HasUserValue();
    4535             : }
    4536             : 
    4537             : /* static */ int32_t
    4538           0 : Preferences::GetType(const char* aPrefName)
    4539             : {
    4540           0 :   NS_ENSURE_TRUE(InitStaticMembers(), nsIPrefBranch::PREF_INVALID);
    4541             : 
    4542             :   Pref* pref;
    4543           0 :   if (!gHashTable || !(pref = pref_HashTableLookup(aPrefName))) {
    4544             :     return PREF_INVALID;
    4545             :   }
    4546             : 
    4547           0 :   switch (pref->Type()) {
    4548             :     case PrefType::String:
    4549             :       return PREF_STRING;
    4550             : 
    4551             :     case PrefType::Int:
    4552           0 :       return PREF_INT;
    4553             : 
    4554             :     case PrefType::Bool:
    4555           0 :       return PREF_BOOL;
    4556             : 
    4557             :     default:
    4558           0 :       MOZ_CRASH();
    4559             :   }
    4560             : }
    4561             : 
    4562             : /* static */ nsresult
    4563           0 : Preferences::AddStrongObserver(nsIObserver* aObserver, const char* aPref)
    4564             : {
    4565           0 :   MOZ_ASSERT(aObserver);
    4566           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4567           0 :   return sPreferences->mRootBranch->AddObserver(aPref, aObserver, false);
    4568             : }
    4569             : 
    4570             : /* static */ nsresult
    4571           0 : Preferences::AddWeakObserver(nsIObserver* aObserver, const char* aPref)
    4572             : {
    4573           0 :   MOZ_ASSERT(aObserver);
    4574           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4575           0 :   return sPreferences->mRootBranch->AddObserver(aPref, aObserver, true);
    4576             : }
    4577             : 
    4578             : /* static */ nsresult
    4579           0 : Preferences::RemoveObserver(nsIObserver* aObserver, const char* aPref)
    4580             : {
    4581           0 :   MOZ_ASSERT(aObserver);
    4582           0 :   if (sShutdown) {
    4583           0 :     MOZ_ASSERT(!sPreferences);
    4584             :     return NS_OK; // Observers have been released automatically.
    4585             :   }
    4586           0 :   NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
    4587           0 :   return sPreferences->mRootBranch->RemoveObserver(aPref, aObserver);
    4588             : }
    4589             : 
    4590             : /* static */ nsresult
    4591           0 : Preferences::AddStrongObservers(nsIObserver* aObserver, const char** aPrefs)
    4592             : {
    4593           0 :   MOZ_ASSERT(aObserver);
    4594           0 :   for (uint32_t i = 0; aPrefs[i]; i++) {
    4595           0 :     nsresult rv = AddStrongObserver(aObserver, aPrefs[i]);
    4596           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4597             :   }
    4598             :   return NS_OK;
    4599             : }
    4600             : 
    4601             : /* static */ nsresult
    4602           0 : Preferences::AddWeakObservers(nsIObserver* aObserver, const char** aPrefs)
    4603             : {
    4604           0 :   MOZ_ASSERT(aObserver);
    4605           0 :   for (uint32_t i = 0; aPrefs[i]; i++) {
    4606           0 :     nsresult rv = AddWeakObserver(aObserver, aPrefs[i]);
    4607           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4608             :   }
    4609             :   return NS_OK;
    4610             : }
    4611             : 
    4612             : /* static */ nsresult
    4613           0 : Preferences::RemoveObservers(nsIObserver* aObserver, const char** aPrefs)
    4614             : {
    4615           0 :   MOZ_ASSERT(aObserver);
    4616           0 :   if (sShutdown) {
    4617           0 :     MOZ_ASSERT(!sPreferences);
    4618             :     return NS_OK; // Observers have been released automatically.
    4619             :   }
    4620           0 :   NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
    4621             : 
    4622           0 :   for (uint32_t i = 0; aPrefs[i]; i++) {
    4623           0 :     nsresult rv = RemoveObserver(aObserver, aPrefs[i]);
    4624           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4625             :   }
    4626             :   return NS_OK;
    4627             : }
    4628             : 
    4629             : /* static */ nsresult
    4630           0 : Preferences::RegisterCallback(PrefChangedFunc aCallback,
    4631             :                               const char* aPrefNode,
    4632             :                               void* aData,
    4633             :                               MatchKind aMatchKind,
    4634             :                               bool aIsPriority)
    4635             : {
    4636           0 :   NS_ENSURE_ARG(aPrefNode);
    4637           0 :   NS_ENSURE_ARG(aCallback);
    4638             : 
    4639           0 :   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
    4640             : 
    4641           0 :   auto node = new CallbackNode(aPrefNode, aCallback, aData, aMatchKind);
    4642             : 
    4643           0 :   if (aIsPriority) {
    4644             :     // Add to the start of the list.
    4645           0 :     node->SetNext(gFirstCallback);
    4646           0 :     gFirstCallback = node;
    4647           0 :     if (!gLastPriorityNode) {
    4648           0 :       gLastPriorityNode = node;
    4649             :     }
    4650             :   } else {
    4651             :     // Add to the start of the non-priority part of the list.
    4652           0 :     if (gLastPriorityNode) {
    4653           0 :       node->SetNext(gLastPriorityNode->Next());
    4654           0 :       gLastPriorityNode->SetNext(node);
    4655             :     } else {
    4656           0 :       node->SetNext(gFirstCallback);
    4657           0 :       gFirstCallback = node;
    4658             :     }
    4659             :   }
    4660             : 
    4661             :   return NS_OK;
    4662             : }
    4663             : 
    4664             : /* static */ nsresult
    4665           0 : Preferences::RegisterCallbackAndCall(PrefChangedFunc aCallback,
    4666             :                                      const char* aPref,
    4667             :                                      void* aClosure,
    4668             :                                      MatchKind aMatchKind)
    4669             : {
    4670           0 :   MOZ_ASSERT(aCallback);
    4671           0 :   nsresult rv = RegisterCallback(aCallback, aPref, aClosure, aMatchKind);
    4672           0 :   if (NS_SUCCEEDED(rv)) {
    4673           0 :     (*aCallback)(aPref, aClosure);
    4674             :   }
    4675           0 :   return rv;
    4676             : }
    4677             : 
    4678             : /* static */ nsresult
    4679           0 : Preferences::UnregisterCallback(PrefChangedFunc aCallback,
    4680             :                                 const char* aPrefNode,
    4681             :                                 void* aData,
    4682             :                                 MatchKind aMatchKind)
    4683             : {
    4684           0 :   MOZ_ASSERT(aCallback);
    4685           0 :   if (sShutdown) {
    4686           0 :     MOZ_ASSERT(!sPreferences);
    4687             :     return NS_OK; // Observers have been released automatically.
    4688             :   }
    4689           0 :   NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
    4690             : 
    4691           0 :   nsresult rv = NS_ERROR_FAILURE;
    4692           0 :   CallbackNode* node = gFirstCallback;
    4693           0 :   CallbackNode* prev_node = nullptr;
    4694             : 
    4695           0 :   while (node) {
    4696           0 :     if (node->Func() == aCallback && node->Data() == aData &&
    4697           0 :         node->MatchKind() == aMatchKind &&
    4698           0 :         strcmp(node->Domain(), aPrefNode) == 0) {
    4699           0 :       if (gCallbacksInProgress) {
    4700             :         // Postpone the node removal until after callbacks enumeration is
    4701             :         // finished.
    4702           0 :         node->ClearFunc();
    4703           0 :         gShouldCleanupDeadNodes = true;
    4704           0 :         prev_node = node;
    4705           0 :         node = node->Next();
    4706             :       } else {
    4707           0 :         node = pref_RemoveCallbackNode(node, prev_node);
    4708             :       }
    4709             :       rv = NS_OK;
    4710             :     } else {
    4711           0 :       prev_node = node;
    4712           0 :       node = node->Next();
    4713             :     }
    4714             :   }
    4715             :   return rv;
    4716             : }
    4717             : 
    4718             : static void
    4719           0 : CacheDataAppendElement(CacheData* aData)
    4720             : {
    4721           0 :   if (!gCacheData) {
    4722           0 :     MOZ_CRASH_UNSAFE_PRINTF("!gCacheData: %s", gCacheDataDesc);
    4723             :   }
    4724           0 :   gCacheData->AppendElement(aData);
    4725           0 : }
    4726             : 
    4727             : static void
    4728           0 : BoolVarChanged(const char* aPref, void* aClosure)
    4729             : {
    4730           0 :   CacheData* cache = static_cast<CacheData*>(aClosure);
    4731           0 :   *static_cast<bool*>(cache->mCacheLocation) =
    4732           0 :     Preferences::GetBool(aPref, cache->mDefaultValueBool);
    4733           0 : }
    4734             : 
    4735             : /* static */ nsresult
    4736           0 : Preferences::AddBoolVarCache(bool* aCache,
    4737             :                              const char* aPref,
    4738             :                              bool aDefault,
    4739             :                              bool aSkipAssignment)
    4740             : {
    4741           0 :   AssertNotAlreadyCached("bool", aPref, aCache);
    4742           0 :   if (!aSkipAssignment) {
    4743           0 :     *aCache = GetBool(aPref, aDefault);
    4744             :   }
    4745           0 :   CacheData* data = new CacheData();
    4746           0 :   data->mCacheLocation = aCache;
    4747           0 :   data->mDefaultValueBool = aDefault;
    4748           0 :   CacheDataAppendElement(data);
    4749             :   Preferences::RegisterCallback(BoolVarChanged,
    4750             :                                 aPref,
    4751             :                                 data,
    4752             :                                 Preferences::ExactMatch,
    4753           0 :                                 /* isPriority */ true);
    4754           0 :   return NS_OK;
    4755             : }
    4756             : 
    4757             : template<MemoryOrdering Order>
    4758             : static void
    4759           0 : AtomicBoolVarChanged(const char* aPref, void* aClosure)
    4760             : {
    4761           0 :   CacheData* cache = static_cast<CacheData*>(aClosure);
    4762           0 :   *static_cast<Atomic<bool, Order>*>(cache->mCacheLocation) =
    4763           0 :     Preferences::GetBool(aPref, cache->mDefaultValueBool);
    4764           0 : }
    4765             : 
    4766             : template<MemoryOrdering Order>
    4767             : /* static */ nsresult
    4768           0 : Preferences::AddAtomicBoolVarCache(Atomic<bool, Order>* aCache,
    4769             :                                    const char* aPref,
    4770             :                                    bool aDefault,
    4771             :                                    bool aSkipAssignment)
    4772             : {
    4773           0 :   AssertNotAlreadyCached("bool", aPref, aCache);
    4774           0 :   if (!aSkipAssignment) {
    4775           0 :     *aCache = GetBool(aPref, aDefault);
    4776             :   }
    4777           0 :   CacheData* data = new CacheData();
    4778           0 :   data->mCacheLocation = aCache;
    4779           0 :   data->mDefaultValueBool = aDefault;
    4780           0 :   CacheDataAppendElement(data);
    4781           0 :   Preferences::RegisterCallback(AtomicBoolVarChanged<Order>,
    4782             :                                 aPref,
    4783             :                                 data,
    4784             :                                 Preferences::ExactMatch,
    4785             :                                 /* isPriority */ true);
    4786           0 :   return NS_OK;
    4787             : }
    4788             : 
    4789             : static void
    4790           0 : IntVarChanged(const char* aPref, void* aClosure)
    4791             : {
    4792           0 :   CacheData* cache = static_cast<CacheData*>(aClosure);
    4793           0 :   *static_cast<int32_t*>(cache->mCacheLocation) =
    4794           0 :     Preferences::GetInt(aPref, cache->mDefaultValueInt);
    4795           0 : }
    4796             : 
    4797             : /* static */ nsresult
    4798           0 : Preferences::AddIntVarCache(int32_t* aCache,
    4799             :                             const char* aPref,
    4800             :                             int32_t aDefault,
    4801             :                             bool aSkipAssignment)
    4802             : {
    4803           0 :   AssertNotAlreadyCached("int", aPref, aCache);
    4804           0 :   if (!aSkipAssignment) {
    4805           0 :     *aCache = GetInt(aPref, aDefault);
    4806             :   }
    4807           0 :   CacheData* data = new CacheData();
    4808           0 :   data->mCacheLocation = aCache;
    4809           0 :   data->mDefaultValueInt = aDefault;
    4810           0 :   CacheDataAppendElement(data);
    4811             :   Preferences::RegisterCallback(
    4812           0 :     IntVarChanged, aPref, data, Preferences::ExactMatch, /* isPriority */ true);
    4813           0 :   return NS_OK;
    4814             : }
    4815             : 
    4816             : template<MemoryOrdering Order>
    4817             : static void
    4818           0 : AtomicIntVarChanged(const char* aPref, void* aClosure)
    4819             : {
    4820           0 :   CacheData* cache = static_cast<CacheData*>(aClosure);
    4821           0 :   *static_cast<Atomic<int32_t, Order>*>(cache->mCacheLocation) =
    4822           0 :     Preferences::GetInt(aPref, cache->mDefaultValueUint);
    4823           0 : }
    4824             : 
    4825             : template<MemoryOrdering Order>
    4826             : /* static */ nsresult
    4827           0 : Preferences::AddAtomicIntVarCache(Atomic<int32_t, Order>* aCache,
    4828             :                                   const char* aPref,
    4829             :                                   int32_t aDefault,
    4830             :                                   bool aSkipAssignment)
    4831             : {
    4832           0 :   AssertNotAlreadyCached("int", aPref, aCache);
    4833           0 :   if (!aSkipAssignment) {
    4834           0 :     *aCache = GetInt(aPref, aDefault);
    4835             :   }
    4836           0 :   CacheData* data = new CacheData();
    4837           0 :   data->mCacheLocation = aCache;
    4838           0 :   data->mDefaultValueUint = aDefault;
    4839           0 :   CacheDataAppendElement(data);
    4840           0 :   Preferences::RegisterCallback(AtomicIntVarChanged<Order>,
    4841             :                                 aPref,
    4842             :                                 data,
    4843             :                                 Preferences::ExactMatch,
    4844             :                                 /* isPriority */ true);
    4845           0 :   return NS_OK;
    4846             : }
    4847             : 
    4848             : static void
    4849           0 : UintVarChanged(const char* aPref, void* aClosure)
    4850             : {
    4851           0 :   CacheData* cache = static_cast<CacheData*>(aClosure);
    4852           0 :   *static_cast<uint32_t*>(cache->mCacheLocation) =
    4853           0 :     Preferences::GetUint(aPref, cache->mDefaultValueUint);
    4854           0 : }
    4855             : 
    4856             : /* static */ nsresult
    4857           0 : Preferences::AddUintVarCache(uint32_t* aCache,
    4858             :                              const char* aPref,
    4859             :                              uint32_t aDefault,
    4860             :                              bool aSkipAssignment)
    4861             : {
    4862           0 :   AssertNotAlreadyCached("uint", aPref, aCache);
    4863           0 :   if (!aSkipAssignment) {
    4864           0 :     *aCache = GetUint(aPref, aDefault);
    4865             :   }
    4866           0 :   CacheData* data = new CacheData();
    4867           0 :   data->mCacheLocation = aCache;
    4868           0 :   data->mDefaultValueUint = aDefault;
    4869           0 :   CacheDataAppendElement(data);
    4870             :   Preferences::RegisterCallback(UintVarChanged,
    4871             :                                 aPref,
    4872             :                                 data,
    4873             :                                 Preferences::ExactMatch,
    4874           0 :                                 /* isPriority */ true);
    4875           0 :   return NS_OK;
    4876             : }
    4877             : 
    4878             : template<MemoryOrdering Order>
    4879             : static void
    4880           0 : AtomicUintVarChanged(const char* aPref, void* aClosure)
    4881             : {
    4882           0 :   CacheData* cache = static_cast<CacheData*>(aClosure);
    4883           0 :   *static_cast<Atomic<uint32_t, Order>*>(cache->mCacheLocation) =
    4884             :     Preferences::GetUint(aPref, cache->mDefaultValueUint);
    4885           0 : }
    4886             : 
    4887             : template<MemoryOrdering Order>
    4888             : /* static */ nsresult
    4889           0 : Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Order>* aCache,
    4890             :                                    const char* aPref,
    4891             :                                    uint32_t aDefault,
    4892             :                                    bool aSkipAssignment)
    4893             : {
    4894           0 :   AssertNotAlreadyCached("uint", aPref, aCache);
    4895           0 :   if (!aSkipAssignment) {
    4896           0 :     *aCache = GetUint(aPref, aDefault);
    4897             :   }
    4898           0 :   CacheData* data = new CacheData();
    4899           0 :   data->mCacheLocation = aCache;
    4900           0 :   data->mDefaultValueUint = aDefault;
    4901           0 :   CacheDataAppendElement(data);
    4902           0 :   Preferences::RegisterCallback(AtomicUintVarChanged<Order>,
    4903             :                                 aPref,
    4904             :                                 data,
    4905             :                                 Preferences::ExactMatch,
    4906             :                                 /* isPriority */ true);
    4907           0 :   return NS_OK;
    4908             : }
    4909             : 
    4910             : // Since the definition of template functions is not in a header file, we
    4911             : // need to explicitly specify the instantiations that are required. Currently
    4912             : // limited orders are needed and therefore implemented.
    4913             : template nsresult
    4914             : Preferences::AddAtomicBoolVarCache(Atomic<bool, Relaxed>*,
    4915             :                                    const char*,
    4916             :                                    bool,
    4917             :                                    bool);
    4918             : 
    4919             : template nsresult
    4920             : Preferences::AddAtomicBoolVarCache(Atomic<bool, ReleaseAcquire>*,
    4921             :                                    const char*,
    4922             :                                    bool,
    4923             :                                    bool);
    4924             : 
    4925             : template nsresult
    4926             : Preferences::AddAtomicBoolVarCache(Atomic<bool, SequentiallyConsistent>*,
    4927             :                                    const char*,
    4928             :                                    bool,
    4929             :                                    bool);
    4930             : 
    4931             : template nsresult
    4932             : Preferences::AddAtomicIntVarCache(Atomic<int32_t, Relaxed>*,
    4933             :                                   const char*,
    4934             :                                   int32_t,
    4935             :                                   bool);
    4936             : 
    4937             : template nsresult
    4938             : Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Relaxed>*,
    4939             :                                    const char*,
    4940             :                                    uint32_t,
    4941             :                                    bool);
    4942             : 
    4943             : template nsresult
    4944             : Preferences::AddAtomicUintVarCache(Atomic<uint32_t, ReleaseAcquire>*,
    4945             :                                    const char*,
    4946             :                                    uint32_t,
    4947             :                                    bool);
    4948             : 
    4949             : template nsresult
    4950             : Preferences::AddAtomicUintVarCache(Atomic<uint32_t, SequentiallyConsistent>*,
    4951             :                                    const char*,
    4952             :                                    uint32_t,
    4953             :                                    bool);
    4954             : 
    4955             : static void
    4956           0 : FloatVarChanged(const char* aPref, void* aClosure)
    4957             : {
    4958           0 :   CacheData* cache = static_cast<CacheData*>(aClosure);
    4959           0 :   *static_cast<float*>(cache->mCacheLocation) =
    4960           0 :     Preferences::GetFloat(aPref, cache->mDefaultValueFloat);
    4961           0 : }
    4962             : 
    4963             : /* static */ nsresult
    4964           0 : Preferences::AddFloatVarCache(float* aCache,
    4965             :                               const char* aPref,
    4966             :                               float aDefault,
    4967             :                               bool aSkipAssignment)
    4968             : {
    4969           0 :   AssertNotAlreadyCached("float", aPref, aCache);
    4970           0 :   if (!aSkipAssignment) {
    4971           0 :     *aCache = GetFloat(aPref, aDefault);
    4972             :   }
    4973           0 :   CacheData* data = new CacheData();
    4974           0 :   data->mCacheLocation = aCache;
    4975           0 :   data->mDefaultValueFloat = aDefault;
    4976           0 :   CacheDataAppendElement(data);
    4977             :   Preferences::RegisterCallback(FloatVarChanged,
    4978             :                                 aPref,
    4979             :                                 data,
    4980             :                                 Preferences::ExactMatch,
    4981           0 :                                 /* isPriority */ true);
    4982           0 :   return NS_OK;
    4983             : }
    4984             : 
    4985             : // For a VarCache pref like this:
    4986             : //
    4987             : //   VARCACHE_PREF("my.varcache", my_varcache, int32_t, 99)
    4988             : //
    4989             : // we generate a static variable definition:
    4990             : //
    4991             : //   int32_t StaticPrefs::sVarCache_my_varcache(99);
    4992             : //
    4993             : #define PREF(name, cpp_type, value)
    4994             : #define VARCACHE_PREF(name, id, cpp_type, value)                               \
    4995             :   cpp_type StaticPrefs::sVarCache_##id(value);
    4996             : #include "mozilla/StaticPrefList.h"
    4997             : #undef PREF
    4998             : #undef VARCACHE_PREF
    4999             : 
    5000             : // The SetPref_*() functions below end in a `_<type>` suffix because they are
    5001             : // used by the PREF macro definition in InitAll() below.
    5002             : 
    5003             : static void
    5004           0 : SetPref_bool(const char* aName, bool aDefaultValue)
    5005             : {
    5006             :   PrefValue value;
    5007           0 :   value.mBoolVal = aDefaultValue;
    5008             :   pref_SetPref(aName,
    5009             :                PrefType::Bool,
    5010             :                PrefValueKind::Default,
    5011             :                value,
    5012             :                /* isSticky */ false,
    5013             :                /* isLocked */ false,
    5014           0 :                /* fromInit */ true);
    5015           0 : }
    5016             : 
    5017             : static void
    5018           0 : SetPref_int32_t(const char* aName, int32_t aDefaultValue)
    5019             : {
    5020             :   PrefValue value;
    5021           0 :   value.mIntVal = aDefaultValue;
    5022             :   pref_SetPref(aName,
    5023             :                PrefType::Int,
    5024             :                PrefValueKind::Default,
    5025             :                value,
    5026             :                /* isSticky */ false,
    5027             :                /* isLocked */ false,
    5028           0 :                /* fromInit */ true);
    5029           0 : }
    5030             : 
    5031             : static void
    5032             : SetPref_float(const char* aName, float aDefaultValue)
    5033             : {
    5034             :   PrefValue value;
    5035             :   nsPrintfCString defaultValue("%f", aDefaultValue);
    5036             :   value.mStringVal = defaultValue.get();
    5037             :   pref_SetPref(aName,
    5038             :                PrefType::String,
    5039             :                PrefValueKind::Default,
    5040             :                value,
    5041             :                /* isSticky */ false,
    5042             :                /* isLocked */ false,
    5043             :                /* fromInit */ true);
    5044             : }
    5045             : 
    5046             : // XXX: this will eventually become used
    5047             : MOZ_MAYBE_UNUSED static void
    5048             : SetPref_String(const char* aName, const char* aDefaultValue)
    5049             : {
    5050             :   PrefValue value;
    5051             :   value.mStringVal = aDefaultValue;
    5052             :   pref_SetPref(aName,
    5053             :                PrefType::String,
    5054             :                PrefValueKind::Default,
    5055             :                value,
    5056             :                /* isSticky */ false,
    5057             :                /* isLocked */ false,
    5058             :                /* fromInit */ true);
    5059             : }
    5060             : 
    5061             : static void
    5062           0 : InitVarCachePref(const char* aName,
    5063             :                  bool* aCache,
    5064             :                  bool aDefaultValue,
    5065             :                  bool aIsStartup)
    5066             : {
    5067           0 :   SetPref_bool(aName, aDefaultValue);
    5068           0 :   *aCache = aDefaultValue;
    5069           0 :   if (aIsStartup) {
    5070           0 :     Preferences::AddBoolVarCache(aCache, aName, aDefaultValue, true);
    5071             :   }
    5072           0 : }
    5073             : 
    5074             : template<MemoryOrdering Order>
    5075             : static void
    5076          21 : InitVarCachePref(const char* aName,
    5077             :                  Atomic<bool, Order>* aCache,
    5078             :                  bool aDefaultValue,
    5079             :                  bool aIsStartup)
    5080             : {
    5081          21 :   SetPref_bool(aName, aDefaultValue);
    5082          42 :   *aCache = aDefaultValue;
    5083          21 :   if (aIsStartup) {
    5084          21 :     Preferences::AddAtomicBoolVarCache(aCache, aName, aDefaultValue, true);
    5085             :   }
    5086          21 : }
    5087             : 
    5088             : // XXX: this will eventually become used
    5089             : MOZ_MAYBE_UNUSED static void
    5090           0 : InitVarCachePref(const char* aName,
    5091             :                  int32_t* aCache,
    5092             :                  int32_t aDefaultValue,
    5093             :                  bool aIsStartup)
    5094             : {
    5095           0 :   SetPref_int32_t(aName, aDefaultValue);
    5096           0 :   *aCache = aDefaultValue;
    5097           0 :   if (aIsStartup) {
    5098           0 :     Preferences::AddIntVarCache(aCache, aName, aDefaultValue, true);
    5099             :   }
    5100           0 : }
    5101             : 
    5102             : template<MemoryOrdering Order>
    5103             : static void
    5104           5 : InitVarCachePref(const char* aName,
    5105             :                  Atomic<int32_t, Order>* aCache,
    5106             :                  int32_t aDefaultValue,
    5107             :                  bool aIsStartup)
    5108             : {
    5109           5 :   SetPref_int32_t(aName, aDefaultValue);
    5110          10 :   *aCache = aDefaultValue;
    5111           5 :   if (aIsStartup) {
    5112           5 :     Preferences::AddAtomicIntVarCache(aCache, aName, aDefaultValue, true);
    5113             :   }
    5114           5 : }
    5115             : 
    5116             : static void
    5117           0 : InitVarCachePref(const char* aName,
    5118             :                  uint32_t* aCache,
    5119             :                  uint32_t aDefaultValue,
    5120             :                  bool aIsStartup)
    5121             : {
    5122           0 :   SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue));
    5123           0 :   *aCache = aDefaultValue;
    5124           0 :   if (aIsStartup) {
    5125           0 :     Preferences::AddUintVarCache(aCache, aName, aDefaultValue, true);
    5126             :   }
    5127           0 : }
    5128             : 
    5129             : template<MemoryOrdering Order>
    5130             : static void
    5131           0 : InitVarCachePref(const char* aName,
    5132             :                  Atomic<uint32_t, Order>* aCache,
    5133             :                  uint32_t aDefaultValue,
    5134             :                  bool aIsStartup)
    5135             : {
    5136           0 :   SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue));
    5137           0 :   *aCache = aDefaultValue;
    5138           0 :   if (aIsStartup) {
    5139           0 :     Preferences::AddAtomicUintVarCache(aCache, aName, aDefaultValue, true);
    5140             :   }
    5141           0 : }
    5142             : 
    5143             : // XXX: this will eventually become used
    5144             : MOZ_MAYBE_UNUSED static void
    5145             : InitVarCachePref(const char* aName,
    5146             :                  float* aCache,
    5147             :                  float aDefaultValue,
    5148             :                  bool aIsStartup)
    5149             : {
    5150             :   SetPref_float(aName, aDefaultValue);
    5151             :   *aCache = aDefaultValue;
    5152             :   if (aIsStartup) {
    5153             :     Preferences::AddFloatVarCache(aCache, aName, aDefaultValue, true);
    5154             :   }
    5155             : }
    5156             : 
    5157             : /* static */ void
    5158           0 : StaticPrefs::InitAll(bool aIsStartup)
    5159             : {
    5160             : // For prefs like these:
    5161             : //
    5162             : //   PREF("foo.bar.baz", bool, true)
    5163             : //   VARCACHE_PREF("my.varcache", my_varcache, int32_t, 99)
    5164             : //
    5165             : // we generate registration calls:
    5166             : //
    5167             : //   SetPref_bool("foo.bar.baz", true);
    5168             : //   InitVarCachePref("my.varcache", &StaticPrefs::sVarCache_my_varcache, 99,
    5169             : //                    aIsStartup);
    5170             : //
    5171             : // The SetPref_*() functions have a type suffix to avoid ambiguity between
    5172             : // prefs having int32_t and float default values. That suffix is not needed for
    5173             : // the InitVarCachePref() functions because they take a pointer parameter,
    5174             : // which prevents automatic int-to-float coercion.
    5175             : #define PREF(name, cpp_type, value) SetPref_##cpp_type(name, value);
    5176             : #define VARCACHE_PREF(name, id, cpp_type, value)                               \
    5177             :   InitVarCachePref(name, &StaticPrefs::sVarCache_##id, value, aIsStartup);
    5178             : #include "mozilla/StaticPrefList.h"
    5179             : #undef PREF
    5180             : #undef VARCACHE_PREF
    5181           0 : }
    5182             : 
    5183             : } // namespace mozilla
    5184             : 
    5185             : #undef ENSURE_PARENT_PROCESS
    5186             : 
    5187             : //===========================================================================
    5188             : // Module and factory stuff
    5189             : //===========================================================================
    5190             : 
    5191           0 : NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(Preferences,
    5192             :                                          Preferences::GetInstanceForService)
    5193           0 : NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrefLocalizedString, Init)
    5194           0 : NS_GENERIC_FACTORY_CONSTRUCTOR(nsRelativeFilePref)
    5195             : 
    5196             : static NS_DEFINE_CID(kPrefServiceCID, NS_PREFSERVICE_CID);
    5197             : static NS_DEFINE_CID(kPrefLocalizedStringCID, NS_PREFLOCALIZEDSTRING_CID);
    5198             : static NS_DEFINE_CID(kRelativeFilePrefCID, NS_RELATIVEFILEPREF_CID);
    5199             : 
    5200             : static mozilla::Module::CIDEntry kPrefCIDs[] = {
    5201             :   { &kPrefServiceCID, true, nullptr, PreferencesConstructor },
    5202             :   { &kPrefLocalizedStringCID,
    5203             :     false,
    5204             :     nullptr,
    5205             :     nsPrefLocalizedStringConstructor },
    5206             :   { &kRelativeFilePrefCID, false, nullptr, nsRelativeFilePrefConstructor },
    5207             :   { nullptr }
    5208             : };
    5209             : 
    5210             : static mozilla::Module::ContractIDEntry kPrefContracts[] = {
    5211             :   { NS_PREFSERVICE_CONTRACTID, &kPrefServiceCID },
    5212             :   { NS_PREFLOCALIZEDSTRING_CONTRACTID, &kPrefLocalizedStringCID },
    5213             :   { NS_RELATIVEFILEPREF_CONTRACTID, &kRelativeFilePrefCID },
    5214             :   { nullptr }
    5215             : };
    5216             : 
    5217             : static void
    5218           0 : UnloadPrefsModule()
    5219             : {
    5220           0 :   Preferences::Shutdown();
    5221           0 : }
    5222             : 
    5223             : static const mozilla::Module kPrefModule = { mozilla::Module::kVersion,
    5224             :                                              kPrefCIDs,
    5225             :                                              kPrefContracts,
    5226             :                                              nullptr,
    5227             :                                              nullptr,
    5228             :                                              nullptr,
    5229             :                                              UnloadPrefsModule };
    5230             : 
    5231           0 : NSMODULE_DEFN(nsPrefModule) = &kPrefModule;

Generated by: LCOV version 1.13-14-ga5dd952