tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Preferences.cpp (212545B)


      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 // Documentation for libpref is in modules/libpref/docs/index.rst.
      8 
      9 #include <ctype.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 
     13 #include "SharedPrefMap.h"
     14 
     15 #include "base/basictypes.h"
     16 #include "MainThreadUtils.h"
     17 #include "mozilla/AppShutdown.h"
     18 #include "mozilla/ArenaAllocatorExtensions.h"
     19 #include "mozilla/ArenaAllocator.h"
     20 #include "mozilla/Attributes.h"
     21 #include "mozilla/Components.h"
     22 #include "mozilla/dom/PContent.h"
     23 #include "mozilla/dom/Promise.h"
     24 #include "mozilla/dom/RemoteType.h"
     25 #include "mozilla/dom/quota/ResultExtensions.h"
     26 #include "mozilla/glean/LibprefMetrics.h"
     27 #include "mozilla/HashFunctions.h"
     28 #include "mozilla/HashTable.h"
     29 #include "mozilla/HelperMacros.h"
     30 #include "mozilla/Logging.h"
     31 #include "mozilla/Maybe.h"
     32 #include "mozilla/MemoryReporting.h"
     33 #include "mozilla/Omnijar.h"
     34 #include "mozilla/Preferences.h"
     35 #include "mozilla/ProfilerLabels.h"
     36 #include "mozilla/ProfilerMarkers.h"
     37 #include "mozilla/ResultExtensions.h"
     38 #include "mozilla/SchedulerGroup.h"
     39 #include "mozilla/ScopeExit.h"
     40 #include "mozilla/ServoStyleSet.h"
     41 #include "mozilla/SpinEventLoopUntil.h"
     42 #include "mozilla/StaticMutex.h"
     43 #include "mozilla/StaticPrefsAll.h"
     44 #include "mozilla/StaticPtr.h"
     45 #include "mozilla/SyncRunnable.h"
     46 #include "mozilla/Try.h"
     47 #include "mozilla/URLPreloader.h"
     48 #include "mozilla/Variant.h"
     49 #include "mozilla/Vector.h"
     50 #include "nsAppDirectoryServiceDefs.h"
     51 #include "nsCategoryManagerUtils.h"
     52 #include "nsClassHashtable.h"
     53 #include "nsCOMArray.h"
     54 #include "nsCOMPtr.h"
     55 #include "nsComponentManagerUtils.h"
     56 #include "nsContentUtils.h"
     57 #include "nsCRT.h"
     58 #include "nsTHashMap.h"
     59 #include "nsDirectoryServiceDefs.h"
     60 #include "nsIConsoleService.h"
     61 #include "nsIFile.h"
     62 #include "nsIMemoryReporter.h"
     63 #include "nsIObserver.h"
     64 #include "nsIObserverService.h"
     65 #include "nsIOutputStream.h"
     66 #include "nsIPrefBranch.h"
     67 #include "nsIPrefLocalizedString.h"
     68 #include "nsIPrefOverrideMap.h"
     69 #include "nsIRelativeFilePref.h"
     70 #include "nsISafeOutputStream.h"
     71 #include "nsISimpleEnumerator.h"
     72 #include "nsIStringBundle.h"
     73 #include "nsISupportsImpl.h"
     74 #include "nsISupportsPrimitives.h"
     75 #include "nsIZipReader.h"
     76 #include "nsNetUtil.h"
     77 #include "nsPrintfCString.h"
     78 #include "nsProxyRelease.h"
     79 #include "nsReadableUtils.h"
     80 #include "nsRefPtrHashtable.h"
     81 #include "nsRelativeFilePref.h"
     82 #include "nsString.h"
     83 #include "nsTArray.h"
     84 #include "nsThreadUtils.h"
     85 #include "nsTStringHasher.h"
     86 #include "nsUTF8Utils.h"
     87 #include "nsWeakReference.h"
     88 #include "nsXPCOMCID.h"
     89 #include "nsXPCOM.h"
     90 #include "nsXULAppAPI.h"
     91 #include "nsZipArchive.h"
     92 #include "plbase64.h"
     93 #include "PLDHashTable.h"
     94 #include "prdtoa.h"
     95 #include "prlink.h"
     96 #include "xpcpublic.h"
     97 #include "js/RootingAPI.h"
     98 #ifdef MOZ_BACKGROUNDTASKS
     99 #  include "mozilla/BackgroundTasks.h"
    100 #endif
    101 
    102 #ifdef DEBUG
    103 #  include <map>
    104 #endif
    105 
    106 #ifdef MOZ_MEMORY
    107 #  include "mozmemory.h"
    108 #endif
    109 
    110 #ifdef XP_WIN
    111 #  include "windows.h"
    112 #endif
    113 
    114 #if defined(MOZ_WIDGET_GTK)
    115 #  include "mozilla/WidgetUtilsGtk.h"
    116 #endif  // defined(MOZ_WIDGET_GTK)
    117 
    118 #ifdef MOZ_WIDGET_COCOA
    119 #  include "ChannelPrefsUtil.h"
    120 #endif
    121 
    122 using namespace mozilla;
    123 
    124 using dom::Promise;
    125 using ipc::FileDescriptor;
    126 
    127 #ifdef DEBUG
    128 
    129 #  define ENSURE_PARENT_PROCESS(func, pref)                                   \
    130    do {                                                                      \
    131      if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {                             \
    132        nsPrintfCString msg(                                                  \
    133            "ENSURE_PARENT_PROCESS: called %s on %s in a non-parent process", \
    134            func, pref);                                                      \
    135        NS_ERROR(msg.get());                                                  \
    136        return NS_ERROR_NOT_AVAILABLE;                                        \
    137      }                                                                       \
    138    } while (0)
    139 
    140 #else  // DEBUG
    141 
    142 #  define ENSURE_PARENT_PROCESS(func, pref)     \
    143    if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
    144      return NS_ERROR_NOT_AVAILABLE;            \
    145    }
    146 
    147 #endif  // DEBUG
    148 
    149 // Forward declarations.
    150 namespace mozilla::StaticPrefs {
    151 
    152 static void InitAll();
    153 static void StartObservingAlwaysPrefs();
    154 static void InitOncePrefs();
    155 static void InitStaticPrefsFromShared();
    156 static void RegisterOncePrefs(SharedPrefMapBuilder& aBuilder);
    157 static void ShutdownAlwaysPrefs();
    158 
    159 }  // namespace mozilla::StaticPrefs
    160 
    161 //===========================================================================
    162 // Low-level types and operations
    163 //===========================================================================
    164 
    165 typedef nsTArray<nsCString> PrefSaveData;
    166 
    167 // 1 MB should be enough for everyone.
    168 static const uint32_t MAX_PREF_LENGTH = 1 * 1024 * 1024;
    169 // Actually, 4kb should be enough for everyone.
    170 static const uint32_t MAX_ADVISABLE_PREF_LENGTH = 4 * 1024;
    171 
    172 // This is used for pref names and string pref values. We encode the string
    173 // length, then a '/', then the string chars. This encoding means there are no
    174 // special chars that are forbidden or require escaping.
    175 static void SerializeAndAppendString(const nsCString& aChars, nsCString& aStr) {
    176  aStr.AppendInt(uint64_t(aChars.Length()));
    177  aStr.Append('/');
    178  aStr.Append(aChars);
    179 }
    180 
    181 static const char* DeserializeString(const char* aChars, nsCString& aStr) {
    182  const char* p = aChars;
    183  uint32_t length = strtol(p, const_cast<char**>(&p), 10);
    184  MOZ_ASSERT(p[0] == '/');
    185  p++;  // move past the '/'
    186  aStr.Assign(p, length);
    187  p += length;  // move past the string itself
    188  return p;
    189 }
    190 
    191 static void GetPrefsJsPreamble(nsACString& aPreamble) {
    192  // clang-format off
    193  aPreamble.AssignLiteral(
    194    "// Mozilla User Preferences" NS_LINEBREAK
    195    NS_LINEBREAK
    196    "// DO NOT EDIT THIS FILE." NS_LINEBREAK
    197    "//" NS_LINEBREAK
    198    "// If you make changes to this file while the application is running," NS_LINEBREAK
    199    "// the changes will be overwritten when the application exits." NS_LINEBREAK
    200    "//" NS_LINEBREAK
    201    "// To change a preference value, you can either:" NS_LINEBREAK
    202    "// - modify it via the UI (e.g. via about:config in the browser); or" NS_LINEBREAK
    203    "// - set it within a user.js file in your profile." NS_LINEBREAK
    204    NS_LINEBREAK);
    205  // clang-format on
    206 }
    207 
    208 // Keep this in sync with PrefValue in parser/src/lib.rs.
    209 union PrefValue {
    210  // PrefValues within Pref objects own their chars. PrefValues passed around
    211  // as arguments don't own their chars.
    212  const char* mStringVal;
    213  int32_t mIntVal;
    214  bool mBoolVal;
    215 
    216  PrefValue() = default;
    217 
    218  explicit PrefValue(bool aVal) : mBoolVal(aVal) {}
    219 
    220  explicit PrefValue(int32_t aVal) : mIntVal(aVal) {}
    221 
    222  explicit PrefValue(const char* aVal) : mStringVal(aVal) {}
    223 
    224  bool Equals(PrefType aType, PrefValue aValue) {
    225    switch (aType) {
    226      case PrefType::String: {
    227        if (mStringVal && aValue.mStringVal) {
    228          return strcmp(mStringVal, aValue.mStringVal) == 0;
    229        }
    230        if (!mStringVal && !aValue.mStringVal) {
    231          return true;
    232        }
    233        return false;
    234      }
    235 
    236      case PrefType::Int:
    237        return mIntVal == aValue.mIntVal;
    238 
    239      case PrefType::Bool:
    240        return mBoolVal == aValue.mBoolVal;
    241 
    242      default:
    243        MOZ_CRASH("Unhandled enum value");
    244    }
    245  }
    246 
    247  template <typename T>
    248  T Get() const;
    249 
    250  void Init(PrefType aNewType, PrefValue aNewValue) {
    251    if (aNewType == PrefType::String) {
    252      MOZ_ASSERT(aNewValue.mStringVal);
    253      aNewValue.mStringVal = moz_xstrdup(aNewValue.mStringVal);
    254    }
    255    *this = aNewValue;
    256  }
    257 
    258  void Clear(PrefType aType) {
    259    if (aType == PrefType::String) {
    260      free(const_cast<char*>(mStringVal));
    261    }
    262 
    263    // Zero the entire value (regardless of type) via mStringVal.
    264    mStringVal = nullptr;
    265  }
    266 
    267  void Replace(bool aHasValue, PrefType aOldType, PrefType aNewType,
    268               PrefValue aNewValue) {
    269    if (aHasValue) {
    270      Clear(aOldType);
    271    }
    272    Init(aNewType, aNewValue);
    273  }
    274 
    275  void ToDomPrefValue(PrefType aType, dom::PrefValue* aDomValue) {
    276    switch (aType) {
    277      case PrefType::String:
    278        *aDomValue = nsDependentCString(mStringVal);
    279        return;
    280 
    281      case PrefType::Int:
    282        *aDomValue = mIntVal;
    283        return;
    284 
    285      case PrefType::Bool:
    286        *aDomValue = mBoolVal;
    287        return;
    288 
    289      default:
    290        MOZ_CRASH();
    291    }
    292  }
    293 
    294  PrefType FromDomPrefValue(const dom::PrefValue& aDomValue) {
    295    switch (aDomValue.type()) {
    296      case dom::PrefValue::TnsCString:
    297        mStringVal = aDomValue.get_nsCString().get();
    298        return PrefType::String;
    299 
    300      case dom::PrefValue::Tint32_t:
    301        mIntVal = aDomValue.get_int32_t();
    302        return PrefType::Int;
    303 
    304      case dom::PrefValue::Tbool:
    305        mBoolVal = aDomValue.get_bool();
    306        return PrefType::Bool;
    307 
    308      default:
    309        MOZ_CRASH();
    310    }
    311  }
    312 
    313  void SerializeAndAppend(PrefType aType, nsCString& aStr) {
    314    switch (aType) {
    315      case PrefType::Bool:
    316        aStr.Append(mBoolVal ? 'T' : 'F');
    317        break;
    318 
    319      case PrefType::Int:
    320        aStr.AppendInt(mIntVal);
    321        break;
    322 
    323      case PrefType::String: {
    324        SerializeAndAppendString(nsDependentCString(mStringVal), aStr);
    325        break;
    326      }
    327 
    328      case PrefType::None:
    329      default:
    330        MOZ_CRASH();
    331    }
    332  }
    333 
    334  void ToString(PrefType aType, nsCString& aStr) {
    335    switch (aType) {
    336      case PrefType::Bool:
    337        aStr.Append(mBoolVal ? "true" : "false");
    338        break;
    339 
    340      case PrefType::Int:
    341        aStr.AppendInt(mIntVal);
    342        break;
    343 
    344      case PrefType::String: {
    345        aStr.Append(nsDependentCString(mStringVal));
    346        break;
    347      }
    348 
    349      case PrefType::None:
    350      default:;
    351    }
    352  }
    353 
    354  static const char* Deserialize(PrefType aType, const char* aStr,
    355                                 Maybe<dom::PrefValue>* aDomValue) {
    356    const char* p = aStr;
    357 
    358    switch (aType) {
    359      case PrefType::Bool:
    360        if (*p == 'T') {
    361          *aDomValue = Some(true);
    362        } else if (*p == 'F') {
    363          *aDomValue = Some(false);
    364        } else {
    365          *aDomValue = Some(false);
    366          NS_ERROR("bad bool pref value");
    367        }
    368        p++;
    369        return p;
    370 
    371      case PrefType::Int: {
    372        *aDomValue = Some(int32_t(strtol(p, const_cast<char**>(&p), 10)));
    373        return p;
    374      }
    375 
    376      case PrefType::String: {
    377        nsCString str;
    378        p = DeserializeString(p, str);
    379        *aDomValue = Some(str);
    380        return p;
    381      }
    382 
    383      default:
    384        MOZ_CRASH();
    385    }
    386  }
    387 };
    388 
    389 template <>
    390 bool PrefValue::Get() const {
    391  return mBoolVal;
    392 }
    393 
    394 template <>
    395 int32_t PrefValue::Get() const {
    396  return mIntVal;
    397 }
    398 
    399 template <>
    400 nsDependentCString PrefValue::Get() const {
    401  return nsDependentCString(mStringVal);
    402 }
    403 
    404 // Like PrefValue but strings are owned.
    405 class OwnedPrefValue {
    406 public:
    407  explicit OwnedPrefValue(bool aVal) : mPrefValue(aVal) {}
    408  explicit OwnedPrefValue(int32_t aVal) : mPrefValue(aVal) {}
    409  explicit OwnedPrefValue(const char* aVal)
    410      : mOwnedStr(aVal), mPrefValue(mOwnedStr.get()) {}
    411 
    412  PrefValue GetPrefValue() { return mPrefValue; }
    413 
    414 private:
    415  nsCString mOwnedStr;
    416  PrefValue mPrefValue;
    417 };
    418 
    419 class nsPrefOverrideMap : public nsIPrefOverrideMap {
    420  NS_DECL_ISUPPORTS
    421  NS_DECL_NSIPREFOVERRIDEMAP
    422 public:
    423  const HashMap<nsCString, Maybe<OwnedPrefValue>>& Ref() const { return mMap; }
    424 
    425 private:
    426  virtual ~nsPrefOverrideMap() = default;
    427  // Map full pref name to value or Nothing if pref was not defined.
    428  HashMap<nsCString, Maybe<OwnedPrefValue>> mMap;
    429 };
    430 
    431 #ifdef DEBUG
    432 const char* PrefTypeToString(PrefType aType) {
    433  switch (aType) {
    434    case PrefType::None:
    435      return "none";
    436    case PrefType::String:
    437      return "string";
    438    case PrefType::Int:
    439      return "int";
    440    case PrefType::Bool:
    441      return "bool";
    442    default:
    443      MOZ_CRASH("Unhandled enum value");
    444  }
    445 }
    446 #endif
    447 
    448 // Assign to aResult a quoted, escaped copy of aOriginal.
    449 static void StrEscape(const char* aOriginal, nsCString& aResult) {
    450  if (aOriginal == nullptr) {
    451    aResult.AssignLiteral("\"\"");
    452    return;
    453  }
    454 
    455  // JavaScript does not allow quotes, slashes, or line terminators inside
    456  // strings so we must escape them. ECMAScript defines four line terminators,
    457  // but we're only worrying about \r and \n here.  We currently feed our pref
    458  // script to the JS interpreter as Latin-1 so  we won't encounter \u2028
    459  // (line separator) or \u2029 (paragraph separator).
    460  //
    461  // WARNING: There are hints that we may be moving to storing prefs as utf8.
    462  // If we ever feed them to the JS compiler as UTF8 then we'll have to worry
    463  // about the multibyte sequences that would be interpreted as \u2028 and
    464  // \u2029.
    465  const char* p;
    466 
    467  aResult.Assign('"');
    468 
    469  // Paranoid worst case all slashes will free quickly.
    470  for (p = aOriginal; *p; ++p) {
    471    switch (*p) {
    472      case '\n':
    473        aResult.AppendLiteral("\\n");
    474        break;
    475 
    476      case '\r':
    477        aResult.AppendLiteral("\\r");
    478        break;
    479 
    480      case '\\':
    481        aResult.AppendLiteral("\\\\");
    482        break;
    483 
    484      case '\"':
    485        aResult.AppendLiteral("\\\"");
    486        break;
    487 
    488      default:
    489        aResult.Append(*p);
    490        break;
    491    }
    492  }
    493 
    494  aResult.Append('"');
    495 }
    496 
    497 // Mimic the behaviour of nsTStringRepr::ToFloat before bug 840706 to preserve
    498 // error case handling for parsing pref strings. Many callers do not check error
    499 // codes, so the returned values may be used even if an error is set.
    500 //
    501 // This method should never return NaN, but may return +-inf if the provided
    502 // number is too large to fit in a float.
    503 static float ParsePrefFloat(const nsCString& aString, nsresult* aError) {
    504  if (aString.IsEmpty()) {
    505    *aError = NS_ERROR_ILLEGAL_VALUE;
    506    return 0.f;
    507  }
    508 
    509  // PR_strtod does a locale-independent conversion.
    510  char* stopped = nullptr;
    511  float result = PR_strtod(aString.get(), &stopped);
    512 
    513  // Defensively avoid potential breakage caused by returning NaN into
    514  // unsuspecting code. AFAIK this should never happen as PR_strtod cannot
    515  // return NaN as currently configured.
    516  if (std::isnan(result)) {
    517    MOZ_ASSERT_UNREACHABLE("PR_strtod shouldn't return NaN");
    518    *aError = NS_ERROR_ILLEGAL_VALUE;
    519    return 0.f;
    520  }
    521 
    522  *aError = (stopped == aString.EndReading()) ? NS_OK : NS_ERROR_ILLEGAL_VALUE;
    523  return result;
    524 }
    525 
    526 struct PreferenceMarker {
    527  static constexpr Span<const char> MarkerTypeName() {
    528    return MakeStringSpan("Preference");
    529  }
    530  static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
    531                                   const ProfilerString8View& aPrefName,
    532                                   const Maybe<PrefValueKind>& aPrefKind,
    533                                   PrefType aPrefType,
    534                                   const ProfilerString8View& aPrefValue) {
    535    aWriter.StringProperty("prefName", aPrefName);
    536    aWriter.StringProperty("prefKind", PrefValueKindToString(aPrefKind));
    537    aWriter.StringProperty("prefType", PrefTypeToString(aPrefType));
    538    aWriter.StringProperty("prefValue", aPrefValue);
    539  }
    540  static MarkerSchema MarkerTypeDisplay() {
    541    using MS = MarkerSchema;
    542    MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
    543    schema.AddKeyLabelFormat("prefName", "Name", MS::Format::String,
    544                             MS::PayloadFlags::Searchable);
    545    schema.AddKeyLabelFormat("prefKind", "Kind", MS::Format::String);
    546    schema.AddKeyLabelFormat("prefType", "Type", MS::Format::String);
    547    schema.AddKeyLabelFormat("prefValue", "Value", MS::Format::String);
    548    schema.SetTableLabel(
    549        "{marker.data.prefName}: {marker.data.prefValue} "
    550        "({marker.data.prefType})");
    551    return schema;
    552  }
    553 
    554 private:
    555  static Span<const char> PrefValueKindToString(
    556      const Maybe<PrefValueKind>& aKind) {
    557    if (aKind) {
    558      return *aKind == PrefValueKind::Default ? MakeStringSpan("Default")
    559                                              : MakeStringSpan("User");
    560    }
    561    return "Shared";
    562  }
    563 
    564  static Span<const char> PrefTypeToString(PrefType type) {
    565    switch (type) {
    566      case PrefType::None:
    567        return "None";
    568      case PrefType::Int:
    569        return "Int";
    570      case PrefType::Bool:
    571        return "Bool";
    572      case PrefType::String:
    573        return "String";
    574      default:
    575        MOZ_ASSERT_UNREACHABLE("Unknown preference type.");
    576        return "Unknown";
    577    }
    578  }
    579 };
    580 
    581 namespace mozilla {
    582 struct PrefsSizes {
    583  PrefsSizes()
    584      : mHashTable(0),
    585        mPrefValues(0),
    586        mStringValues(0),
    587        mRootBranches(0),
    588        mPrefNameArena(0),
    589        mCallbacksObjects(0),
    590        mCallbacksDomains(0),
    591        mMisc(0) {}
    592 
    593  size_t mHashTable;
    594  size_t mPrefValues;
    595  size_t mStringValues;
    596  size_t mRootBranches;
    597  size_t mPrefNameArena;
    598  size_t mCallbacksObjects;
    599  size_t mCallbacksDomains;
    600  size_t mMisc;
    601 };
    602 }  // namespace mozilla
    603 
    604 static StaticRefPtr<SharedPrefMap> gSharedMap;
    605 
    606 // Arena for Pref names.
    607 // Never access sPrefNameArena directly, always use PrefNameArena()
    608 // because it must only be accessed on the Main Thread
    609 typedef ArenaAllocator<4096, 1> NameArena;
    610 static NameArena* sPrefNameArena;
    611 
    612 static inline NameArena& PrefNameArena() {
    613  MOZ_ASSERT(NS_IsMainThread());
    614 
    615  if (!sPrefNameArena) {
    616    sPrefNameArena = new NameArena();
    617  }
    618  return *sPrefNameArena;
    619 }
    620 
    621 class PrefWrapper;
    622 
    623 // Three forward declarations for immediately below
    624 class Pref;
    625 static bool IsPreferenceSanitized(const Pref* const aPref);
    626 static bool ShouldSanitizePreference(const Pref* const aPref);
    627 
    628 // Note that this never changes in the parent process, and is only read in
    629 // content processes.
    630 static bool gContentProcessPrefsAreInited = false;
    631 
    632 class Pref {
    633 public:
    634  explicit Pref(const nsACString& aName)
    635      : mName(ArenaStrdup(aName, PrefNameArena()), aName.Length()),
    636        mType(static_cast<uint32_t>(PrefType::None)),
    637        mIsSticky(false),
    638        mIsLocked(false),
    639        mIsSanitized(false),
    640        mHasDefaultValue(false),
    641        mHasUserValue(false),
    642        mIsSkippedByIteration(false),
    643        mDefaultValue(),
    644        mUserValue() {}
    645 
    646  ~Pref() {
    647    // There's no need to free mName because it's allocated in memory owned by
    648    // sPrefNameArena.
    649 
    650    mDefaultValue.Clear(Type());
    651    mUserValue.Clear(Type());
    652  }
    653 
    654  const char* Name() const { return mName.get(); }
    655  const nsDependentCString& NameString() const { return mName; }
    656 
    657  // Types.
    658 
    659  PrefType Type() const { return static_cast<PrefType>(mType); }
    660  void SetType(PrefType aType) { mType = static_cast<uint32_t>(aType); }
    661 
    662  bool IsType(PrefType aType) const { return Type() == aType; }
    663  bool IsTypeNone() const { return IsType(PrefType::None); }
    664  bool IsTypeString() const { return IsType(PrefType::String); }
    665  bool IsTypeInt() const { return IsType(PrefType::Int); }
    666  bool IsTypeBool() const { return IsType(PrefType::Bool); }
    667 
    668  // Other properties.
    669 
    670  bool IsLocked() const { return mIsLocked; }
    671  void SetIsLocked(bool aValue) { mIsLocked = aValue; }
    672  bool IsSkippedByIteration() const { return mIsSkippedByIteration; }
    673  void SetIsSkippedByIteration(bool aValue) { mIsSkippedByIteration = aValue; }
    674 
    675  bool IsSticky() const { return mIsSticky; }
    676 
    677  bool IsSanitized() const { return mIsSanitized; }
    678 
    679  bool HasDefaultValue() const { return mHasDefaultValue; }
    680  bool HasUserValue() const { return mHasUserValue; }
    681 
    682  template <typename T>
    683  void AddToMap(SharedPrefMapBuilder& aMap) {
    684    // Sanitized preferences should never be added to the shared pref map
    685    MOZ_ASSERT(!ShouldSanitizePreference(this));
    686    aMap.Add(NameString(),
    687             {HasDefaultValue(), HasUserValue(), IsSticky(), IsLocked(),
    688              /* isSanitized */ false, IsSkippedByIteration()},
    689             HasDefaultValue() ? mDefaultValue.Get<T>() : T(),
    690             HasUserValue() ? mUserValue.Get<T>() : T());
    691  }
    692 
    693  void AddToMap(SharedPrefMapBuilder& aMap) {
    694    if (IsTypeBool()) {
    695      AddToMap<bool>(aMap);
    696    } else if (IsTypeInt()) {
    697      AddToMap<int32_t>(aMap);
    698    } else if (IsTypeString()) {
    699      AddToMap<nsDependentCString>(aMap);
    700    } else {
    701      MOZ_ASSERT_UNREACHABLE("Unexpected preference type");
    702    }
    703  }
    704 
    705  // Other operations.
    706 
    707 #define CHECK_SANITIZATION()                                                \
    708  if (IsPreferenceSanitized(this)) {                                        \
    709    glean::security::pref_usage_content_process.Record(                     \
    710        Some(glean::security::PrefUsageContentProcessExtra{Some(Name())})); \
    711    if (sCrashOnBlocklistedPref) {                                          \
    712      MOZ_CRASH_UNSAFE_PRINTF(                                              \
    713          "Should not access the preference '%s' in the Content Processes", \
    714          Name());                                                          \
    715    }                                                                       \
    716  }
    717 
    718  bool GetBoolValue(PrefValueKind aKind = PrefValueKind::User) const {
    719    MOZ_ASSERT(IsTypeBool());
    720    MOZ_ASSERT(aKind == PrefValueKind::Default ? HasDefaultValue()
    721                                               : HasUserValue());
    722 
    723    CHECK_SANITIZATION();
    724 
    725    return aKind == PrefValueKind::Default ? mDefaultValue.mBoolVal
    726                                           : mUserValue.mBoolVal;
    727  }
    728 
    729  int32_t GetIntValue(PrefValueKind aKind = PrefValueKind::User) const {
    730    MOZ_ASSERT(IsTypeInt());
    731    MOZ_ASSERT(aKind == PrefValueKind::Default ? HasDefaultValue()
    732                                               : HasUserValue());
    733 
    734    CHECK_SANITIZATION();
    735 
    736    return aKind == PrefValueKind::Default ? mDefaultValue.mIntVal
    737                                           : mUserValue.mIntVal;
    738  }
    739 
    740  const char* GetBareStringValue(
    741      PrefValueKind aKind = PrefValueKind::User) const {
    742    MOZ_ASSERT(IsTypeString());
    743    MOZ_ASSERT(aKind == PrefValueKind::Default ? HasDefaultValue()
    744                                               : HasUserValue());
    745 
    746    CHECK_SANITIZATION();
    747 
    748    return aKind == PrefValueKind::Default ? mDefaultValue.mStringVal
    749                                           : mUserValue.mStringVal;
    750  }
    751 
    752 #undef CHECK_SANITIZATION
    753 
    754  nsDependentCString GetStringValue(
    755      PrefValueKind aKind = PrefValueKind::User) const {
    756    return nsDependentCString(GetBareStringValue(aKind));
    757  }
    758 
    759  void ToDomPref(dom::Pref* aDomPref, bool aIsDestinationWebContentProcess) {
    760    MOZ_ASSERT(XRE_IsParentProcess());
    761 
    762    aDomPref->name() = mName;
    763 
    764    aDomPref->isLocked() = mIsLocked;
    765 
    766    aDomPref->isSanitized() =
    767        aIsDestinationWebContentProcess && ShouldSanitizePreference(this);
    768 
    769    if (mHasDefaultValue) {
    770      aDomPref->defaultValue() = Some(dom::PrefValue());
    771      mDefaultValue.ToDomPrefValue(Type(), &aDomPref->defaultValue().ref());
    772    } else {
    773      aDomPref->defaultValue() = Nothing();
    774    }
    775 
    776    if (mHasUserValue &&
    777        !(aDomPref->isSanitized() && sOmitBlocklistedPrefValues)) {
    778      aDomPref->userValue() = Some(dom::PrefValue());
    779      mUserValue.ToDomPrefValue(Type(), &aDomPref->userValue().ref());
    780    } else {
    781      aDomPref->userValue() = Nothing();
    782    }
    783 
    784    MOZ_ASSERT(aDomPref->defaultValue().isNothing() ||
    785               aDomPref->userValue().isNothing() ||
    786               (mIsSanitized && sOmitBlocklistedPrefValues) ||
    787               (aDomPref->defaultValue().ref().type() ==
    788                aDomPref->userValue().ref().type()));
    789  }
    790 
    791  void FromDomPref(const dom::Pref& aDomPref, bool* aValueChanged) {
    792    MOZ_ASSERT(!XRE_IsParentProcess());
    793    MOZ_ASSERT(mName == aDomPref.name());
    794 
    795    mIsLocked = aDomPref.isLocked();
    796    mIsSanitized = aDomPref.isSanitized();
    797 
    798    const Maybe<dom::PrefValue>& defaultValue = aDomPref.defaultValue();
    799    bool defaultValueChanged = false;
    800    if (defaultValue.isSome()) {
    801      PrefValue value;
    802      PrefType type = value.FromDomPrefValue(defaultValue.ref());
    803      if (!ValueMatches(PrefValueKind::Default, type, value)) {
    804        // Type() is PrefType::None if it's a newly added pref. This is ok.
    805        mDefaultValue.Replace(mHasDefaultValue, Type(), type, value);
    806        SetType(type);
    807        mHasDefaultValue = true;
    808        defaultValueChanged = true;
    809      }
    810    } else if (mHasDefaultValue) {
    811      ClearDefaultValue();
    812      defaultValueChanged = true;
    813    }
    814 
    815    const Maybe<dom::PrefValue>& userValue = aDomPref.userValue();
    816    bool userValueChanged = false;
    817    if (userValue.isSome()) {
    818      PrefValue value;
    819      PrefType type = value.FromDomPrefValue(userValue.ref());
    820      if (!ValueMatches(PrefValueKind::User, type, value)) {
    821        // Type() is PrefType::None if it's a newly added pref. This is ok.
    822        mUserValue.Replace(mHasUserValue, Type(), type, value);
    823        SetType(type);
    824        mHasUserValue = true;
    825        userValueChanged = true;
    826      }
    827    } else if (mHasUserValue) {
    828      ClearUserValue();
    829      userValueChanged = true;
    830    }
    831 
    832    if (userValueChanged || (defaultValueChanged && !mHasUserValue)) {
    833      *aValueChanged = true;
    834    }
    835  }
    836 
    837  void FromWrapper(PrefWrapper& aWrapper);
    838 
    839  bool HasAdvisablySizedValues() {
    840    MOZ_ASSERT(XRE_IsParentProcess());
    841 
    842    if (!IsTypeString()) {
    843      return true;
    844    }
    845 
    846    if (mHasDefaultValue &&
    847        strlen(mDefaultValue.mStringVal) > MAX_ADVISABLE_PREF_LENGTH) {
    848      return false;
    849    }
    850 
    851    if (mHasUserValue &&
    852        strlen(mUserValue.mStringVal) > MAX_ADVISABLE_PREF_LENGTH) {
    853      return false;
    854    }
    855 
    856    return true;
    857  }
    858 
    859 private:
    860  bool ValueMatches(PrefValueKind aKind, PrefType aType, PrefValue aValue) {
    861    return IsType(aType) &&
    862           (aKind == PrefValueKind::Default
    863                ? mHasDefaultValue && mDefaultValue.Equals(aType, aValue)
    864                : mHasUserValue && mUserValue.Equals(aType, aValue));
    865  }
    866 
    867 public:
    868  void ClearUserValue() {
    869    mUserValue.Clear(Type());
    870    mHasUserValue = false;
    871  }
    872  void ClearDefaultValue() {
    873    mDefaultValue.Clear(Type());
    874    mHasDefaultValue = false;
    875  }
    876 
    877  nsresult SetDefaultValue(PrefType aType, PrefValue aValue, bool aIsSticky,
    878                           bool aIsLocked, bool* aValueChanged) {
    879    // Types must always match when setting the default value.
    880    if (!IsType(aType)) {
    881      return NS_ERROR_UNEXPECTED;
    882    }
    883 
    884    // Should we set the default value? Only if the pref is not locked, and
    885    // doing so would change the default value.
    886    if (!IsLocked()) {
    887      if (aIsLocked) {
    888        SetIsLocked(true);
    889      }
    890      if (aIsSticky) {
    891        mIsSticky = true;
    892      }
    893      if (!ValueMatches(PrefValueKind::Default, aType, aValue)) {
    894        mDefaultValue.Replace(mHasDefaultValue, Type(), aType, aValue);
    895        mHasDefaultValue = true;
    896        if (!mHasUserValue) {
    897          *aValueChanged = true;
    898        }
    899        // What if we change the default to be the same as the user value?
    900        // Should we clear the user value? Currently we don't.
    901      }
    902    }
    903    return NS_OK;
    904  }
    905 
    906  nsresult SetUserValue(PrefType aType, PrefValue aValue, bool aFromInit,
    907                        bool* aValueChanged) {
    908    // If we have a default value, types must match when setting the user
    909    // value.
    910    if (mHasDefaultValue && !IsType(aType)) {
    911      return NS_ERROR_UNEXPECTED;
    912    }
    913 
    914    // Should we clear the user value, if present? Only if the new user value
    915    // matches the default value, and the pref isn't sticky, and we aren't
    916    // force-setting it during initialization.
    917    if (ValueMatches(PrefValueKind::Default, aType, aValue) && !mIsSticky &&
    918        !aFromInit) {
    919      if (mHasUserValue) {
    920        ClearUserValue();
    921        if (!IsLocked()) {
    922          *aValueChanged = true;
    923        }
    924      }
    925 
    926      // Otherwise, should we set the user value? Only if doing so would
    927      // change the user value.
    928    } else if (!ValueMatches(PrefValueKind::User, aType, aValue)) {
    929      mUserValue.Replace(mHasUserValue, Type(), aType, aValue);
    930      SetType(aType);  // needed because we may have changed the type
    931      mHasUserValue = true;
    932      if (!IsLocked()) {
    933        *aValueChanged = true;
    934      }
    935    }
    936    return NS_OK;
    937  }
    938 
    939  // Prefs are serialized in a manner that mirrors dom::Pref. The two should be
    940  // kept in sync. E.g. if something is added to one it should also be added to
    941  // the other. (It would be nice to be able to use the code generated from
    942  // IPDL for serializing dom::Pref here instead of writing by hand this
    943  // serialization/deserialization. Unfortunately, that generated code is
    944  // difficult to use directly, outside of the IPDL IPC code.)
    945  //
    946  // The grammar for the serialized prefs has the following form.
    947  //
    948  // <pref>         = <type> <locked> <sanitized> ':' <name> ':' <value>? ':'
    949  //                  <value>? '\n'
    950  // <type>         = 'B' | 'I' | 'S'
    951  // <locked>       = 'L' | '-'
    952  // <sanitized>    = 'S' | '-'
    953  // <name>         = <string-value>
    954  // <value>        = <bool-value> | <int-value> | <string-value>
    955  // <bool-value>   = 'T' | 'F'
    956  // <int-value>    = an integer literal accepted by strtol()
    957  // <string-value> = <int-value> '/' <chars>
    958  // <chars>        = any char sequence of length dictated by the preceding
    959  //                  <int-value>.
    960  //
    961  // No whitespace is tolerated between tokens. <type> must match the types of
    962  // the values.
    963  //
    964  // The serialization is text-based, rather than binary, for the following
    965  // reasons.
    966  //
    967  // - The size difference wouldn't be much different between text-based and
    968  //   binary. Most of the space is for strings (pref names and string pref
    969  //   values), which would be the same in both styles. And other differences
    970  //   would be minimal, e.g. small integers are shorter in text but long
    971  //   integers are longer in text.
    972  //
    973  // - Likewise, speed differences should be negligible.
    974  //
    975  // - It's much easier to debug a text-based serialization. E.g. you can
    976  //   print it and inspect it easily in a debugger.
    977  //
    978  // Examples of unlocked boolean prefs:
    979  // - "B--:8/my.bool1:F:T\n"
    980  // - "B--:8/my.bool2:F:\n"
    981  // - "B--:8/my.bool3::T\n"
    982  //
    983  // Examples of sanitized, unlocked boolean prefs:
    984  // - "B-S:8/my.bool1:F:T\n"
    985  // - "B-S:8/my.bool2:F:\n"
    986  // - "B-S:8/my.bool3::T\n"
    987  //
    988  // Examples of locked integer prefs:
    989  // - "IL-:7/my.int1:0:1\n"
    990  // - "IL-:7/my.int2:123:\n"
    991  // - "IL-:7/my.int3::-99\n"
    992  //
    993  // Examples of unlocked string prefs:
    994  // - "S--:10/my.string1:3/abc:4/wxyz\n"
    995  // - "S--:10/my.string2:5/1.234:\n"
    996  // - "S--:10/my.string3::7/string!\n"
    997 
    998  void SerializeAndAppend(nsCString& aStr, bool aSanitizeUserValue) {
    999    switch (Type()) {
   1000      case PrefType::Bool:
   1001        aStr.Append('B');
   1002        break;
   1003 
   1004      case PrefType::Int:
   1005        aStr.Append('I');
   1006        break;
   1007 
   1008      case PrefType::String: {
   1009        aStr.Append('S');
   1010        break;
   1011      }
   1012 
   1013      case PrefType::None:
   1014      default:
   1015        MOZ_CRASH();
   1016    }
   1017 
   1018    aStr.Append(mIsLocked ? 'L' : '-');
   1019    aStr.Append(aSanitizeUserValue ? 'S' : '-');
   1020    aStr.Append(':');
   1021 
   1022    SerializeAndAppendString(mName, aStr);
   1023    aStr.Append(':');
   1024 
   1025    if (mHasDefaultValue) {
   1026      mDefaultValue.SerializeAndAppend(Type(), aStr);
   1027    }
   1028    aStr.Append(':');
   1029 
   1030    if (mHasUserValue && !(aSanitizeUserValue && sOmitBlocklistedPrefValues)) {
   1031      mUserValue.SerializeAndAppend(Type(), aStr);
   1032    }
   1033    aStr.Append('\n');
   1034  }
   1035 
   1036  static const char* Deserialize(const char* aStr, dom::Pref* aDomPref) {
   1037    const char* p = aStr;
   1038 
   1039    // The type.
   1040    PrefType type;
   1041    if (*p == 'B') {
   1042      type = PrefType::Bool;
   1043    } else if (*p == 'I') {
   1044      type = PrefType::Int;
   1045    } else if (*p == 'S') {
   1046      type = PrefType::String;
   1047    } else {
   1048      NS_ERROR("bad pref type");
   1049      type = PrefType::None;
   1050    }
   1051    p++;  // move past the type char
   1052 
   1053    // Locked?
   1054    bool isLocked;
   1055    if (*p == 'L') {
   1056      isLocked = true;
   1057    } else if (*p == '-') {
   1058      isLocked = false;
   1059    } else {
   1060      NS_ERROR("bad pref locked status");
   1061      isLocked = false;
   1062    }
   1063    p++;  // move past the isLocked char
   1064 
   1065    // Sanitize?
   1066    bool isSanitized;
   1067    if (*p == 'S') {
   1068      isSanitized = true;
   1069    } else if (*p == '-') {
   1070      isSanitized = false;
   1071    } else {
   1072      NS_ERROR("bad pref sanitized status");
   1073      isSanitized = false;
   1074    }
   1075    p++;  // move past the isSanitized char
   1076 
   1077    MOZ_ASSERT(*p == ':');
   1078    p++;  // move past the ':'
   1079 
   1080    // The pref name.
   1081    nsCString name;
   1082    p = DeserializeString(p, name);
   1083 
   1084    MOZ_ASSERT(*p == ':');
   1085    p++;  // move past the ':' preceding the default value
   1086 
   1087    Maybe<dom::PrefValue> maybeDefaultValue;
   1088    if (*p != ':') {
   1089      dom::PrefValue defaultValue;
   1090      p = PrefValue::Deserialize(type, p, &maybeDefaultValue);
   1091    }
   1092 
   1093    MOZ_ASSERT(*p == ':');
   1094    p++;  // move past the ':' between the default and user values
   1095 
   1096    Maybe<dom::PrefValue> maybeUserValue;
   1097    if (*p != '\n') {
   1098      dom::PrefValue userValue;
   1099      p = PrefValue::Deserialize(type, p, &maybeUserValue);
   1100    }
   1101 
   1102    MOZ_ASSERT(*p == '\n');
   1103    p++;  // move past the '\n' following the user value
   1104 
   1105    *aDomPref = dom::Pref(name, isLocked, isSanitized, maybeDefaultValue,
   1106                          maybeUserValue);
   1107 
   1108    return p;
   1109  }
   1110 
   1111  void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, PrefsSizes& aSizes) {
   1112    // Note: mName is allocated in sPrefNameArena, measured elsewhere.
   1113    aSizes.mPrefValues += aMallocSizeOf(this);
   1114    if (IsTypeString()) {
   1115      if (mHasDefaultValue) {
   1116        aSizes.mStringValues += aMallocSizeOf(mDefaultValue.mStringVal);
   1117      }
   1118      if (mHasUserValue) {
   1119        aSizes.mStringValues += aMallocSizeOf(mUserValue.mStringVal);
   1120      }
   1121    }
   1122  }
   1123 
   1124  void RelocateName(NameArena* aArena) {
   1125    mName.Rebind(ArenaStrdup(mName.get(), *aArena), mName.Length());
   1126  }
   1127 
   1128 private:
   1129  nsDependentCString mName;  // allocated in sPrefNameArena
   1130 
   1131  uint32_t mType : 2;
   1132  uint32_t mIsSticky : 1;
   1133  uint32_t mIsLocked : 1;
   1134  uint32_t mIsSanitized : 1;
   1135  uint32_t mHasDefaultValue : 1;
   1136  uint32_t mHasUserValue : 1;
   1137  uint32_t mIsSkippedByIteration : 1;
   1138 
   1139  PrefValue mDefaultValue;
   1140  PrefValue mUserValue;
   1141 };
   1142 
   1143 struct PrefHasher {
   1144  using Key = UniquePtr<Pref>;
   1145  using Lookup = const char*;
   1146 
   1147  static HashNumber hash(const Lookup aLookup) { return HashString(aLookup); }
   1148 
   1149  static bool match(const Key& aKey, const Lookup aLookup) {
   1150    if (!aLookup || !aKey->Name()) {
   1151      return false;
   1152    }
   1153 
   1154    return strcmp(aLookup, aKey->Name()) == 0;
   1155  }
   1156 };
   1157 
   1158 using PrefWrapperBase = Variant<Pref*, SharedPrefMap::Pref>;
   1159 class MOZ_STACK_CLASS PrefWrapper : public PrefWrapperBase {
   1160  using SharedPref = const SharedPrefMap::Pref;
   1161 
   1162 public:
   1163  MOZ_IMPLICIT PrefWrapper(Pref* aPref) : PrefWrapperBase(AsVariant(aPref)) {}
   1164 
   1165  MOZ_IMPLICIT PrefWrapper(const SharedPrefMap::Pref& aPref)
   1166      : PrefWrapperBase(AsVariant(aPref)) {}
   1167 
   1168  // Types.
   1169 
   1170  bool IsType(PrefType aType) const { return Type() == aType; }
   1171  bool IsTypeNone() const { return IsType(PrefType::None); }
   1172  bool IsTypeString() const { return IsType(PrefType::String); }
   1173  bool IsTypeInt() const { return IsType(PrefType::Int); }
   1174  bool IsTypeBool() const { return IsType(PrefType::Bool); }
   1175 
   1176 #define FORWARD(retType, method)                                        \
   1177  retType method() const {                                              \
   1178    struct Matcher {                                                    \
   1179      retType operator()(const Pref* aPref) { return aPref->method(); } \
   1180      retType operator()(SharedPref& aPref) { return aPref.method(); }  \
   1181    };                                                                  \
   1182    return match(Matcher());                                            \
   1183  }
   1184 
   1185  FORWARD(bool, IsLocked)
   1186  FORWARD(bool, IsSanitized)
   1187  FORWARD(bool, IsSticky)
   1188  FORWARD(bool, HasDefaultValue)
   1189  FORWARD(bool, HasUserValue)
   1190  FORWARD(const char*, Name)
   1191  FORWARD(nsCString, NameString)
   1192  FORWARD(PrefType, Type)
   1193 #undef FORWARD
   1194 
   1195 #define FORWARD(retType, method)                                             \
   1196  retType method(PrefValueKind aKind = PrefValueKind::User) const {          \
   1197    struct Matcher {                                                         \
   1198      PrefValueKind mKind;                                                   \
   1199                                                                             \
   1200      retType operator()(const Pref* aPref) { return aPref->method(mKind); } \
   1201      retType operator()(SharedPref& aPref) { return aPref.method(mKind); }  \
   1202    };                                                                       \
   1203    return match(Matcher{aKind});                                            \
   1204  }
   1205 
   1206  FORWARD(bool, GetBoolValue)
   1207  FORWARD(int32_t, GetIntValue)
   1208  FORWARD(nsCString, GetStringValue)
   1209  FORWARD(const char*, GetBareStringValue)
   1210 #undef FORWARD
   1211 
   1212  PrefValue GetValue(PrefValueKind aKind = PrefValueKind::User) const {
   1213    switch (Type()) {
   1214      case PrefType::Bool:
   1215        return PrefValue{GetBoolValue(aKind)};
   1216      case PrefType::Int:
   1217        return PrefValue{GetIntValue(aKind)};
   1218      case PrefType::String:
   1219        return PrefValue{GetBareStringValue(aKind)};
   1220      case PrefType::None:
   1221        // This check will be performed in the above functions; but for NoneType
   1222        // we need to do it explicitly, then fall-through.
   1223        if (IsPreferenceSanitized(Name())) {
   1224          glean::security::pref_usage_content_process.Record(Some(
   1225              glean::security::PrefUsageContentProcessExtra{Some(Name())}));
   1226 
   1227          if (sCrashOnBlocklistedPref) {
   1228            MOZ_CRASH_UNSAFE_PRINTF(
   1229                "Should not access the preference '%s' in the Content "
   1230                "Processes",
   1231                Name());
   1232          }
   1233        }
   1234        [[fallthrough]];
   1235      default:
   1236        MOZ_ASSERT_UNREACHABLE("Unexpected pref type");
   1237        return PrefValue{};
   1238    }
   1239  }
   1240 
   1241  Result<PrefValueKind, nsresult> WantValueKind(PrefType aType,
   1242                                                PrefValueKind aKind) const {
   1243    // WantValueKind may short-circuit GetValue functions and cause them to
   1244    // return early, before this check occurs in GetFooValue()
   1245    if (this->is<Pref*>() && IsPreferenceSanitized(this->as<Pref*>())) {
   1246      glean::security::pref_usage_content_process.Record(
   1247          Some(glean::security::PrefUsageContentProcessExtra{Some(Name())}));
   1248 
   1249      if (sCrashOnBlocklistedPref) {
   1250        MOZ_CRASH_UNSAFE_PRINTF(
   1251            "Should not access the preference '%s' in the Content Processes",
   1252            Name());
   1253      }
   1254    } else if (!this->is<Pref*>()) {
   1255      // While we could use Name() above, and avoid the Variant checks, it
   1256      // would less efficient than needed and we can instead do a debug-only
   1257      // assert here to limit the inefficientcy
   1258      MOZ_ASSERT(!IsPreferenceSanitized(Name()),
   1259                 "We should never have a sanitized SharedPrefMap::Pref.");
   1260    }
   1261 
   1262    if (Type() != aType) {
   1263      return Err(NS_ERROR_UNEXPECTED);
   1264    }
   1265 
   1266    if (aKind == PrefValueKind::Default || IsLocked() || !HasUserValue()) {
   1267      if (!HasDefaultValue()) {
   1268        return Err(NS_ERROR_UNEXPECTED);
   1269      }
   1270      return PrefValueKind::Default;
   1271    }
   1272    return PrefValueKind::User;
   1273  }
   1274 
   1275  nsresult GetValue(PrefValueKind aKind, bool* aResult) const {
   1276    PrefValueKind kind = MOZ_TRY(WantValueKind(PrefType::Bool, aKind));
   1277 
   1278    *aResult = GetBoolValue(kind);
   1279    return NS_OK;
   1280  }
   1281 
   1282  nsresult GetValue(PrefValueKind aKind, int32_t* aResult) const {
   1283    PrefValueKind kind = MOZ_TRY(WantValueKind(PrefType::Int, aKind));
   1284 
   1285    *aResult = GetIntValue(kind);
   1286    return NS_OK;
   1287  }
   1288 
   1289  nsresult GetValue(PrefValueKind aKind, uint32_t* aResult) const {
   1290    return GetValue(aKind, reinterpret_cast<int32_t*>(aResult));
   1291  }
   1292 
   1293  nsresult GetValue(PrefValueKind aKind, float* aResult) const {
   1294    nsAutoCString result;
   1295    nsresult rv = GetValue(aKind, result);
   1296    if (NS_SUCCEEDED(rv)) {
   1297      // ParsePrefFloat() does a locale-independent conversion.
   1298      // FIXME: Other `GetValue` overloads don't clobber `aResult` on error.
   1299      *aResult = ParsePrefFloat(result, &rv);
   1300    }
   1301    return rv;
   1302  }
   1303 
   1304  nsresult GetValue(PrefValueKind aKind, nsACString& aResult) const {
   1305    PrefValueKind kind = MOZ_TRY(WantValueKind(PrefType::String, aKind));
   1306 
   1307    aResult = GetStringValue(kind);
   1308    return NS_OK;
   1309  }
   1310 
   1311  nsresult GetValue(PrefValueKind aKind, nsACString* aResult) const {
   1312    return GetValue(aKind, *aResult);
   1313  }
   1314 
   1315  // Returns false if this pref doesn't have a user value worth saving.
   1316  bool UserValueToStringForSaving(nsCString& aStr,
   1317                                  const nsIPrefOverrideMap* aPrefOverrideMap) {
   1318    auto getPrefValue = [&]() -> Result<PrefValue, bool> {
   1319      if (aPrefOverrideMap) {
   1320        auto& overrideMap =
   1321            static_cast<const nsPrefOverrideMap*>(aPrefOverrideMap)->Ref();
   1322        if (auto it = overrideMap.lookup(NameString())) {
   1323          if (it->value().isNothing()) {
   1324            // Don't save pref if pref was not defined before Nimbus set it.
   1325            return Err(false);
   1326          }
   1327          return it->value()->GetPrefValue();
   1328        }
   1329      }
   1330      // Use the user value.
   1331      if (!HasUserValue()) {
   1332        return Err(false);
   1333      }
   1334      return GetValue();
   1335    };
   1336    PrefValue prefValue = MOZ_TRY(getPrefValue());
   1337 
   1338    // Should we save the value, if present? Only if it does not match the
   1339    // default value, or it is sticky.
   1340    if (!ValueMatches(PrefValueKind::Default, Type(), prefValue) ||
   1341        IsSticky()) {
   1342      if (IsTypeString()) {
   1343        StrEscape(prefValue.Get<nsDependentCString>().get(), aStr);
   1344 
   1345      } else if (IsTypeInt()) {
   1346        aStr.AppendInt(prefValue.Get<int32_t>());
   1347 
   1348      } else if (IsTypeBool()) {
   1349        aStr = prefValue.Get<bool>() ? "true" : "false";
   1350      }
   1351      return true;
   1352    }
   1353 
   1354    // Do not save default prefs that haven't changed.
   1355    return false;
   1356  }
   1357 
   1358  bool Matches(PrefType aType, PrefValueKind aKind, PrefValue& aValue,
   1359               bool aIsSticky, bool aIsLocked) const {
   1360    return (ValueMatches(aKind, aType, aValue) && aIsSticky == IsSticky() &&
   1361            aIsLocked == IsLocked());
   1362  }
   1363 
   1364  bool ValueMatches(PrefValueKind aKind, PrefType aType,
   1365                    const PrefValue& aValue) const {
   1366    if (!IsType(aType)) {
   1367      return false;
   1368    }
   1369    if (!(aKind == PrefValueKind::Default ? HasDefaultValue()
   1370                                          : HasUserValue())) {
   1371      return false;
   1372    }
   1373    switch (aType) {
   1374      case PrefType::Bool:
   1375        return GetBoolValue(aKind) == aValue.mBoolVal;
   1376      case PrefType::Int:
   1377        return GetIntValue(aKind) == aValue.mIntVal;
   1378      case PrefType::String:
   1379        return strcmp(GetBareStringValue(aKind), aValue.mStringVal) == 0;
   1380      default:
   1381        MOZ_ASSERT_UNREACHABLE("Unexpected preference type");
   1382        return false;
   1383    }
   1384  }
   1385 };
   1386 
   1387 void Pref::FromWrapper(PrefWrapper& aWrapper) {
   1388  MOZ_ASSERT(aWrapper.is<SharedPrefMap::Pref>());
   1389  auto pref = aWrapper.as<SharedPrefMap::Pref>();
   1390 
   1391  MOZ_ASSERT(IsTypeNone());
   1392  MOZ_ASSERT(mName == pref.NameString());
   1393 
   1394  mType = uint32_t(pref.Type());
   1395 
   1396  mIsLocked = pref.IsLocked();
   1397  mIsSanitized = pref.IsSanitized();
   1398  mIsSticky = pref.IsSticky();
   1399 
   1400  mHasDefaultValue = pref.HasDefaultValue();
   1401  mHasUserValue = pref.HasUserValue();
   1402 
   1403  if (mHasDefaultValue) {
   1404    mDefaultValue.Init(Type(), aWrapper.GetValue(PrefValueKind::Default));
   1405  }
   1406  if (mHasUserValue) {
   1407    mUserValue.Init(Type(), aWrapper.GetValue(PrefValueKind::User));
   1408  }
   1409 }
   1410 
   1411 class CallbackNode {
   1412 public:
   1413  CallbackNode(const nsACString& aDomain, PrefChangedFunc aFunc, void* aData,
   1414               Preferences::MatchKind aMatchKind)
   1415      : mDomain(AsVariant(nsCString(aDomain))),
   1416        mFunc(aFunc),
   1417        mData(aData),
   1418        mNextAndMatchKind(aMatchKind) {}
   1419 
   1420  CallbackNode(const char* const* aDomains, PrefChangedFunc aFunc, void* aData,
   1421               Preferences::MatchKind aMatchKind)
   1422      : mDomain(AsVariant(aDomains)),
   1423        mFunc(aFunc),
   1424        mData(aData),
   1425        mNextAndMatchKind(aMatchKind) {}
   1426 
   1427  // mDomain is a UniquePtr<>, so any uses of Domain() should only be temporary
   1428  // borrows.
   1429  const Variant<nsCString, const char* const*>& Domain() const {
   1430    return mDomain;
   1431  }
   1432 
   1433  PrefChangedFunc Func() const { return mFunc; }
   1434  void ClearFunc() { mFunc = nullptr; }
   1435 
   1436  void* Data() const { return mData; }
   1437 
   1438  Preferences::MatchKind MatchKind() const {
   1439    return static_cast<Preferences::MatchKind>(mNextAndMatchKind &
   1440                                               kMatchKindMask);
   1441  }
   1442 
   1443  bool DomainIs(const nsACString& aDomain) const {
   1444    return mDomain.is<nsCString>() && mDomain.as<nsCString>() == aDomain;
   1445  }
   1446 
   1447  bool DomainIs(const char* const* aPrefs) const {
   1448    return mDomain == AsVariant(aPrefs);
   1449  }
   1450 
   1451  bool Matches(const nsACString& aPrefName) const {
   1452    auto match = [&](const nsACString& aStr) {
   1453      return MatchKind() == Preferences::ExactMatch
   1454                 ? aPrefName == aStr
   1455                 : StringBeginsWith(aPrefName, aStr);
   1456    };
   1457 
   1458    if (mDomain.is<nsCString>()) {
   1459      return match(mDomain.as<nsCString>());
   1460    }
   1461    for (const char* const* ptr = mDomain.as<const char* const*>(); *ptr;
   1462         ptr++) {
   1463      if (match(nsDependentCString(*ptr))) {
   1464        return true;
   1465      }
   1466    }
   1467    return false;
   1468  }
   1469 
   1470  CallbackNode* Next() const {
   1471    return reinterpret_cast<CallbackNode*>(mNextAndMatchKind & kNextMask);
   1472  }
   1473 
   1474  void SetNext(CallbackNode* aNext) {
   1475    uintptr_t matchKind = mNextAndMatchKind & kMatchKindMask;
   1476    mNextAndMatchKind = reinterpret_cast<uintptr_t>(aNext);
   1477    MOZ_ASSERT((mNextAndMatchKind & kMatchKindMask) == 0);
   1478    mNextAndMatchKind |= matchKind;
   1479  }
   1480 
   1481  void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, PrefsSizes& aSizes) {
   1482    aSizes.mCallbacksObjects += aMallocSizeOf(this);
   1483    if (mDomain.is<nsCString>()) {
   1484      aSizes.mCallbacksDomains +=
   1485          mDomain.as<nsCString>().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   1486    }
   1487  }
   1488 
   1489 private:
   1490  static const uintptr_t kMatchKindMask = uintptr_t(0x1);
   1491  static const uintptr_t kNextMask = ~kMatchKindMask;
   1492 
   1493  Variant<nsCString, const char* const*> mDomain;
   1494 
   1495  // If someone attempts to remove the node from the callback list while
   1496  // NotifyCallbacks() is running, |func| is set to nullptr. Such nodes will
   1497  // be removed at the end of NotifyCallbacks().
   1498  PrefChangedFunc mFunc;
   1499  void* mData;
   1500 
   1501  // Conceptually this is two fields:
   1502  // - CallbackNode* mNext;
   1503  // - Preferences::MatchKind mMatchKind;
   1504  // They are combined into a tagged pointer to save memory.
   1505  uintptr_t mNextAndMatchKind;
   1506 };
   1507 
   1508 using PrefsHashTable = HashSet<UniquePtr<Pref>, PrefHasher>;
   1509 
   1510 // The main prefs hash table. Inside a function so we can assert it's only
   1511 // accessed on the main thread. (That assertion can be avoided but only do so
   1512 // with great care!)
   1513 static inline PrefsHashTable*& HashTable(bool aOffMainThread = false) {
   1514  MOZ_ASSERT(NS_IsMainThread() || ServoStyleSet::IsInServoTraversal());
   1515  static PrefsHashTable* sHashTable = nullptr;
   1516  return sHashTable;
   1517 }
   1518 
   1519 #ifdef DEBUG
   1520 // This defines the type used to store our `once` mirrors checker. We can't use
   1521 // HashMap for now due to alignment restrictions when dealing with
   1522 // std::function<void()> (see bug 1557617).
   1523 typedef std::function<void()> AntiFootgunCallback;
   1524 struct CompareStr {
   1525  bool operator()(char const* a, char const* b) const {
   1526    return std::strcmp(a, b) < 0;
   1527  }
   1528 };
   1529 typedef std::map<const char*, AntiFootgunCallback, CompareStr> AntiFootgunMap;
   1530 static StaticAutoPtr<AntiFootgunMap> gOnceStaticPrefsAntiFootgun;
   1531 #endif
   1532 
   1533 // The callback list contains all the priority callbacks followed by the
   1534 // non-priority callbacks. gLastPriorityNode records where the first part ends.
   1535 static CallbackNode* gFirstCallback = nullptr;
   1536 static CallbackNode* gLastPriorityNode = nullptr;
   1537 
   1538 #ifdef DEBUG
   1539 #  define ACCESS_COUNTS
   1540 #endif
   1541 
   1542 #ifdef ACCESS_COUNTS
   1543 using AccessCountsHashTable = nsTHashMap<nsCStringHashKey, uint32_t>;
   1544 static StaticAutoPtr<AccessCountsHashTable> gAccessCounts;
   1545 
   1546 static void AddAccessCount(const nsACString& aPrefName) {
   1547  // FIXME: Servo reads preferences from background threads in unsafe ways (bug
   1548  // 1474789), and triggers assertions here if we try to add usage count entries
   1549  // from background threads.
   1550  if (NS_IsMainThread()) {
   1551    JS::AutoSuppressGCAnalysis nogc;  // Hash functions will not GC.
   1552    uint32_t& count = gAccessCounts->LookupOrInsert(aPrefName);
   1553    count++;
   1554  }
   1555 }
   1556 
   1557 static void AddAccessCount(const char* aPrefName) {
   1558  AddAccessCount(nsDependentCString(aPrefName));
   1559 }
   1560 #else
   1561 [[maybe_unused]] static void AddAccessCount(const nsACString& aPrefName) {}
   1562 
   1563 static void AddAccessCount(const char* aPrefName) {}
   1564 #endif
   1565 
   1566 // These are only used during the call to NotifyCallbacks().
   1567 static bool gCallbacksInProgress = false;
   1568 static bool gShouldCleanupDeadNodes = false;
   1569 
   1570 class PrefsHashIter {
   1571  using Iterator = decltype(HashTable()->modIter());
   1572  using ElemType = Pref*;
   1573 
   1574  Iterator mIter;
   1575 
   1576 public:
   1577  explicit PrefsHashIter(PrefsHashTable* aTable) : mIter(aTable->modIter()) {}
   1578 
   1579  class Elem {
   1580    friend class PrefsHashIter;
   1581 
   1582    PrefsHashIter& mParent;
   1583    bool mDone;
   1584 
   1585    Elem(PrefsHashIter& aIter, bool aDone) : mParent(aIter), mDone(aDone) {}
   1586 
   1587    Iterator& Iter() { return mParent.mIter; }
   1588 
   1589   public:
   1590    Elem& operator*() { return *this; }
   1591 
   1592    ElemType get() {
   1593      if (mDone) {
   1594        return nullptr;
   1595      }
   1596      return Iter().get().get();
   1597    }
   1598    ElemType get() const { return const_cast<Elem*>(this)->get(); }
   1599 
   1600    ElemType operator->() { return get(); }
   1601    ElemType operator->() const { return get(); }
   1602 
   1603    operator ElemType() { return get(); }
   1604 
   1605    void Remove() { Iter().remove(); }
   1606 
   1607    Elem& operator++() {
   1608      MOZ_ASSERT(!mDone);
   1609      Iter().next();
   1610      mDone = Iter().done();
   1611      return *this;
   1612    }
   1613 
   1614    bool operator!=(Elem& other) {
   1615      return mDone != other.mDone || this->get() != other.get();
   1616    }
   1617  };
   1618 
   1619  Elem begin() { return Elem(*this, mIter.done()); }
   1620 
   1621  Elem end() { return Elem(*this, true); }
   1622 };
   1623 
   1624 class PrefsIter {
   1625  using Iterator = decltype(HashTable()->iter());
   1626  using ElemType = PrefWrapper;
   1627 
   1628  using HashElem = PrefsHashIter::Elem;
   1629  using SharedElem = SharedPrefMap::Pref;
   1630 
   1631  using ElemTypeVariant = Variant<HashElem, SharedElem>;
   1632 
   1633  SharedPrefMap* mSharedMap;
   1634  PrefsHashTable* mHashTable;
   1635  PrefsHashIter mIter;
   1636 
   1637  ElemTypeVariant mPos;
   1638  ElemTypeVariant mEnd;
   1639 
   1640  Maybe<PrefWrapper> mEntry;
   1641 
   1642 public:
   1643  PrefsIter(PrefsHashTable* aHashTable, SharedPrefMap* aSharedMap)
   1644      : mSharedMap(aSharedMap),
   1645        mHashTable(aHashTable),
   1646        mIter(aHashTable),
   1647        mPos(AsVariant(mIter.begin())),
   1648        mEnd(AsVariant(mIter.end())) {
   1649    if (Done()) {
   1650      NextIterator();
   1651    }
   1652  }
   1653 
   1654 private:
   1655 #define MATCH(type, ...)                                                \
   1656  do {                                                                  \
   1657    struct Matcher {                                                    \
   1658      PrefsIter& mIter;                                                 \
   1659      type operator()(HashElem& pos) {                                  \
   1660        [[maybe_unused]] HashElem& end = mIter.mEnd.as<HashElem>();     \
   1661        __VA_ARGS__;                                                    \
   1662      }                                                                 \
   1663      type operator()(SharedElem& pos) {                                \
   1664        [[maybe_unused]] SharedElem& end = mIter.mEnd.as<SharedElem>(); \
   1665        __VA_ARGS__;                                                    \
   1666      }                                                                 \
   1667    };                                                                  \
   1668    return mPos.match(Matcher{*this});                                  \
   1669  } while (0);
   1670 
   1671  bool Done() { MATCH(bool, return pos == end); }
   1672 
   1673  PrefWrapper MakeEntry() { MATCH(PrefWrapper, return PrefWrapper(pos)); }
   1674 
   1675  void NextEntry() {
   1676    mEntry.reset();
   1677    MATCH(void, ++pos);
   1678  }
   1679 #undef MATCH
   1680 
   1681  bool Next() {
   1682    NextEntry();
   1683    return !Done() || NextIterator();
   1684  }
   1685 
   1686  bool NextIterator() {
   1687    if (mPos.is<HashElem>() && mSharedMap) {
   1688      mPos = AsVariant(mSharedMap->begin());
   1689      mEnd = AsVariant(mSharedMap->end());
   1690      return !Done();
   1691    }
   1692    return false;
   1693  }
   1694 
   1695  bool IteratingBase() { return mPos.is<SharedElem>(); }
   1696 
   1697  PrefWrapper& Entry() {
   1698    MOZ_ASSERT(!Done());
   1699 
   1700    if (!mEntry.isSome()) {
   1701      mEntry.emplace(MakeEntry());
   1702    }
   1703    return mEntry.ref();
   1704  }
   1705 
   1706 public:
   1707  class Elem {
   1708    friend class PrefsIter;
   1709 
   1710    PrefsIter& mParent;
   1711    bool mDone;
   1712 
   1713    Elem(PrefsIter& aIter, bool aDone) : mParent(aIter), mDone(aDone) {
   1714      SkipDuplicates();
   1715    }
   1716 
   1717    void Next() { mDone = !mParent.Next(); }
   1718 
   1719    void SkipDuplicates() {
   1720      while (!mDone &&
   1721             (mParent.IteratingBase() ? mParent.mHashTable->has(ref().Name())
   1722                                      : ref().IsTypeNone())) {
   1723        Next();
   1724      }
   1725    }
   1726 
   1727   public:
   1728    Elem& operator*() { return *this; }
   1729 
   1730    ElemType& ref() { return mParent.Entry(); }
   1731    const ElemType& ref() const { return const_cast<Elem*>(this)->ref(); }
   1732 
   1733    ElemType* operator->() { return &ref(); }
   1734    const ElemType* operator->() const { return &ref(); }
   1735 
   1736    operator ElemType() { return ref(); }
   1737 
   1738    Elem& operator++() {
   1739      MOZ_ASSERT(!mDone);
   1740      Next();
   1741      SkipDuplicates();
   1742      return *this;
   1743    }
   1744 
   1745    bool operator!=(Elem& other) {
   1746      if (mDone != other.mDone) {
   1747        return true;
   1748      }
   1749      if (mDone) {
   1750        return false;
   1751      }
   1752      return &this->ref() != &other.ref();
   1753    }
   1754  };
   1755 
   1756  Elem begin() { return {*this, Done()}; }
   1757 
   1758  Elem end() { return {*this, true}; }
   1759 };
   1760 
   1761 static Pref* pref_HashTableLookup(const char* aPrefName);
   1762 
   1763 static void NotifyCallbacks(const nsCString& aPrefName,
   1764                            const PrefWrapper* aPref = nullptr);
   1765 
   1766 static void NotifyCallbacks(const nsCString& aPrefName,
   1767                            const PrefWrapper& aPref) {
   1768  NotifyCallbacks(aPrefName, &aPref);
   1769 }
   1770 
   1771 // The approximate number of preferences in the dynamic hashtable for the parent
   1772 // and content processes, respectively. These numbers are used to determine the
   1773 // initial size of the dynamic preference hashtables, and should be chosen to
   1774 // avoid rehashing during normal usage. The actual number of preferences will,
   1775 // or course, change over time, but these numbers only need to be within a
   1776 // binary order of magnitude of the actual values to remain effective.
   1777 //
   1778 // The number for the parent process should reflect the total number of
   1779 // preferences in the database, since the parent process needs to initially
   1780 // build a dynamic hashtable of the entire preference database. The number for
   1781 // the child process should reflect the number of preferences which are likely
   1782 // to change after the startup of the first content process, since content
   1783 // processes only store changed preferences on top of a snapshot of the database
   1784 // created at startup.
   1785 //
   1786 // Note: The capacity of a hashtable doubles when its length reaches an exact
   1787 // power of two. A table with an initial length of 64 is twice as large as one
   1788 // with an initial length of 63. This is important in content processes, where
   1789 // lookup speed is less critical and we pay the price of the additional overhead
   1790 // for each content process. So the initial content length should generally be
   1791 // *under* the next power-of-two larger than its expected length.
   1792 constexpr size_t kHashTableInitialLengthParent = 3000;
   1793 constexpr size_t kHashTableInitialLengthContent = 64;
   1794 
   1795 static PrefSaveData pref_savePrefs(const nsIPrefOverrideMap* aPrefOverrideMap) {
   1796  MOZ_ASSERT(NS_IsMainThread());
   1797 
   1798  PrefSaveData savedPrefs(HashTable()->count());
   1799 
   1800  for (auto& pref : PrefsIter(HashTable(), gSharedMap)) {
   1801    nsAutoCString prefValueStr;
   1802    if (!pref->UserValueToStringForSaving(prefValueStr, aPrefOverrideMap)) {
   1803      continue;
   1804    }
   1805 
   1806    nsAutoCString prefNameStr;
   1807    StrEscape(pref->Name(), prefNameStr);
   1808 
   1809    nsPrintfCString str("user_pref(%s, %s);", prefNameStr.get(),
   1810                        prefValueStr.get());
   1811 
   1812    savedPrefs.AppendElement(str);
   1813  }
   1814 
   1815  return savedPrefs;
   1816 }
   1817 
   1818 static Pref* pref_HashTableLookup(const char* aPrefName) {
   1819  MOZ_ASSERT(NS_IsMainThread() || ServoStyleSet::IsInServoTraversal());
   1820 
   1821  MOZ_ASSERT_IF(!XRE_IsParentProcess(), gContentProcessPrefsAreInited);
   1822 
   1823  // We use readonlyThreadsafeLookup() because we often have concurrent lookups
   1824  // from multiple Stylo threads. This is safe because those threads cannot
   1825  // modify sHashTable, and the main thread is blocked while Stylo threads are
   1826  // doing these lookups.
   1827  auto p = HashTable()->readonlyThreadsafeLookup(aPrefName);
   1828  return p ? p->get() : nullptr;
   1829 }
   1830 
   1831 // While notifying preference callbacks, this holds the wrapper for the
   1832 // preference being notified, in order to optimize lookups.
   1833 //
   1834 // Note: Callbacks and lookups only happen on the main thread, so this is safe
   1835 // to use without locking.
   1836 static const PrefWrapper* gCallbackPref;
   1837 
   1838 Maybe<PrefWrapper> pref_SharedLookup(const char* aPrefName) {
   1839  MOZ_DIAGNOSTIC_ASSERT(gSharedMap, "gSharedMap must be initialized");
   1840  if (Maybe<SharedPrefMap::Pref> pref = gSharedMap->Get(aPrefName)) {
   1841    return Some(*pref);
   1842  }
   1843  return Nothing();
   1844 }
   1845 
   1846 Maybe<PrefWrapper> pref_Lookup(const char* aPrefName,
   1847                               bool aIncludeTypeNone = false) {
   1848  MOZ_ASSERT(NS_IsMainThread() || ServoStyleSet::IsInServoTraversal());
   1849 
   1850  AddAccessCount(aPrefName);
   1851 
   1852  if (gCallbackPref && strcmp(aPrefName, gCallbackPref->Name()) == 0) {
   1853    return Some(*gCallbackPref);
   1854  }
   1855  if (Pref* pref = pref_HashTableLookup(aPrefName)) {
   1856    if (aIncludeTypeNone || !pref->IsTypeNone() || pref->IsSanitized()) {
   1857      return Some(pref);
   1858    }
   1859  } else if (gSharedMap) {
   1860    return pref_SharedLookup(aPrefName);
   1861  }
   1862 
   1863  return Nothing();
   1864 }
   1865 
   1866 static Result<Pref*, nsresult> pref_LookupForModify(
   1867    const char* aPrefName,
   1868    const std::function<bool(const PrefWrapper&)>& aCheckFn) {
   1869  Maybe<PrefWrapper> wrapper =
   1870      pref_Lookup(aPrefName, /* includeTypeNone */ true);
   1871  if (wrapper.isNothing()) {
   1872    return Err(NS_ERROR_INVALID_ARG);
   1873  }
   1874  if (!aCheckFn(*wrapper)) {
   1875    return nullptr;
   1876  }
   1877  if (wrapper->is<Pref*>()) {
   1878    return wrapper->as<Pref*>();
   1879  }
   1880 
   1881  Pref* pref = new Pref(nsDependentCString{aPrefName});
   1882  if (!HashTable()->putNew(aPrefName, pref)) {
   1883    delete pref;
   1884    return Err(NS_ERROR_OUT_OF_MEMORY);
   1885  }
   1886  pref->FromWrapper(*wrapper);
   1887  return pref;
   1888 }
   1889 
   1890 static nsresult pref_SetPref(const nsCString& aPrefName, PrefType aType,
   1891                             PrefValueKind aKind, PrefValue aValue,
   1892                             bool aIsSticky, bool aIsLocked, bool aFromInit) {
   1893  MOZ_ASSERT(XRE_IsParentProcess());
   1894  MOZ_ASSERT(NS_IsMainThread());
   1895 
   1896  if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads)) {
   1897    printf(
   1898        "pref_SetPref: Attempt to write pref %s after XPCOMShutdownThreads "
   1899        "started.\n",
   1900        aPrefName.get());
   1901    if (nsContentUtils::IsInitialized()) {
   1902      xpc_DumpJSStack(true, true, false);
   1903    }
   1904    MOZ_ASSERT(false, "Late preference writes should be avoided.");
   1905    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
   1906  }
   1907 
   1908  if (!HashTable()) {
   1909    return NS_ERROR_OUT_OF_MEMORY;
   1910  }
   1911 
   1912  Pref* pref = nullptr;
   1913  if (gSharedMap) {
   1914    auto result =
   1915        pref_LookupForModify(aPrefName.get(), [&](const PrefWrapper& aWrapper) {
   1916          return !aWrapper.Matches(aType, aKind, aValue, aIsSticky, aIsLocked);
   1917        });
   1918    if (result.isOk() && !(pref = result.unwrap())) {
   1919      // No changes required.
   1920      return NS_OK;
   1921    }
   1922  }
   1923 
   1924  if (!pref) {
   1925    auto p = HashTable()->lookupForAdd(aPrefName.get());
   1926    if (!p) {
   1927      pref = new Pref(aPrefName);
   1928      pref->SetType(aType);
   1929      if (!HashTable()->add(p, pref)) {
   1930        delete pref;
   1931        return NS_ERROR_OUT_OF_MEMORY;
   1932      }
   1933    } else {
   1934      pref = p->get();
   1935    }
   1936  }
   1937 
   1938  bool valueChanged = false;
   1939  nsresult rv;
   1940  if (aKind == PrefValueKind::Default) {
   1941    rv = pref->SetDefaultValue(aType, aValue, aIsSticky, aIsLocked,
   1942                               &valueChanged);
   1943  } else {
   1944    MOZ_ASSERT(!aIsLocked);  // `locked` is disallowed in user pref files
   1945    rv = pref->SetUserValue(aType, aValue, aFromInit, &valueChanged);
   1946  }
   1947  if (NS_FAILED(rv)) {
   1948    NS_WARNING(
   1949        nsPrintfCString("Rejected attempt to change type of pref %s's %s value "
   1950                        "from %s to %s",
   1951                        aPrefName.get(),
   1952                        (aKind == PrefValueKind::Default) ? "default" : "user",
   1953                        PrefTypeToString(pref->Type()), PrefTypeToString(aType))
   1954            .get());
   1955 
   1956    return rv;
   1957  }
   1958 
   1959  if (valueChanged) {
   1960    if (!aFromInit && profiler_thread_is_being_profiled_for_markers()) {
   1961      nsAutoCString value;
   1962      aValue.ToString(aType, value);
   1963      profiler_add_marker(
   1964          "Preference Write", baseprofiler::category::OTHER_PreferenceRead, {},
   1965          PreferenceMarker{}, aPrefName, Some(aKind), aType, value);
   1966    }
   1967 
   1968    if (aKind == PrefValueKind::User) {
   1969      Preferences::HandleDirty();
   1970    }
   1971    NotifyCallbacks(aPrefName, PrefWrapper(pref));
   1972  }
   1973 
   1974  return NS_OK;
   1975 }
   1976 
   1977 // Removes |node| from callback list. Returns the node after the deleted one.
   1978 static CallbackNode* pref_RemoveCallbackNode(CallbackNode* aNode,
   1979                                             CallbackNode* aPrevNode) {
   1980  MOZ_ASSERT(!aPrevNode || aPrevNode->Next() == aNode);
   1981  MOZ_ASSERT(aPrevNode || gFirstCallback == aNode);
   1982  MOZ_ASSERT(!gCallbacksInProgress);
   1983 
   1984  CallbackNode* next_node = aNode->Next();
   1985  if (aPrevNode) {
   1986    aPrevNode->SetNext(next_node);
   1987  } else {
   1988    gFirstCallback = next_node;
   1989  }
   1990  if (gLastPriorityNode == aNode) {
   1991    gLastPriorityNode = aPrevNode;
   1992  }
   1993  delete aNode;
   1994  return next_node;
   1995 }
   1996 
   1997 static void NotifyCallbacks(const nsCString& aPrefName,
   1998                            const PrefWrapper* aPref) {
   1999  bool reentered = gCallbacksInProgress;
   2000 
   2001  gCallbackPref = aPref;
   2002  auto cleanup = MakeScopeExit([]() { gCallbackPref = nullptr; });
   2003 
   2004  // Nodes must not be deleted while gCallbacksInProgress is true.
   2005  // Nodes that need to be deleted are marked for deletion by nulling
   2006  // out the |func| pointer. We release them at the end of this function
   2007  // if we haven't reentered.
   2008  gCallbacksInProgress = true;
   2009 
   2010  for (CallbackNode* node = gFirstCallback; node; node = node->Next()) {
   2011    if (node->Func()) {
   2012      if (node->Matches(aPrefName)) {
   2013        (node->Func())(aPrefName.get(), node->Data());
   2014      }
   2015    }
   2016  }
   2017 
   2018  gCallbacksInProgress = reentered;
   2019 
   2020  if (gShouldCleanupDeadNodes && !gCallbacksInProgress) {
   2021    CallbackNode* prev_node = nullptr;
   2022    CallbackNode* node = gFirstCallback;
   2023 
   2024    while (node) {
   2025      if (!node->Func()) {
   2026        node = pref_RemoveCallbackNode(node, prev_node);
   2027      } else {
   2028        prev_node = node;
   2029        node = node->Next();
   2030      }
   2031    }
   2032    gShouldCleanupDeadNodes = false;
   2033  }
   2034 
   2035 #ifdef DEBUG
   2036  if (XRE_IsParentProcess() &&
   2037      !StaticPrefs::preferences_force_disable_check_once_policy() &&
   2038      (StaticPrefs::preferences_check_once_policy() || xpc::IsInAutomation())) {
   2039    // Check that we aren't modifying a `once`-mirrored pref using that pref
   2040    // name. We have about 100 `once`-mirrored prefs. std::map performs a
   2041    // search in O(log n), so this is fast enough.
   2042    MOZ_ASSERT(gOnceStaticPrefsAntiFootgun);
   2043    auto search = gOnceStaticPrefsAntiFootgun->find(aPrefName.get());
   2044    if (search != gOnceStaticPrefsAntiFootgun->end()) {
   2045      // Run the callback.
   2046      (search->second)();
   2047    }
   2048  }
   2049 #endif
   2050 }
   2051 
   2052 //===========================================================================
   2053 // Prefs parsing
   2054 //===========================================================================
   2055 
   2056 extern "C" {
   2057 
   2058 // Keep this in sync with PrefFn in parser/src/lib.rs.
   2059 typedef void (*PrefsParserPrefFn)(const char* aPrefName, PrefType aType,
   2060                                  PrefValueKind aKind, PrefValue aValue,
   2061                                  bool aIsSticky, bool aIsLocked);
   2062 
   2063 // Keep this in sync with ErrorFn in parser/src/lib.rs.
   2064 //
   2065 // `aMsg` is just a borrow of the string, and must be copied if it is used
   2066 // outside the lifetime of the prefs_parser_parse() call.
   2067 typedef void (*PrefsParserErrorFn)(const char* aMsg);
   2068 
   2069 // Keep this in sync with prefs_parser_parse() in parser/src/lib.rs.
   2070 bool prefs_parser_parse(const char* aPath, PrefValueKind aKind,
   2071                        const char* aBuf, size_t aLen,
   2072                        PrefsParserPrefFn aPrefFn, PrefsParserErrorFn aErrorFn);
   2073 }
   2074 
   2075 class Parser {
   2076 public:
   2077  Parser() = default;
   2078  ~Parser() = default;
   2079 
   2080  bool Parse(PrefValueKind aKind, const char* aPath, const nsCString& aBuf) {
   2081    MOZ_ASSERT(XRE_IsParentProcess());
   2082    return prefs_parser_parse(aPath, aKind, aBuf.get(), aBuf.Length(),
   2083                              HandlePref, HandleError);
   2084  }
   2085 
   2086 private:
   2087  static void HandlePref(const char* aPrefName, PrefType aType,
   2088                         PrefValueKind aKind, PrefValue aValue, bool aIsSticky,
   2089                         bool aIsLocked) {
   2090    MOZ_ASSERT(XRE_IsParentProcess());
   2091    pref_SetPref(nsDependentCString(aPrefName), aType, aKind, aValue, aIsSticky,
   2092                 aIsLocked,
   2093                 /* fromInit */ true);
   2094  }
   2095 
   2096  static void HandleError(const char* aMsg) {
   2097    nsresult rv;
   2098    nsCOMPtr<nsIConsoleService> console =
   2099        do_GetService("@mozilla.org/consoleservice;1", &rv);
   2100    if (NS_SUCCEEDED(rv)) {
   2101      console->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
   2102    }
   2103 #ifdef DEBUG
   2104    NS_ERROR(aMsg);
   2105 #else
   2106    printf_stderr("%s\n", aMsg);
   2107 #endif
   2108  }
   2109 };
   2110 
   2111 // The following code is test code for the gtest.
   2112 
   2113 static void TestParseErrorHandlePref(const char* aPrefName, PrefType aType,
   2114                                     PrefValueKind aKind, PrefValue aValue,
   2115                                     bool aIsSticky, bool aIsLocked) {}
   2116 
   2117 constinit static nsCString gTestParseErrorMsgs;
   2118 
   2119 static void TestParseErrorHandleError(const char* aMsg) {
   2120  gTestParseErrorMsgs.Append(aMsg);
   2121  gTestParseErrorMsgs.Append('\n');
   2122 }
   2123 
   2124 // Keep this in sync with the declaration in test/gtest/Parser.cpp.
   2125 void TestParseError(PrefValueKind aKind, const char* aText,
   2126                    nsCString& aErrorMsg) {
   2127  prefs_parser_parse("test", aKind, aText, strlen(aText),
   2128                     TestParseErrorHandlePref, TestParseErrorHandleError);
   2129 
   2130  // Copy the error messages into the outparam, then clear them from
   2131  // gTestParseErrorMsgs.
   2132  aErrorMsg.Assign(gTestParseErrorMsgs);
   2133  gTestParseErrorMsgs.Truncate();
   2134 }
   2135 
   2136 //===========================================================================
   2137 // nsPrefBranch et al.
   2138 //===========================================================================
   2139 
   2140 namespace mozilla {
   2141 class PreferenceServiceReporter;
   2142 }  // namespace mozilla
   2143 
   2144 class PrefCallback : public PLDHashEntryHdr {
   2145  friend class mozilla::PreferenceServiceReporter;
   2146 
   2147 public:
   2148  typedef PrefCallback* KeyType;
   2149  typedef const PrefCallback* KeyTypePointer;
   2150 
   2151  static const PrefCallback* KeyToPointer(PrefCallback* aKey) { return aKey; }
   2152 
   2153  static PLDHashNumber HashKey(const PrefCallback* aKey) {
   2154    uint32_t hash = HashString(aKey->mDomain);
   2155    return AddToHash(hash, aKey->mCanonical);
   2156  }
   2157 
   2158 public:
   2159  // Create a PrefCallback with a strong reference to its observer.
   2160  PrefCallback(const nsACString& aDomain, nsIObserver* aObserver,
   2161               nsPrefBranch* aBranch)
   2162      : mDomain(aDomain),
   2163        mBranch(aBranch),
   2164        mWeakRef(nullptr),
   2165        mStrongRef(aObserver) {
   2166    MOZ_COUNT_CTOR(PrefCallback);
   2167    nsCOMPtr<nsISupports> canonical = do_QueryInterface(aObserver);
   2168    mCanonical = canonical;
   2169  }
   2170 
   2171  // Create a PrefCallback with a weak reference to its observer.
   2172  PrefCallback(const nsACString& aDomain, nsISupportsWeakReference* aObserver,
   2173               nsPrefBranch* aBranch)
   2174      : mDomain(aDomain),
   2175        mBranch(aBranch),
   2176        mWeakRef(do_GetWeakReference(aObserver)),
   2177        mStrongRef(nullptr) {
   2178    MOZ_COUNT_CTOR(PrefCallback);
   2179    nsCOMPtr<nsISupports> canonical = do_QueryInterface(aObserver);
   2180    mCanonical = canonical;
   2181  }
   2182 
   2183  // This is explicitly not a copy constructor.
   2184  explicit PrefCallback(const PrefCallback*& aCopy)
   2185      : mDomain(aCopy->mDomain),
   2186        mBranch(aCopy->mBranch),
   2187        mWeakRef(aCopy->mWeakRef),
   2188        mStrongRef(aCopy->mStrongRef),
   2189        mCanonical(aCopy->mCanonical) {
   2190    MOZ_COUNT_CTOR(PrefCallback);
   2191  }
   2192 
   2193  PrefCallback(const PrefCallback&) = delete;
   2194  PrefCallback(PrefCallback&&) = default;
   2195 
   2196  MOZ_COUNTED_DTOR(PrefCallback)
   2197 
   2198  bool KeyEquals(const PrefCallback* aKey) const {
   2199    // We want to be able to look up a weakly-referencing PrefCallback after
   2200    // its observer has died so we can remove it from the table. Once the
   2201    // callback's observer dies, its canonical pointer is stale -- in
   2202    // particular, we may have allocated a new observer in the same spot in
   2203    // memory! So we can't just compare canonical pointers to determine whether
   2204    // aKey refers to the same observer as this.
   2205    //
   2206    // Our workaround is based on the way we use this hashtable: When we ask
   2207    // the hashtable to remove a PrefCallback whose weak reference has expired,
   2208    // we use as the key for removal the same object as was inserted into the
   2209    // hashtable. Thus we can say that if one of the keys' weak references has
   2210    // expired, the two keys are equal iff they're the same object.
   2211 
   2212    if (IsExpired() || aKey->IsExpired()) {
   2213      return this == aKey;
   2214    }
   2215 
   2216    if (mCanonical != aKey->mCanonical) {
   2217      return false;
   2218    }
   2219 
   2220    return mDomain.Equals(aKey->mDomain);
   2221  }
   2222 
   2223  PrefCallback* GetKey() const { return const_cast<PrefCallback*>(this); }
   2224 
   2225  // Get a reference to the callback's observer, or null if the observer was
   2226  // weakly referenced and has been destroyed.
   2227  already_AddRefed<nsIObserver> GetObserver() const {
   2228    if (!IsWeak()) {
   2229      nsCOMPtr<nsIObserver> copy = mStrongRef;
   2230      return copy.forget();
   2231    }
   2232 
   2233    nsCOMPtr<nsIObserver> observer = do_QueryReferent(mWeakRef);
   2234    return observer.forget();
   2235  }
   2236 
   2237  const nsCString& GetDomain() const { return mDomain; }
   2238 
   2239  nsPrefBranch* GetPrefBranch() const { return mBranch; }
   2240 
   2241  // Has this callback's weak reference died?
   2242  bool IsExpired() const {
   2243    if (!IsWeak()) return false;
   2244 
   2245    nsCOMPtr<nsIObserver> observer(do_QueryReferent(mWeakRef));
   2246    return !observer;
   2247  }
   2248 
   2249  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
   2250    size_t n = aMallocSizeOf(this);
   2251    n += mDomain.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   2252 
   2253    // All the other fields are non-owning pointers, so we don't measure them.
   2254 
   2255    return n;
   2256  }
   2257 
   2258  enum { ALLOW_MEMMOVE = true };
   2259 
   2260 private:
   2261  nsCString mDomain;
   2262  nsPrefBranch* mBranch;
   2263 
   2264  // Exactly one of mWeakRef and mStrongRef should be non-null.
   2265  nsWeakPtr mWeakRef;
   2266  nsCOMPtr<nsIObserver> mStrongRef;
   2267 
   2268  // We need a canonical nsISupports pointer, per bug 578392.
   2269  nsISupports* mCanonical;
   2270 
   2271  bool IsWeak() const { return !!mWeakRef; }
   2272 };
   2273 
   2274 class nsPrefBranch final : public nsIPrefBranch,
   2275                           public nsIObserver,
   2276                           public nsSupportsWeakReference {
   2277  friend class mozilla::PreferenceServiceReporter;
   2278 
   2279 public:
   2280  NS_DECL_ISUPPORTS
   2281  NS_DECL_NSIPREFBRANCH
   2282  NS_DECL_NSIOBSERVER
   2283 
   2284  nsPrefBranch(const char* aPrefRoot, PrefValueKind aKind);
   2285  nsPrefBranch() = delete;
   2286 
   2287  static void NotifyObserver(const char* aNewpref, void* aData);
   2288 
   2289  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
   2290 
   2291 private:
   2292  using PrefName = nsCString;
   2293 
   2294  virtual ~nsPrefBranch();
   2295 
   2296  int32_t GetRootLength() const { return mPrefRoot.Length(); }
   2297 
   2298  nsresult GetDefaultFromPropertiesFile(const char* aPrefName,
   2299                                        nsAString& aReturn);
   2300 
   2301  // As SetCharPref, but without any check on the length of |aValue|.
   2302  nsresult SetCharPrefNoLengthCheck(const char* aPrefName,
   2303                                    const nsACString& aValue);
   2304 
   2305  // Reject strings that are more than 1Mb, warn if strings are more than 16kb.
   2306  nsresult CheckSanityOfStringLength(const char* aPrefName,
   2307                                     const nsAString& aValue);
   2308  nsresult CheckSanityOfStringLength(const char* aPrefName,
   2309                                     const nsACString& aValue);
   2310  nsresult CheckSanityOfStringLength(const char* aPrefName,
   2311                                     const uint32_t aLength);
   2312 
   2313  void RemoveExpiredCallback(PrefCallback* aCallback);
   2314 
   2315  PrefName GetPrefName(const char* aPrefName) const {
   2316    return GetPrefName(nsDependentCString(aPrefName));
   2317  }
   2318 
   2319  PrefName GetPrefName(const nsACString& aPrefName) const;
   2320 
   2321  void FreeObserverList(void);
   2322 
   2323  const nsCString mPrefRoot;
   2324  PrefValueKind mKind;
   2325 
   2326  bool mFreeingObserverList;
   2327  nsClassHashtable<PrefCallback, PrefCallback> mObservers;
   2328 };
   2329 
   2330 class nsPrefLocalizedString final : public nsIPrefLocalizedString {
   2331 public:
   2332  nsPrefLocalizedString();
   2333 
   2334  NS_DECL_ISUPPORTS
   2335  NS_FORWARD_NSISUPPORTSPRIMITIVE(mUnicodeString->)
   2336  NS_FORWARD_NSISUPPORTSSTRING(mUnicodeString->)
   2337 
   2338  nsresult Init();
   2339 
   2340 private:
   2341  virtual ~nsPrefLocalizedString();
   2342 
   2343  nsCOMPtr<nsISupportsString> mUnicodeString;
   2344 };
   2345 
   2346 //----------------------------------------------------------------------------
   2347 // nsPrefBranch
   2348 //----------------------------------------------------------------------------
   2349 
   2350 nsPrefBranch::nsPrefBranch(const char* aPrefRoot, PrefValueKind aKind)
   2351    : mPrefRoot(aPrefRoot), mKind(aKind), mFreeingObserverList(false) {
   2352  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   2353  if (observerService) {
   2354    ++mRefCnt;  // must be > 0 when we call this, or we'll get deleted!
   2355 
   2356    // Add weakly so we don't have to clean up at shutdown.
   2357    observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
   2358    --mRefCnt;
   2359  }
   2360 }
   2361 
   2362 nsPrefBranch::~nsPrefBranch() { FreeObserverList(); }
   2363 
   2364 NS_IMPL_ISUPPORTS(nsPrefBranch, nsIPrefBranch, nsIObserver,
   2365                  nsISupportsWeakReference)
   2366 
   2367 NS_IMETHODIMP
   2368 nsPrefBranch::GetRoot(nsACString& aRoot) {
   2369  aRoot = mPrefRoot;
   2370  return NS_OK;
   2371 }
   2372 
   2373 NS_IMETHODIMP
   2374 nsPrefBranch::GetPrefType(const char* aPrefName, int32_t* aRetVal) {
   2375  NS_ENSURE_ARG(aPrefName);
   2376 
   2377  const PrefName& prefName = GetPrefName(aPrefName);
   2378  *aRetVal = Preferences::GetType(prefName.get());
   2379  return NS_OK;
   2380 }
   2381 
   2382 NS_IMETHODIMP
   2383 nsPrefBranch::GetBoolPrefWithDefault(const char* aPrefName, bool aDefaultValue,
   2384                                     uint8_t aArgc, bool* aRetVal) {
   2385  nsresult rv = GetBoolPref(aPrefName, aRetVal);
   2386  if (NS_FAILED(rv) && aArgc == 1) {
   2387    *aRetVal = aDefaultValue;
   2388    return NS_OK;
   2389  }
   2390 
   2391  return rv;
   2392 }
   2393 
   2394 NS_IMETHODIMP
   2395 nsPrefBranch::GetBoolPref(const char* aPrefName, bool* aRetVal) {
   2396  NS_ENSURE_ARG(aPrefName);
   2397 
   2398  const PrefName& pref = GetPrefName(aPrefName);
   2399  return Preferences::GetBool(pref.get(), aRetVal, mKind);
   2400 }
   2401 
   2402 NS_IMETHODIMP
   2403 nsPrefBranch::SetBoolPref(const char* aPrefName, bool aValue) {
   2404  NS_ENSURE_ARG(aPrefName);
   2405 
   2406  const PrefName& pref = GetPrefName(aPrefName);
   2407  return Preferences::SetBool(pref.get(), aValue, mKind);
   2408 }
   2409 
   2410 NS_IMETHODIMP
   2411 nsPrefBranch::GetFloatPrefWithDefault(const char* aPrefName,
   2412                                      float aDefaultValue, uint8_t aArgc,
   2413                                      float* aRetVal) {
   2414  nsresult rv = GetFloatPref(aPrefName, aRetVal);
   2415 
   2416  if (NS_FAILED(rv) && aArgc == 1) {
   2417    *aRetVal = aDefaultValue;
   2418    return NS_OK;
   2419  }
   2420 
   2421  return rv;
   2422 }
   2423 
   2424 NS_IMETHODIMP
   2425 nsPrefBranch::GetFloatPref(const char* aPrefName, float* aRetVal) {
   2426  NS_ENSURE_ARG(aPrefName);
   2427 
   2428  nsAutoCString stringVal;
   2429  nsresult rv = GetCharPref(aPrefName, stringVal);
   2430  if (NS_SUCCEEDED(rv)) {
   2431    // ParsePrefFloat() does a locale-independent conversion.
   2432    *aRetVal = ParsePrefFloat(stringVal, &rv);
   2433  }
   2434 
   2435  return rv;
   2436 }
   2437 
   2438 NS_IMETHODIMP
   2439 nsPrefBranch::GetCharPrefWithDefault(const char* aPrefName,
   2440                                     const nsACString& aDefaultValue,
   2441                                     uint8_t aArgc, nsACString& aRetVal) {
   2442  nsresult rv = GetCharPref(aPrefName, aRetVal);
   2443 
   2444  if (NS_FAILED(rv) && aArgc == 1) {
   2445    aRetVal = aDefaultValue;
   2446    return NS_OK;
   2447  }
   2448 
   2449  return rv;
   2450 }
   2451 
   2452 NS_IMETHODIMP
   2453 nsPrefBranch::GetCharPref(const char* aPrefName, nsACString& aRetVal) {
   2454  NS_ENSURE_ARG(aPrefName);
   2455 
   2456  const PrefName& pref = GetPrefName(aPrefName);
   2457  return Preferences::GetCString(pref.get(), aRetVal, mKind);
   2458 }
   2459 
   2460 NS_IMETHODIMP
   2461 nsPrefBranch::SetCharPref(const char* aPrefName, const nsACString& aValue) {
   2462  nsresult rv = CheckSanityOfStringLength(aPrefName, aValue);
   2463  if (NS_FAILED(rv)) {
   2464    return rv;
   2465  }
   2466  return SetCharPrefNoLengthCheck(aPrefName, aValue);
   2467 }
   2468 
   2469 nsresult nsPrefBranch::SetCharPrefNoLengthCheck(const char* aPrefName,
   2470                                                const nsACString& aValue) {
   2471  NS_ENSURE_ARG(aPrefName);
   2472 
   2473  const PrefName& pref = GetPrefName(aPrefName);
   2474  return Preferences::SetCString(pref.get(), aValue, mKind);
   2475 }
   2476 
   2477 NS_IMETHODIMP
   2478 nsPrefBranch::GetStringPref(const char* aPrefName,
   2479                            const nsACString& aDefaultValue, uint8_t aArgc,
   2480                            nsACString& aRetVal) {
   2481  nsCString utf8String;
   2482  nsresult rv = GetCharPref(aPrefName, utf8String);
   2483  if (NS_SUCCEEDED(rv)) {
   2484    aRetVal = utf8String;
   2485    return rv;
   2486  }
   2487 
   2488  if (aArgc == 1) {
   2489    aRetVal = aDefaultValue;
   2490    return NS_OK;
   2491  }
   2492 
   2493  return rv;
   2494 }
   2495 
   2496 NS_IMETHODIMP
   2497 nsPrefBranch::SetStringPref(const char* aPrefName, const nsACString& aValue) {
   2498  nsresult rv = CheckSanityOfStringLength(aPrefName, aValue);
   2499  if (NS_FAILED(rv)) {
   2500    return rv;
   2501  }
   2502 
   2503  return SetCharPrefNoLengthCheck(aPrefName, aValue);
   2504 }
   2505 
   2506 NS_IMETHODIMP
   2507 nsPrefBranch::GetIntPrefWithDefault(const char* aPrefName,
   2508                                    int32_t aDefaultValue, uint8_t aArgc,
   2509                                    int32_t* aRetVal) {
   2510  nsresult rv = GetIntPref(aPrefName, aRetVal);
   2511 
   2512  if (NS_FAILED(rv) && aArgc == 1) {
   2513    *aRetVal = aDefaultValue;
   2514    return NS_OK;
   2515  }
   2516 
   2517  return rv;
   2518 }
   2519 
   2520 NS_IMETHODIMP
   2521 nsPrefBranch::GetIntPref(const char* aPrefName, int32_t* aRetVal) {
   2522  NS_ENSURE_ARG(aPrefName);
   2523  const PrefName& pref = GetPrefName(aPrefName);
   2524  return Preferences::GetInt(pref.get(), aRetVal, mKind);
   2525 }
   2526 
   2527 NS_IMETHODIMP
   2528 nsPrefBranch::SetIntPref(const char* aPrefName, int32_t aValue) {
   2529  NS_ENSURE_ARG(aPrefName);
   2530 
   2531  const PrefName& pref = GetPrefName(aPrefName);
   2532  return Preferences::SetInt(pref.get(), aValue, mKind);
   2533 }
   2534 
   2535 NS_IMETHODIMP
   2536 nsPrefBranch::GetComplexValue(const char* aPrefName, const nsIID& aType,
   2537                              void** aRetVal) {
   2538  NS_ENSURE_ARG(aPrefName);
   2539 
   2540  nsresult rv;
   2541  nsAutoCString utf8String;
   2542 
   2543  // We have to do this one first because it's different to all the rest.
   2544  if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
   2545    nsCOMPtr<nsIPrefLocalizedString> theString(
   2546        do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv));
   2547    if (NS_FAILED(rv)) {
   2548      return rv;
   2549    }
   2550 
   2551    const PrefName& pref = GetPrefName(aPrefName);
   2552    bool bNeedDefault = false;
   2553 
   2554    if (mKind == PrefValueKind::Default) {
   2555      bNeedDefault = true;
   2556    } else {
   2557      // if there is no user (or locked) value
   2558      if (!Preferences::HasUserValue(pref.get()) &&
   2559          !Preferences::IsLocked(pref.get())) {
   2560        bNeedDefault = true;
   2561      }
   2562    }
   2563 
   2564    // if we need to fetch the default value, do that instead, otherwise use the
   2565    // value we pulled in at the top of this function
   2566    if (bNeedDefault) {
   2567      nsAutoString utf16String;
   2568      rv = GetDefaultFromPropertiesFile(pref.get(), utf16String);
   2569      if (NS_SUCCEEDED(rv)) {
   2570        theString->SetData(utf16String);
   2571      }
   2572    } else {
   2573      rv = GetCharPref(aPrefName, utf8String);
   2574      if (NS_SUCCEEDED(rv)) {
   2575        theString->SetData(NS_ConvertUTF8toUTF16(utf8String));
   2576      }
   2577    }
   2578 
   2579    if (NS_SUCCEEDED(rv)) {
   2580      theString.forget(reinterpret_cast<nsIPrefLocalizedString**>(aRetVal));
   2581    }
   2582 
   2583    return rv;
   2584  }
   2585 
   2586  // if we can't get the pref, there's no point in being here
   2587  rv = GetCharPref(aPrefName, utf8String);
   2588  if (NS_FAILED(rv)) {
   2589    return rv;
   2590  }
   2591 
   2592  if (aType.Equals(NS_GET_IID(nsIFile))) {
   2593    ENSURE_PARENT_PROCESS("GetComplexValue(nsIFile)", aPrefName);
   2594    MOZ_TRY(NS_NewLocalFileWithPersistentDescriptor(
   2595        utf8String, reinterpret_cast<nsIFile**>(aRetVal)));
   2596    return NS_OK;
   2597  }
   2598 
   2599  if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
   2600    ENSURE_PARENT_PROCESS("GetComplexValue(nsIRelativeFilePref)", aPrefName);
   2601 
   2602    nsACString::const_iterator keyBegin, strEnd;
   2603    utf8String.BeginReading(keyBegin);
   2604    utf8String.EndReading(strEnd);
   2605 
   2606    // The pref has the format: [fromKey]a/b/c
   2607    if (*keyBegin++ != '[') {
   2608      return NS_ERROR_FAILURE;
   2609    }
   2610 
   2611    nsACString::const_iterator keyEnd(keyBegin);
   2612    if (!FindCharInReadable(']', keyEnd, strEnd)) {
   2613      return NS_ERROR_FAILURE;
   2614    }
   2615 
   2616    nsAutoCString key(Substring(keyBegin, keyEnd));
   2617 
   2618    nsCOMPtr<nsIFile> fromFile;
   2619    nsCOMPtr<nsIProperties> directoryService(
   2620        do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
   2621    if (NS_FAILED(rv)) {
   2622      return rv;
   2623    }
   2624 
   2625    rv = directoryService->Get(key.get(), NS_GET_IID(nsIFile),
   2626                               getter_AddRefs(fromFile));
   2627    if (NS_FAILED(rv)) {
   2628      return rv;
   2629    }
   2630 
   2631    nsCOMPtr<nsIFile> theFile;
   2632    MOZ_TRY(NS_NewLocalFileWithRelativeDescriptor(
   2633        fromFile, Substring(++keyEnd, strEnd), getter_AddRefs(theFile)));
   2634 
   2635    nsCOMPtr<nsIRelativeFilePref> relativePref = new nsRelativeFilePref();
   2636    (void)relativePref->SetFile(theFile);
   2637    (void)relativePref->SetRelativeToKey(key);
   2638 
   2639    relativePref.forget(reinterpret_cast<nsIRelativeFilePref**>(aRetVal));
   2640    return NS_OK;
   2641  }
   2642 
   2643  NS_WARNING("nsPrefBranch::GetComplexValue - Unsupported interface type");
   2644  return NS_NOINTERFACE;
   2645 }
   2646 
   2647 nsresult nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
   2648                                                 const nsAString& aValue) {
   2649  return CheckSanityOfStringLength(aPrefName, aValue.Length());
   2650 }
   2651 
   2652 nsresult nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
   2653                                                 const nsACString& aValue) {
   2654  return CheckSanityOfStringLength(aPrefName, aValue.Length());
   2655 }
   2656 
   2657 nsresult nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
   2658                                                 const uint32_t aLength) {
   2659  if (aLength > MAX_PREF_LENGTH) {
   2660    return NS_ERROR_ILLEGAL_VALUE;
   2661  }
   2662  if (aLength <= MAX_ADVISABLE_PREF_LENGTH) {
   2663    return NS_OK;
   2664  }
   2665 
   2666  nsresult rv;
   2667  nsCOMPtr<nsIConsoleService> console =
   2668      do_GetService("@mozilla.org/consoleservice;1", &rv);
   2669  if (NS_FAILED(rv)) {
   2670    return rv;
   2671  }
   2672 
   2673  nsAutoCString message(nsPrintfCString(
   2674      "Warning: attempting to write %d bytes to preference %s. This is bad "
   2675      "for general performance and memory usage. Such an amount of data "
   2676      "should rather be written to an external file.",
   2677      aLength, GetPrefName(aPrefName).get()));
   2678 
   2679  rv = console->LogStringMessage(NS_ConvertUTF8toUTF16(message).get());
   2680  if (NS_FAILED(rv)) {
   2681    return rv;
   2682  }
   2683  return NS_OK;
   2684 }
   2685 
   2686 NS_IMETHODIMP
   2687 nsPrefBranch::SetComplexValue(const char* aPrefName, const nsIID& aType,
   2688                              nsISupports* aValue) {
   2689  ENSURE_PARENT_PROCESS("SetComplexValue", aPrefName);
   2690  NS_ENSURE_ARG(aPrefName);
   2691 
   2692  nsresult rv = NS_NOINTERFACE;
   2693 
   2694  if (aType.Equals(NS_GET_IID(nsIFile))) {
   2695    nsCOMPtr<nsIFile> file = do_QueryInterface(aValue);
   2696    if (!file) {
   2697      return NS_NOINTERFACE;
   2698    }
   2699 
   2700    nsAutoCString descriptorString;
   2701    rv = file->GetPersistentDescriptor(descriptorString);
   2702    if (NS_SUCCEEDED(rv)) {
   2703      rv = SetCharPrefNoLengthCheck(aPrefName, descriptorString);
   2704    }
   2705    return rv;
   2706  }
   2707 
   2708  if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
   2709    nsCOMPtr<nsIRelativeFilePref> relFilePref = do_QueryInterface(aValue);
   2710    if (!relFilePref) {
   2711      return NS_NOINTERFACE;
   2712    }
   2713 
   2714    nsCOMPtr<nsIFile> file;
   2715    relFilePref->GetFile(getter_AddRefs(file));
   2716    if (!file) {
   2717      return NS_NOINTERFACE;
   2718    }
   2719 
   2720    nsAutoCString relativeToKey;
   2721    (void)relFilePref->GetRelativeToKey(relativeToKey);
   2722 
   2723    nsCOMPtr<nsIFile> relativeToFile;
   2724    nsCOMPtr<nsIProperties> directoryService(
   2725        do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
   2726    if (NS_FAILED(rv)) {
   2727      return rv;
   2728    }
   2729 
   2730    rv = directoryService->Get(relativeToKey.get(), NS_GET_IID(nsIFile),
   2731                               getter_AddRefs(relativeToFile));
   2732    if (NS_FAILED(rv)) {
   2733      return rv;
   2734    }
   2735 
   2736    nsAutoCString relDescriptor;
   2737    rv = file->GetRelativeDescriptor(relativeToFile, relDescriptor);
   2738    if (NS_FAILED(rv)) {
   2739      return rv;
   2740    }
   2741 
   2742    nsAutoCString descriptorString;
   2743    descriptorString.Append('[');
   2744    descriptorString.Append(relativeToKey);
   2745    descriptorString.Append(']');
   2746    descriptorString.Append(relDescriptor);
   2747    return SetCharPrefNoLengthCheck(aPrefName, descriptorString);
   2748  }
   2749 
   2750  if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
   2751    nsCOMPtr<nsISupportsString> theString = do_QueryInterface(aValue);
   2752 
   2753    if (theString) {
   2754      nsString wideString;
   2755 
   2756      rv = theString->GetData(wideString);
   2757      if (NS_SUCCEEDED(rv)) {
   2758        // Check sanity of string length before any lengthy conversion
   2759        rv = CheckSanityOfStringLength(aPrefName, wideString);
   2760        if (NS_FAILED(rv)) {
   2761          return rv;
   2762        }
   2763        rv = SetCharPrefNoLengthCheck(aPrefName,
   2764                                      NS_ConvertUTF16toUTF8(wideString));
   2765      }
   2766    }
   2767    return rv;
   2768  }
   2769 
   2770  NS_WARNING("nsPrefBranch::SetComplexValue - Unsupported interface type");
   2771  return NS_NOINTERFACE;
   2772 }
   2773 
   2774 NS_IMETHODIMP
   2775 nsPrefBranch::ClearUserPref(const char* aPrefName) {
   2776  NS_ENSURE_ARG(aPrefName);
   2777 
   2778  const PrefName& pref = GetPrefName(aPrefName);
   2779  return Preferences::ClearUser(pref.get());
   2780 }
   2781 
   2782 NS_IMETHODIMP
   2783 nsPrefBranch::PrefHasUserValue(const char* aPrefName, bool* aRetVal) {
   2784  NS_ENSURE_ARG_POINTER(aRetVal);
   2785  NS_ENSURE_ARG(aPrefName);
   2786 
   2787  const PrefName& pref = GetPrefName(aPrefName);
   2788  *aRetVal = Preferences::HasUserValue(pref.get());
   2789  return NS_OK;
   2790 }
   2791 
   2792 NS_IMETHODIMP
   2793 nsPrefBranch::PrefHasDefaultValue(const char* aPrefName, bool* aRetVal) {
   2794  NS_ENSURE_ARG_POINTER(aRetVal);
   2795  NS_ENSURE_ARG(aPrefName);
   2796 
   2797  const PrefName& pref = GetPrefName(aPrefName);
   2798  *aRetVal = Preferences::HasDefaultValue(pref.get());
   2799  return NS_OK;
   2800 }
   2801 
   2802 NS_IMETHODIMP
   2803 nsPrefBranch::LockPref(const char* aPrefName) {
   2804  NS_ENSURE_ARG(aPrefName);
   2805 
   2806  const PrefName& pref = GetPrefName(aPrefName);
   2807  return Preferences::Lock(pref.get());
   2808 }
   2809 
   2810 NS_IMETHODIMP
   2811 nsPrefBranch::PrefIsLocked(const char* aPrefName, bool* aRetVal) {
   2812  NS_ENSURE_ARG_POINTER(aRetVal);
   2813  NS_ENSURE_ARG(aPrefName);
   2814 
   2815  const PrefName& pref = GetPrefName(aPrefName);
   2816  *aRetVal = Preferences::IsLocked(pref.get());
   2817  return NS_OK;
   2818 }
   2819 
   2820 NS_IMETHODIMP
   2821 nsPrefBranch::PrefIsSanitized(const char* aPrefName, bool* aRetVal) {
   2822  NS_ENSURE_ARG_POINTER(aRetVal);
   2823  NS_ENSURE_ARG(aPrefName);
   2824 
   2825  const PrefName& pref = GetPrefName(aPrefName);
   2826  *aRetVal = Preferences::IsSanitized(pref.get());
   2827  return NS_OK;
   2828 }
   2829 
   2830 NS_IMETHODIMP
   2831 nsPrefBranch::UnlockPref(const char* aPrefName) {
   2832  NS_ENSURE_ARG(aPrefName);
   2833 
   2834  const PrefName& pref = GetPrefName(aPrefName);
   2835  return Preferences::Unlock(pref.get());
   2836 }
   2837 
   2838 NS_IMETHODIMP
   2839 nsPrefBranch::DeleteBranch(const char* aStartingAt) {
   2840  ENSURE_PARENT_PROCESS("DeleteBranch", aStartingAt);
   2841  NS_ENSURE_ARG(aStartingAt);
   2842 
   2843  MOZ_ASSERT(NS_IsMainThread());
   2844 
   2845  if (!HashTable()) {
   2846    return NS_ERROR_NOT_INITIALIZED;
   2847  }
   2848 
   2849  const PrefName& pref = GetPrefName(aStartingAt);
   2850  nsAutoCString branchName(pref.get());
   2851 
   2852  // Add a trailing '.' if it doesn't already have one.
   2853  if (branchName.Length() > 1 && !StringEndsWith(branchName, "."_ns)) {
   2854    branchName += '.';
   2855  }
   2856 
   2857  const nsACString& branchNameNoDot =
   2858      Substring(branchName, 0, branchName.Length() - 1);
   2859 
   2860  // Collect the list of prefs to remove
   2861  AutoTArray<const char*, 32> prefNames;
   2862  for (auto& pref : PrefsIter(HashTable(), gSharedMap)) {
   2863    // Match preferences that start with branchName or equal branchNameNoDot.
   2864    // For inputs ending with "..", this matches the node without the trailing
   2865    // dot and all children with the double dot prefix.
   2866    // For other inputs, this matches both the node itself and all children.
   2867    if (StringBeginsWith(pref->NameString(), branchName) ||
   2868        pref->NameString() == branchNameNoDot) {
   2869      prefNames.AppendElement(pref->Name());
   2870    }
   2871  }
   2872 
   2873  // Remove the listed preferences.
   2874  for (auto& prefName : prefNames) {
   2875    auto result = pref_LookupForModify(
   2876        prefName, [](const PrefWrapper& aPref) { return !aPref.IsTypeNone(); });
   2877    if (result.isErr()) {
   2878      // Pref was likely removed by a previously-notified callback
   2879      continue;
   2880    }
   2881 
   2882    if (Pref* pref = result.unwrap()) {
   2883      pref->ClearUserValue();
   2884      pref->ClearDefaultValue();
   2885 
   2886      MOZ_ASSERT(
   2887          !gSharedMap || !pref->IsSanitized() || !gSharedMap->Has(pref->Name()),
   2888          "A sanitized pref should never be in the shared pref map.");
   2889      if (!pref->IsSanitized() &&
   2890          (!gSharedMap || !gSharedMap->Has(pref->Name()))) {
   2891        HashTable()->remove(prefName);
   2892      } else {
   2893        // If there is a matching shared pref, it must be shadowed by an empty
   2894        // entry in the HashTable().
   2895        pref->SetType(PrefType::None);
   2896      }
   2897      NotifyCallbacks(nsDependentCString{prefName});
   2898    }
   2899  }
   2900 
   2901  Preferences::HandleDirty();
   2902  return NS_OK;
   2903 }
   2904 
   2905 NS_IMETHODIMP
   2906 nsPrefBranch::GetChildList(const char* aStartingAt,
   2907                           nsTArray<nsCString>& aChildArray) {
   2908  NS_ENSURE_ARG(aStartingAt);
   2909 
   2910  MOZ_ASSERT(NS_IsMainThread());
   2911 
   2912  // This will contain a list of all the pref name strings. Allocated on the
   2913  // stack for speed.
   2914  AutoTArray<nsCString, 32> prefArray;
   2915 
   2916  const PrefName& parent = GetPrefName(aStartingAt);
   2917  size_t parentLen = parent.Length();
   2918  for (auto& pref : PrefsIter(HashTable(), gSharedMap)) {
   2919    if (strncmp(pref->Name(), parent.get(), parentLen) == 0) {
   2920      prefArray.AppendElement(pref->NameString());
   2921    }
   2922  }
   2923 
   2924  // Now that we've built up the list, run the callback on all the matching
   2925  // elements.
   2926  aChildArray.SetCapacity(prefArray.Length());
   2927  for (auto& element : prefArray) {
   2928    // we need to lop off mPrefRoot in case the user is planning to pass this
   2929    // back to us because if they do we are going to add mPrefRoot again.
   2930    aChildArray.AppendElement(Substring(element, mPrefRoot.Length()));
   2931  }
   2932 
   2933  return NS_OK;
   2934 }
   2935 
   2936 NS_IMETHODIMP
   2937 nsPrefBranch::AddObserverImpl(const nsACString& aDomain, nsIObserver* aObserver,
   2938                              bool aHoldWeak) {
   2939  UniquePtr<PrefCallback> pCallback;
   2940 
   2941  NS_ENSURE_ARG(aObserver);
   2942 
   2943  const nsCString& prefName = GetPrefName(aDomain);
   2944 
   2945  // Hold a weak reference to the observer if so requested.
   2946  if (aHoldWeak) {
   2947    nsCOMPtr<nsISupportsWeakReference> weakRefFactory =
   2948        do_QueryInterface(aObserver);
   2949    if (!weakRefFactory) {
   2950      // The caller didn't give us a object that supports weak reference...
   2951      // tell them.
   2952      return NS_ERROR_INVALID_ARG;
   2953    }
   2954 
   2955    // Construct a PrefCallback with a weak reference to the observer.
   2956    pCallback = MakeUnique<PrefCallback>(prefName, weakRefFactory, this);
   2957 
   2958  } else {
   2959    // Construct a PrefCallback with a strong reference to the observer.
   2960    pCallback = MakeUnique<PrefCallback>(prefName, aObserver, this);
   2961  }
   2962 
   2963  mObservers.WithEntryHandle(pCallback.get(), [&](auto&& p) {
   2964    if (p) {
   2965      NS_WARNING(
   2966          nsPrintfCString("Ignoring duplicate observer: %s", prefName.get())
   2967              .get());
   2968    } else {
   2969      // We must pass a fully qualified preference name to the callback
   2970      // aDomain == nullptr is the only possible failure, and we trapped it with
   2971      // NS_ENSURE_ARG above.
   2972      Preferences::RegisterCallback(NotifyObserver, prefName, pCallback.get(),
   2973                                    Preferences::PrefixMatch,
   2974                                    /* isPriority */ false);
   2975 
   2976      p.Insert(std::move(pCallback));
   2977    }
   2978  });
   2979 
   2980  return NS_OK;
   2981 }
   2982 
   2983 NS_IMETHODIMP
   2984 nsPrefBranch::RemoveObserverImpl(const nsACString& aDomain,
   2985                                 nsIObserver* aObserver) {
   2986  NS_ENSURE_ARG(aObserver);
   2987 
   2988  nsresult rv = NS_OK;
   2989 
   2990  // If we're in the middle of a call to FreeObserverList, don't process this
   2991  // RemoveObserver call -- the observer in question will be removed soon, if
   2992  // it hasn't been already.
   2993  //
   2994  // It's important that we don't touch mObservers in any way -- even a Get()
   2995  // which returns null might cause the hashtable to resize itself, which will
   2996  // break the iteration in FreeObserverList.
   2997  if (mFreeingObserverList) {
   2998    return NS_OK;
   2999  }
   3000 
   3001  // Remove the relevant PrefCallback from mObservers and get an owning pointer
   3002  // to it. Unregister the callback first, and then let the owning pointer go
   3003  // out of scope and destroy the callback.
   3004  const nsCString& prefName = GetPrefName(aDomain);
   3005  PrefCallback key(prefName, aObserver, this);
   3006  mozilla::UniquePtr<PrefCallback> pCallback;
   3007  mObservers.Remove(&key, &pCallback);
   3008  if (pCallback) {
   3009    rv = Preferences::UnregisterCallback(
   3010        NotifyObserver, prefName, pCallback.get(), Preferences::PrefixMatch);
   3011  }
   3012 
   3013  return rv;
   3014 }
   3015 
   3016 NS_IMETHODIMP
   3017 nsPrefBranch::Observe(nsISupports* aSubject, const char* aTopic,
   3018                      const char16_t* aData) {
   3019  // Watch for xpcom shutdown and free our observers to eliminate any cyclic
   3020  // references.
   3021  if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
   3022    FreeObserverList();
   3023  }
   3024  return NS_OK;
   3025 }
   3026 
   3027 /* static */
   3028 void nsPrefBranch::NotifyObserver(const char* aNewPref, void* aData) {
   3029  PrefCallback* pCallback = (PrefCallback*)aData;
   3030 
   3031  nsCOMPtr<nsIObserver> observer = pCallback->GetObserver();
   3032  if (!observer) {
   3033    // The observer has expired.  Let's remove this callback.
   3034    pCallback->GetPrefBranch()->RemoveExpiredCallback(pCallback);
   3035    return;
   3036  }
   3037 
   3038  // Remove any root this string may contain so as to not confuse the observer
   3039  // by passing them something other than what they passed us as a topic.
   3040  uint32_t len = pCallback->GetPrefBranch()->GetRootLength();
   3041  nsDependentCString suffix(aNewPref + len);
   3042 
   3043  observer->Observe(static_cast<nsIPrefBranch*>(pCallback->GetPrefBranch()),
   3044                    NS_PREFBRANCH_PREFCHANGE_TOPIC_ID,
   3045                    NS_ConvertASCIItoUTF16(suffix).get());
   3046 }
   3047 
   3048 size_t nsPrefBranch::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
   3049  size_t n = aMallocSizeOf(this);
   3050 
   3051  n += mPrefRoot.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   3052 
   3053  n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
   3054  for (const auto& entry : mObservers) {
   3055    const PrefCallback* data = entry.GetWeak();
   3056    n += data->SizeOfIncludingThis(aMallocSizeOf);
   3057  }
   3058 
   3059  return n;
   3060 }
   3061 
   3062 void nsPrefBranch::FreeObserverList() {
   3063  // We need to prevent anyone from modifying mObservers while we're iterating
   3064  // over it. In particular, some clients will call RemoveObserver() when
   3065  // they're removed and destructed via the iterator; we set
   3066  // mFreeingObserverList to keep those calls from touching mObservers.
   3067  mFreeingObserverList = true;
   3068  for (auto iter = mObservers.Iter(); !iter.Done(); iter.Next()) {
   3069    auto callback = iter.UserData();
   3070    Preferences::UnregisterCallback(nsPrefBranch::NotifyObserver,
   3071                                    callback->GetDomain(), callback,
   3072                                    Preferences::PrefixMatch);
   3073    iter.Remove();
   3074  }
   3075 
   3076  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   3077  if (observerService) {
   3078    observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
   3079  }
   3080 
   3081  mFreeingObserverList = false;
   3082 }
   3083 
   3084 void nsPrefBranch::RemoveExpiredCallback(PrefCallback* aCallback) {
   3085  MOZ_ASSERT(aCallback->IsExpired());
   3086  mObservers.Remove(aCallback);
   3087 }
   3088 
   3089 nsresult nsPrefBranch::GetDefaultFromPropertiesFile(const char* aPrefName,
   3090                                                    nsAString& aReturn) {
   3091  // The default value contains a URL to a .properties file.
   3092 
   3093  nsAutoCString propertyFileURL;
   3094  nsresult rv = Preferences::GetCString(aPrefName, propertyFileURL,
   3095                                        PrefValueKind::Default);
   3096  if (NS_FAILED(rv)) {
   3097    return rv;
   3098  }
   3099 
   3100  nsCOMPtr<nsIStringBundleService> bundleService =
   3101      components::StringBundle::Service();
   3102  if (!bundleService) {
   3103    return NS_ERROR_FAILURE;
   3104  }
   3105 
   3106  nsCOMPtr<nsIStringBundle> bundle;
   3107  rv = bundleService->CreateBundle(propertyFileURL.get(),
   3108                                   getter_AddRefs(bundle));
   3109  if (NS_FAILED(rv)) {
   3110    return rv;
   3111  }
   3112 
   3113  return bundle->GetStringFromName(aPrefName, aReturn);
   3114 }
   3115 
   3116 nsPrefBranch::PrefName nsPrefBranch::GetPrefName(
   3117    const nsACString& aPrefName) const {
   3118  if (mPrefRoot.IsEmpty()) {
   3119    return PrefName(PromiseFlatCString(aPrefName));
   3120  }
   3121 
   3122  return PrefName(mPrefRoot + aPrefName);
   3123 }
   3124 
   3125 //----------------------------------------------------------------------------
   3126 // nsPrefLocalizedString
   3127 //----------------------------------------------------------------------------
   3128 
   3129 nsPrefLocalizedString::nsPrefLocalizedString() = default;
   3130 
   3131 nsPrefLocalizedString::~nsPrefLocalizedString() = default;
   3132 
   3133 NS_IMPL_ISUPPORTS(nsPrefLocalizedString, nsIPrefLocalizedString,
   3134                  nsISupportsString)
   3135 
   3136 nsresult nsPrefLocalizedString::Init() {
   3137  nsresult rv;
   3138  mUnicodeString = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
   3139 
   3140  return rv;
   3141 }
   3142 
   3143 //----------------------------------------------------------------------------
   3144 // nsPrefOverrideMap
   3145 //----------------------------------------------------------------------------
   3146 
   3147 NS_IMPL_ISUPPORTS(nsPrefOverrideMap, nsIPrefOverrideMap)
   3148 
   3149 NS_IMETHODIMP
   3150 nsPrefOverrideMap::AddEntry(const nsACString& aPrefName,
   3151                            JS::Handle<JS::Value> aPrefValue, JSContext* aCx) {
   3152  nsCString prefName(aPrefName);
   3153  auto maybePrefWrapper = pref_Lookup(prefName.get());
   3154  if (NS_WARN_IF(!maybePrefWrapper)) {
   3155    return NS_ERROR_DOM_NOT_FOUND_ERR;
   3156  }
   3157  nsAutoCString str;
   3158  auto jsValueToPrefValue = [&]() -> Result<Maybe<OwnedPrefValue>, nsresult> {
   3159    switch (aPrefValue.type()) {
   3160      case JS::ValueType::Boolean:
   3161        if (NS_WARN_IF(!maybePrefWrapper->IsTypeBool())) {
   3162          return Err(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
   3163        }
   3164        return Some(OwnedPrefValue(aPrefValue.toBoolean()));
   3165      case JS::ValueType::Int32:
   3166        if (NS_WARN_IF(!maybePrefWrapper->IsTypeInt())) {
   3167          return Err(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
   3168        }
   3169        return Some(OwnedPrefValue(aPrefValue.toInt32()));
   3170      case JS::ValueType::String: {
   3171        if (NS_WARN_IF(!maybePrefWrapper->IsTypeString())) {
   3172          return Err(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
   3173        }
   3174        if (NS_WARN_IF(!AssignJSString(aCx, str, aPrefValue.toString()))) {
   3175          return Err(NS_ERROR_OUT_OF_MEMORY);
   3176        }
   3177        return Some(OwnedPrefValue(str.get()));
   3178      }
   3179      case JS::ValueType::Null:
   3180        return Result<Maybe<OwnedPrefValue>, nsresult>(Nothing());
   3181      default:
   3182        NS_WARNING("Invalid type in nsPrefOverrideMap::AddEntry");
   3183        return Err(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
   3184    }
   3185  };
   3186  Maybe<OwnedPrefValue> prefValue = MOZ_TRY(jsValueToPrefValue());
   3187  if (NS_WARN_IF(!mMap.put(prefName, prefValue))) {
   3188    return NS_ERROR_OUT_OF_MEMORY;
   3189  }
   3190  return NS_OK;
   3191 }
   3192 
   3193 NS_IMETHODIMP
   3194 nsPrefOverrideMap::GetEntry(const nsACString& aPrefName, JSContext* aCx,
   3195                            JS::MutableHandle<JS::Value> aPrefValue) {
   3196  nsCString prefName(aPrefName);
   3197  auto maybePrefWrapper = pref_Lookup(prefName.get());
   3198  if (NS_WARN_IF(!maybePrefWrapper)) {
   3199    return NS_ERROR_DOM_NOT_FOUND_ERR;
   3200  }
   3201  auto prefType = maybePrefWrapper->Type();
   3202  auto prefValueToJsValue = [&]() -> mozilla::Result<JS::Value, nsresult> {
   3203    if (auto it = mMap.lookup(prefName)) {
   3204      if (it->value().isNothing()) {
   3205        return JS::NullValue();
   3206      }
   3207      switch (prefType) {
   3208        case PrefType::Bool:
   3209          return JS::BooleanValue(it->value()->GetPrefValue().Get<bool>());
   3210        case PrefType::Int:
   3211          return JS::Int32Value(it->value()->GetPrefValue().Get<int32_t>());
   3212        case PrefType::String: {
   3213          auto str = it->value()->GetPrefValue().Get<nsDependentCString>();
   3214          return JS::StringValue(
   3215              JS_NewStringCopyN(aCx, str.get(), str.Length()));
   3216        }
   3217        default:
   3218          // Do not expect type NONE
   3219          NS_WARNING("Invalid type in nsPrefOverrideMap::GetEntry");
   3220          return Err(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
   3221      }
   3222    }
   3223    return Err(NS_ERROR_ILLEGAL_VALUE);
   3224  };
   3225  auto ret = MOZ_TRY(prefValueToJsValue());
   3226  aPrefValue.set(ret);
   3227  return NS_OK;
   3228 }
   3229 
   3230 //----------------------------------------------------------------------------
   3231 // nsRelativeFilePref
   3232 //----------------------------------------------------------------------------
   3233 
   3234 NS_IMPL_ISUPPORTS(nsRelativeFilePref, nsIRelativeFilePref)
   3235 
   3236 nsRelativeFilePref::nsRelativeFilePref() = default;
   3237 
   3238 nsRelativeFilePref::~nsRelativeFilePref() = default;
   3239 
   3240 NS_IMETHODIMP
   3241 nsRelativeFilePref::GetFile(nsIFile** aFile) {
   3242  NS_ENSURE_ARG_POINTER(aFile);
   3243  *aFile = mFile;
   3244  NS_IF_ADDREF(*aFile);
   3245  return NS_OK;
   3246 }
   3247 
   3248 NS_IMETHODIMP
   3249 nsRelativeFilePref::SetFile(nsIFile* aFile) {
   3250  mFile = aFile;
   3251  return NS_OK;
   3252 }
   3253 
   3254 NS_IMETHODIMP
   3255 nsRelativeFilePref::GetRelativeToKey(nsACString& aRelativeToKey) {
   3256  aRelativeToKey.Assign(mRelativeToKey);
   3257  return NS_OK;
   3258 }
   3259 
   3260 NS_IMETHODIMP
   3261 nsRelativeFilePref::SetRelativeToKey(const nsACString& aRelativeToKey) {
   3262  mRelativeToKey.Assign(aRelativeToKey);
   3263  return NS_OK;
   3264 }
   3265 
   3266 //===========================================================================
   3267 // class Preferences and related things
   3268 //===========================================================================
   3269 
   3270 namespace mozilla {
   3271 
   3272 #define INITIAL_PREF_FILES 10
   3273 
   3274 void Preferences::HandleDirty() {
   3275  MOZ_ASSERT(XRE_IsParentProcess());
   3276 
   3277  if (!HashTable() || !sPreferences) {
   3278    return;
   3279  }
   3280 
   3281  if (sPreferences->mProfileShutdown) {
   3282    NS_WARNING("Setting user pref after profile shutdown.");
   3283    return;
   3284  }
   3285 
   3286  if (!sPreferences->mDirty) {
   3287    sPreferences->mDirty = true;
   3288 
   3289    if (sPreferences->mCurrentFile && sPreferences->AllowOffMainThreadSave() &&
   3290        !sPreferences->mSavePending) {
   3291      sPreferences->mSavePending = true;
   3292      static const int PREF_DELAY_MS = 500;
   3293      NS_DelayedDispatchToCurrentThread(
   3294          NewRunnableMethod("Preferences::SavePrefFileAsynchronous",
   3295                            sPreferences.get(),
   3296                            &Preferences::SavePrefFileAsynchronous),
   3297          PREF_DELAY_MS);
   3298    }
   3299  }
   3300 }
   3301 
   3302 static nsresult openPrefFile(nsIFile* aFile, PrefValueKind aKind);
   3303 
   3304 static nsresult parsePrefData(const nsCString& aData, PrefValueKind aKind);
   3305 
   3306 // Note: if sShutdown is true, sPreferences will be nullptr.
   3307 StaticRefPtr<Preferences> Preferences::sPreferences;
   3308 bool Preferences::sShutdown = false;
   3309 
   3310 // This globally enables or disables OMT pref writing, both sync and async.
   3311 static int32_t sAllowOMTPrefWrite = -1;
   3312 
   3313 // Write the preference data to a file.
   3314 class PreferencesWriter final {
   3315 public:
   3316  PreferencesWriter() = default;
   3317 
   3318  static nsresult Write(nsIFile* aFile, PrefSaveData& aPrefs) {
   3319    nsCOMPtr<nsIOutputStream> outStreamSink;
   3320    nsCOMPtr<nsIOutputStream> outStream;
   3321    uint32_t writeAmount;
   3322    nsresult rv;
   3323 
   3324    // Execute a "safe" save by saving through a tempfile.
   3325    rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStreamSink), aFile,
   3326                                         -1, 0600);
   3327    if (NS_FAILED(rv)) {
   3328      return rv;
   3329    }
   3330 
   3331    rv = NS_NewBufferedOutputStream(getter_AddRefs(outStream),
   3332                                    outStreamSink.forget(), 4096);
   3333    if (NS_FAILED(rv)) {
   3334      return rv;
   3335    }
   3336 
   3337    struct CharComparator {
   3338      bool LessThan(const nsCString& aA, const nsCString& aB) const {
   3339        return aA < aB;
   3340      }
   3341 
   3342      bool Equals(const nsCString& aA, const nsCString& aB) const {
   3343        return aA == aB;
   3344      }
   3345    };
   3346 
   3347    // Sort the preferences to make a readable file on disk.
   3348    aPrefs.Sort(CharComparator());
   3349 
   3350    // Write out the file header.
   3351    nsAutoCString preamble;
   3352    ::GetPrefsJsPreamble(preamble);
   3353    outStream->Write(preamble.get(), preamble.Length(), &writeAmount);
   3354 
   3355    for (nsCString& pref : aPrefs) {
   3356      outStream->Write(pref.get(), pref.Length(), &writeAmount);
   3357      outStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &writeAmount);
   3358    }
   3359 
   3360    // Tell the safe output stream to overwrite the real prefs file.
   3361    // (It'll abort if there were any errors during writing.)
   3362    nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outStream);
   3363    MOZ_ASSERT(safeStream, "expected a safe output stream!");
   3364    if (safeStream) {
   3365      rv = safeStream->Finish();
   3366    }
   3367 
   3368 #ifdef DEBUG
   3369    if (NS_FAILED(rv)) {
   3370      NS_WARNING("failed to save prefs file! possible data loss");
   3371    }
   3372 #endif
   3373 
   3374    return rv;
   3375  }
   3376 
   3377  static void Flush() {
   3378    MOZ_DIAGNOSTIC_ASSERT(sPendingWriteCount >= 0);
   3379    // SpinEventLoopUntil is unfortunate, but ultimately it's the best thing
   3380    // we can do here given the constraint that we need to ensure that
   3381    // the preferences on disk match what we have in memory. We could
   3382    // easily perform the write here ourselves by doing exactly what
   3383    // happens in PWRunnable::Run. This would be the right thing to do
   3384    // if we're stuck here because other unrelated runnables are taking
   3385    // a long time, and the wrong thing to do if PreferencesWriter::Write
   3386    // is what takes a long time, as we would be trading a SpinEventLoopUntil
   3387    // for a synchronous disk write, wherein we could not even spin the
   3388    // event loop. Given that PWRunnable generally runs on a thread pool,
   3389    // if we're stuck here, it's likely because of PreferencesWriter::Write
   3390    // and not some other runnable. Thus, spin away.
   3391    mozilla::SpinEventLoopUntil("PreferencesWriter::Flush"_ns,
   3392                                []() { return sPendingWriteCount <= 0; });
   3393  }
   3394 
   3395  // This is the data that all of the runnables (see below) will attempt
   3396  // to write.  It will always have the most up to date version, or be
   3397  // null, if the up to date information has already been written out.
   3398  static Atomic<PrefSaveData*> sPendingWriteData;
   3399 
   3400  // This is the number of writes via PWRunnables which have been dispatched
   3401  // but not yet completed. This is intended to be used by Flush to ensure
   3402  // that there are no outstanding writes left incomplete, and thus our prefs
   3403  // on disk are in sync with what we have in memory.
   3404  static Atomic<int> sPendingWriteCount;
   3405 
   3406  // See PWRunnable::Run for details on why we need this lock.
   3407  static StaticMutex sWritingToFile MOZ_UNANNOTATED;
   3408 };
   3409 
   3410 Atomic<PrefSaveData*> PreferencesWriter::sPendingWriteData(nullptr);
   3411 Atomic<int> PreferencesWriter::sPendingWriteCount(0);
   3412 StaticMutex PreferencesWriter::sWritingToFile;
   3413 
   3414 class PWRunnable : public Runnable {
   3415 public:
   3416  explicit PWRunnable(
   3417      nsIFile* aFile,
   3418      UniquePtr<MozPromiseHolder<Preferences::WritePrefFilePromise>>
   3419          aPromiseHolder)
   3420      : Runnable("PWRunnable"),
   3421        mFile(aFile),
   3422        mPromiseHolder(std::move(aPromiseHolder)) {}
   3423 
   3424  NS_IMETHOD Run() override {
   3425    // Preference writes are handled a bit strangely, in that a "newer"
   3426    // write is generally regarded as always better. For this reason,
   3427    // sPendingWriteData can be overwritten multiple times before anyone
   3428    // gets around to actually using it, minimizing writes. However,
   3429    // once we've acquired sPendingWriteData we've reached a
   3430    // "point of no return" and have to complete the write.
   3431    //
   3432    // Unfortunately, this design allows the following behaviour:
   3433    //
   3434    // 1. write1 is queued up
   3435    // 2. thread1 acquires write1
   3436    // 3. write2 is queued up
   3437    // 4. thread2 acquires write2
   3438    // 5. thread1 and thread2 concurrently clobber each other
   3439    //
   3440    // To avoid this, we use this lock to ensure that only one thread
   3441    // at a time is trying to acquire the write, and when it does,
   3442    // all other threads are prevented from acquiring writes until it
   3443    // completes the write. New writes are still allowed to be queued
   3444    // up in this time.
   3445    //
   3446    // Although it's atomic, the acquire needs to be guarded by the mutex
   3447    // to avoid reordering of writes -- we don't want an older write to
   3448    // run after a newer one. To avoid this causing too much waiting, we check
   3449    // if sPendingWriteData is already null before acquiring the mutex. If it
   3450    // is, then there's definitely no work to be done (or someone is in the
   3451    // middle of doing it for us).
   3452    //
   3453    // Note that every time a new write is queued up, a new write task is
   3454    // is also queued up, so there will always be a task that can see the newest
   3455    // write.
   3456    //
   3457    // Ideally this lock wouldn't be necessary, and the PreferencesWriter
   3458    // would be used more carefully, but it's hard to untangle all that.
   3459    nsresult rv = NS_OK;
   3460    if (PreferencesWriter::sPendingWriteData) {
   3461      StaticMutexAutoLock lock(PreferencesWriter::sWritingToFile);
   3462      // If we get a nullptr on the exchange, it means that somebody
   3463      // else has already processed the request, and we can just return.
   3464      UniquePtr<PrefSaveData> prefs(
   3465          PreferencesWriter::sPendingWriteData.exchange(nullptr));
   3466      if (prefs) {
   3467        rv = PreferencesWriter::Write(mFile, *prefs);
   3468        // Make a copy of these so we can have them in runnable lambda.
   3469        // nsIFile is only there so that we would never release the
   3470        // ref counted pointer off main thread.
   3471        nsresult rvCopy = rv;
   3472        nsCOMPtr<nsIFile> fileCopy(mFile);
   3473        SchedulerGroup::Dispatch(NS_NewRunnableFunction(
   3474            "Preferences::WriterRunnable",
   3475            [fileCopy, rvCopy, promiseHolder = std::move(mPromiseHolder)] {
   3476              MOZ_RELEASE_ASSERT(NS_IsMainThread());
   3477              if (NS_FAILED(rvCopy)) {
   3478                Preferences::HandleDirty();
   3479              }
   3480              if (promiseHolder) {
   3481                promiseHolder->ResolveIfExists(true, __func__);
   3482              }
   3483            }));
   3484      }
   3485    }
   3486    // We've completed the write to the best of our abilities, whether
   3487    // we had prefs to write or another runnable got to them first. If
   3488    // PreferencesWriter::Write failed, this is still correct as the
   3489    // write is no longer outstanding, and the above HandleDirty call
   3490    // will just start the cycle again.
   3491    PreferencesWriter::sPendingWriteCount--;
   3492    return rv;
   3493  }
   3494 
   3495 private:
   3496  ~PWRunnable() {
   3497    if (mPromiseHolder) {
   3498      mPromiseHolder->RejectIfExists(NS_ERROR_ABORT, __func__);
   3499    }
   3500  }
   3501 
   3502 protected:
   3503  nsCOMPtr<nsIFile> mFile;
   3504  UniquePtr<MozPromiseHolder<Preferences::WritePrefFilePromise>> mPromiseHolder;
   3505 };
   3506 
   3507 // Although this is a member of Preferences, it measures sPreferences and
   3508 // several other global structures.
   3509 /* static */
   3510 void Preferences::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
   3511                                         PrefsSizes& aSizes) {
   3512  if (!sPreferences) {
   3513    return;
   3514  }
   3515 
   3516  aSizes.mMisc += aMallocSizeOf(sPreferences.get());
   3517 
   3518  aSizes.mRootBranches +=
   3519      static_cast<nsPrefBranch*>(sPreferences->mRootBranch.get())
   3520          ->SizeOfIncludingThis(aMallocSizeOf) +
   3521      static_cast<nsPrefBranch*>(sPreferences->mDefaultRootBranch.get())
   3522          ->SizeOfIncludingThis(aMallocSizeOf);
   3523 }
   3524 
   3525 class PreferenceServiceReporter final : public nsIMemoryReporter {
   3526  ~PreferenceServiceReporter() = default;
   3527 
   3528 public:
   3529  NS_DECL_ISUPPORTS
   3530  NS_DECL_NSIMEMORYREPORTER
   3531 
   3532 protected:
   3533  static const uint32_t kSuspectReferentCount = 1000;
   3534 };
   3535 
   3536 NS_IMPL_ISUPPORTS(PreferenceServiceReporter, nsIMemoryReporter)
   3537 
   3538 MOZ_DEFINE_MALLOC_SIZE_OF(PreferenceServiceMallocSizeOf)
   3539 
   3540 NS_IMETHODIMP
   3541 PreferenceServiceReporter::CollectReports(
   3542    nsIHandleReportCallback* aHandleReport, nsISupports* aData,
   3543    bool aAnonymize) {
   3544  MOZ_ASSERT(NS_IsMainThread());
   3545 
   3546  MallocSizeOf mallocSizeOf = PreferenceServiceMallocSizeOf;
   3547  PrefsSizes sizes;
   3548 
   3549  Preferences::AddSizeOfIncludingThis(mallocSizeOf, sizes);
   3550 
   3551  if (HashTable()) {
   3552    sizes.mHashTable += HashTable()->shallowSizeOfIncludingThis(mallocSizeOf);
   3553    for (auto iter = HashTable()->iter(); !iter.done(); iter.next()) {
   3554      iter.get()->AddSizeOfIncludingThis(mallocSizeOf, sizes);
   3555    }
   3556  }
   3557 
   3558  sizes.mPrefNameArena += PrefNameArena().SizeOfExcludingThis(mallocSizeOf);
   3559 
   3560  for (CallbackNode* node = gFirstCallback; node; node = node->Next()) {
   3561    node->AddSizeOfIncludingThis(mallocSizeOf, sizes);
   3562  }
   3563 
   3564  if (gSharedMap) {
   3565    sizes.mMisc += mallocSizeOf(gSharedMap);
   3566  }
   3567 
   3568 #ifdef ACCESS_COUNTS
   3569  if (gAccessCounts) {
   3570    sizes.mMisc += gAccessCounts->ShallowSizeOfIncludingThis(mallocSizeOf);
   3571  }
   3572 #endif
   3573 
   3574  MOZ_COLLECT_REPORT("explicit/preferences/hash-table", KIND_HEAP, UNITS_BYTES,
   3575                     sizes.mHashTable, "Memory used by libpref's hash table.");
   3576 
   3577  MOZ_COLLECT_REPORT("explicit/preferences/pref-values", KIND_HEAP, UNITS_BYTES,
   3578                     sizes.mPrefValues,
   3579                     "Memory used by PrefValues hanging off the hash table.");
   3580 
   3581  MOZ_COLLECT_REPORT("explicit/preferences/string-values", KIND_HEAP,
   3582                     UNITS_BYTES, sizes.mStringValues,
   3583                     "Memory used by libpref's string pref values.");
   3584 
   3585  MOZ_COLLECT_REPORT("explicit/preferences/root-branches", KIND_HEAP,
   3586                     UNITS_BYTES, sizes.mRootBranches,
   3587                     "Memory used by libpref's root branches.");
   3588 
   3589  MOZ_COLLECT_REPORT("explicit/preferences/pref-name-arena", KIND_HEAP,
   3590                     UNITS_BYTES, sizes.mPrefNameArena,
   3591                     "Memory used by libpref's arena for pref names.");
   3592 
   3593  MOZ_COLLECT_REPORT("explicit/preferences/callbacks/objects", KIND_HEAP,
   3594                     UNITS_BYTES, sizes.mCallbacksObjects,
   3595                     "Memory used by pref callback objects.");
   3596 
   3597  MOZ_COLLECT_REPORT("explicit/preferences/callbacks/domains", KIND_HEAP,
   3598                     UNITS_BYTES, sizes.mCallbacksDomains,
   3599                     "Memory used by pref callback domains (pref names and "
   3600                     "prefixes).");
   3601 
   3602  MOZ_COLLECT_REPORT("explicit/preferences/misc", KIND_HEAP, UNITS_BYTES,
   3603                     sizes.mMisc, "Miscellaneous memory used by libpref.");
   3604 
   3605  if (gSharedMap) {
   3606    if (XRE_IsParentProcess()) {
   3607      MOZ_COLLECT_REPORT("explicit/preferences/shared-memory-map", KIND_NONHEAP,
   3608                         UNITS_BYTES, gSharedMap->MapSize(),
   3609                         "The shared memory mapping used to share a "
   3610                         "snapshot of preference values across processes.");
   3611    }
   3612  }
   3613 
   3614  nsPrefBranch* rootBranch =
   3615      static_cast<nsPrefBranch*>(Preferences::GetRootBranch());
   3616  if (!rootBranch) {
   3617    return NS_OK;
   3618  }
   3619 
   3620  size_t numStrong = 0;
   3621  size_t numWeakAlive = 0;
   3622  size_t numWeakDead = 0;
   3623  nsTArray<nsCString> suspectPreferences;
   3624  // Count of the number of referents for each preference.
   3625  nsTHashMap<nsCStringHashKey, uint32_t> prefCounter;
   3626 
   3627  for (const auto& entry : rootBranch->mObservers) {
   3628    auto* callback = entry.GetWeak();
   3629 
   3630    if (callback->IsWeak()) {
   3631      nsCOMPtr<nsIObserver> callbackRef = do_QueryReferent(callback->mWeakRef);
   3632      if (callbackRef) {
   3633        numWeakAlive++;
   3634      } else {
   3635        numWeakDead++;
   3636      }
   3637    } else {
   3638      numStrong++;
   3639    }
   3640 
   3641    const uint32_t currentCount = prefCounter.Get(callback->GetDomain()) + 1;
   3642    prefCounter.InsertOrUpdate(callback->GetDomain(), currentCount);
   3643 
   3644    // Keep track of preferences that have a suspiciously large number of
   3645    // referents (a symptom of a leak).
   3646    if (currentCount == kSuspectReferentCount) {
   3647      suspectPreferences.AppendElement(callback->GetDomain());
   3648    }
   3649  }
   3650 
   3651  for (uint32_t i = 0; i < suspectPreferences.Length(); i++) {
   3652    nsCString& suspect = suspectPreferences[i];
   3653    const uint32_t totalReferentCount = prefCounter.Get(suspect);
   3654 
   3655    nsPrintfCString suspectPath(
   3656        "preference-service-suspect/"
   3657        "referent(pref=%s)",
   3658        suspect.get());
   3659 
   3660    aHandleReport->Callback(
   3661        /* process = */ ""_ns, suspectPath, KIND_OTHER, UNITS_COUNT,
   3662        totalReferentCount,
   3663        "A preference with a suspiciously large number "
   3664        "referents (symptom of a leak)."_ns,
   3665        aData);
   3666  }
   3667 
   3668  MOZ_COLLECT_REPORT(
   3669      "preference-service/referent/strong", KIND_OTHER, UNITS_COUNT, numStrong,
   3670      "The number of strong referents held by the preference service.");
   3671 
   3672  MOZ_COLLECT_REPORT(
   3673      "preference-service/referent/weak/alive", KIND_OTHER, UNITS_COUNT,
   3674      numWeakAlive,
   3675      "The number of weak referents held by the preference service that are "
   3676      "still alive.");
   3677 
   3678  MOZ_COLLECT_REPORT(
   3679      "preference-service/referent/weak/dead", KIND_OTHER, UNITS_COUNT,
   3680      numWeakDead,
   3681      "The number of weak referents held by the preference service that are "
   3682      "dead.");
   3683 
   3684  return NS_OK;
   3685 }
   3686 
   3687 namespace {
   3688 
   3689 class AddPreferencesMemoryReporterRunnable : public Runnable {
   3690 public:
   3691  AddPreferencesMemoryReporterRunnable()
   3692      : Runnable("AddPreferencesMemoryReporterRunnable") {}
   3693 
   3694  NS_IMETHOD Run() override {
   3695    return RegisterStrongMemoryReporter(new PreferenceServiceReporter());
   3696  }
   3697 };
   3698 
   3699 }  // namespace
   3700 
   3701 // A list of changed prefs sent from the parent via shared memory.
   3702 static StaticAutoPtr<nsTArray<dom::Pref>> gChangedDomPrefs;
   3703 
   3704 static const char kTelemetryPref[] = "toolkit.telemetry.enabled";
   3705 #if !(defined(MOZ_WIDGET_ANDROID) && defined(MOZ_TELEMETRY_ON_BY_DEFAULT))
   3706 static const char kChannelPref[] = "app.update.channel";
   3707 #endif
   3708 
   3709 #ifdef MOZ_WIDGET_ANDROID
   3710 
   3711 static Maybe<bool> TelemetryPrefValue() {
   3712  // Leave it unchanged if it's already set.
   3713  // XXX: how could it already be set?
   3714  if (Preferences::GetType(kTelemetryPref) != nsIPrefBranch::PREF_INVALID) {
   3715    return Nothing();
   3716  }
   3717 
   3718  // Determine the correct default for toolkit.telemetry.enabled. If this
   3719  // build has MOZ_TELEMETRY_ON_BY_DEFAULT *or* we're on the beta channel,
   3720  // telemetry is on by default, otherwise not. This is necessary so that
   3721  // beta users who are testing final release builds don't flipflop defaults.
   3722 #  ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
   3723  return Some(true);
   3724 #  else
   3725  nsAutoCString channelPrefValue;
   3726  (void)Preferences::GetCString(kChannelPref, channelPrefValue,
   3727                                PrefValueKind::Default);
   3728  return Some(channelPrefValue.EqualsLiteral("beta"));
   3729 #  endif
   3730 }
   3731 
   3732 /* static */
   3733 void Preferences::SetupTelemetryPref() {
   3734  MOZ_ASSERT(XRE_IsParentProcess());
   3735 
   3736  Maybe<bool> telemetryPrefValue = TelemetryPrefValue();
   3737  if (telemetryPrefValue.isSome()) {
   3738    Preferences::SetBool(kTelemetryPref, *telemetryPrefValue,
   3739                         PrefValueKind::Default);
   3740  }
   3741 }
   3742 
   3743 #else  // !MOZ_WIDGET_ANDROID
   3744 
   3745 static bool TelemetryPrefValue() {
   3746  // For platforms with Unified Telemetry (here meaning not-Android),
   3747  // toolkit.telemetry.enabled determines whether we send "extended" data.
   3748  // We only want extended data from pre-release channels due to size.
   3749 
   3750  constexpr auto channel = MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL) ""_ns;
   3751 
   3752  // Easy cases: Nightly, Aurora, Beta.
   3753  if (channel.EqualsLiteral("nightly") || channel.EqualsLiteral("aurora") ||
   3754      channel.EqualsLiteral("beta")) {
   3755    return true;
   3756  }
   3757 
   3758 #  ifndef MOZILLA_OFFICIAL
   3759  // Local developer builds: non-official builds on the "default" channel.
   3760  if (channel.EqualsLiteral("default")) {
   3761    return true;
   3762  }
   3763 #  endif
   3764 
   3765  // Release Candidate builds: builds that think they are release builds, but
   3766  // are shipped to beta users.
   3767  if (channel.EqualsLiteral("release")) {
   3768    nsAutoCString channelPrefValue;
   3769    (void)Preferences::GetCString(kChannelPref, channelPrefValue,
   3770                                  PrefValueKind::Default);
   3771    if (channelPrefValue.EqualsLiteral("beta")) {
   3772      return true;
   3773    }
   3774  }
   3775 
   3776  return false;
   3777 }
   3778 
   3779 /* static */
   3780 void Preferences::SetupTelemetryPref() {
   3781  MOZ_ASSERT(XRE_IsParentProcess());
   3782 
   3783  Preferences::SetBool(kTelemetryPref, TelemetryPrefValue(),
   3784                       PrefValueKind::Default);
   3785  Preferences::Lock(kTelemetryPref);
   3786 }
   3787 
   3788 #endif  // MOZ_WIDGET_ANDROID
   3789 
   3790 /* static */
   3791 already_AddRefed<Preferences> Preferences::GetInstanceForService() {
   3792  if (sPreferences) {
   3793    return do_AddRef(sPreferences);
   3794  }
   3795 
   3796  if (sShutdown) {
   3797    return nullptr;
   3798  }
   3799 
   3800  sPreferences = new Preferences();
   3801 
   3802  MOZ_ASSERT(!HashTable());
   3803  HashTable() = new PrefsHashTable(XRE_IsParentProcess()
   3804                                       ? kHashTableInitialLengthParent
   3805                                       : kHashTableInitialLengthContent);
   3806 
   3807 #ifdef DEBUG
   3808  gOnceStaticPrefsAntiFootgun = new AntiFootgunMap();
   3809 #endif
   3810 
   3811 #ifdef ACCESS_COUNTS
   3812  MOZ_ASSERT(!gAccessCounts);
   3813  gAccessCounts = new AccessCountsHashTable();
   3814 #endif
   3815 
   3816  nsresult rv = InitInitialObjects(/* isStartup */ true);
   3817  if (NS_FAILED(rv)) {
   3818    sPreferences = nullptr;
   3819    return nullptr;
   3820  }
   3821 
   3822  if (!XRE_IsParentProcess()) {
   3823    MOZ_ASSERT(gChangedDomPrefs);
   3824    for (unsigned int i = 0; i < gChangedDomPrefs->Length(); i++) {
   3825      Preferences::SetPreference(gChangedDomPrefs->ElementAt(i));
   3826    }
   3827    gChangedDomPrefs = nullptr;
   3828  } else {
   3829    // Check if there is a deployment configuration file. If so, set up the
   3830    // pref config machinery, which will actually read the file.
   3831    nsAutoCString lockFileName;
   3832    nsresult rv = Preferences::GetCString("general.config.filename",
   3833                                          lockFileName, PrefValueKind::User);
   3834    if (NS_SUCCEEDED(rv)) {
   3835      NS_CreateServicesFromCategory(
   3836          "pref-config-startup",
   3837          static_cast<nsISupports*>(static_cast<void*>(sPreferences)),
   3838          "pref-config-startup");
   3839    }
   3840 
   3841    nsCOMPtr<nsIObserverService> observerService =
   3842        services::GetObserverService();
   3843    if (!observerService) {
   3844      sPreferences = nullptr;
   3845      return nullptr;
   3846    }
   3847 
   3848    observerService->AddObserver(sPreferences,
   3849                                 "profile-before-change-telemetry", true);
   3850    rv = observerService->AddObserver(sPreferences, "profile-before-change",
   3851                                      true);
   3852 
   3853    observerService->AddObserver(sPreferences, "suspend_process_notification",
   3854                                 true);
   3855 
   3856    if (NS_FAILED(rv)) {
   3857      sPreferences = nullptr;
   3858      return nullptr;
   3859    }
   3860  }
   3861 
   3862  const char* defaultPrefs = getenv("MOZ_DEFAULT_PREFS");
   3863  if (defaultPrefs) {
   3864    parsePrefData(nsCString(defaultPrefs), PrefValueKind::Default);
   3865  }
   3866 
   3867  // Preferences::GetInstanceForService() can be called from GetService(), and
   3868  // RegisterStrongMemoryReporter calls GetService(nsIMemoryReporter).  To
   3869  // avoid a potential recursive GetService() call, we can't register the
   3870  // memory reporter here; instead, do it off a runnable.
   3871  RefPtr<AddPreferencesMemoryReporterRunnable> runnable =
   3872      new AddPreferencesMemoryReporterRunnable();
   3873  NS_DispatchToMainThread(runnable);
   3874 
   3875  return do_AddRef(sPreferences);
   3876 }
   3877 
   3878 /* static */
   3879 bool Preferences::IsServiceAvailable() { return !!sPreferences; }
   3880 
   3881 /* static */
   3882 bool Preferences::InitStaticMembers() {
   3883  MOZ_ASSERT(NS_IsMainThread() || ServoStyleSet::IsInServoTraversal());
   3884 
   3885  if (MOZ_LIKELY(sPreferences)) {
   3886    return true;
   3887  }
   3888 
   3889  if (!sShutdown) {
   3890    MOZ_ASSERT(NS_IsMainThread());
   3891    nsCOMPtr<nsIPrefService> prefService =
   3892        do_GetService(NS_PREFSERVICE_CONTRACTID);
   3893  }
   3894 
   3895  return sPreferences != nullptr;
   3896 }
   3897 
   3898 /* static */
   3899 void Preferences::Shutdown() {
   3900  if (!sShutdown) {
   3901    sShutdown = true;  // Don't create the singleton instance after here.
   3902    sPreferences = nullptr;
   3903    StaticPrefs::ShutdownAlwaysPrefs();
   3904  }
   3905 }
   3906 
   3907 Preferences::Preferences()
   3908    : mRootBranch(new nsPrefBranch("", PrefValueKind::User)),
   3909      mDefaultRootBranch(new nsPrefBranch("", PrefValueKind::Default)) {}
   3910 
   3911 Preferences::~Preferences() {
   3912  MOZ_ASSERT(!sPreferences);
   3913 
   3914  MOZ_ASSERT(!gCallbacksInProgress);
   3915 
   3916  CallbackNode* node = gFirstCallback;
   3917  while (node) {
   3918    CallbackNode* next_node = node->Next();
   3919    delete node;
   3920    node = next_node;
   3921  }
   3922  gLastPriorityNode = gFirstCallback = nullptr;
   3923 
   3924  delete HashTable();
   3925  HashTable() = nullptr;
   3926 
   3927 #ifdef DEBUG
   3928  gOnceStaticPrefsAntiFootgun = nullptr;
   3929 #endif
   3930 
   3931 #ifdef ACCESS_COUNTS
   3932  gAccessCounts = nullptr;
   3933 #endif
   3934 
   3935  gSharedMap = nullptr;
   3936 
   3937  PrefNameArena().Clear();
   3938 }
   3939 
   3940 NS_IMPL_ISUPPORTS(Preferences, nsIPrefService, nsIObserver, nsIPrefBranch,
   3941                  nsISupportsWeakReference)
   3942 
   3943 /* static */
   3944 void Preferences::SerializePreferences(nsCString& aStr,
   3945                                       bool aIsDestinationWebContentProcess) {
   3946  MOZ_RELEASE_ASSERT(InitStaticMembers());
   3947 
   3948  aStr.Truncate();
   3949 
   3950  for (auto iter = HashTable()->iter(); !iter.done(); iter.next()) {
   3951    Pref* pref = iter.get().get();
   3952    if (!pref->IsTypeNone() && pref->HasAdvisablySizedValues()) {
   3953      pref->SerializeAndAppend(aStr, aIsDestinationWebContentProcess &&
   3954                                         ShouldSanitizePreference(pref));
   3955    }
   3956  }
   3957 
   3958  aStr.Append('\0');
   3959 }
   3960 
   3961 /* static */
   3962 void Preferences::DeserializePreferences(const char* aStr, size_t aPrefsLen) {
   3963  MOZ_ASSERT(!XRE_IsParentProcess());
   3964 
   3965  MOZ_ASSERT(!gChangedDomPrefs);
   3966  gChangedDomPrefs = new nsTArray<dom::Pref>();
   3967 
   3968  const char* p = aStr;
   3969  while (*p != '\0') {
   3970    dom::Pref pref;
   3971    p = Pref::Deserialize(p, &pref);
   3972    gChangedDomPrefs->AppendElement(pref);
   3973  }
   3974 
   3975  // We finished parsing on a '\0'. That should be the last char in the shared
   3976  // memory. (aPrefsLen includes the '\0'.)
   3977  MOZ_ASSERT(p == aStr + aPrefsLen - 1);
   3978 
   3979  MOZ_ASSERT(!gContentProcessPrefsAreInited);
   3980  gContentProcessPrefsAreInited = true;
   3981 }
   3982 
   3983 /* static */
   3984 mozilla::ipc::ReadOnlySharedMemoryHandle Preferences::EnsureSnapshot() {
   3985  MOZ_ASSERT(XRE_IsParentProcess());
   3986  MOZ_ASSERT(NS_IsMainThread());
   3987 
   3988  if (!gSharedMap) {
   3989    SharedPrefMapBuilder builder;
   3990 
   3991    nsTArray<Pref*> toRepopulate;
   3992    NameArena* newPrefNameArena = new NameArena();
   3993    for (auto iter = HashTable()->modIter(); !iter.done(); iter.next()) {
   3994      if (!ShouldSanitizePreference(iter.get().get())) {
   3995        iter.get()->AddToMap(builder);
   3996      } else {
   3997        Pref* pref = iter.getMutable().release();
   3998        pref->RelocateName(newPrefNameArena);
   3999        toRepopulate.AppendElement(pref);
   4000      }
   4001    }
   4002 
   4003    // Store the current value of `once`-mirrored prefs. After this point they
   4004    // will be immutable.
   4005    StaticPrefs::RegisterOncePrefs(builder);
   4006 
   4007    gSharedMap = new SharedPrefMap(std::move(builder));
   4008 
   4009    // Once we've built a snapshot of the database, there's no need to continue
   4010    // storing dynamic copies of the preferences it contains. Once we reset the
   4011    // hashtable, preference lookups will fall back to the snapshot for any
   4012    // preferences not in the dynamic hashtable.
   4013    //
   4014    // And since the majority of the database is now contained in the snapshot,
   4015    // we can initialize the hashtable with the expected number of per-session
   4016    // changed preferences, rather than the expected total number of
   4017    // preferences.
   4018    HashTable()->clearAndCompact();
   4019    (void)HashTable()->reserve(kHashTableInitialLengthContent);
   4020 
   4021    delete sPrefNameArena;
   4022    sPrefNameArena = newPrefNameArena;
   4023    gCallbackPref = nullptr;
   4024 
   4025    for (uint32_t i = 0; i < toRepopulate.Length(); i++) {
   4026      auto pref = toRepopulate[i];
   4027      auto p = HashTable()->lookupForAdd(pref->Name());
   4028      MOZ_ASSERT(!p.found());
   4029      (void)HashTable()->add(p, pref);
   4030    }
   4031  }
   4032 
   4033  return gSharedMap->CloneHandle();
   4034 }
   4035 
   4036 /* static */
   4037 void Preferences::InitSnapshot(
   4038    const mozilla::ipc::ReadOnlySharedMemoryHandle& aHandle) {
   4039  MOZ_ASSERT(!XRE_IsParentProcess());
   4040  MOZ_ASSERT(!gSharedMap);
   4041 
   4042  gSharedMap = new SharedPrefMap(aHandle);
   4043 
   4044  StaticPrefs::InitStaticPrefsFromShared();
   4045 }
   4046 
   4047 /* static */
   4048 void Preferences::InitializeUserPrefs() {
   4049  MOZ_ASSERT(XRE_IsParentProcess());
   4050  MOZ_ASSERT(!sPreferences->mCurrentFile, "Should only initialize prefs once");
   4051 
   4052  // Prefs which are set before we initialize the profile are silently
   4053  // discarded. This is stupid, but there are various tests which depend on
   4054  // this behavior.
   4055  sPreferences->ResetUserPrefs();
   4056 
   4057  nsCOMPtr<nsIFile> prefsFile = sPreferences->ReadSavedPrefs();
   4058  sPreferences->ReadUserOverridePrefs();
   4059 
   4060  sPreferences->mDirty = false;
   4061 
   4062  // Don't set mCurrentFile until we're done so that dirty flags work properly.
   4063  sPreferences->mCurrentFile = std::move(prefsFile);
   4064 }
   4065 
   4066 /* static */
   4067 void Preferences::FinishInitializingUserPrefs() {
   4068  sPreferences->NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID);
   4069 }
   4070 
   4071 NS_IMETHODIMP
   4072 Preferences::Observe(nsISupports* aSubject, const char* aTopic,
   4073                     const char16_t* someData) {
   4074  if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
   4075    return NS_ERROR_NOT_AVAILABLE;
   4076  }
   4077 
   4078  nsresult rv = NS_OK;
   4079 
   4080  if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
   4081    // Normally prefs aren't written after this point, and so we kick off
   4082    // an asynchronous pref save so that I/O can be done in parallel with
   4083    // other shutdown.
   4084    if (AllowOffMainThreadSave()) {
   4085      SavePrefFile(nullptr);
   4086    }
   4087 
   4088  } else if (!nsCRT::strcmp(aTopic, "profile-before-change-telemetry")) {
   4089    // It's possible that a profile-before-change observer after ours
   4090    // set a pref. A blocking save here re-saves if necessary and also waits
   4091    // for any pending saves to complete.
   4092    SavePrefFileBlocking();
   4093    MOZ_ASSERT(!mDirty, "Preferences should not be dirty");
   4094    mProfileShutdown = true;
   4095 
   4096  } else if (!nsCRT::strcmp(aTopic, "suspend_process_notification")) {
   4097    // Our process is being suspended. The OS may wake our process later,
   4098    // or it may kill the process. In case our process is going to be killed
   4099    // from the suspended state, we save preferences before suspending.
   4100    rv = SavePrefFileBlocking();
   4101  }
   4102 
   4103  return rv;
   4104 }
   4105 
   4106 NS_IMETHODIMP
   4107 Preferences::ReadDefaultPrefsFromFile(nsIFile* aFile) {
   4108  ENSURE_PARENT_PROCESS("Preferences::ReadDefaultPrefsFromFile", "all prefs");
   4109 
   4110  if (!aFile) {
   4111    NS_ERROR("ReadDefaultPrefsFromFile requires a parameter");
   4112    return NS_ERROR_INVALID_ARG;
   4113  }
   4114 
   4115  return openPrefFile(aFile, PrefValueKind::Default);
   4116 }
   4117 
   4118 NS_IMETHODIMP
   4119 Preferences::ReadUserPrefsFromFile(nsIFile* aFile) {
   4120  ENSURE_PARENT_PROCESS("Preferences::ReadUserPrefsFromFile", "all prefs");
   4121 
   4122  if (!aFile) {
   4123    NS_ERROR("ReadUserPrefsFromFile requires a parameter");
   4124    return NS_ERROR_INVALID_ARG;
   4125  }
   4126 
   4127  return openPrefFile(aFile, PrefValueKind::User);
   4128 }
   4129 
   4130 NS_IMETHODIMP
   4131 Preferences::ResetPrefs() {
   4132  ENSURE_PARENT_PROCESS("Preferences::ResetPrefs", "all prefs");
   4133 
   4134  if (gSharedMap) {
   4135    return NS_ERROR_NOT_AVAILABLE;
   4136  }
   4137 
   4138  HashTable()->clearAndCompact();
   4139  (void)HashTable()->reserve(kHashTableInitialLengthParent);
   4140 
   4141  PrefNameArena().Clear();
   4142 
   4143  return InitInitialObjects(/* isStartup */ false);
   4144 }
   4145 
   4146 nsresult Preferences::ResetUserPrefs() {
   4147  ENSURE_PARENT_PROCESS("Preferences::ResetUserPrefs", "all prefs");
   4148  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   4149  MOZ_ASSERT(NS_IsMainThread());
   4150 
   4151  Vector<const char*> prefNames;
   4152  for (auto iter = HashTable()->modIter(); !iter.done(); iter.next()) {
   4153    Pref* pref = iter.get().get();
   4154 
   4155    if (pref->HasUserValue()) {
   4156      if (!prefNames.append(pref->Name())) {
   4157        return NS_ERROR_OUT_OF_MEMORY;
   4158      }
   4159 
   4160      pref->ClearUserValue();
   4161      if (!pref->HasDefaultValue()) {
   4162        iter.remove();
   4163      }
   4164    }
   4165  }
   4166 
   4167  for (const char* prefName : prefNames) {
   4168    NotifyCallbacks(nsDependentCString{prefName});
   4169  }
   4170 
   4171  Preferences::HandleDirty();
   4172  return NS_OK;
   4173 }
   4174 
   4175 bool Preferences::AllowOffMainThreadSave() {
   4176  // Put in a preference that allows us to disable off main thread preference
   4177  // file save.
   4178  if (sAllowOMTPrefWrite < 0) {
   4179    bool value = false;
   4180    Preferences::GetBool("preferences.allow.omt-write", &value);
   4181    sAllowOMTPrefWrite = value ? 1 : 0;
   4182  }
   4183 
   4184  return !!sAllowOMTPrefWrite;
   4185 }
   4186 
   4187 nsresult Preferences::SavePrefFileBlocking() {
   4188  if (mDirty) {
   4189    return SavePrefFileInternal(nullptr, SaveMethod::Blocking);
   4190  }
   4191 
   4192  // If we weren't dirty to start, SavePrefFileInternal will early exit so
   4193  // there is no guarantee that we don't have oustanding async saves in the
   4194  // pipe. Since the contract of SavePrefFileOnMainThread is that the file on
   4195  // disk matches the preferences, we have to make sure those requests are
   4196  // completed.
   4197 
   4198  if (AllowOffMainThreadSave()) {
   4199    PreferencesWriter::Flush();
   4200  }
   4201 
   4202  return NS_OK;
   4203 }
   4204 
   4205 nsresult Preferences::SavePrefFileAsynchronous() {
   4206  return SavePrefFileInternal(nullptr, SaveMethod::Asynchronous);
   4207 }
   4208 
   4209 NS_IMETHODIMP
   4210 Preferences::SavePrefFile(nsIFile* aFile) {
   4211  // This is the method accessible from service API. Make it off main thread.
   4212  return SavePrefFileInternal(aFile, SaveMethod::Asynchronous);
   4213 }
   4214 
   4215 NS_IMETHODIMP
   4216 Preferences::BackupPrefFile(nsIFile* aFile, nsIPrefOverrideMap* aJSOverrideMap,
   4217                            JSContext* aCx, Promise** aPromise) {
   4218  MOZ_ASSERT(NS_IsMainThread());
   4219 
   4220  if (!aFile) {
   4221    return NS_ERROR_INVALID_ARG;
   4222  }
   4223 
   4224  if (mCurrentFile) {
   4225    bool equalsCurrent = false;
   4226    nsresult rv = aFile->Equals(mCurrentFile, &equalsCurrent);
   4227 
   4228    if (NS_FAILED(rv)) {
   4229      return rv;
   4230    }
   4231 
   4232    if (equalsCurrent) {
   4233      return NS_ERROR_INVALID_ARG;
   4234    }
   4235  }
   4236 
   4237  ErrorResult result;
   4238  RefPtr<Promise> promise =
   4239      Promise::Create(xpc::CurrentNativeGlobal(aCx), result);
   4240 
   4241  if (MOZ_UNLIKELY(result.Failed())) {
   4242    return result.StealNSResult();
   4243  }
   4244 
   4245  nsMainThreadPtrHandle<Promise> domPromiseHolder(
   4246      new nsMainThreadPtrHolder<Promise>("Preferences::BackupPrefFile promise",
   4247                                         promise));
   4248 
   4249  auto mozPromiseHolder = MakeUnique<MozPromiseHolder<WritePrefFilePromise>>();
   4250  RefPtr<WritePrefFilePromise> writePrefPromise =
   4251      mozPromiseHolder->Ensure(__func__);
   4252 
   4253  // Write prefs, minus ExperimentAPI prefs and overrides.
   4254  nsresult rv = WritePrefFile(aFile, SaveMethod::Asynchronous,
   4255                              std::move(mozPromiseHolder), aJSOverrideMap);
   4256  if (NS_FAILED(rv)) {
   4257    // WritePrefFile is responsible for rejecting the underlying MozPromise in
   4258    // the event that it the method failed somewhere.
   4259    return rv;
   4260  }
   4261 
   4262  writePrefPromise->Then(
   4263      GetMainThreadSerialEventTarget(), __func__,
   4264      [domPromiseHolder](bool) {
   4265        MOZ_ASSERT(NS_IsMainThread());
   4266        domPromiseHolder.get()->MaybeResolveWithUndefined();
   4267      },
   4268      [domPromiseHolder](nsresult rv) {
   4269        MOZ_ASSERT(NS_IsMainThread());
   4270        domPromiseHolder.get()->MaybeReject(rv);
   4271      });
   4272 
   4273  promise.forget(aPromise);
   4274  return NS_OK;
   4275 }
   4276 
   4277 /* static */
   4278 void Preferences::SetPreference(const dom::Pref& aDomPref) {
   4279  MOZ_ASSERT(!XRE_IsParentProcess());
   4280  NS_ENSURE_TRUE(InitStaticMembers(), (void)0);
   4281 
   4282  const nsCString& prefName = aDomPref.name();
   4283 
   4284  Pref* pref;
   4285  auto p = HashTable()->lookupForAdd(prefName.get());
   4286  if (!p) {
   4287    pref = new Pref(prefName);
   4288    if (!HashTable()->add(p, pref)) {
   4289      delete pref;
   4290      return;
   4291    }
   4292  } else {
   4293    pref = p->get();
   4294  }
   4295 
   4296  bool valueChanged = false;
   4297  pref->FromDomPref(aDomPref, &valueChanged);
   4298 
   4299  // When the parent process clears a pref's user value or deletes a pref
   4300  // we get a DomPref here with no default value and no user value.
   4301  // There are two possibilities.
   4302  //
   4303  // - There was an existing pref with only a user value. FromDomPref() will
   4304  //   have just cleared that user value, so the pref can be removed.
   4305  //
   4306  // - There was no existing pref. FromDomPref() will have done nothing, and
   4307  //   `pref` will be valueless. We will end up adding and removing the value
   4308  //   needlessly, but that's ok because this case is rare.
   4309  //
   4310  if (!pref->HasDefaultValue() && !pref->HasUserValue() &&
   4311      !pref->IsSanitized()) {
   4312    // If the preference exists in the shared map, we need to keep the dynamic
   4313    // entry around to mask it.
   4314    if (gSharedMap->Has(pref->Name())) {
   4315      pref->SetType(PrefType::None);
   4316    } else {
   4317      HashTable()->remove(prefName.get());
   4318    }
   4319    pref = nullptr;
   4320  }
   4321 
   4322  // Note: we don't have to worry about HandleDirty() because we are setting
   4323  // prefs in the content process that have come from the parent process.
   4324 
   4325  if (valueChanged) {
   4326    if (pref) {
   4327      NotifyCallbacks(prefName, PrefWrapper(pref));
   4328    } else {
   4329      NotifyCallbacks(prefName);
   4330    }
   4331  }
   4332 }
   4333 
   4334 /* static */
   4335 void Preferences::GetPreference(dom::Pref* aDomPref,
   4336                                const GeckoProcessType aDestinationProcessType,
   4337                                const nsACString& aDestinationRemoteType) {
   4338  MOZ_ASSERT(XRE_IsParentProcess());
   4339  bool destIsWebContent =
   4340      aDestinationProcessType == GeckoProcessType_Content &&
   4341      (StringBeginsWith(aDestinationRemoteType, WEB_REMOTE_TYPE) ||
   4342       StringBeginsWith(aDestinationRemoteType, PREALLOC_REMOTE_TYPE) ||
   4343       StringBeginsWith(aDestinationRemoteType, PRIVILEGEDMOZILLA_REMOTE_TYPE));
   4344 
   4345  Pref* pref = pref_HashTableLookup(aDomPref->name().get());
   4346  if (pref && pref->HasAdvisablySizedValues()) {
   4347    pref->ToDomPref(aDomPref, destIsWebContent);
   4348  }
   4349 }
   4350 
   4351 #ifdef DEBUG
   4352 bool Preferences::ArePrefsInitedInContentProcess() {
   4353  MOZ_ASSERT(!XRE_IsParentProcess());
   4354  return gContentProcessPrefsAreInited;
   4355 }
   4356 #endif
   4357 
   4358 NS_IMETHODIMP
   4359 Preferences::GetBranch(const char* aPrefRoot, nsIPrefBranch** aRetVal) {
   4360  if ((nullptr != aPrefRoot) && (*aPrefRoot != '\0')) {
   4361    // TODO: Cache this stuff and allow consumers to share branches (hold weak
   4362    // references, I think).
   4363    RefPtr<nsPrefBranch> prefBranch =
   4364        new nsPrefBranch(aPrefRoot, PrefValueKind::User);
   4365    prefBranch.forget(aRetVal);
   4366  } else {
   4367    // Special case: caching the default root.
   4368    nsCOMPtr<nsIPrefBranch> root(sPreferences->mRootBranch);
   4369    root.forget(aRetVal);
   4370  }
   4371 
   4372  return NS_OK;
   4373 }
   4374 
   4375 NS_IMETHODIMP
   4376 Preferences::GetDefaultBranch(const char* aPrefRoot, nsIPrefBranch** aRetVal) {
   4377  if (!aPrefRoot || !aPrefRoot[0]) {
   4378    nsCOMPtr<nsIPrefBranch> root(sPreferences->mDefaultRootBranch);
   4379    root.forget(aRetVal);
   4380    return NS_OK;
   4381  }
   4382 
   4383  // TODO: Cache this stuff and allow consumers to share branches (hold weak
   4384  // references, I think).
   4385  RefPtr<nsPrefBranch> prefBranch =
   4386      new nsPrefBranch(aPrefRoot, PrefValueKind::Default);
   4387  if (!prefBranch) {
   4388    return NS_ERROR_OUT_OF_MEMORY;
   4389  }
   4390 
   4391  prefBranch.forget(aRetVal);
   4392  return NS_OK;
   4393 }
   4394 
   4395 NS_IMETHODIMP
   4396 Preferences::ReadStats(nsIPrefStatsCallback* aCallback) {
   4397 #ifdef ACCESS_COUNTS
   4398  for (const auto& entry : *gAccessCounts) {
   4399    aCallback->Visit(entry.GetKey(), entry.GetData());
   4400  }
   4401 
   4402  return NS_OK;
   4403 #else
   4404  return NS_ERROR_NOT_IMPLEMENTED;
   4405 #endif
   4406 }
   4407 
   4408 NS_IMETHODIMP
   4409 Preferences::ResetStats() {
   4410 #ifdef ACCESS_COUNTS
   4411  gAccessCounts->Clear();
   4412  return NS_OK;
   4413 #else
   4414  return NS_ERROR_NOT_IMPLEMENTED;
   4415 #endif
   4416 }
   4417 
   4418 // We would much prefer to use C++ lambdas, but we cannot convert
   4419 // lambdas that capture (here, the underlying observer) to C pointer
   4420 // to functions.  So, here we are, with icky C callbacks.  Be aware
   4421 // that nothing is thread-safe here because there's a single global
   4422 // `nsIPrefObserver` instance.  Use this from the main thread only.
   4423 nsIPrefObserver* PrefObserver = nullptr;
   4424 
   4425 void HandlePref(const char* aPrefName, PrefType aType, PrefValueKind aKind,
   4426                PrefValue aValue, bool aIsSticky, bool aIsLocked) {
   4427  MOZ_ASSERT(NS_IsMainThread());
   4428 
   4429  if (!PrefObserver) {
   4430    return;
   4431  }
   4432 
   4433  const char* kind = aKind == PrefValueKind::Default ? "Default" : "User";
   4434 
   4435  switch (aType) {
   4436    case PrefType::String:
   4437      PrefObserver->OnStringPref(kind, aPrefName, aValue.mStringVal, aIsSticky,
   4438                                 aIsLocked);
   4439      break;
   4440    case PrefType::Int:
   4441      PrefObserver->OnIntPref(kind, aPrefName, aValue.mIntVal, aIsSticky,
   4442                              aIsLocked);
   4443      break;
   4444    case PrefType::Bool:
   4445      PrefObserver->OnBoolPref(kind, aPrefName, aValue.mBoolVal, aIsSticky,
   4446                               aIsLocked);
   4447      break;
   4448    default:
   4449      PrefObserver->OnError("Unexpected pref type.");
   4450  }
   4451 }
   4452 
   4453 void HandleError(const char* aMsg) {
   4454  MOZ_ASSERT(NS_IsMainThread());
   4455 
   4456  if (!PrefObserver) {
   4457    return;
   4458  }
   4459 
   4460  PrefObserver->OnError(aMsg);
   4461 }
   4462 
   4463 NS_IMETHODIMP
   4464 Preferences::ParsePrefsFromBuffer(const nsTArray<uint8_t>& aBytes,
   4465                                  nsIPrefObserver* aObserver,
   4466                                  const char* aPathLabel) {
   4467  MOZ_ASSERT(NS_IsMainThread());
   4468 
   4469  // We need a null-terminated buffer.
   4470  nsTArray<uint8_t> data = aBytes.Clone();
   4471  data.AppendElement(0);
   4472 
   4473  // Parsing as default handles both `pref` and `user_pref`.
   4474  PrefObserver = aObserver;
   4475  prefs_parser_parse(aPathLabel ? aPathLabel : "<ParsePrefsFromBuffer data>",
   4476                     PrefValueKind::Default, (const char*)data.Elements(),
   4477                     data.Length() - 1, HandlePref, HandleError);
   4478  PrefObserver = nullptr;
   4479 
   4480  return NS_OK;
   4481 }
   4482 
   4483 NS_IMETHODIMP
   4484 Preferences::GetUserPrefsFileLastModifiedAtStartup(PRTime* aLastModified) {
   4485  *aLastModified = mUserPrefsFileLastModifiedAtStartup;
   4486  return NS_OK;
   4487 }
   4488 
   4489 NS_IMETHODIMP
   4490 Preferences::GetDirty(bool* aRetVal) {
   4491  *aRetVal = mDirty;
   4492  return NS_OK;
   4493 }
   4494 
   4495 NS_IMETHODIMP
   4496 Preferences::GetPrefsJsPreamble(nsACString& aPreamble) {
   4497  ::GetPrefsJsPreamble(aPreamble);
   4498  return NS_OK;
   4499 }
   4500 
   4501 nsresult Preferences::NotifyServiceObservers(const char* aTopic) {
   4502  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   4503  if (!observerService) {
   4504    return NS_ERROR_FAILURE;
   4505  }
   4506 
   4507  auto subject = static_cast<nsIPrefService*>(this);
   4508  observerService->NotifyObservers(subject, aTopic, nullptr);
   4509 
   4510  return NS_OK;
   4511 }
   4512 
   4513 already_AddRefed<nsIFile> Preferences::ReadSavedPrefs() {
   4514  nsCOMPtr<nsIFile> file;
   4515  nsresult rv =
   4516      NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE, getter_AddRefs(file));
   4517  if (NS_WARN_IF(NS_FAILED(rv))) {
   4518    return nullptr;
   4519  }
   4520 
   4521  rv = openPrefFile(file, PrefValueKind::User);
   4522  if (rv == NS_ERROR_FILE_NOT_FOUND) {
   4523    // This is a normal case for new users.
   4524    rv = NS_OK;
   4525  } else {
   4526    // Store the last modified time of the file while we've got it.
   4527    // We don't really care if this fails.
   4528    (void)file->GetLastModifiedTime(&mUserPrefsFileLastModifiedAtStartup);
   4529 
   4530    if (NS_FAILED(rv)) {
   4531      // Save a backup copy of the current (invalid) prefs file, since all prefs
   4532      // from the error line to the end of the file will be lost (bug 361102).
   4533      // TODO we should notify the user about it (bug 523725).
   4534      glean::preferences::prefs_file_was_invalid.Set(true);
   4535      MakeBackupPrefFile(file);
   4536    }
   4537  }
   4538 
   4539  return file.forget();
   4540 }
   4541 
   4542 void Preferences::ReadUserOverridePrefs() {
   4543  nsCOMPtr<nsIFile> aFile;
   4544  nsresult rv =
   4545      NS_GetSpecialDirectory(NS_APP_PREFS_50_DIR, getter_AddRefs(aFile));
   4546  if (NS_WARN_IF(NS_FAILED(rv))) {
   4547    return;
   4548  }
   4549 
   4550  aFile->AppendNative("user.js"_ns);
   4551  rv = openPrefFile(aFile, PrefValueKind::User);
   4552 }
   4553 
   4554 nsresult Preferences::MakeBackupPrefFile(nsIFile* aFile) {
   4555  // Example: this copies "prefs.js" to "Invalidprefs.js" in the same directory.
   4556  // "Invalidprefs.js" is removed if it exists, prior to making the copy.
   4557  nsAutoString newFilename;
   4558  nsresult rv = aFile->GetLeafName(newFilename);
   4559  NS_ENSURE_SUCCESS(rv, rv);
   4560 
   4561  newFilename.InsertLiteral(u"Invalid", 0);
   4562  nsCOMPtr<nsIFile> newFile;
   4563  rv = aFile->GetParent(getter_AddRefs(newFile));
   4564  NS_ENSURE_SUCCESS(rv, rv);
   4565 
   4566  rv = newFile->Append(newFilename);
   4567  NS_ENSURE_SUCCESS(rv, rv);
   4568 
   4569  bool exists = false;
   4570  newFile->Exists(&exists);
   4571  if (exists) {
   4572    rv = newFile->Remove(false);
   4573    NS_ENSURE_SUCCESS(rv, rv);
   4574  }
   4575 
   4576  rv = aFile->CopyTo(nullptr, newFilename);
   4577  NS_ENSURE_SUCCESS(rv, rv);
   4578 
   4579  return rv;
   4580 }
   4581 
   4582 nsresult Preferences::SavePrefFileInternal(nsIFile* aFile,
   4583                                           SaveMethod aSaveMethod) {
   4584  ENSURE_PARENT_PROCESS("Preferences::SavePrefFileInternal", "all prefs");
   4585 
   4586  // We allow different behavior here when aFile argument is not null, but it
   4587  // happens to be the same as the current file.  It is not clear that we
   4588  // should, but it does give us a "force" save on the unmodified pref file
   4589  // (see the original bug 160377 when we added this.)
   4590 
   4591  if (nullptr == aFile) {
   4592    mSavePending = false;
   4593 
   4594    // Off main thread writing only if allowed.
   4595    if (!AllowOffMainThreadSave()) {
   4596      aSaveMethod = SaveMethod::Blocking;
   4597    }
   4598 
   4599    // The mDirty flag tells us if we should write to mCurrentFile. We only
   4600    // check this flag when the caller wants to write to the default.
   4601    if (!mDirty) {
   4602      return NS_OK;
   4603    }
   4604 
   4605    // Check for profile shutdown after mDirty because the runnables from
   4606    // HandleDirty() can still be pending.
   4607    if (mProfileShutdown) {
   4608      NS_WARNING("Cannot save pref file after profile shutdown.");
   4609      return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
   4610    }
   4611 
   4612    // It's possible that we never got a prefs file.
   4613    nsresult rv = NS_OK;
   4614    if (mCurrentFile) {
   4615      rv = WritePrefFile(mCurrentFile, aSaveMethod);
   4616    }
   4617 
   4618    // If we succeeded writing to mCurrentFile, reset the dirty flag.
   4619    if (NS_SUCCEEDED(rv)) {
   4620      mDirty = false;
   4621    }
   4622    return rv;
   4623 
   4624  } else {
   4625    // We only allow off main thread writes on mCurrentFile using this method.
   4626    // If you want to write asynchronously, use BackupPrefFile instead.
   4627    return WritePrefFile(aFile, SaveMethod::Blocking);
   4628  }
   4629 }
   4630 
   4631 nsresult Preferences::WritePrefFile(
   4632    nsIFile* aFile, SaveMethod aSaveMethod,
   4633    UniquePtr<MozPromiseHolder<WritePrefFilePromise>>
   4634        aPromiseHolder /* = nullptr */,
   4635    const nsIPrefOverrideMap* aPrefOverrideMap /* = nullptr */) {
   4636  MOZ_ASSERT(XRE_IsParentProcess());
   4637 
   4638 #define REJECT_IF_PROMISE_HOLDER_EXISTS(rv)       \
   4639  if (aPromiseHolder) {                           \
   4640    aPromiseHolder->RejectIfExists(rv, __func__); \
   4641  }                                               \
   4642  return rv;
   4643 
   4644  if (!HashTable()) {
   4645    REJECT_IF_PROMISE_HOLDER_EXISTS(NS_ERROR_NOT_INITIALIZED);
   4646  }
   4647 
   4648  AUTO_PROFILER_LABEL("Preferences::WritePrefFile", OTHER);
   4649 
   4650  if (AllowOffMainThreadSave()) {
   4651    UniquePtr<PrefSaveData> prefs =
   4652        MakeUnique<PrefSaveData>(pref_savePrefs(aPrefOverrideMap));
   4653 
   4654    nsresult rv = NS_OK;
   4655    bool writingToCurrent = false;
   4656 
   4657    if (mCurrentFile) {
   4658      rv = mCurrentFile->Equals(aFile, &writingToCurrent);
   4659      if (NS_FAILED(rv)) {
   4660        REJECT_IF_PROMISE_HOLDER_EXISTS(rv);
   4661      }
   4662    }
   4663 
   4664    // Put the newly constructed preference data into sPendingWriteData
   4665    // for the next request to pick up
   4666    prefs.reset(PreferencesWriter::sPendingWriteData.exchange(prefs.release()));
   4667    if (prefs && !writingToCurrent) {
   4668      MOZ_ASSERT(!aPromiseHolder,
   4669                 "Shouldn't be able to enter here if aPromiseHolder is set");
   4670      // There was a previous request writing to the default location that
   4671      // hasn't been processed. It will do the work of eventually writing this
   4672      // latest batch of data to disk.
   4673      return NS_OK;
   4674    }
   4675 
   4676    // There were no previous requests. Dispatch one since sPendingWriteData has
   4677    // the up to date information.
   4678    nsCOMPtr<nsIEventTarget> target =
   4679        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
   4680    if (NS_SUCCEEDED(rv)) {
   4681      bool async = aSaveMethod == SaveMethod::Asynchronous;
   4682 
   4683      // Increment sPendingWriteCount, even though it's redundant to track this
   4684      // in the case of a sync runnable; it just makes it easier to simply
   4685      // decrement this inside PWRunnable. We cannot use the constructor /
   4686      // destructor for increment/decrement, as on dispatch failure we might
   4687      // leak the runnable in order to not destroy it on the wrong thread, which
   4688      // would make us get stuck in an infinite SpinEventLoopUntil inside
   4689      // PreferencesWriter::Flush. Better that in future code we miss an
   4690      // increment of sPendingWriteCount and cause a simple crash due to it
   4691      // ending up negative.
   4692      //
   4693      // If aPromiseHolder is not null, ownership is transferred to PWRunnable.
   4694      // The PWRunnable will automatically reject the MozPromise if it is
   4695      // destroyed before being resolved or rejected by the Run method.
   4696      PreferencesWriter::sPendingWriteCount++;
   4697      if (async) {
   4698        rv = target->Dispatch(new PWRunnable(aFile, std::move(aPromiseHolder)),
   4699                              nsIEventTarget::DISPATCH_NORMAL);
   4700      } else {
   4701        rv = SyncRunnable::DispatchToThread(
   4702            target, new PWRunnable(aFile, std::move(aPromiseHolder)), true);
   4703      }
   4704      if (NS_FAILED(rv)) {
   4705        // If our dispatch failed, we should correct our bookkeeping to
   4706        // avoid shutdown hangs.
   4707        PreferencesWriter::sPendingWriteCount--;
   4708        // No need to reject the aPromiseHolder here, as the PWRunnable will
   4709        // have already done so.
   4710        return rv;
   4711      }
   4712      return NS_OK;
   4713    }
   4714 
   4715    // If we can't get the thread for writing, for whatever reason, do the main
   4716    // thread write after making some noise.
   4717    MOZ_ASSERT(false, "failed to get the target thread for OMT pref write");
   4718  }
   4719 
   4720  // This will do a main thread write. It is safe to do it this way because
   4721  // AllowOffMainThreadSave() returns a consistent value for the lifetime of
   4722  // the parent process.
   4723  PrefSaveData prefsData = pref_savePrefs(aPrefOverrideMap);
   4724 
   4725  // If we were given a MozPromiseHolder, this means the caller is attempting
   4726  // to write prefs asynchronously to the disk - but if we get here, it means
   4727  // that AllowOffMainThreadSave() return false, and that we will be forced
   4728  // to write on the main thread instead. We still have to resolve or reject
   4729  // that MozPromise regardless.
   4730  nsresult rv = PreferencesWriter::Write(aFile, prefsData);
   4731  if (aPromiseHolder) {
   4732    NS_WARNING(
   4733        "Cannot write to prefs asynchronously, as AllowOffMainThreadSave() "
   4734        "returned false.");
   4735    if (NS_SUCCEEDED(rv)) {
   4736      aPromiseHolder->ResolveIfExists(true, __func__);
   4737    } else {
   4738      aPromiseHolder->RejectIfExists(rv, __func__);
   4739    }
   4740  }
   4741  return rv;
   4742 
   4743 #undef REJECT_IF_PROMISE_HOLDER_EXISTS
   4744 }
   4745 
   4746 static nsresult openPrefFile(nsIFile* aFile, PrefValueKind aKind) {
   4747  MOZ_ASSERT(XRE_IsParentProcess());
   4748 
   4749  nsCString data = MOZ_TRY(URLPreloader::ReadFile(aFile));
   4750 
   4751  nsAutoString filenameUtf16;
   4752  aFile->GetLeafName(filenameUtf16);
   4753  NS_ConvertUTF16toUTF8 filename(filenameUtf16);
   4754 
   4755  nsAutoString path;
   4756  aFile->GetPath(path);
   4757 
   4758  Parser parser;
   4759  if (!parser.Parse(aKind, NS_ConvertUTF16toUTF8(path).get(), data)) {
   4760    return NS_ERROR_FILE_CORRUPTED;
   4761  }
   4762 
   4763  return NS_OK;
   4764 }
   4765 
   4766 static nsresult parsePrefData(const nsCString& aData, PrefValueKind aKind) {
   4767  const nsCString path = "$MOZ_DEFAULT_PREFS"_ns;
   4768 
   4769  Parser parser;
   4770  if (!parser.Parse(aKind, path.get(), aData)) {
   4771    return NS_ERROR_FILE_CORRUPTED;
   4772  }
   4773 
   4774  return NS_OK;
   4775 }
   4776 
   4777 static int pref_CompareFileNames(nsIFile* aFile1, nsIFile* aFile2) {
   4778  nsAutoCString filename1, filename2;
   4779  aFile1->GetNativeLeafName(filename1);
   4780  aFile2->GetNativeLeafName(filename2);
   4781 
   4782  return Compare(filename2, filename1);
   4783 }
   4784 
   4785 // Load default pref files from a directory. The files in the directory are
   4786 // sorted reverse-alphabetically.
   4787 static nsresult pref_LoadPrefsInDir(nsIFile* aDir) {
   4788  MOZ_ASSERT(XRE_IsParentProcess());
   4789 
   4790  nsresult rv, rv2;
   4791 
   4792  nsCOMPtr<nsIDirectoryEnumerator> dirIterator;
   4793 
   4794  // This may fail in some normal cases, such as embedders who do not use a
   4795  // GRE.
   4796  rv = aDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
   4797  if (NS_FAILED(rv)) {
   4798    // If the directory doesn't exist, then we have no reason to complain. We
   4799    // loaded everything (and nothing) successfully.
   4800    if (rv == NS_ERROR_FILE_NOT_FOUND) {
   4801      rv = NS_OK;
   4802    }
   4803    return rv;
   4804  }
   4805 
   4806  nsCOMArray<nsIFile> prefFiles(INITIAL_PREF_FILES);
   4807  nsCOMPtr<nsIFile> prefFile;
   4808 
   4809  while (NS_SUCCEEDED(dirIterator->GetNextFile(getter_AddRefs(prefFile))) &&
   4810         prefFile) {
   4811    nsAutoCString leafName;
   4812    prefFile->GetNativeLeafName(leafName);
   4813    MOZ_ASSERT(
   4814        !leafName.IsEmpty(),
   4815        "Failure in default prefs: directory enumerator returned empty file?");
   4816 
   4817    // Skip non-js files.
   4818    if (StringEndsWith(leafName, ".js"_ns,
   4819                       nsCaseInsensitiveCStringComparator)) {
   4820      prefFiles.AppendObject(prefFile);
   4821    }
   4822  }
   4823 
   4824  if (prefFiles.Count() == 0) {
   4825    NS_WARNING("No default pref files found.");
   4826    if (NS_SUCCEEDED(rv)) {
   4827      rv = NS_SUCCESS_FILE_DIRECTORY_EMPTY;
   4828    }
   4829    return rv;
   4830  }
   4831 
   4832  prefFiles.Sort(pref_CompareFileNames);
   4833 
   4834  uint32_t arrayCount = prefFiles.Count();
   4835  uint32_t i;
   4836  for (i = 0; i < arrayCount; ++i) {
   4837    rv2 = openPrefFile(prefFiles[i], PrefValueKind::Default);
   4838    if (NS_FAILED(rv2)) {
   4839      NS_ERROR("Default pref file not parsed successfully.");
   4840      rv = rv2;
   4841    }
   4842  }
   4843 
   4844  return rv;
   4845 }
   4846 
   4847 static nsresult pref_ReadPrefFromJar(nsZipArchive* aJarReader,
   4848                                     const char* aName) {
   4849  nsCString manifest =
   4850      MOZ_TRY(URLPreloader::ReadZip(aJarReader, nsDependentCString(aName)));
   4851 
   4852  Parser parser;
   4853  if (!parser.Parse(PrefValueKind::Default, aName, manifest)) {
   4854    return NS_ERROR_FILE_CORRUPTED;
   4855  }
   4856 
   4857  return NS_OK;
   4858 }
   4859 
   4860 static nsresult pref_ReadDefaultPrefs(const RefPtr<nsZipArchive> jarReader,
   4861                                      const char* path) {
   4862  UniquePtr<nsZipFind> find;
   4863  nsTArray<nsCString> prefEntries;
   4864  const char* entryName;
   4865  uint16_t entryNameLen;
   4866 
   4867  nsresult rv = jarReader->FindInit(path, getter_Transfers(find));
   4868  NS_ENSURE_SUCCESS(rv, rv);
   4869 
   4870  while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
   4871    prefEntries.AppendElement(Substring(entryName, entryNameLen));
   4872  }
   4873 
   4874  prefEntries.Sort();
   4875  for (uint32_t i = prefEntries.Length(); i--;) {
   4876    rv = pref_ReadPrefFromJar(jarReader, prefEntries[i].get());
   4877    if (NS_FAILED(rv)) {
   4878      NS_WARNING("Error parsing preferences.");
   4879    }
   4880  }
   4881 
   4882  return NS_OK;
   4883 }
   4884 
   4885 static nsCString PrefValueToString(const bool* b) {
   4886  return nsCString(*b ? "true" : "false");
   4887 }
   4888 static nsCString PrefValueToString(const int* i) {
   4889  return nsPrintfCString("%d", *i);
   4890 }
   4891 static nsCString PrefValueToString(const uint32_t* u) {
   4892  return nsPrintfCString("%d", *u);
   4893 }
   4894 static nsCString PrefValueToString(const float* f) {
   4895  return nsPrintfCString("%f", *f);
   4896 }
   4897 static nsCString PrefValueToString(const nsACString* s) {
   4898  return nsCString(*s);
   4899 }
   4900 static nsCString PrefValueToString(const nsACString& s) { return nsCString(s); }
   4901 
   4902 // These preference getter wrappers allow us to look up the value for static
   4903 // preferences based on their native types, rather than manually mapping them to
   4904 // the appropriate Preferences::Get* functions.
   4905 // We define these methods in a struct which is made friend of Preferences in
   4906 // order to access private members.
   4907 struct Internals {
   4908  template <typename T>
   4909  static nsresult GetPrefValue(const char* aPrefName, T&& aResult,
   4910                               PrefValueKind aKind) {
   4911    nsresult rv = NS_ERROR_UNEXPECTED;
   4912    NS_ENSURE_TRUE(Preferences::InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   4913 
   4914    if (Maybe<PrefWrapper> pref = pref_Lookup(aPrefName)) {
   4915      rv = pref->GetValue(aKind, std::forward<T>(aResult));
   4916 
   4917      if (profiler_thread_is_being_profiled_for_markers()) {
   4918        profiler_add_marker(
   4919            "Preference Read", baseprofiler::category::OTHER_PreferenceRead, {},
   4920            PreferenceMarker{},
   4921            ProfilerString8View::WrapNullTerminatedString(aPrefName),
   4922            Some(aKind), pref->Type(), PrefValueToString(aResult));
   4923      }
   4924    }
   4925 
   4926    return rv;
   4927  }
   4928 
   4929  template <typename T>
   4930  static nsresult GetSharedPrefValue(const char* aName, T* aResult) {
   4931    nsresult rv = NS_ERROR_UNEXPECTED;
   4932 
   4933    if (Maybe<PrefWrapper> pref = pref_SharedLookup(aName)) {
   4934      rv = pref->GetValue(PrefValueKind::User, aResult);
   4935 
   4936      if (profiler_thread_is_being_profiled_for_markers()) {
   4937        profiler_add_marker(
   4938            "Preference Read", baseprofiler::category::OTHER_PreferenceRead, {},
   4939            PreferenceMarker{},
   4940            ProfilerString8View::WrapNullTerminatedString(aName),
   4941            Nothing() /* indicates Shared */, pref->Type(),
   4942            PrefValueToString(aResult));
   4943      }
   4944    }
   4945 
   4946    return rv;
   4947  }
   4948 
   4949  template <typename T>
   4950  static T GetPref(const char* aPrefName, T aFallback,
   4951                   PrefValueKind aKind = PrefValueKind::User) {
   4952    T result = aFallback;
   4953    GetPrefValue(aPrefName, &result, aKind);
   4954    return result;
   4955  }
   4956 
   4957  template <typename T, typename V>
   4958  static void MOZ_NEVER_INLINE AssignMirror(T& aMirror, V aValue) {
   4959    aMirror = aValue;
   4960  }
   4961 
   4962  static void MOZ_NEVER_INLINE AssignMirror(DataMutexString& aMirror,
   4963                                            nsCString&& aValue) {
   4964    auto lock = aMirror.Lock();
   4965    lock->Assign(std::move(aValue));
   4966  }
   4967 
   4968  static void MOZ_NEVER_INLINE AssignMirror(DataMutexString& aMirror,
   4969                                            const nsLiteralCString& aValue) {
   4970    auto lock = aMirror.Lock();
   4971    lock->Assign(aValue);
   4972  }
   4973 
   4974  static void ClearMirror(DataMutexString& aMirror) {
   4975    auto lock = aMirror.Lock();
   4976    lock->Assign(nsCString());
   4977  }
   4978 
   4979  template <typename T>
   4980  static void UpdateMirror(const char* aPref, void* aMirror) {
   4981    StripAtomic<T> value;
   4982 
   4983    nsresult rv = GetPrefValue(aPref, &value, PrefValueKind::User);
   4984    if (NS_SUCCEEDED(rv)) {
   4985      AssignMirror(*static_cast<T*>(aMirror),
   4986                   std::forward<StripAtomic<T>>(value));
   4987    } else {
   4988      // GetPrefValue() can fail if the update is caused by the pref being
   4989      // deleted or if it fails to make a cast. This assertion is the only place
   4990      // where we safeguard these. In this case the mirror variable will be
   4991      // untouched, thus keeping the value it had prior to the change.
   4992      // (Note that this case won't happen for a deletion via DeleteBranch()
   4993      // unless bug 343600 is fixed, but it will happen for a deletion via
   4994      // ClearUserPref().)
   4995      NS_WARNING(nsPrintfCString("Pref changed failure: %s\n", aPref).get());
   4996      MOZ_ASSERT(false);
   4997    }
   4998  }
   4999 
   5000  template <typename T>
   5001  static nsresult RegisterCallback(void* aMirror, const nsACString& aPref) {
   5002    return Preferences::RegisterCallback(UpdateMirror<T>, aPref, aMirror,
   5003                                         Preferences::ExactMatch,
   5004                                         /* isPriority */ true);
   5005  }
   5006 };
   5007 
   5008 // Initialize default preference JavaScript buffers from appropriate TEXT
   5009 // resources.
   5010 /* static */
   5011 nsresult Preferences::InitInitialObjects(bool aIsStartup) {
   5012  MOZ_ASSERT(NS_IsMainThread());
   5013 
   5014  if (!XRE_IsParentProcess()) {
   5015    MOZ_DIAGNOSTIC_ASSERT(gSharedMap);
   5016    if (aIsStartup) {
   5017      StaticPrefs::StartObservingAlwaysPrefs();
   5018    }
   5019    return NS_OK;
   5020  }
   5021 
   5022  // Initialize static prefs before prefs from data files so that the latter
   5023  // will override the former.
   5024  StaticPrefs::InitAll();
   5025 
   5026  // In the omni.jar case, we load the following prefs:
   5027  // - jar:$gre/omni.jar!/greprefs.js
   5028  // - jar:$gre/omni.jar!/defaults/pref/*.js
   5029  //
   5030  // In the non-omni.jar case, we load:
   5031  // - $gre/greprefs.js
   5032  //
   5033  // In both cases, we also load:
   5034  // - $gre/defaults/pref/*.js
   5035  //
   5036  // This is kept for bug 591866 (channel-prefs.js should not be in omni.jar)
   5037  // in the `$app == $gre` case; we load all files instead of channel-prefs.js
   5038  // only to have the same behaviour as `$app != $gre`, where this is required
   5039  // as a supported location for GRE preferences.
   5040  //
   5041  // When `$app != $gre`, we additionally load, in the omni.jar case:
   5042  // - jar:$app/omni.jar!/defaults/preferences/*.js
   5043  // - $app/defaults/preferences/*.js
   5044  //
   5045  // and in the non-omni.jar case:
   5046  // - $app/defaults/preferences/*.js
   5047  //
   5048  // When `$app == $gre`, we additionally load, in the omni.jar case:
   5049  // - jar:$gre/omni.jar!/defaults/preferences/*.js
   5050  //
   5051  // Thus, in the omni.jar case, we always load app-specific default
   5052  // preferences from omni.jar, whether or not `$app == $gre`.
   5053 
   5054  nsresult rv = NS_ERROR_FAILURE;
   5055  UniquePtr<nsZipFind> find;
   5056  nsTArray<nsCString> prefEntries;
   5057  const char* entryName;
   5058  uint16_t entryNameLen;
   5059 
   5060  RefPtr<nsZipArchive> jarReader = Omnijar::GetReader(Omnijar::GRE);
   5061  if (jarReader) {
   5062 #ifdef MOZ_WIDGET_ANDROID
   5063    // Try to load an architecture-specific greprefs.js first. This will be
   5064    // present in FAT AAR builds of GeckoView on Android.
   5065    const char* abi = getenv("MOZ_ANDROID_CPU_ABI");
   5066    if (abi) {
   5067      nsAutoCString path;
   5068      path.AppendPrintf("%s/greprefs.js", abi);
   5069      rv = pref_ReadPrefFromJar(jarReader, path.get());
   5070    }
   5071 
   5072    if (NS_FAILED(rv)) {
   5073      // Fallback to toplevel greprefs.js if arch-specific load fails.
   5074      rv = pref_ReadPrefFromJar(jarReader, "greprefs.js");
   5075    }
   5076 #else
   5077    // Load jar:$gre/omni.jar!/greprefs.js.
   5078    rv = pref_ReadPrefFromJar(jarReader, "greprefs.js");
   5079 #endif
   5080    NS_ENSURE_SUCCESS(rv, rv);
   5081 
   5082    // Load jar:$gre/omni.jar!/defaults/pref/*.js.
   5083    rv = pref_ReadDefaultPrefs(jarReader, "defaults/pref/*.js$");
   5084    NS_ENSURE_SUCCESS(rv, rv);
   5085 
   5086 #ifdef MOZ_BACKGROUNDTASKS
   5087    if (BackgroundTasks::IsBackgroundTaskMode()) {
   5088      rv = pref_ReadDefaultPrefs(jarReader, "defaults/backgroundtasks/*.js$");
   5089      NS_ENSURE_SUCCESS(rv, rv);
   5090    }
   5091 #endif
   5092 
   5093 #ifdef MOZ_WIDGET_ANDROID
   5094    // Load jar:$gre/omni.jar!/defaults/pref/$MOZ_ANDROID_CPU_ABI/*.js.
   5095    nsAutoCString path;
   5096    path.AppendPrintf("jar:$gre/omni.jar!/defaults/pref/%s/*.js$", abi);
   5097    pref_ReadDefaultPrefs(jarReader, path.get());
   5098    NS_ENSURE_SUCCESS(rv, rv);
   5099 #endif
   5100  } else {
   5101    // Load $gre/greprefs.js.
   5102    nsCOMPtr<nsIFile> greprefsFile;
   5103    rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greprefsFile));
   5104    NS_ENSURE_SUCCESS(rv, rv);
   5105 
   5106    rv = greprefsFile->AppendNative("greprefs.js"_ns);
   5107    NS_ENSURE_SUCCESS(rv, rv);
   5108 
   5109    rv = openPrefFile(greprefsFile, PrefValueKind::Default);
   5110    if (NS_FAILED(rv)) {
   5111      NS_WARNING(
   5112          "Error parsing GRE default preferences. Is this an old-style "
   5113          "embedding app?");
   5114    }
   5115  }
   5116 
   5117  // Load $gre/defaults/pref/*.js.
   5118  nsCOMPtr<nsIFile> defaultPrefDir;
   5119  rv = NS_GetSpecialDirectory(NS_APP_PREF_DEFAULTS_50_DIR,
   5120                              getter_AddRefs(defaultPrefDir));
   5121  NS_ENSURE_SUCCESS(rv, rv);
   5122 
   5123  rv = pref_LoadPrefsInDir(defaultPrefDir);
   5124  if (NS_FAILED(rv)) {
   5125    NS_WARNING("Error parsing application default preferences.");
   5126  }
   5127 
   5128 #ifdef MOZ_WIDGET_COCOA
   5129  // On macOS, channel-prefs.js is no longer bundled with the application and
   5130  // the "app.update.channel" pref is now read from a Framework instead.
   5131  // Previously, channel-prefs.js was read as one of the files in
   5132  // NS_APP_PREF_DEFAULTS_50_DIR (see just above). See bug 1799332 for more
   5133  // info.
   5134  nsAutoCString appUpdatePrefKey;
   5135  appUpdatePrefKey.Assign(kChannelPref);
   5136  nsAutoCString appUpdatePrefValue;
   5137  PrefValue channelPrefValue;
   5138  channelPrefValue.mStringVal = MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL);
   5139  if (ChannelPrefsUtil::GetChannelPrefValue(appUpdatePrefValue)) {
   5140    channelPrefValue.mStringVal = appUpdatePrefValue.get();
   5141  }
   5142  pref_SetPref(appUpdatePrefKey, PrefType::String, PrefValueKind::Default,
   5143               channelPrefValue,
   5144               /* isSticky */ false,
   5145               /* isLocked */ true,
   5146               /* fromInit */ true);
   5147 #endif
   5148 
   5149  // Load jar:$app/omni.jar!/defaults/preferences/*.js
   5150  // or jar:$gre/omni.jar!/defaults/preferences/*.js.
   5151  RefPtr<nsZipArchive> appJarReader = Omnijar::GetReader(Omnijar::APP);
   5152 
   5153  // GetReader(Omnijar::APP) returns null when `$app == $gre`, in
   5154  // which case we look for app-specific default preferences in $gre.
   5155  if (!appJarReader) {
   5156    appJarReader = Omnijar::GetReader(Omnijar::GRE);
   5157  }
   5158 
   5159  if (appJarReader) {
   5160    rv = appJarReader->FindInit("defaults/preferences/*.js$",
   5161                                getter_Transfers(find));
   5162    NS_ENSURE_SUCCESS(rv, rv);
   5163    prefEntries.Clear();
   5164    while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
   5165      prefEntries.AppendElement(Substring(entryName, entryNameLen));
   5166    }
   5167    prefEntries.Sort();
   5168    for (uint32_t i = prefEntries.Length(); i--;) {
   5169      rv = pref_ReadPrefFromJar(appJarReader, prefEntries[i].get());
   5170      if (NS_FAILED(rv)) {
   5171        NS_WARNING("Error parsing preferences.");
   5172      }
   5173    }
   5174 
   5175 #ifdef MOZ_BACKGROUNDTASKS
   5176    if (BackgroundTasks::IsBackgroundTaskMode()) {
   5177      rv = appJarReader->FindInit("defaults/backgroundtasks/*.js$",
   5178                                  getter_Transfers(find));
   5179      NS_ENSURE_SUCCESS(rv, rv);
   5180      prefEntries.Clear();
   5181      while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
   5182        prefEntries.AppendElement(Substring(entryName, entryNameLen));
   5183      }
   5184      prefEntries.Sort();
   5185      for (uint32_t i = prefEntries.Length(); i--;) {
   5186        rv = pref_ReadPrefFromJar(appJarReader, prefEntries[i].get());
   5187        if (NS_FAILED(rv)) {
   5188          NS_WARNING("Error parsing preferences.");
   5189        }
   5190      }
   5191    }
   5192 #endif
   5193  }
   5194 
   5195  nsCOMPtr<nsIProperties> dirSvc(
   5196      do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
   5197  NS_ENSURE_SUCCESS(rv, rv);
   5198 
   5199  nsCOMPtr<nsISimpleEnumerator> list;
   5200  dirSvc->Get(NS_APP_PREFS_DEFAULTS_DIR_LIST, NS_GET_IID(nsISimpleEnumerator),
   5201              getter_AddRefs(list));
   5202  if (list) {
   5203    bool hasMore;
   5204    while (NS_SUCCEEDED(list->HasMoreElements(&hasMore)) && hasMore) {
   5205      nsCOMPtr<nsISupports> elem;
   5206      list->GetNext(getter_AddRefs(elem));
   5207      if (!elem) {
   5208        continue;
   5209      }
   5210 
   5211      nsCOMPtr<nsIFile> path = do_QueryInterface(elem);
   5212      if (!path) {
   5213        continue;
   5214      }
   5215 
   5216      // Do we care if a file provided by this process fails to load?
   5217      pref_LoadPrefsInDir(path);
   5218    }
   5219  }
   5220 
   5221 #if defined(MOZ_WIDGET_GTK) && defined(MOZ_SYSTEM_PREFERENCES)
   5222  // To ensure the system-wide preferences are not overwritten by
   5223  // firefox/browser/defauts/preferences/*.js we need to load
   5224  // the /etc/firefox/defaults/pref/*.js settings as last.
   5225  // Under Flatpak, the NS_OS_SYSTEM_CONFIG_DIR points to /app/etc/firefox
   5226  nsCOMPtr<nsIFile> defaultSystemPrefDir;
   5227  rv = NS_GetSpecialDirectory(NS_OS_SYSTEM_CONFIG_DIR,
   5228                              getter_AddRefs(defaultSystemPrefDir));
   5229  NS_ENSURE_SUCCESS(rv, rv);
   5230  defaultSystemPrefDir->AppendNative("defaults"_ns);
   5231  defaultSystemPrefDir->AppendNative("pref"_ns);
   5232 
   5233  rv = pref_LoadPrefsInDir(defaultSystemPrefDir);
   5234  if (NS_FAILED(rv)) {
   5235    NS_WARNING("Error parsing application default preferences.");
   5236  }
   5237 #endif
   5238 
   5239  if (XRE_IsParentProcess()) {
   5240    SetupTelemetryPref();
   5241  }
   5242 
   5243  if (aIsStartup) {
   5244    // Now that all prefs have their initial values, install the callbacks for
   5245    // `always`-mirrored static prefs. We do this now rather than in
   5246    // StaticPrefs::InitAll() so that the callbacks don't need to be traversed
   5247    // while we load prefs from data files.
   5248    StaticPrefs::StartObservingAlwaysPrefs();
   5249  }
   5250 
   5251  NS_CreateServicesFromCategory(NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID, nullptr,
   5252                                NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID);
   5253 
   5254  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   5255  if (NS_WARN_IF(!observerService)) {
   5256    return NS_ERROR_FAILURE;
   5257  }
   5258 
   5259  observerService->NotifyObservers(nullptr, NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID,
   5260                                   nullptr);
   5261 
   5262  return NS_OK;
   5263 }
   5264 
   5265 /* static */
   5266 nsresult Preferences::GetBool(const char* aPrefName, bool* aResult,
   5267                              PrefValueKind aKind) {
   5268  MOZ_ASSERT(aResult);
   5269  return Internals::GetPrefValue(aPrefName, aResult, aKind);
   5270 }
   5271 
   5272 /* static */
   5273 nsresult Preferences::GetInt(const char* aPrefName, int32_t* aResult,
   5274                             PrefValueKind aKind) {
   5275  MOZ_ASSERT(aResult);
   5276  return Internals::GetPrefValue(aPrefName, aResult, aKind);
   5277 }
   5278 
   5279 /* static */
   5280 nsresult Preferences::GetFloat(const char* aPrefName, float* aResult,
   5281                               PrefValueKind aKind) {
   5282  MOZ_ASSERT(aResult);
   5283  return Internals::GetPrefValue(aPrefName, aResult, aKind);
   5284 }
   5285 
   5286 /* static */
   5287 nsresult Preferences::GetCString(const char* aPrefName, nsACString& aResult,
   5288                                 PrefValueKind aKind) {
   5289  aResult.SetIsVoid(true);
   5290  return Internals::GetPrefValue(aPrefName, aResult, aKind);
   5291 }
   5292 
   5293 /* static */
   5294 nsresult Preferences::GetString(const char* aPrefName, nsAString& aResult,
   5295                                PrefValueKind aKind) {
   5296  nsAutoCString result;
   5297  nsresult rv = Preferences::GetCString(aPrefName, result, aKind);
   5298  if (NS_SUCCEEDED(rv)) {
   5299    CopyUTF8toUTF16(result, aResult);
   5300  }
   5301  return rv;
   5302 }
   5303 
   5304 /* static */
   5305 nsresult Preferences::GetLocalizedCString(const char* aPrefName,
   5306                                          nsACString& aResult,
   5307                                          PrefValueKind aKind) {
   5308  nsAutoString result;
   5309  nsresult rv = GetLocalizedString(aPrefName, result, aKind);
   5310  if (NS_SUCCEEDED(rv)) {
   5311    CopyUTF16toUTF8(result, aResult);
   5312  }
   5313  return rv;
   5314 }
   5315 
   5316 /* static */
   5317 nsresult Preferences::GetLocalizedString(const char* aPrefName,
   5318                                         nsAString& aResult,
   5319                                         PrefValueKind aKind) {
   5320  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5321  nsCOMPtr<nsIPrefLocalizedString> prefLocalString;
   5322  nsresult rv = GetRootBranch(aKind)->GetComplexValue(
   5323      aPrefName, NS_GET_IID(nsIPrefLocalizedString),
   5324      getter_AddRefs(prefLocalString));
   5325  if (NS_SUCCEEDED(rv)) {
   5326    MOZ_ASSERT(prefLocalString, "Succeeded but the result is NULL");
   5327    prefLocalString->GetData(aResult);
   5328  }
   5329  return rv;
   5330 }
   5331 
   5332 /* static */
   5333 nsresult Preferences::GetComplex(const char* aPrefName, const nsIID& aType,
   5334                                 void** aResult, PrefValueKind aKind) {
   5335  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5336  return GetRootBranch(aKind)->GetComplexValue(aPrefName, aType, aResult);
   5337 }
   5338 
   5339 /* static */
   5340 bool Preferences::GetBool(const char* aPrefName, bool aFallback,
   5341                          PrefValueKind aKind) {
   5342  return Internals::GetPref(aPrefName, aFallback, aKind);
   5343 }
   5344 
   5345 /* static */
   5346 int32_t Preferences::GetInt(const char* aPrefName, int32_t aFallback,
   5347                            PrefValueKind aKind) {
   5348  return Internals::GetPref(aPrefName, aFallback, aKind);
   5349 }
   5350 
   5351 /* static */
   5352 uint32_t Preferences::GetUint(const char* aPrefName, uint32_t aFallback,
   5353                              PrefValueKind aKind) {
   5354  return Internals::GetPref(aPrefName, aFallback, aKind);
   5355 }
   5356 
   5357 /* static */
   5358 float Preferences::GetFloat(const char* aPrefName, float aFallback,
   5359                            PrefValueKind aKind) {
   5360  return Internals::GetPref(aPrefName, aFallback, aKind);
   5361 }
   5362 
   5363 /* static */
   5364 nsresult Preferences::SetCString(const char* aPrefName,
   5365                                 const nsACString& aValue,
   5366                                 PrefValueKind aKind) {
   5367  ENSURE_PARENT_PROCESS("SetCString", aPrefName);
   5368  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5369 
   5370  if (aValue.Length() > MAX_PREF_LENGTH) {
   5371    return NS_ERROR_ILLEGAL_VALUE;
   5372  }
   5373 
   5374  // It's ok to stash a pointer to the temporary PromiseFlatCString's chars in
   5375  // pref because pref_SetPref() duplicates those chars.
   5376  PrefValue prefValue;
   5377  const nsCString& flat = PromiseFlatCString(aValue);
   5378  prefValue.mStringVal = flat.get();
   5379  return pref_SetPref(nsDependentCString(aPrefName), PrefType::String, aKind,
   5380                      prefValue,
   5381                      /* isSticky */ false,
   5382                      /* isLocked */ false,
   5383                      /* fromInit */ false);
   5384 }
   5385 
   5386 /* static */
   5387 nsresult Preferences::SetBool(const char* aPrefName, bool aValue,
   5388                              PrefValueKind aKind) {
   5389  ENSURE_PARENT_PROCESS("SetBool", aPrefName);
   5390  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5391 
   5392  PrefValue prefValue;
   5393  prefValue.mBoolVal = aValue;
   5394  return pref_SetPref(nsDependentCString(aPrefName), PrefType::Bool, aKind,
   5395                      prefValue,
   5396                      /* isSticky */ false,
   5397                      /* isLocked */ false,
   5398                      /* fromInit */ false);
   5399 }
   5400 
   5401 /* static */
   5402 nsresult Preferences::SetInt(const char* aPrefName, int32_t aValue,
   5403                             PrefValueKind aKind) {
   5404  ENSURE_PARENT_PROCESS("SetInt", aPrefName);
   5405  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5406 
   5407  PrefValue prefValue;
   5408  prefValue.mIntVal = aValue;
   5409  return pref_SetPref(nsDependentCString(aPrefName), PrefType::Int, aKind,
   5410                      prefValue,
   5411                      /* isSticky */ false,
   5412                      /* isLocked */ false,
   5413                      /* fromInit */ false);
   5414 }
   5415 
   5416 /* static */
   5417 nsresult Preferences::SetComplex(const char* aPrefName, const nsIID& aType,
   5418                                 nsISupports* aValue, PrefValueKind aKind) {
   5419  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5420  return GetRootBranch(aKind)->SetComplexValue(aPrefName, aType, aValue);
   5421 }
   5422 
   5423 /* static */
   5424 nsresult Preferences::Lock(const char* aPrefName) {
   5425  ENSURE_PARENT_PROCESS("Lock", aPrefName);
   5426  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5427 
   5428  Pref* pref = MOZ_TRY(pref_LookupForModify(
   5429      aPrefName, [](const PrefWrapper& aPref) { return !aPref.IsLocked(); }));
   5430 
   5431  if (pref) {
   5432    pref->SetIsLocked(true);
   5433    NotifyCallbacks(nsDependentCString{aPrefName}, PrefWrapper(pref));
   5434  }
   5435 
   5436  return NS_OK;
   5437 }
   5438 
   5439 /* static */
   5440 nsresult Preferences::Unlock(const char* aPrefName) {
   5441  ENSURE_PARENT_PROCESS("Unlock", aPrefName);
   5442  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5443 
   5444  Pref* pref = MOZ_TRY(pref_LookupForModify(
   5445      aPrefName, [](const PrefWrapper& aPref) { return aPref.IsLocked(); }));
   5446 
   5447  if (pref) {
   5448    pref->SetIsLocked(false);
   5449    NotifyCallbacks(nsDependentCString{aPrefName}, PrefWrapper(pref));
   5450  }
   5451 
   5452  return NS_OK;
   5453 }
   5454 
   5455 /* static */
   5456 bool Preferences::IsLocked(const char* aPrefName) {
   5457  NS_ENSURE_TRUE(InitStaticMembers(), false);
   5458 
   5459  Maybe<PrefWrapper> pref = pref_Lookup(aPrefName);
   5460  return pref.isSome() && pref->IsLocked();
   5461 }
   5462 
   5463 /* static */
   5464 bool Preferences::IsSanitized(const char* aPrefName) {
   5465  NS_ENSURE_TRUE(InitStaticMembers(), false);
   5466 
   5467  Maybe<PrefWrapper> pref = pref_Lookup(aPrefName);
   5468  return pref.isSome() && pref->IsSanitized();
   5469 }
   5470 
   5471 /* static */
   5472 nsresult Preferences::ClearUser(const char* aPrefName) {
   5473  ENSURE_PARENT_PROCESS("ClearUser", aPrefName);
   5474  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5475 
   5476  auto result = pref_LookupForModify(
   5477      aPrefName, [](const PrefWrapper& aPref) { return aPref.HasUserValue(); });
   5478  if (result.isErr()) {
   5479    return NS_OK;
   5480  }
   5481 
   5482  if (Pref* pref = result.unwrap()) {
   5483    pref->ClearUserValue();
   5484 
   5485    if (!pref->HasDefaultValue()) {
   5486      MOZ_ASSERT(
   5487          !gSharedMap || !pref->IsSanitized() || !gSharedMap->Has(pref->Name()),
   5488          "A sanitized pref should never be in the shared pref map.");
   5489      if (!pref->IsSanitized() &&
   5490          (!gSharedMap || !gSharedMap->Has(pref->Name()))) {
   5491        HashTable()->remove(aPrefName);
   5492      } else {
   5493        pref->SetType(PrefType::None);
   5494      }
   5495 
   5496      NotifyCallbacks(nsDependentCString{aPrefName});
   5497    } else {
   5498      NotifyCallbacks(nsDependentCString{aPrefName}, PrefWrapper(pref));
   5499    }
   5500 
   5501    Preferences::HandleDirty();
   5502  }
   5503  return NS_OK;
   5504 }
   5505 
   5506 /* static */
   5507 bool Preferences::HasUserValue(const char* aPrefName) {
   5508  NS_ENSURE_TRUE(InitStaticMembers(), false);
   5509 
   5510  Maybe<PrefWrapper> pref = pref_Lookup(aPrefName);
   5511  return pref.isSome() && pref->HasUserValue();
   5512 }
   5513 
   5514 /* static */
   5515 bool Preferences::HasDefaultValue(const char* aPrefName) {
   5516  NS_ENSURE_TRUE(InitStaticMembers(), false);
   5517 
   5518  Maybe<PrefWrapper> pref = pref_Lookup(aPrefName);
   5519  return pref.isSome() && pref->HasDefaultValue();
   5520 }
   5521 
   5522 /* static */
   5523 int32_t Preferences::GetType(const char* aPrefName) {
   5524  NS_ENSURE_TRUE(InitStaticMembers(), nsIPrefBranch::PREF_INVALID);
   5525 
   5526  if (!HashTable()) {
   5527    return PREF_INVALID;
   5528  }
   5529 
   5530  Maybe<PrefWrapper> pref = pref_Lookup(aPrefName);
   5531  if (!pref.isSome()) {
   5532    return PREF_INVALID;
   5533  }
   5534 
   5535  switch (pref->Type()) {
   5536    case PrefType::String:
   5537      return PREF_STRING;
   5538 
   5539    case PrefType::Int:
   5540      return PREF_INT;
   5541 
   5542    case PrefType::Bool:
   5543      return PREF_BOOL;
   5544 
   5545    case PrefType::None:
   5546      if (IsPreferenceSanitized(aPrefName)) {
   5547        glean::security::pref_usage_content_process.Record(Some(
   5548            glean::security::PrefUsageContentProcessExtra{Some(aPrefName)}));
   5549 
   5550        if (sCrashOnBlocklistedPref) {
   5551          MOZ_CRASH_UNSAFE_PRINTF(
   5552              "Should not access the preference '%s' in the Content Processes",
   5553              aPrefName);
   5554        } else {
   5555          return PREF_INVALID;
   5556        }
   5557      }
   5558      [[fallthrough]];
   5559 
   5560    default:
   5561      MOZ_CRASH();
   5562  }
   5563 }
   5564 
   5565 /* static */
   5566 nsresult Preferences::AddStrongObserver(nsIObserver* aObserver,
   5567                                        const nsACString& aPref) {
   5568  MOZ_ASSERT(aObserver);
   5569  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5570  return sPreferences->mRootBranch->AddObserver(aPref, aObserver, false);
   5571 }
   5572 
   5573 /* static */
   5574 nsresult Preferences::AddWeakObserver(nsIObserver* aObserver,
   5575                                      const nsACString& aPref) {
   5576  MOZ_ASSERT(aObserver);
   5577  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5578  return sPreferences->mRootBranch->AddObserver(aPref, aObserver, true);
   5579 }
   5580 
   5581 /* static */
   5582 nsresult Preferences::RemoveObserver(nsIObserver* aObserver,
   5583                                     const nsACString& aPref) {
   5584  MOZ_ASSERT(aObserver);
   5585  if (sShutdown) {
   5586    MOZ_ASSERT(!sPreferences);
   5587    return NS_OK;  // Observers have been released automatically.
   5588  }
   5589  NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
   5590  return sPreferences->mRootBranch->RemoveObserver(aPref, aObserver);
   5591 }
   5592 
   5593 template <typename T>
   5594 static void AssertNotMallocAllocated(T* aPtr) {
   5595 #if defined(DEBUG) && defined(MOZ_MEMORY)
   5596  jemalloc_ptr_info_t info;
   5597  jemalloc_ptr_info((void*)aPtr, &info);
   5598  MOZ_ASSERT(info.tag == TagUnknown);
   5599 #endif
   5600 }
   5601 
   5602 /* static */
   5603 nsresult Preferences::AddStrongObservers(nsIObserver* aObserver,
   5604                                         const char* const* aPrefs) {
   5605  MOZ_ASSERT(aObserver);
   5606  for (uint32_t i = 0; aPrefs[i]; i++) {
   5607    AssertNotMallocAllocated(aPrefs[i]);
   5608 
   5609    nsCString pref;
   5610    pref.AssignLiteral(aPrefs[i], strlen(aPrefs[i]));
   5611    nsresult rv = AddStrongObserver(aObserver, pref);
   5612    NS_ENSURE_SUCCESS(rv, rv);
   5613  }
   5614  return NS_OK;
   5615 }
   5616 
   5617 /* static */
   5618 nsresult Preferences::AddWeakObservers(nsIObserver* aObserver,
   5619                                       const char* const* aPrefs) {
   5620  MOZ_ASSERT(aObserver);
   5621  for (uint32_t i = 0; aPrefs[i]; i++) {
   5622    AssertNotMallocAllocated(aPrefs[i]);
   5623 
   5624    nsCString pref;
   5625    pref.AssignLiteral(aPrefs[i], strlen(aPrefs[i]));
   5626    nsresult rv = AddWeakObserver(aObserver, pref);
   5627    NS_ENSURE_SUCCESS(rv, rv);
   5628  }
   5629  return NS_OK;
   5630 }
   5631 
   5632 /* static */
   5633 nsresult Preferences::RemoveObservers(nsIObserver* aObserver,
   5634                                      const char* const* aPrefs) {
   5635  MOZ_ASSERT(aObserver);
   5636  if (sShutdown) {
   5637    MOZ_ASSERT(!sPreferences);
   5638    return NS_OK;  // Observers have been released automatically.
   5639  }
   5640  NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
   5641 
   5642  for (uint32_t i = 0; aPrefs[i]; i++) {
   5643    nsresult rv = RemoveObserver(aObserver, nsDependentCString(aPrefs[i]));
   5644    NS_ENSURE_SUCCESS(rv, rv);
   5645  }
   5646  return NS_OK;
   5647 }
   5648 
   5649 template <typename T>
   5650 /* static */
   5651 nsresult Preferences::RegisterCallbackImpl(PrefChangedFunc aCallback,
   5652                                           T& aPrefNode, void* aData,
   5653                                           MatchKind aMatchKind,
   5654                                           bool aIsPriority) {
   5655  NS_ENSURE_ARG(aCallback);
   5656 
   5657  NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   5658 
   5659  auto node = new CallbackNode(aPrefNode, aCallback, aData, aMatchKind);
   5660 
   5661  if (aIsPriority) {
   5662    // Add to the start of the list.
   5663    node->SetNext(gFirstCallback);
   5664    gFirstCallback = node;
   5665    if (!gLastPriorityNode) {
   5666      gLastPriorityNode = node;
   5667    }
   5668  } else {
   5669    // Add to the start of the non-priority part of the list.
   5670    if (gLastPriorityNode) {
   5671      node->SetNext(gLastPriorityNode->Next());
   5672      gLastPriorityNode->SetNext(node);
   5673    } else {
   5674      node->SetNext(gFirstCallback);
   5675      gFirstCallback = node;
   5676    }
   5677  }
   5678 
   5679  return NS_OK;
   5680 }
   5681 
   5682 /* static */
   5683 nsresult Preferences::RegisterCallback(PrefChangedFunc aCallback,
   5684                                       const nsACString& aPrefNode, void* aData,
   5685                                       MatchKind aMatchKind, bool aIsPriority) {
   5686  return RegisterCallbackImpl(aCallback, aPrefNode, aData, aMatchKind,
   5687                              aIsPriority);
   5688 }
   5689 
   5690 /* static */
   5691 nsresult Preferences::RegisterCallbacks(PrefChangedFunc aCallback,
   5692                                        const char* const* aPrefs, void* aData,
   5693                                        MatchKind aMatchKind) {
   5694  return RegisterCallbackImpl(aCallback, aPrefs, aData, aMatchKind);
   5695 }
   5696 
   5697 /* static */
   5698 nsresult Preferences::RegisterCallbackAndCall(PrefChangedFunc aCallback,
   5699                                              const nsACString& aPref,
   5700                                              void* aClosure,
   5701                                              MatchKind aMatchKind) {
   5702  MOZ_ASSERT(aCallback);
   5703  nsresult rv = RegisterCallback(aCallback, aPref, aClosure, aMatchKind);
   5704  if (NS_SUCCEEDED(rv)) {
   5705    (*aCallback)(PromiseFlatCString(aPref).get(), aClosure);
   5706  }
   5707  return rv;
   5708 }
   5709 
   5710 /* static */
   5711 nsresult Preferences::RegisterCallbacksAndCall(PrefChangedFunc aCallback,
   5712                                               const char* const* aPrefs,
   5713                                               void* aClosure) {
   5714  MOZ_ASSERT(aCallback);
   5715 
   5716  nsresult rv =
   5717      RegisterCallbacks(aCallback, aPrefs, aClosure, MatchKind::ExactMatch);
   5718  if (NS_SUCCEEDED(rv)) {
   5719    for (const char* const* ptr = aPrefs; *ptr; ptr++) {
   5720      (*aCallback)(*ptr, aClosure);
   5721    }
   5722  }
   5723  return rv;
   5724 }
   5725 
   5726 template <typename T>
   5727 /* static */
   5728 nsresult Preferences::UnregisterCallbackImpl(PrefChangedFunc aCallback,
   5729                                             T& aPrefNode, void* aData,
   5730                                             MatchKind aMatchKind) {
   5731  MOZ_ASSERT(aCallback);
   5732  if (sShutdown) {
   5733    MOZ_ASSERT(!sPreferences);
   5734    return NS_OK;  // Observers have been released automatically.
   5735  }
   5736  NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
   5737 
   5738  nsresult rv = NS_ERROR_FAILURE;
   5739  CallbackNode* node = gFirstCallback;
   5740  CallbackNode* prev_node = nullptr;
   5741 
   5742  while (node) {
   5743    if (node->Func() == aCallback && node->Data() == aData &&
   5744        node->MatchKind() == aMatchKind && node->DomainIs(aPrefNode)) {
   5745      if (gCallbacksInProgress) {
   5746        // Postpone the node removal until after callbacks enumeration is
   5747        // finished.
   5748        node->ClearFunc();
   5749        gShouldCleanupDeadNodes = true;
   5750        prev_node = node;
   5751        node = node->Next();
   5752      } else {
   5753        node = pref_RemoveCallbackNode(node, prev_node);
   5754      }
   5755      rv = NS_OK;
   5756    } else {
   5757      prev_node = node;
   5758      node = node->Next();
   5759    }
   5760  }
   5761  return rv;
   5762 }
   5763 
   5764 /* static */
   5765 nsresult Preferences::UnregisterCallback(PrefChangedFunc aCallback,
   5766                                         const nsACString& aPrefNode,
   5767                                         void* aData, MatchKind aMatchKind) {
   5768  return UnregisterCallbackImpl<const nsACString&>(aCallback, aPrefNode, aData,
   5769                                                   aMatchKind);
   5770 }
   5771 
   5772 /* static */
   5773 nsresult Preferences::UnregisterCallbacks(PrefChangedFunc aCallback,
   5774                                          const char* const* aPrefs,
   5775                                          void* aData, MatchKind aMatchKind) {
   5776  return UnregisterCallbackImpl(aCallback, aPrefs, aData, aMatchKind);
   5777 }
   5778 
   5779 template <typename T>
   5780 static void AddMirrorCallback(T* aMirror, const nsACString& aPref) {
   5781  MOZ_ASSERT(NS_IsMainThread());
   5782 
   5783  Internals::RegisterCallback<T>(aMirror, aPref);
   5784 }
   5785 
   5786 // Don't inline because it explodes compile times.
   5787 template <typename T>
   5788 static MOZ_NEVER_INLINE void AddMirror(T* aMirror, const nsACString& aPref,
   5789                                       StripAtomic<T> aDefault) {
   5790  *aMirror = Internals::GetPref(PromiseFlatCString(aPref).get(), aDefault);
   5791  AddMirrorCallback(aMirror, aPref);
   5792 }
   5793 
   5794 static MOZ_NEVER_INLINE void AddMirror(DataMutexString& aMirror,
   5795                                       const nsACString& aPref) {
   5796  auto lock = aMirror.Lock();
   5797  nsCString result(*lock);
   5798  Internals::GetPrefValue(PromiseFlatCString(aPref).get(), result,
   5799                          PrefValueKind::User);
   5800  lock->Assign(std::move(result));
   5801  AddMirrorCallback(&aMirror, aPref);
   5802 }
   5803 
   5804 // The InitPref_*() functions below end in a `_<type>` suffix because they are
   5805 // used by the PREF macro definition in InitAll() below.
   5806 
   5807 static void InitPref_bool(const nsCString& aName, bool aDefaultValue) {
   5808  MOZ_ASSERT(XRE_IsParentProcess());
   5809  PrefValue value;
   5810  value.mBoolVal = aDefaultValue;
   5811  pref_SetPref(aName, PrefType::Bool, PrefValueKind::Default, value,
   5812               /* isSticky */ false,
   5813               /* isLocked */ false,
   5814               /* fromInit */ true);
   5815 }
   5816 
   5817 static void InitPref_int32_t(const nsCString& aName, int32_t aDefaultValue) {
   5818  MOZ_ASSERT(XRE_IsParentProcess());
   5819  PrefValue value;
   5820  value.mIntVal = aDefaultValue;
   5821  pref_SetPref(aName, PrefType::Int, PrefValueKind::Default, value,
   5822               /* isSticky */ false,
   5823               /* isLocked */ false,
   5824               /* fromInit */ true);
   5825 }
   5826 
   5827 static void InitPref_uint32_t(const nsCString& aName, uint32_t aDefaultValue) {
   5828  InitPref_int32_t(aName, int32_t(aDefaultValue));
   5829 }
   5830 
   5831 static void InitPref_float(const nsCString& aName, float aDefaultValue) {
   5832  MOZ_ASSERT(XRE_IsParentProcess());
   5833  PrefValue value;
   5834  // Convert the value in a locale-independent way, including a trailing ".0"
   5835  // if necessary to distinguish floating-point from integer prefs when viewing
   5836  // them in about:config.
   5837  nsAutoCString defaultValue;
   5838  defaultValue.AppendFloat(aDefaultValue);
   5839  if (!defaultValue.Contains('.') && !defaultValue.Contains('e')) {
   5840    defaultValue.AppendLiteral(".0");
   5841  }
   5842  value.mStringVal = defaultValue.get();
   5843  pref_SetPref(aName, PrefType::String, PrefValueKind::Default, value,
   5844               /* isSticky */ false,
   5845               /* isLocked */ false,
   5846               /* fromInit */ true);
   5847 }
   5848 
   5849 static void InitPref_String(const nsCString& aName, const char* aDefaultValue) {
   5850  MOZ_ASSERT(XRE_IsParentProcess());
   5851  PrefValue value;
   5852  value.mStringVal = aDefaultValue;
   5853  pref_SetPref(aName, PrefType::String, PrefValueKind::Default, value,
   5854               /* isSticky */ false,
   5855               /* isLocked */ false,
   5856               /* fromInit */ true);
   5857 }
   5858 
   5859 static void InitPref(const nsCString& aName, bool aDefaultValue) {
   5860  InitPref_bool(aName, aDefaultValue);
   5861 }
   5862 static void InitPref(const nsCString& aName, int32_t aDefaultValue) {
   5863  InitPref_int32_t(aName, aDefaultValue);
   5864 }
   5865 static void InitPref(const nsCString& aName, uint32_t aDefaultValue) {
   5866  InitPref_uint32_t(aName, aDefaultValue);
   5867 }
   5868 static void InitPref(const nsCString& aName, float aDefaultValue) {
   5869  InitPref_float(aName, aDefaultValue);
   5870 }
   5871 
   5872 template <typename T>
   5873 static void InitAlwaysPref(const nsCString& aName, T* aCache,
   5874                           StripAtomic<T> aDefaultValue) {
   5875  // Only called in the parent process. Set/reset the pref value and the
   5876  // `always` mirror to the default value.
   5877  // `once` mirrors will be initialized lazily in InitOncePrefs().
   5878  InitPref(aName, aDefaultValue);
   5879  *aCache = aDefaultValue;
   5880 }
   5881 
   5882 static void InitAlwaysPref(const nsCString& aName, DataMutexString& aCache,
   5883                           const nsLiteralCString& aDefaultValue) {
   5884  // Only called in the parent process. Set/reset the pref value and the
   5885  // `always` mirror to the default value.
   5886  // `once` mirrors will be initialized lazily in InitOncePrefs().
   5887  InitPref_String(aName, aDefaultValue.get());
   5888  Internals::AssignMirror(aCache, aDefaultValue);
   5889 }
   5890 
   5891 static Atomic<bool> sOncePrefRead(false);
   5892 static StaticMutex sOncePrefMutex MOZ_UNANNOTATED;
   5893 
   5894 namespace StaticPrefs {
   5895 
   5896 void MaybeInitOncePrefs() {
   5897  if (MOZ_LIKELY(sOncePrefRead)) {
   5898    // `once`-mirrored prefs have already been initialized to their default
   5899    // value.
   5900    return;
   5901  }
   5902  StaticMutexAutoLock lock(sOncePrefMutex);
   5903  if (NS_IsMainThread()) {
   5904    InitOncePrefs();
   5905  } else {
   5906    RefPtr<Runnable> runnable = NS_NewRunnableFunction(
   5907        "Preferences::MaybeInitOncePrefs", [&]() { InitOncePrefs(); });
   5908    // This logic needs to run on the main thread
   5909    SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(), runnable);
   5910  }
   5911  sOncePrefRead = true;
   5912 }
   5913 
   5914 // For mirrored prefs we generate a variable definition.
   5915 #define NEVER_PREF(name, cpp_type, value)
   5916 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, default_value) \
   5917  cpp_type sMirror_##full_id(default_value);
   5918 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, default_value) \
   5919  MOZ_RUNINIT cpp_type sMirror_##full_id("DataMutexString");
   5920 #define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \
   5921  cpp_type sMirror_##full_id(default_value);
   5922 #include "mozilla/StaticPrefListAll.h"
   5923 #undef NEVER_PREF
   5924 #undef ALWAYS_PREF
   5925 #undef ALWAYS_DATAMUTEX_PREF
   5926 #undef ONCE_PREF
   5927 
   5928 static void InitAll() {
   5929  MOZ_ASSERT(NS_IsMainThread());
   5930  MOZ_ASSERT(XRE_IsParentProcess());
   5931 
   5932  // For all prefs we generate some initialization code.
   5933  //
   5934  // The InitPref_*() functions have a type suffix to avoid ambiguity between
   5935  // prefs having int32_t and float default values. That suffix is not needed
   5936  // for the InitAlwaysPref() functions because they take a pointer parameter,
   5937  // which prevents automatic int-to-float coercion.
   5938 #define NEVER_PREF(name, cpp_type, value) \
   5939  InitPref_##cpp_type(name ""_ns, value);
   5940 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) \
   5941  InitAlwaysPref(name ""_ns, &sMirror_##full_id, value);
   5942 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value) \
   5943  InitAlwaysPref(name ""_ns, sMirror_##full_id, value);
   5944 #define ONCE_PREF(name, base_id, full_id, cpp_type, value) \
   5945  InitPref_##cpp_type(name ""_ns, value);
   5946 #include "mozilla/StaticPrefListAll.h"
   5947 #undef NEVER_PREF
   5948 #undef ALWAYS_PREF
   5949 #undef ALWAYS_DATAMUTEX_PREF
   5950 #undef ONCE_PREF
   5951 }
   5952 
   5953 static void StartObservingAlwaysPrefs() {
   5954  MOZ_ASSERT(NS_IsMainThread());
   5955 
   5956  // Call AddMirror so that our mirrors for `always` prefs will stay updated.
   5957  // The call to AddMirror re-reads the current pref value into the mirror, so
   5958  // our mirror will now be up-to-date even if some of the prefs have changed
   5959  // since the call to InitAll().
   5960 #define NEVER_PREF(name, cpp_type, value)
   5961 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) \
   5962  AddMirror(&sMirror_##full_id, name ""_ns, sMirror_##full_id);
   5963 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value) \
   5964  AddMirror(sMirror_##full_id, name ""_ns);
   5965 #define ONCE_PREF(name, base_id, full_id, cpp_type, value)
   5966 #include "mozilla/StaticPrefListAll.h"
   5967 #undef NEVER_PREF
   5968 #undef ALWAYS_PREF
   5969 #undef ALWAYS_DATAMUTEX_PREF
   5970 #undef ONCE_PREF
   5971 }
   5972 
   5973 static void InitOncePrefs() {
   5974  // For `once`-mirrored prefs we generate some initialization code. This is
   5975  // done in case the pref value was updated when reading pref data files. It's
   5976  // necessary because we don't have callbacks registered for `once`-mirrored
   5977  // prefs.
   5978  //
   5979  // In debug builds, we also install a mechanism that can check if the
   5980  // preference value is modified after `once`-mirrored prefs are initialized.
   5981  // In tests this would indicate a likely misuse of a `once`-mirrored pref and
   5982  // suggest that it should instead be `always`-mirrored.
   5983 #define NEVER_PREF(name, cpp_type, value)
   5984 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value)
   5985 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value)
   5986 #ifdef DEBUG
   5987 #  define ONCE_PREF(name, base_id, full_id, cpp_type, value)                   \
   5988    {                                                                          \
   5989      MOZ_ASSERT(gOnceStaticPrefsAntiFootgun);                                 \
   5990      sMirror_##full_id = Internals::GetPref(name, cpp_type(value));           \
   5991      auto checkPref = [&]() {                                                 \
   5992        MOZ_ASSERT(sOncePrefRead);                                             \
   5993        cpp_type staticPrefValue = full_id();                                  \
   5994        cpp_type preferenceValue =                                             \
   5995            Internals::GetPref(GetPrefName_##base_id(), cpp_type(value));      \
   5996        MOZ_ASSERT(staticPrefValue == preferenceValue,                         \
   5997                   "Preference '" name                                         \
   5998                   "' got modified since StaticPrefs::" #full_id               \
   5999                   " was initialized. Consider using an `always` mirror kind " \
   6000                   "instead");                                                 \
   6001      };                                                                       \
   6002      gOnceStaticPrefsAntiFootgun->insert(                                     \
   6003          std::pair<const char*, AntiFootgunCallback>(GetPrefName_##base_id(), \
   6004                                                      std::move(checkPref)));  \
   6005    }
   6006 #else
   6007 #  define ONCE_PREF(name, base_id, full_id, cpp_type, value) \
   6008    sMirror_##full_id = Internals::GetPref(name, cpp_type(value));
   6009 #endif
   6010 
   6011 #include "mozilla/StaticPrefListAll.h"
   6012 #undef NEVER_PREF
   6013 #undef ALWAYS_PREF
   6014 #undef ALWAYS_DATAMUTEX_PREF
   6015 #undef ONCE_PREF
   6016 }
   6017 
   6018 static void ShutdownAlwaysPrefs() {
   6019  MOZ_ASSERT(NS_IsMainThread());
   6020 
   6021  // We may need to do clean up for leak detection for some StaticPrefs.
   6022 #define NEVER_PREF(name, cpp_type, value)
   6023 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value)
   6024 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value) \
   6025  Internals::ClearMirror(sMirror_##full_id);
   6026 #define ONCE_PREF(name, base_id, full_id, cpp_type, value)
   6027 #include "mozilla/StaticPrefListAll.h"
   6028 #undef NEVER_PREF
   6029 #undef ALWAYS_PREF
   6030 #undef ALWAYS_DATAMUTEX_PREF
   6031 #undef ONCE_PREF
   6032 }
   6033 
   6034 }  // namespace StaticPrefs
   6035 
   6036 [[maybe_unused]] static void SaveOncePrefToSharedMap(
   6037    SharedPrefMapBuilder& aBuilder, const nsACString& aName, bool aValue) {
   6038  auto oncePref = MakeUnique<Pref>(aName);
   6039  oncePref->SetType(PrefType::Bool);
   6040  oncePref->SetIsSkippedByIteration(true);
   6041  bool valueChanged = false;
   6042  MOZ_ALWAYS_SUCCEEDS(
   6043      oncePref->SetDefaultValue(PrefType::Bool, PrefValue(aValue),
   6044                                /* isSticky */ true,
   6045                                /* isLocked */ true, &valueChanged));
   6046  oncePref->AddToMap(aBuilder);
   6047 }
   6048 
   6049 [[maybe_unused]] static void SaveOncePrefToSharedMap(
   6050    SharedPrefMapBuilder& aBuilder, const nsACString& aName, int32_t aValue) {
   6051  auto oncePref = MakeUnique<Pref>(aName);
   6052  oncePref->SetType(PrefType::Int);
   6053  oncePref->SetIsSkippedByIteration(true);
   6054  bool valueChanged = false;
   6055  MOZ_ALWAYS_SUCCEEDS(
   6056      oncePref->SetDefaultValue(PrefType::Int, PrefValue(aValue),
   6057                                /* isSticky */ true,
   6058                                /* isLocked */ true, &valueChanged));
   6059  oncePref->AddToMap(aBuilder);
   6060 }
   6061 
   6062 [[maybe_unused]] static void SaveOncePrefToSharedMap(
   6063    SharedPrefMapBuilder& aBuilder, const nsACString& aName, uint32_t aValue) {
   6064  SaveOncePrefToSharedMap(aBuilder, aName, int32_t(aValue));
   6065 }
   6066 
   6067 [[maybe_unused]] static void SaveOncePrefToSharedMap(
   6068    SharedPrefMapBuilder& aBuilder, const nsACString& aName, float aValue) {
   6069  auto oncePref = MakeUnique<Pref>(aName);
   6070  oncePref->SetType(PrefType::String);
   6071  oncePref->SetIsSkippedByIteration(true);
   6072  nsAutoCString value;
   6073  value.AppendFloat(aValue);
   6074  bool valueChanged = false;
   6075  // It's ok to stash a pointer to the temporary PromiseFlatCString's chars in
   6076  // pref because pref_SetPref() duplicates those chars.
   6077  const nsCString& flat = PromiseFlatCString(value);
   6078  MOZ_ALWAYS_SUCCEEDS(
   6079      oncePref->SetDefaultValue(PrefType::String, PrefValue(flat.get()),
   6080                                /* isSticky */ true,
   6081                                /* isLocked */ true, &valueChanged));
   6082  oncePref->AddToMap(aBuilder);
   6083 }
   6084 
   6085 #define ONCE_PREF_NAME(name) "$$$" name "$$$"
   6086 
   6087 namespace StaticPrefs {
   6088 
   6089 static void RegisterOncePrefs(SharedPrefMapBuilder& aBuilder) {
   6090  MOZ_ASSERT(XRE_IsParentProcess());
   6091  MOZ_DIAGNOSTIC_ASSERT(!gSharedMap,
   6092                        "Must be called before gSharedMap has been created");
   6093  MaybeInitOncePrefs();
   6094 
   6095  // For `once`-mirrored prefs we generate a save call, which saves the value
   6096  // as it was at parent startup. It is stored in a special (hidden and locked)
   6097  // entry in the global SharedPreferenceMap. In order for the entry to be
   6098  // hidden and not appear in about:config nor ever be stored to disk, we set
   6099  // its IsSkippedByIteration flag to true. We also distinguish it by adding a
   6100  // "$$$" prefix and suffix to the preference name.
   6101 #define NEVER_PREF(name, cpp_type, value)
   6102 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value)
   6103 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value)
   6104 #define ONCE_PREF(name, base_id, full_id, cpp_type, value)      \
   6105  SaveOncePrefToSharedMap(aBuilder, ONCE_PREF_NAME(name) ""_ns, \
   6106                          cpp_type(sMirror_##full_id));
   6107 #include "mozilla/StaticPrefListAll.h"
   6108 #undef NEVER_PREF
   6109 #undef ALWAYS_PREF
   6110 #undef ALWAYS_DATAMUTEX_PREF
   6111 #undef ONCE_PREF
   6112 }
   6113 
   6114 // Disable thread safety analysis on this function, because it explodes build
   6115 // times and memory usage.
   6116 MOZ_NO_THREAD_SAFETY_ANALYSIS
   6117 static void InitStaticPrefsFromShared() {
   6118  MOZ_ASSERT(!XRE_IsParentProcess());
   6119  MOZ_DIAGNOSTIC_ASSERT(gSharedMap,
   6120                        "Must be called once gSharedMap has been created");
   6121 
   6122 #ifdef DEBUG
   6123 #  define ASSERT_PREF_NOT_SANITIZED(name, cpp_type)                          \
   6124    if (IsString<cpp_type>::value && IsPreferenceSanitized(name)) {          \
   6125      MOZ_CRASH("Unexpected sanitized string preference '" name              \
   6126                "'. "                                                        \
   6127                "Static Preferences cannot be sanitized currently, because " \
   6128                "they expect to be initialized from the Static Map, and "    \
   6129                "sanitized preferences are not present there.");             \
   6130    }
   6131 #else
   6132 #  define ASSERT_PREF_NOT_SANITIZED(name, cpp_type)
   6133 #endif
   6134 
   6135  // For mirrored static prefs we generate some initialization code. Each
   6136  // mirror variable is already initialized in the binary with the default
   6137  // value. If the pref value hasn't changed from the default in the main
   6138  // process (the common case) then the overwriting here won't change the
   6139  // mirror variable's value.
   6140  //
   6141  // Note that the MOZ_ASSERT calls below can fail in one obscure case: when a
   6142  // Firefox update occurs and we get a main process from the old binary (with
   6143  // static prefs {A,B,C,D}) plus a new content process from the new binary
   6144  // (with static prefs {A,B,C,D,E}). The content process' call to
   6145  // GetSharedPrefValue() for pref E will fail because the shared pref map was
   6146  // created by the main process, which doesn't have pref E.
   6147  //
   6148  // This silent failure is safe. The mirror variable for pref E is already
   6149  // initialized to the default value in the content process, and the main
   6150  // process cannot have changed pref E because it doesn't know about it!
   6151  //
   6152  // Nonetheless, it's useful to have the MOZ_ASSERT here for testing of debug
   6153  // builds, where this scenario involving inconsistent binaries should not
   6154  // occur.
   6155 #define NEVER_PREF(name, cpp_type, default_value)
   6156 #define ALWAYS_PREF(name, base_id, full_id, cpp_type, default_value)    \
   6157  {                                                                     \
   6158    StripAtomic<cpp_type> val;                                          \
   6159    ASSERT_PREF_NOT_SANITIZED(name, cpp_type);                          \
   6160    DebugOnly<nsresult> rv = Internals::GetSharedPrefValue(name, &val); \
   6161    MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed accessing " name);             \
   6162    StaticPrefs::sMirror_##full_id = val;                               \
   6163  }
   6164 #define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, default_value) \
   6165  {                                                                            \
   6166    StripAtomic<cpp_type> val;                                                 \
   6167    ASSERT_PREF_NOT_SANITIZED(name, cpp_type);                                 \
   6168    DebugOnly<nsresult> rv = Internals::GetSharedPrefValue(name, &val);        \
   6169    MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed accessing " name);                    \
   6170    Internals::AssignMirror(StaticPrefs::sMirror_##full_id,                    \
   6171                            std::forward<StripAtomic<cpp_type>>(val));         \
   6172  }
   6173 #define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \
   6174  {                                                                \
   6175    cpp_type val;                                                  \
   6176    ASSERT_PREF_NOT_SANITIZED(name, cpp_type);                     \
   6177    DebugOnly<nsresult> rv =                                       \
   6178        Internals::GetSharedPrefValue(ONCE_PREF_NAME(name), &val); \
   6179    MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed accessing " name);        \
   6180    StaticPrefs::sMirror_##full_id = val;                          \
   6181  }
   6182 #include "mozilla/StaticPrefListAll.h"
   6183 #undef NEVER_PREF
   6184 #undef ALWAYS_PREF
   6185 #undef ALWAYS_DATAMUTEX_PREF
   6186 #undef ONCE_PREF
   6187 #undef ASSERT_PREF_NOT_SANITIZED
   6188 
   6189  // `once`-mirrored prefs have been set to their value in the step above and
   6190  // outside the parent process they are immutable. We set sOncePrefRead so
   6191  // that we can directly skip any lazy initializations.
   6192  sOncePrefRead = true;
   6193 }
   6194 
   6195 }  // namespace StaticPrefs
   6196 
   6197 }  // namespace mozilla
   6198 
   6199 #undef ENSURE_PARENT_PROCESS
   6200 
   6201 //===========================================================================
   6202 // Module and factory stuff
   6203 //===========================================================================
   6204 
   6205 NS_IMPL_COMPONENT_FACTORY(nsPrefLocalizedString) {
   6206  auto str = MakeRefPtr<nsPrefLocalizedString>();
   6207  if (NS_SUCCEEDED(str->Init())) {
   6208    return str.forget().downcast<nsISupports>();
   6209  }
   6210  return nullptr;
   6211 }
   6212 
   6213 NS_IMPL_COMPONENT_FACTORY(nsPrefOverrideMap) {
   6214  auto comp = MakeRefPtr<nsPrefOverrideMap>();
   6215  return comp.forget().downcast<nsISupports>();
   6216 }
   6217 
   6218 namespace mozilla {
   6219 
   6220 void UnloadPrefsModule() { Preferences::Shutdown(); }
   6221 
   6222 }  // namespace mozilla
   6223 
   6224 // Preference Sanitization Related Code ---------------------------------------
   6225 
   6226 #define PREF_LIST_ENTRY(s) {s, (sizeof(s) / sizeof(char)) - 1}
   6227 struct PrefListEntry {
   6228  const char* mPrefBranch;
   6229  size_t mLen;
   6230 };
   6231 
   6232 // A preference is 'sanitized' (i.e. not sent to web content processes) if
   6233 // one of two criteria are met:
   6234 //   1. The pref name matches one of the prefixes in the following list
   6235 //   2. The pref is dynamically named (i.e. not specified in all.js or
   6236 //      StaticPrefList.yml), a string pref, and it is NOT exempted in
   6237 //      sDynamicPrefOverrideList
   6238 //
   6239 // This behavior is codified in ShouldSanitizePreference() below.
   6240 // Exclusions of preferences can be defined in sOverrideRestrictionsList[].
   6241 static const PrefListEntry sRestrictFromWebContentProcesses[] = {
   6242    // Remove prefs with user data
   6243    PREF_LIST_ENTRY("datareporting.policy."),
   6244    PREF_LIST_ENTRY("browser.download.lastDir"),
   6245    PREF_LIST_ENTRY("browser.newtabpage.pinned"),
   6246    PREF_LIST_ENTRY("browser.uiCustomization.state"),
   6247    PREF_LIST_ENTRY("browser.urlbar"),
   6248    PREF_LIST_ENTRY("devtools.debugger.pending-selected-location"),
   6249    PREF_LIST_ENTRY("identity.fxaccounts.account.device.name"),
   6250    PREF_LIST_ENTRY("identity.fxaccounts.account.telemetry.sanitized_uid"),
   6251    PREF_LIST_ENTRY("identity.fxaccounts.lastSignedInUserHash"),
   6252    PREF_LIST_ENTRY("print_printer"),
   6253    PREF_LIST_ENTRY("services."),
   6254    PREF_LIST_ENTRY("termsofuse."),
   6255 
   6256    // Remove UUIDs
   6257    PREF_LIST_ENTRY("app.normandy.user_id"),
   6258    PREF_LIST_ENTRY("browser.newtabpage.activity-stream.impressionId"),
   6259    PREF_LIST_ENTRY("browser.pageActions.persistedActions"),
   6260    PREF_LIST_ENTRY("browser.startup.lastColdStartupCheck"),
   6261    PREF_LIST_ENTRY("dom.push.userAgentID"),
   6262    PREF_LIST_ENTRY("extensions.webextensions.uuids"),
   6263    PREF_LIST_ENTRY("privacy.userContext.extension"),
   6264    PREF_LIST_ENTRY("toolkit.telemetry.cachedClientID"),
   6265    PREF_LIST_ENTRY("toolkit.telemetry.cachedProfileGroupID"),
   6266 
   6267    // Remove IDs that could be used to correlate across origins
   6268    PREF_LIST_ENTRY("app.update.lastUpdateTime."),
   6269    PREF_LIST_ENTRY(
   6270        "browser.contentblocking.cfr-milestone.milestone-shown-time"),
   6271    PREF_LIST_ENTRY("browser.contextual-services.contextId"),
   6272    PREF_LIST_ENTRY("browser.laterrun.bookkeeping.profileCreationTime"),
   6273    PREF_LIST_ENTRY("browser.newtabpage.activity-stream.discoverystream."),
   6274    PREF_LIST_ENTRY("browser.sessionstore.upgradeBackup.latestBuildID"),
   6275    PREF_LIST_ENTRY("browser.shell.mostRecentDateSetAsDefault"),
   6276    PREF_LIST_ENTRY("idle.lastDailyNotification"),
   6277    PREF_LIST_ENTRY("media.gmp-gmpopenh264.lastUpdate"),
   6278    PREF_LIST_ENTRY("media.gmp-manager.lastCheck"),
   6279    PREF_LIST_ENTRY("places.database.lastMaintenance"),
   6280    PREF_LIST_ENTRY("privacy.purge_trackers.last_purge"),
   6281    PREF_LIST_ENTRY("storage.vacuum.last.places.sqlite"),
   6282    PREF_LIST_ENTRY("toolkit.startup.last_success"),
   6283 
   6284    // Remove fingerprintable things
   6285    PREF_LIST_ENTRY("browser.startup.homepage_override.buildID"),
   6286    PREF_LIST_ENTRY("extensions.lastAppBuildId"),
   6287    PREF_LIST_ENTRY("media.gmp-manager.buildID"),
   6288    PREF_LIST_ENTRY("toolkit.telemetry.previousBuildID"),
   6289 };
   6290 
   6291 // Allowlist for prefs and branches blocklisted in
   6292 // sRestrictFromWebContentProcesses[], including prefs from
   6293 // StaticPrefList.yaml and *.js, to let them pass.
   6294 static const PrefListEntry sOverrideRestrictionsList[]{
   6295    PREF_LIST_ENTRY("services.settings.clock_skew_seconds"),
   6296    PREF_LIST_ENTRY("services.settings.last_update_seconds"),
   6297    PREF_LIST_ENTRY("services.settings.loglevel"),
   6298    // This is really a boolean dynamic pref, but one Nightly user
   6299    // has it set as a string...
   6300    PREF_LIST_ENTRY("services.settings.preview_enabled"),
   6301    PREF_LIST_ENTRY("services.settings.server"),
   6302 
   6303    // tor-browser#41165, tor-browser!765: leave this static pref in
   6304    // gSharedMap to prevent a crash in gpu process in debug builds.
   6305    PREF_LIST_ENTRY("browser.urlbar.onionRewrites.enabled"),
   6306 };
   6307 
   6308 // These prefs are dynamically-named (i.e. not specified in prefs.js or
   6309 // StaticPrefList) and would normally by blocklisted but we allow them through
   6310 // anyway, so this override list acts as an allowlist
   6311 static const PrefListEntry sDynamicPrefOverrideList[]{
   6312    PREF_LIST_ENTRY("accessibility.tabfocus"),
   6313    PREF_LIST_ENTRY("app.update.channel"),
   6314    PREF_LIST_ENTRY("apz.subtest"),
   6315    PREF_LIST_ENTRY("browser.contentblocking.category"),
   6316    PREF_LIST_ENTRY("browser.dom.window.dump.file"),
   6317    PREF_LIST_ENTRY("browser.search.region"),
   6318    PREF_LIST_ENTRY(
   6319        "browser.tabs.remote.testOnly.failPBrowserCreation.browsingContext"),
   6320    PREF_LIST_ENTRY("browser.uitour.testingOrigins"),
   6321    PREF_LIST_ENTRY("browser.urlbar.loglevel"),
   6322    PREF_LIST_ENTRY("browser.urlbar.opencompanionsearch.enabled"),
   6323    PREF_LIST_ENTRY("capability.policy"),
   6324    PREF_LIST_ENTRY("dom.securecontext.allowlist"),
   6325    PREF_LIST_ENTRY("extensions.foobaz"),
   6326    PREF_LIST_ENTRY(
   6327        "extensions.formautofill.creditCards.heuristics.testConfidence"),
   6328    PREF_LIST_ENTRY("general.appversion.override"),
   6329    PREF_LIST_ENTRY("general.buildID.override"),
   6330    PREF_LIST_ENTRY("general.oscpu.override"),
   6331    PREF_LIST_ENTRY("general.useragent.override"),
   6332    PREF_LIST_ENTRY("general.platform.override"),
   6333    PREF_LIST_ENTRY("gfx.blacklist."),
   6334    PREF_LIST_ENTRY("font.system.whitelist"),
   6335    PREF_LIST_ENTRY("font.name."),
   6336    PREF_LIST_ENTRY("intl.date_time.pattern_override."),
   6337    PREF_LIST_ENTRY("intl.hyphenation-alias."),
   6338    PREF_LIST_ENTRY("logging.config.LOG_FILE"),
   6339    PREF_LIST_ENTRY("media.audio_loopback_dev"),
   6340    PREF_LIST_ENTRY("media.decoder-doctor."),
   6341    PREF_LIST_ENTRY("media.cubeb.backend"),
   6342    PREF_LIST_ENTRY("media.cubeb.output_device"),
   6343    PREF_LIST_ENTRY("media.getusermedia.fake-camera-name"),
   6344    PREF_LIST_ENTRY("media.hls.server.url"),
   6345    PREF_LIST_ENTRY("media.peerconnection.nat_simulator.filtering_type"),
   6346    PREF_LIST_ENTRY("media.peerconnection.nat_simulator.mapping_type"),
   6347    PREF_LIST_ENTRY("media.peerconnection.nat_simulator.redirect_address"),
   6348    PREF_LIST_ENTRY("media.peerconnection.nat_simulator.redirect_targets"),
   6349    PREF_LIST_ENTRY("media.peerconnection.nat_simulator.network_delay_ms"),
   6350    PREF_LIST_ENTRY("media.video_loopback_dev"),
   6351    PREF_LIST_ENTRY("media.webspeech.service.endpoint"),
   6352    PREF_LIST_ENTRY("network.gio.supported-protocols"),
   6353    PREF_LIST_ENTRY("network.protocol-handler.external."),
   6354    PREF_LIST_ENTRY("network.security.ports.banned"),
   6355    PREF_LIST_ENTRY("nimbus.syncdatastore."),
   6356    PREF_LIST_ENTRY("pdfjs."),
   6357    PREF_LIST_ENTRY("plugins.force.wmode"),
   6358    PREF_LIST_ENTRY("print.printer_"),
   6359    PREF_LIST_ENTRY("print_printer"),
   6360    PREF_LIST_ENTRY("places.interactions.customBlocklist"),
   6361    PREF_LIST_ENTRY("remote.log.level"),
   6362    // services.* preferences should be added in sOverrideRestrictionsList[] -
   6363    // the whole preference branch gets sanitized by default.
   6364    PREF_LIST_ENTRY("spellchecker.dictionary"),
   6365    PREF_LIST_ENTRY("test.char"),
   6366    PREF_LIST_ENTRY("Test.IPC."),
   6367    PREF_LIST_ENTRY("exists.thenDoesNot"),
   6368    PREF_LIST_ENTRY("type.String."),
   6369    PREF_LIST_ENTRY("toolkit.mozprotocol.url"),
   6370    PREF_LIST_ENTRY("toolkit.telemetry.log.level"),
   6371    PREF_LIST_ENTRY("ui."),
   6372 };
   6373 
   6374 #undef PREF_LIST_ENTRY
   6375 
   6376 static bool ShouldSanitizePreference(const Pref* const aPref) {
   6377  // In the parent process, we use a heuristic to decide if a pref
   6378  // value should be sanitized before sending to subprocesses.
   6379  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   6380 
   6381  const char* prefName = aPref->Name();
   6382 
   6383  // If a pref starts with this magic string, it is a Once-Initialized pref
   6384  // from Static Prefs. It should* not be in the above list and while it looks
   6385  // like a dnyamically named pref, it is not.
   6386  // * nothing enforces this
   6387  if (strncmp(prefName, "$$$", 3) == 0) {
   6388    return false;
   6389  }
   6390 
   6391  // First check against the denylist.
   6392  // The services pref is an annoying one - it's much easier to blocklist
   6393  // the whole branch and then add this one check to let this one annoying
   6394  // pref through.
   6395  for (const auto& entry : sRestrictFromWebContentProcesses) {
   6396    if (strncmp(entry.mPrefBranch, prefName, entry.mLen) == 0) {
   6397      for (const auto& pasEnt : sOverrideRestrictionsList) {
   6398        if (strncmp(pasEnt.mPrefBranch, prefName, pasEnt.mLen) == 0) {
   6399          return false;
   6400        }
   6401      }
   6402      return true;
   6403    }
   6404  }
   6405 
   6406  // Then check if it's a dynamically named string preference and not
   6407  // in the override list
   6408  if (aPref->Type() == PrefType::String && !aPref->HasDefaultValue()) {
   6409    for (const auto& entry : sDynamicPrefOverrideList) {
   6410      if (strncmp(entry.mPrefBranch, prefName, entry.mLen) == 0) {
   6411        return false;
   6412      }
   6413    }
   6414    return true;
   6415  }
   6416 
   6417  return false;
   6418 }
   6419 
   6420 // Forward Declaration - it's not defined in the .h, because we don't need to;
   6421 // it's only used here.
   6422 template <class T>
   6423 static bool IsPreferenceSanitized_Impl(const T& aPref);
   6424 
   6425 static bool IsPreferenceSanitized(const Pref* const aPref) {
   6426  return IsPreferenceSanitized_Impl(*aPref);
   6427 }
   6428 
   6429 static bool IsPreferenceSanitized(const PrefWrapper& aPref) {
   6430  return IsPreferenceSanitized_Impl(aPref);
   6431 }
   6432 
   6433 template <class T>
   6434 static bool IsPreferenceSanitized_Impl(const T& aPref) {
   6435  if (aPref.IsSanitized()) {
   6436    MOZ_DIAGNOSTIC_ASSERT(!XRE_IsParentProcess());
   6437    MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
   6438    return true;
   6439  }
   6440  return false;
   6441 }
   6442 
   6443 namespace mozilla {
   6444 
   6445 // This is the only Check Sanitization function exposed outside of
   6446 // Preferences.cpp, because this is the only one ever called from
   6447 // outside this file.
   6448 bool IsPreferenceSanitized(const char* aPrefName) {
   6449  // Perform this comparison (see notes above) early to avoid a lookup
   6450  // if we can avoid it.
   6451  if (strncmp(aPrefName, "$$$", 3) == 0) {
   6452    return false;
   6453  }
   6454 
   6455  if (!gContentProcessPrefsAreInited) {
   6456    return false;
   6457  }
   6458 
   6459  if (Maybe<PrefWrapper> pref = pref_Lookup(aPrefName)) {
   6460    if (pref.isNothing()) {
   6461      return true;
   6462    }
   6463    return IsPreferenceSanitized(pref.value());
   6464  }
   6465 
   6466  return true;
   6467 }
   6468 
   6469 Atomic<bool, Relaxed> sOmitBlocklistedPrefValues(false);
   6470 Atomic<bool, Relaxed> sCrashOnBlocklistedPref(false);
   6471 
   6472 void OnFissionBlocklistPrefChange(const char* aPref, void* aData) {
   6473  if (strcmp(aPref, kFissionEnforceBlockList) == 0) {
   6474    sCrashOnBlocklistedPref =
   6475        StaticPrefs::fission_enforceBlocklistedPrefsInSubprocesses();
   6476  } else if (strcmp(aPref, kFissionOmitBlockListValues) == 0) {
   6477    sOmitBlocklistedPrefValues =
   6478        StaticPrefs::fission_omitBlocklistedPrefsInSubprocesses();
   6479  } else {
   6480    MOZ_CRASH("Unknown pref passed to callback");
   6481  }
   6482 }
   6483 
   6484 }  // namespace mozilla
   6485 
   6486 // This file contains the C wrappers for the C++ static pref getters, as used
   6487 // by Rust code.
   6488 #include "init/StaticPrefsCGetters.cpp"