Bug 1436655 - Introduce a mechanism for VarCache prefs to be defined entirely in the binary. r=glandium draft
authorNicholas Nethercote <nnethercote@mozilla.com>
Fri, 23 Mar 2018 15:15:22 +1100
changeset 771490 94997d5f0885ea568f01a264d5811ba9813077c8
parent 771489 73be150e8abf58cc8436dc30f3be08a7a6373c45
child 771495 8fd78713cfa0834d61cf612cde0b6140df5e8504
push id103687
push usernnethercote@mozilla.com
push dateFri, 23 Mar 2018 04:16:02 +0000
reviewersglandium
bugs1436655, 1346224
milestone61.0a1
Bug 1436655 - Introduce a mechanism for VarCache prefs to be defined entirely in the binary. r=glandium Currently VarCache prefs are setup in two parts: - The vanilla pref part, installed via a data file such as all.js, or via an API call. - The VarCache variable part, setup by an Add*VarCache() call. Both parts are needed for the pref to actually operate as a proper VarCache pref. (There are various prefs for which we do one but not the other unless a certain condition is met.) This patch introduces a new way of doing things. There is a new file, modules/libpref/init/StaticPrefList.h, which defines prefs like this: > VARCACHE_PREF( > "layout.accessiblecaret.width", > layout_accessiblecaret_width, > float, 34.0 > ) This replaces both the existing parts. The preprocessor is used to generate multiple things from this single definition: - A global variable (the VarCache itself). - A getter for that global variable. - A call to an init function that unconditionally installs the pref in the prefs hash table at startup. C++ files can include the new StaticPrefs.h file to access the getter. Rust code cannot use the getter, but can access the global variable directly via structs.rs. This is similar to how things currently work for Rust code. Non-VarCache prefs can also be declared in StaticPrefList.h, using PREF instead of the VARCACHE_PREF. The new approach has the following advantages. + It eliminates the duplication (in all.js and the Add*VarCache() call) of the pref name and default value, preventing potential mismatches. (This is a real problem in practice!) + There is now a single initialization point for these VarCache prefs. + This avoids need to find a place to insert the Add*VarCache() calls, which are currently spread all over the place. + It also eliminates the common pattern whereby these calls are wrapped in a execute-once block protected by a static boolean (see bug 1346224). + It's no longer possible to have a VarCache pref for which only one of the pieces has been setup. + It encapsulates the VarCache global variable, so there is no need to declare it separately. + VarCache reads are done via a getter (e.g. StaticPrefs::foo_bar_baz()) instead of a raw global variable read. + This makes it clearer that you're reading a pref value. + This prevents accidental writes to the global variable. + This prevents accidental mistyping of the pref name. + This provides a single chokepoint in the code for such accesses, which make adding checking and instrumentation feasible. + It subsumes MediaPrefs, and will allow that class to be removed. (gfxPrefs is a harder lift, unfortunately.) + Once all VarCache prefs are migrated to the new approach, the VarCache mechanism will be better encapsulated, with fewer details publicly visible. + (Future work) This will allow the pref names to be stored statically, saving memory in every process. The main downside of the new approach is that all of these prefs are in a single header that is included in quite a few places, so any changes to this header will cause a fair amount of recompilation. Another minor downside is that all VarCache prefs are defined and visible from start-up. For test-only prefs like network.predictor.doing-tests, having them show in about:config isn't particularly useful. The patch also moves three network VarCache prefs to the new mechanism as a basic demonstration. (And note the inconsistencies in the multiple initial values that were provided for network.auth.subresource-img-cross-origin-http-auth-allow!) There will be numerous follow-up bugs to convert the remaining VarCache prefs. MozReview-Commit-ID: 9ABNpOR16uW
modules/libpref/Preferences.cpp
modules/libpref/Preferences.h
modules/libpref/StaticPrefs.h
modules/libpref/init/StaticPrefList.h
modules/libpref/init/all.js
modules/libpref/moz.build
netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
netwerk/protocol/http/nsHttpChannelAuthProvider.h
netwerk/protocol/http/nsHttpHandler.cpp
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/Omnijar.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Services.h"
 #include "mozilla/ServoStyleSet.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/URLPreloader.h"
 #include "mozilla/Variant.h"
 #include "mozilla/Vector.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsAutoPtr.h"
@@ -3243,17 +3244,20 @@ Preferences::GetInstanceForService()
 
   MOZ_ASSERT(!gHashTable);
   gHashTable = new PLDHashTable(
     &pref_HashTableOps, sizeof(PrefEntry), PREF_HASHTABLE_INITIAL_LENGTH);
 
   gTelemetryLoadData =
     new nsDataHashtable<nsCStringHashKey, TelemetryLoadData>();
 
-  Result<Ok, const char*> res = InitInitialObjects();
+  gCacheData = new nsTArray<nsAutoPtr<CacheData>>();
+  gCacheDataDesc = "set by GetInstanceForService() (1)";
+
+  Result<Ok, const char*> res = InitInitialObjects(/* isStartup */ true);
   if (res.isErr()) {
     sPreferences = nullptr;
     gCacheDataDesc = res.unwrapErr();
     return nullptr;
   }
 
   if (!XRE_IsParentProcess()) {
     MOZ_ASSERT(gChangedDomPrefs);
@@ -3298,18 +3302,17 @@ Preferences::GetInstanceForService()
 
     if (NS_FAILED(rv)) {
       sPreferences = nullptr;
       gCacheDataDesc = "AddObserver(\"profile-before-change\") failed";
       return nullptr;
     }
   }
 
-  gCacheData = new nsTArray<nsAutoPtr<CacheData>>();
-  gCacheDataDesc = "set by GetInstanceForService()";
+  gCacheDataDesc = "set by GetInstanceForService() (2)";
 
   // Preferences::GetInstanceForService() can be called from GetService(), and
   // RegisterStrongMemoryReporter calls GetService(nsIMemoryReporter).  To
   // avoid a potential recursive GetService() call, we can't register the
   // memory reporter here; instead, do it off a runnable.
   RefPtr<AddPreferencesMemoryReporterRunnable> runnable =
     new AddPreferencesMemoryReporterRunnable();
   NS_DispatchToMainThread(runnable);
@@ -3480,17 +3483,17 @@ Preferences::Observe(nsISupports* aSubje
     // set a pref. A blocking save here re-saves if necessary and also waits
     // for any pending saves to complete.
     SavePrefFileBlocking();
     MOZ_ASSERT(!mDirty, "Preferences should not be dirty");
     mProfileShutdown = true;
 
   } else if (!nsCRT::strcmp(aTopic, "reload-default-prefs")) {
     // Reload the default prefs from file.
-    Unused << InitInitialObjects();
+    Unused << InitInitialObjects(/* isStartup */ false);
 
   } else if (!nsCRT::strcmp(aTopic, "suspend_process_notification")) {
     // Our process is being suspended. The OS may wake our process later,
     // or it may kill the process. In case our process is going to be killed
     // from the suspended state, we save preferences before suspending.
     rv = SavePrefFileBlocking();
   }
 
@@ -3526,17 +3529,18 @@ Preferences::ReadUserPrefsFromFile(nsIFi
 NS_IMETHODIMP
 Preferences::ResetPrefs()
 {
   ENSURE_PARENT_PROCESS("Preferences::ResetPrefs", "all prefs");
 
   gHashTable->ClearAndPrepareForLength(PREF_HASHTABLE_INITIAL_LENGTH);
   gPrefNameArena.Clear();
 
-  return InitInitialObjects().isOk() ? NS_OK : NS_ERROR_FAILURE;
+  return InitInitialObjects(/* isStartup */ false).isOk() ? NS_OK
+                                                          : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 Preferences::ResetUserPrefs()
 {
   ENSURE_PARENT_PROCESS("Preferences::ResetUserPrefs", "all prefs");
   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
   MOZ_ASSERT(NS_IsMainThread());
@@ -4101,17 +4105,17 @@ pref_ReadPrefFromJar(nsZipArchive* aJarR
   }
 
   return NS_OK;
 }
 
 // Initialize default preference JavaScript buffers from appropriate TEXT
 // resources.
 /* static */ Result<Ok, const char*>
-Preferences::InitInitialObjects()
+Preferences::InitInitialObjects(bool aIsStartup)
 {
   // In the omni.jar case, we load the following prefs:
   // - jar:$gre/omni.jar!/greprefs.js
   // - jar:$gre/omni.jar!/defaults/pref/*.js
   //
   // In the non-omni.jar case, we load:
   // - $gre/greprefs.js
   //
@@ -4265,16 +4269,21 @@ Preferences::InitInitialObjects()
         continue;
       }
 
       // Do we care if a file provided by this process fails to load?
       pref_LoadPrefsInDir(path, nullptr, 0);
     }
   }
 
+  // We initialize static prefs after prefs from data files so that we can
+  // detect and complain if there are any static prefs that are also defined in
+  // data files.
+  StaticPrefs::InitAll(aIsStartup);
+
   if (XRE_IsParentProcess()) {
     SetupTelemetryPref();
   }
 
   NS_CreateServicesFromCategory(NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID,
                                 nullptr,
                                 NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID);
 
@@ -4733,20 +4742,25 @@ static void
 BoolVarChanged(const char* aPref, void* aClosure)
 {
   CacheData* cache = static_cast<CacheData*>(aClosure);
   *static_cast<bool*>(cache->mCacheLocation) =
     Preferences::GetBool(aPref, cache->mDefaultValueBool);
 }
 
 /* static */ nsresult
-Preferences::AddBoolVarCache(bool* aCache, const char* aPref, bool aDefault)
+Preferences::AddBoolVarCache(bool* aCache,
+                             const char* aPref,
+                             bool aDefault,
+                             bool aSkipAssignment)
 {
   AssertNotAlreadyCached("bool", aPref, aCache);
-  *aCache = GetBool(aPref, aDefault);
+  if (!aSkipAssignment) {
+    *aCache = GetBool(aPref, aDefault);
+  }
   CacheData* data = new CacheData();
   data->mCacheLocation = aCache;
   data->mDefaultValueBool = aDefault;
   CacheDataAppendElement(data);
   Preferences::RegisterCallback(BoolVarChanged,
                                 aPref,
                                 data,
                                 Preferences::ExactMatch,
@@ -4762,20 +4776,23 @@ AtomicBoolVarChanged(const char* aPref, 
   *static_cast<Atomic<bool, Order>*>(cache->mCacheLocation) =
     Preferences::GetBool(aPref, cache->mDefaultValueBool);
 }
 
 template<MemoryOrdering Order>
 /* static */ nsresult
 Preferences::AddAtomicBoolVarCache(Atomic<bool, Order>* aCache,
                                    const char* aPref,
-                                   bool aDefault)
+                                   bool aDefault,
+                                   bool aSkipAssignment)
 {
   AssertNotAlreadyCached("bool", aPref, aCache);
-  *aCache = Preferences::GetBool(aPref, aDefault);
+  if (!aSkipAssignment) {
+    *aCache = GetBool(aPref, aDefault);
+  }
   CacheData* data = new CacheData();
   data->mCacheLocation = aCache;
   data->mDefaultValueBool = aDefault;
   CacheDataAppendElement(data);
   Preferences::RegisterCallback(AtomicBoolVarChanged<Order>,
                                 aPref,
                                 data,
                                 Preferences::ExactMatch,
@@ -4789,20 +4806,23 @@ IntVarChanged(const char* aPref, void* a
   CacheData* cache = static_cast<CacheData*>(aClosure);
   *static_cast<int32_t*>(cache->mCacheLocation) =
     Preferences::GetInt(aPref, cache->mDefaultValueInt);
 }
 
 /* static */ nsresult
 Preferences::AddIntVarCache(int32_t* aCache,
                             const char* aPref,
-                            int32_t aDefault)
+                            int32_t aDefault,
+                            bool aSkipAssignment)
 {
   AssertNotAlreadyCached("int", aPref, aCache);
-  *aCache = Preferences::GetInt(aPref, aDefault);
+  if (!aSkipAssignment) {
+    *aCache = GetInt(aPref, aDefault);
+  }
   CacheData* data = new CacheData();
   data->mCacheLocation = aCache;
   data->mDefaultValueInt = aDefault;
   CacheDataAppendElement(data);
   Preferences::RegisterCallback(
     IntVarChanged, aPref, data, Preferences::ExactMatch, /* isPriority */ true);
   return NS_OK;
 }
@@ -4815,20 +4835,23 @@ AtomicIntVarChanged(const char* aPref, v
   *static_cast<Atomic<int32_t, Order>*>(cache->mCacheLocation) =
     Preferences::GetInt(aPref, cache->mDefaultValueUint);
 }
 
 template<MemoryOrdering Order>
 /* static */ nsresult
 Preferences::AddAtomicIntVarCache(Atomic<int32_t, Order>* aCache,
                                   const char* aPref,
-                                  int32_t aDefault)
+                                  int32_t aDefault,
+                                  bool aSkipAssignment)
 {
   AssertNotAlreadyCached("int", aPref, aCache);
-  *aCache = Preferences::GetInt(aPref, aDefault);
+  if (!aSkipAssignment) {
+    *aCache = GetInt(aPref, aDefault);
+  }
   CacheData* data = new CacheData();
   data->mCacheLocation = aCache;
   data->mDefaultValueUint = aDefault;
   CacheDataAppendElement(data);
   Preferences::RegisterCallback(AtomicIntVarChanged<Order>,
                                 aPref,
                                 data,
                                 Preferences::ExactMatch,
@@ -4842,20 +4865,23 @@ UintVarChanged(const char* aPref, void* 
   CacheData* cache = static_cast<CacheData*>(aClosure);
   *static_cast<uint32_t*>(cache->mCacheLocation) =
     Preferences::GetUint(aPref, cache->mDefaultValueUint);
 }
 
 /* static */ nsresult
 Preferences::AddUintVarCache(uint32_t* aCache,
                              const char* aPref,
-                             uint32_t aDefault)
+                             uint32_t aDefault,
+                             bool aSkipAssignment)
 {
   AssertNotAlreadyCached("uint", aPref, aCache);
-  *aCache = Preferences::GetUint(aPref, aDefault);
+  if (!aSkipAssignment) {
+    *aCache = GetUint(aPref, aDefault);
+  }
   CacheData* data = new CacheData();
   data->mCacheLocation = aCache;
   data->mDefaultValueUint = aDefault;
   CacheDataAppendElement(data);
   Preferences::RegisterCallback(UintVarChanged,
                                 aPref,
                                 data,
                                 Preferences::ExactMatch,
@@ -4871,88 +4897,320 @@ AtomicUintVarChanged(const char* aPref, 
   *static_cast<Atomic<uint32_t, Order>*>(cache->mCacheLocation) =
     Preferences::GetUint(aPref, cache->mDefaultValueUint);
 }
 
 template<MemoryOrdering Order>
 /* static */ nsresult
 Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Order>* aCache,
                                    const char* aPref,
-                                   uint32_t aDefault)
+                                   uint32_t aDefault,
+                                   bool aSkipAssignment)
 {
   AssertNotAlreadyCached("uint", aPref, aCache);
-  *aCache = Preferences::GetUint(aPref, aDefault);
+  if (!aSkipAssignment) {
+    *aCache = GetUint(aPref, aDefault);
+  }
   CacheData* data = new CacheData();
   data->mCacheLocation = aCache;
   data->mDefaultValueUint = aDefault;
   CacheDataAppendElement(data);
   Preferences::RegisterCallback(AtomicUintVarChanged<Order>,
                                 aPref,
                                 data,
                                 Preferences::ExactMatch,
                                 /* isPriority */ true);
   return NS_OK;
 }
 
 // Since the definition of template functions is not in a header file, we
 // need to explicitly specify the instantiations that are required. Currently
 // limited orders are needed and therefore implemented.
 template nsresult
-Preferences::AddAtomicBoolVarCache(Atomic<bool, Relaxed>*, const char*, bool);
+Preferences::AddAtomicBoolVarCache(Atomic<bool, Relaxed>*,
+                                   const char*,
+                                   bool,
+                                   bool);
 
 template nsresult
 Preferences::AddAtomicBoolVarCache(Atomic<bool, ReleaseAcquire>*,
                                    const char*,
+                                   bool,
                                    bool);
 
 template nsresult
 Preferences::AddAtomicBoolVarCache(Atomic<bool, SequentiallyConsistent>*,
                                    const char*,
+                                   bool,
                                    bool);
 
 template nsresult
 Preferences::AddAtomicIntVarCache(Atomic<int32_t, Relaxed>*,
                                   const char*,
-                                  int32_t);
+                                  int32_t,
+                                  bool);
 
 template nsresult
 Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Relaxed>*,
                                    const char*,
-                                   uint32_t);
+                                   uint32_t,
+                                   bool);
 
 template nsresult
 Preferences::AddAtomicUintVarCache(Atomic<uint32_t, ReleaseAcquire>*,
                                    const char*,
-                                   uint32_t);
+                                   uint32_t,
+                                   bool);
 
 static void
 FloatVarChanged(const char* aPref, void* aClosure)
 {
   CacheData* cache = static_cast<CacheData*>(aClosure);
   *static_cast<float*>(cache->mCacheLocation) =
     Preferences::GetFloat(aPref, cache->mDefaultValueFloat);
 }
 
 /* static */ nsresult
-Preferences::AddFloatVarCache(float* aCache, const char* aPref, float aDefault)
+Preferences::AddFloatVarCache(float* aCache,
+                              const char* aPref,
+                              float aDefault,
+                              bool aSkipAssignment)
 {
   AssertNotAlreadyCached("float", aPref, aCache);
-  *aCache = Preferences::GetFloat(aPref, aDefault);
+  if (!aSkipAssignment) {
+    *aCache = GetFloat(aPref, aDefault);
+  }
   CacheData* data = new CacheData();
   data->mCacheLocation = aCache;
   data->mDefaultValueFloat = aDefault;
   CacheDataAppendElement(data);
   Preferences::RegisterCallback(FloatVarChanged,
                                 aPref,
                                 data,
                                 Preferences::ExactMatch,
                                 /* isPriority */ true);
   return NS_OK;
 }
 
+// For a VarCache pref like this:
+//
+//   VARCACHE_PREF("my.varcache", my_varcache, int32_t, 99)
+//
+// we generate a static variable definition and a getter definition:
+//
+//   int32_t StaticPrefs::sVarCache_my_varcache(99);
+//   int32_t StaticPrefs::my_varcache() { return sVarCache_my_varcache; }
+//
+#define PREF(name, cpp_type, value)
+#define VARCACHE_PREF(name, id, cpp_type, value)                               \
+  cpp_type StaticPrefs::sVarCache_##id(value);                                 \
+  StripAtomic<cpp_type> StaticPrefs::id() { return sVarCache_##id; }
+#include "mozilla/StaticPrefList.h"
+#undef PREF
+#undef VARCACHE_PREF
+
+static void
+CheckForExistence(const char* aName)
+{
+#ifdef DEBUG
+  // If this warning is hit, it's probably because a pref is present in both a
+  // data file (such as all.js) and StaticPrefsList.h, which is not desirable.
+  if (Preferences::GetType(aName) != Preferences::PREF_INVALID) {
+    NS_ERROR(nsPrintfCString("'%s' already registered\n", aName).get());
+  }
+#endif
+}
+
+// The SetPref_*() functions below end in a `_<type>` suffix because they are
+// used by the PREF macro definition in InitAll() below.
+
+static void
+SetPref_bool(const char* aName, bool aDefaultValue)
+{
+  CheckForExistence(aName);
+  PrefValue value;
+  value.mBoolVal = aDefaultValue;
+  pref_SetPref(aName,
+               PrefType::Bool,
+               PrefValueKind::Default,
+               value,
+               /* isSticky */ false,
+               /* isLocked */ false,
+               /* fromInit */ true);
+}
+
+static void
+SetPref_int32_t(const char* aName, int32_t aDefaultValue)
+{
+  CheckForExistence(aName);
+  PrefValue value;
+  value.mIntVal = aDefaultValue;
+  pref_SetPref(aName,
+               PrefType::Int,
+               PrefValueKind::Default,
+               value,
+               /* isSticky */ false,
+               /* isLocked */ false,
+               /* fromInit */ true);
+}
+
+static void
+SetPref_float(const char* aName, float aDefaultValue)
+{
+  CheckForExistence(aName);
+  PrefValue value;
+  nsPrintfCString defaultValue("%f", aDefaultValue);
+  value.mStringVal = defaultValue.get();
+  pref_SetPref(aName,
+               PrefType::String,
+               PrefValueKind::Default,
+               value,
+               /* isSticky */ false,
+               /* isLocked */ false,
+               /* fromInit */ true);
+}
+
+// XXX: this will eventually become used
+MOZ_MAYBE_UNUSED static void
+SetPref_String(const char* aName, const char* aDefaultValue)
+{
+  CheckForExistence(aName);
+  PrefValue value;
+  value.mStringVal = aDefaultValue;
+  pref_SetPref(aName,
+               PrefType::String,
+               PrefValueKind::Default,
+               value,
+               /* isSticky */ false,
+               /* isLocked */ false,
+               /* fromInit */ true);
+}
+
+static void
+InitVarCachePref(const char* aName,
+                 bool* aCache,
+                 bool aDefaultValue,
+                 bool aIsStartup)
+{
+  SetPref_bool(aName, aDefaultValue);
+  *aCache = aDefaultValue;
+  if (aIsStartup) {
+    Preferences::AddBoolVarCache(aCache, aName, aDefaultValue, true);
+  }
+}
+
+template<MemoryOrdering Order>
+static void
+InitVarCachePref(const char* aName,
+                 Atomic<bool, Order>* aCache,
+                 bool aDefaultValue,
+                 bool aIsStartup)
+{
+  SetPref_bool(aName, aDefaultValue);
+  *aCache = aDefaultValue;
+  if (aIsStartup) {
+    Preferences::AddAtomicBoolVarCache(aCache, aName, aDefaultValue, true);
+  }
+}
+
+// XXX: this will eventually become used
+MOZ_MAYBE_UNUSED static void
+InitVarCachePref(const char* aName,
+                 int32_t* aCache,
+                 int32_t aDefaultValue,
+                 bool aIsStartup)
+{
+  SetPref_int32_t(aName, aDefaultValue);
+  *aCache = aDefaultValue;
+  if (aIsStartup) {
+    Preferences::AddIntVarCache(aCache, aName, aDefaultValue, true);
+  }
+}
+
+template<MemoryOrdering Order>
+static void
+InitVarCachePref(const char* aName,
+                 Atomic<int32_t, Order>* aCache,
+                 int32_t aDefaultValue,
+                 bool aIsStartup)
+{
+  SetPref_int32_t(aName, aDefaultValue);
+  *aCache = aDefaultValue;
+  if (aIsStartup) {
+    Preferences::AddAtomicIntVarCache(aCache, aName, aDefaultValue, true);
+  }
+}
+
+static void
+InitVarCachePref(const char* aName,
+                 uint32_t* aCache,
+                 uint32_t aDefaultValue,
+                 bool aIsStartup)
+{
+  SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue));
+  *aCache = aDefaultValue;
+  if (aIsStartup) {
+    Preferences::AddUintVarCache(aCache, aName, aDefaultValue, true);
+  }
+}
+
+template<MemoryOrdering Order>
+static void
+InitVarCachePref(const char* aName,
+                 Atomic<uint32_t, Order>* aCache,
+                 uint32_t aDefaultValue,
+                 bool aIsStartup)
+{
+  SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue));
+  *aCache = aDefaultValue;
+  if (aIsStartup) {
+    Preferences::AddAtomicUintVarCache(aCache, aName, aDefaultValue, true);
+  }
+}
+
+// XXX: this will eventually become used
+MOZ_MAYBE_UNUSED static void
+InitVarCachePref(const char* aName,
+                 float* aCache,
+                 float aDefaultValue,
+                 bool aIsStartup)
+{
+  SetPref_float(aName, aDefaultValue);
+  *aCache = aDefaultValue;
+  if (aIsStartup) {
+    Preferences::AddFloatVarCache(aCache, aName, aDefaultValue, true);
+  }
+}
+
+/* static */ void
+StaticPrefs::InitAll(bool aIsStartup)
+{
+// For prefs like these:
+//
+//   PREF("foo.bar.baz", bool, true)
+//   VARCACHE_PREF("my.varcache", my_varcache, int32_t, 99)
+//
+// we generate registration calls:
+//
+//   SetPref_bool("foo.bar.baz", true);
+//   InitVarCachePref("my.varcache", &StaticPrefs::sVarCache_my_varcache, 99,
+//                    aIsStartup);
+//
+// The SetPref_*() functions have a type suffix to avoid ambiguity between
+// prefs having int32_t and float default values. That suffix is not needed for
+// the InitVarCachePref() functions because they take a pointer parameter,
+// which prevents automatic int-to-float coercion.
+#define PREF(name, cpp_type, value) SetPref_##cpp_type(name, value);
+#define VARCACHE_PREF(name, id, cpp_type, value)                               \
+  InitVarCachePref(name, &StaticPrefs::sVarCache_##id, value, aIsStartup);
+#include "mozilla/StaticPrefList.h"
+#undef PREF
+#undef VARCACHE_PREF
+}
+
 } // namespace mozilla
 
 #undef ENSURE_PARENT_PROCESS
 
 //===========================================================================
 // Module and factory stuff
 //===========================================================================
 
--- a/modules/libpref/Preferences.h
+++ b/modules/libpref/Preferences.h
@@ -300,38 +300,45 @@ public:
   }
 
   // Adds the aVariable to cache table. |aVariable| must be a pointer for a
   // static variable. The value will be modified when the pref value is changed
   // but note that even if you modified it, the value isn't assigned to the
   // pref.
   static nsresult AddBoolVarCache(bool* aVariable,
                                   const char* aPref,
-                                  bool aDefault = false);
+                                  bool aDefault = false,
+                                  bool aSkipAssignment = false);
   template<MemoryOrdering Order>
   static nsresult AddAtomicBoolVarCache(Atomic<bool, Order>* aVariable,
                                         const char* aPref,
-                                        bool aDefault = false);
+                                        bool aDefault = false,
+                                        bool aSkipAssignment = false);
   static nsresult AddIntVarCache(int32_t* aVariable,
                                  const char* aPref,
-                                 int32_t aDefault = 0);
+                                 int32_t aDefault = 0,
+                                 bool aSkipAssignment = false);
   template<MemoryOrdering Order>
   static nsresult AddAtomicIntVarCache(Atomic<int32_t, Order>* aVariable,
                                        const char* aPref,
-                                       int32_t aDefault = 0);
+                                       int32_t aDefault = 0,
+                                       bool aSkipAssignment = false);
   static nsresult AddUintVarCache(uint32_t* aVariable,
                                   const char* aPref,
-                                  uint32_t aDefault = 0);
+                                  uint32_t aDefault = 0,
+                                  bool aSkipAssignment = false);
   template<MemoryOrdering Order>
   static nsresult AddAtomicUintVarCache(Atomic<uint32_t, Order>* aVariable,
                                         const char* aPref,
-                                        uint32_t aDefault = 0);
+                                        uint32_t aDefault = 0,
+                                        bool aSkipAssignment = false);
   static nsresult AddFloatVarCache(float* aVariable,
                                    const char* aPref,
-                                   float aDefault = 0.0f);
+                                   float aDefault = 0.0f,
+                                   bool aSkipAssignment = false);
 
   // When a content process is created these methods are used to pass changed
   // prefs in bulk from the parent process, via shared memory.
   static void SerializePreferences(nsCString& aStr);
   static void DeserializePreferences(char* aStr, size_t aPrefsLen);
 
   // When a single pref is changed in the parent process, these methods are
   // used to pass the update to content processes.
@@ -389,17 +396,18 @@ public:
   enum MatchKind
   {
     PrefixMatch,
     ExactMatch,
   };
 
 private:
   static void SetupTelemetryPref();
-  static mozilla::Result<mozilla::Ok, const char*> InitInitialObjects();
+  static mozilla::Result<mozilla::Ok, const char*> InitInitialObjects(
+    bool aIsStartup);
 
   static nsresult RegisterCallback(PrefChangedFunc aCallback,
                                    const char* aPref,
                                    void* aClosure,
                                    MatchKind aMatchKind,
                                    bool aIsPriority = false);
   static nsresult UnregisterCallback(PrefChangedFunc aCallback,
                                      const char* aPref,
new file mode 100644
--- /dev/null
+++ b/modules/libpref/StaticPrefs.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_StaticPrefs_h
+#define mozilla_StaticPrefs_h
+
+namespace mozilla {
+
+// These typedefs are for use within StaticPrefList.h.
+
+typedef const char* String;
+
+typedef Atomic<bool, Relaxed> RelaxedAtomicBool;
+typedef Atomic<bool, ReleaseAcquire> ReleaseAcquireAtomicBool;
+typedef Atomic<bool, SequentiallyConsistent> SequentiallyConsistentAtomicBool;
+
+typedef Atomic<int32_t, Relaxed> RelaxedAtomicInt32;
+typedef Atomic<int32_t, ReleaseAcquire> ReleaseAcquireAtomicInt32;
+typedef Atomic<int32_t, SequentiallyConsistent>
+  SequentiallyConsistentAtomicInt32;
+
+typedef Atomic<uint32_t, Relaxed> RelaxedAtomicUint32;
+typedef Atomic<uint32_t, ReleaseAcquire> ReleaseAcquireAtomicUint32;
+typedef Atomic<uint32_t, SequentiallyConsistent>
+  SequentiallyConsistentAtomicUint32;
+
+template<typename T>
+struct StripAtomicImpl
+{
+  typedef T Type;
+};
+
+template<typename T, MemoryOrdering Order>
+struct StripAtomicImpl<Atomic<T, Order>>
+{
+  typedef T Type;
+};
+
+template<typename T>
+using StripAtomic = typename StripAtomicImpl<T>::Type;
+
+class StaticPrefs
+{
+// For a VarCache pref like this:
+//
+//   VARCACHE_PREF("my.varcache", my_varcache, int32_t, 99)
+//
+// we generate a static variable declaration and a getter declaration:
+//
+//   private:
+//     static int32_t sVarCache_my_varcache;
+//   public:
+//     static int32_t my_varcache();
+//
+#define PREF(str, cpp_type, value)
+#define VARCACHE_PREF(str, id, cpp_type, value)                                \
+private:                                                                       \
+  static cpp_type sVarCache_##id;                                              \
+public:                                                                        \
+  static StripAtomic<cpp_type> id();
+#include "mozilla/StaticPrefList.h"
+#undef PREF
+#undef VARCACHE_PREF
+
+public:
+  static void InitAll(bool aIsStartup);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_StaticPrefs_h
new file mode 100644
--- /dev/null
+++ b/modules/libpref/init/StaticPrefList.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file defines static prefs, i.e. those that are defined at startup and
+// used entirely or mostly from C++ code.
+//
+// Prefs defined in this file should *not* be listed in a prefs data file such
+// as all.js. If they are, CheckForExistence() will issue an NS_ERROR.
+//
+// The file is separated into sections, where the sections are determined by
+// the first segment of the prefnames within (e.g. "network.predictor.enabled"
+// is within the "Network" section). Sections should be kept in alphabetical
+// order, but prefs within sections need not be.
+//
+// Normal prefs
+// ------------
+// Definitions of normal prefs in this file have the following form.
+//
+//   PREF(<pref-name-string>, <cpp-type>, <default-value>)
+//
+// - <pref-name-string> is the name of the pref, as it appears in about:config.
+//   It is used in most libpref API functions (from both C++ and JS code).
+//
+// - <cpp-type> is one of bool, int32_t, float, or String (which is just a
+//   typedef for `const char*` in StaticPrefs.h). Note that float prefs are
+//   stored internally as floats.
+//
+// - <default-value> is the default value. Its type should match <cpp-type>.
+//
+// VarCache prefs
+// --------------
+// A VarCache pref is a special type of pref. It can be accessed via the normal
+// pref hash table lookup functions, but it also has an associated global
+// variable (the VarCache) that mirrors the pref value in the prefs hash table,
+// and a getter function that reads that global variable. Using the getter to
+// read the pref's value has the two following advantages over the normal API
+// functions.
+//
+// - A direct global variable access is faster than a hash table lookup.
+//
+// - A global variable can be accessed off the main thread. If a pref *is*
+//   accessed off the main thread, it should use an atomic type. (But note that
+//   many VarCaches that should be atomic are not, in particular because
+//   Atomic<float> is not available, alas.)
+//
+// Definitions of VarCache prefs in this file has the following form.
+//
+//   VARCACHE_PREF(
+//     <pref-name-string>,
+//     <pref-name-id>,
+//     <cpp-type>, <default-value>
+//   )
+//
+// - <pref-name-string> is the same as for normal prefs.
+//
+// - <pref-name-id> is the name of the static getter function generated within
+//   the StaticPrefs class. For consistency, the identifier for every pref
+//   should be created by starting with <pref-name-string> and converting any
+//   '.' or '-' chars to '_'. For example, "foo.bar_baz" becomes
+//   |foo_bar_baz|. This is arguably ugly, but clear, and you can search for
+//   both using the regexp /foo.bar.baz/.
+//
+// - <cpp-type> is one of bool, int32_t, uint32_t, float, or an Atomic version
+//   of one of those. The C++ preprocessor doesn't like template syntax in a
+//   macro argument, so use the typedefs defines in StaticPrefs.h; for example,
+//   use `ReleaseAcquireAtomicBool` instead of `Atomic<bool, ReleaseAcquire>`.
+//
+// - <default-value> is the same as for normal prefs.
+//
+// Note that Rust code must access the global variable directly, rather than via
+// the getter.
+
+// clang-format off
+
+//---------------------------------------------------------------------------
+// Network prefs
+//---------------------------------------------------------------------------
+
+// Sub-resources HTTP-authentication:
+//   0 - don't allow sub-resources to open HTTP authentication credentials
+//       dialogs
+//   1 - allow sub-resources to open HTTP authentication credentials dialogs,
+//       but don't allow it for cross-origin sub-resources
+//   2 - allow the cross-origin authentication as well.
+VARCACHE_PREF(
+  "network.auth.subresource-http-auth-allow",
+   network_auth_subresource_http_auth_allow,
+  uint32_t, 2
+)
+
+// Sub-resources HTTP-authentication for cross-origin images:
+// - true: It is allowed to present http auth. dialog for cross-origin images.
+// - false: It is not allowed.
+// If network.auth.subresource-http-auth-allow has values 0 or 1 this pref does
+// not have any effect.
+VARCACHE_PREF(
+  "network.auth.subresource-img-cross-origin-http-auth-allow",
+   network_auth_subresource_img_cross_origin_http_auth_allow,
+  bool, false
+)
+
+// Resources that are triggered by some non-web-content:
+// - true: They are allow to present http auth. dialog
+// - false: They are not allow to present http auth. dialog.
+VARCACHE_PREF(
+  "network.auth.non-web-content-triggered-resources-http-auth-allow",
+   network_auth_non_web_content_triggered_resources_http_auth_allow,
+  bool, false
+)
+
+//---------------------------------------------------------------------------
+// Preferences prefs
+//---------------------------------------------------------------------------
+
+PREF("preferences.allow.omt-write", bool, true)
+
+//---------------------------------------------------------------------------
+// End of prefs
+//---------------------------------------------------------------------------
+
+// clang-format on
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2244,36 +2244,16 @@ pref("network.automatic-ntlm-auth.truste
 // system hostname, could be used for user fingerprinting.
 //
 // However, in some network environments where allowedWorkstations is in use
 // to provide a level of host-based access control, it must be set to a string
 // that is listed in allowedWorkstations for the user's account in their
 // AD Domain.
 pref("network.generic-ntlm-auth.workstation", "WORKSTATION");
 
-// Sub-resources HTTP-authentication:
-//   0 - don't allow sub-resources to open HTTP authentication credentials
-//       dialogs
-//   1 - allow sub-resources to open HTTP authentication credentials dialogs,
-//       but don't allow it for cross-origin sub-resources
-//   2 - allow the cross-origin authentication as well.
-pref("network.auth.subresource-http-auth-allow", 2);
-
-// Sub-resources HTTP-authentication for cross-origin images:
-// true - it is allowed to present http auth. dialog for cross-origin images.
-// false - it is not allowed.
-// If network.auth.subresource-http-auth-allow has values 0 or 1 this pref does not
-// have any effect.
-pref("network.auth.subresource-img-cross-origin-http-auth-allow", false);
-
-// Resources that are triggered by some non-web-content:
-// true - they are allow to present http auth. dialog
-// false - they are not allow to present http auth. dialog.
-pref("network.auth.non-web-content-triggered-resources-http-auth-allow", false);
-
 // This preference controls whether to allow sending default credentials (SSO) to
 // NTLM/Negotiate servers allowed in the "trusted uri" list when navigating them
 // in a Private Browsing window.
 // If set to false, Private Browsing windows will not use default credentials and ask
 // for credentials from the user explicitly.
 // If set to true, and a server URL conforms other conditions for sending default
 // credentials, those will be sent automatically in Private Browsing windows.
 //
--- a/modules/libpref/moz.build
+++ b/modules/libpref/moz.build
@@ -20,17 +20,19 @@ XPIDL_SOURCES += [
     'nsIPrefLocalizedString.idl',
     'nsIPrefService.idl',
     'nsIRelativeFilePref.idl',
 ]
 
 XPIDL_MODULE = 'pref'
 
 EXPORTS.mozilla += [
+    'init/StaticPrefList.h',
     'Preferences.h',
+    'StaticPrefs.h',
 ]
 
 UNIFIED_SOURCES += [
     'Preferences.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -23,16 +23,17 @@
 #include "nsIPrompt.h"
 #include "netCore.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsIURI.h"
 #include "nsContentUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsILoadContext.h"
 #include "nsIURL.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozilla/Telemetry.h"
 #include "nsIProxiedChannel.h"
 #include "nsIProxyInfo.h"
 
 namespace mozilla {
 namespace net {
 
 #define SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL 0
@@ -87,37 +88,16 @@ nsHttpChannelAuthProvider::nsHttpChannel
 {
 }
 
 nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider()
 {
     MOZ_ASSERT(!mAuthChannel, "Disconnect wasn't called");
 }
 
-uint32_t nsHttpChannelAuthProvider::sAuthAllowPref =
-    SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL;
-
-bool nsHttpChannelAuthProvider::sImgCrossOriginAuthAllowPref = true;
-bool nsHttpChannelAuthProvider::sNonWebContentTriggeredAuthAllow = false;
-
-void
-nsHttpChannelAuthProvider::InitializePrefs()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mozilla::Preferences::AddUintVarCache(&sAuthAllowPref,
-                                        "network.auth.subresource-http-auth-allow",
-                                        SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL);
-  mozilla::Preferences::AddBoolVarCache(&sImgCrossOriginAuthAllowPref,
-                                        "network.auth.subresource-img-cross-origin-http-auth-allow",
-                                        true);
-  mozilla::Preferences::AddBoolVarCache(&sNonWebContentTriggeredAuthAllow,
-                                        "network.auth.non-web-content-triggered-resources-http-auth-allow",
-                                        false);
-}
-
 NS_IMETHODIMP
 nsHttpChannelAuthProvider::Init(nsIHttpAuthenticableChannel *channel)
 {
     MOZ_ASSERT(channel, "channel expected!");
 
     mAuthChannel = channel;
 
     nsresult rv = mAuthChannel->GetURI(getter_AddRefs(mURI));
@@ -914,18 +894,20 @@ nsHttpChannelAuthProvider::GetCredential
             // blocked for all sub-resources, blocked for cross-origin
             // sub-resources, or always allowed for sub-resources.
             // For more details look at the bug 647010.
             // BlockPrompt will set mCrossOrigin parameter as well.
             if (BlockPrompt(proxyAuth)) {
                 LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge: "
                      "Prompt is blocked [this=%p pref=%d img-pref=%d "
                      "non-web-content-triggered-pref=%d]\n",
-                      this, sAuthAllowPref, sImgCrossOriginAuthAllowPref,
-                      sNonWebContentTriggeredAuthAllow));
+                     this,
+                     StaticPrefs::network_auth_subresource_http_auth_allow(),
+                     StaticPrefs::network_auth_subresource_img_cross_origin_http_auth_allow(),
+                     StaticPrefs::network_auth_non_web_content_triggered_resources_http_auth_allow()));
                 return NS_ERROR_ABORT;
             }
 
             // at this point we are forced to interact with the user to get
             // their username and password for this domain.
             rv = PromptForIdentity(level, proxyAuth, realm.get(),
                                    authType, authFlags, *ident);
             if (NS_FAILED(rv)) return rv;
@@ -1050,36 +1032,38 @@ nsHttpChannelAuthProvider::BlockPrompt(b
                                       HTTP_AUTH_DIALOG_SAME_ORIGIN_SUBRESOURCE);
             }
         } else {
             Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_3,
                                   loadInfo->GetExternalContentPolicyType());
         }
     }
 
-    if (!topDoc && !sNonWebContentTriggeredAuthAllow && nonWebContent) {
+    if (!topDoc &&
+        !StaticPrefs::network_auth_non_web_content_triggered_resources_http_auth_allow() &&
+        nonWebContent) {
         return true;
     }
 
-    switch (sAuthAllowPref) {
+    switch (StaticPrefs::network_auth_subresource_http_auth_allow()) {
     case SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL:
         // Do not open the http-authentication credentials dialog for
         // the sub-resources.
         return !topDoc && !xhr;
     case SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN:
         // Open the http-authentication credentials dialog for
         // the sub-resources only if they are not cross-origin.
         return !topDoc && !xhr && mCrossOrigin;
     case SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL:
         // Allow the http-authentication dialog for subresources.
         // If pref network.auth.subresource-img-cross-origin-http-auth-allow
         // is set, http-authentication dialog for image subresources is
         // blocked.
         if (mCrossOrigin &&
-            !sImgCrossOriginAuthAllowPref &&
+            !StaticPrefs::network_auth_subresource_img_cross_origin_http_auth_allow() &&
             loadInfo &&
             ((loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_IMAGE) ||
              (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_IMAGESET))) {
             return true;
         }
         return false;
     default:
         // This is an invalid value.
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.h
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.h
@@ -32,17 +32,16 @@ class nsHttpChannelAuthProvider : public
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSICANCELABLE
     NS_DECL_NSIHTTPCHANNELAUTHPROVIDER
     NS_DECL_NSIAUTHPROMPTCALLBACK
     NS_DECL_NSIHTTPAUTHENTICATORCALLBACK
 
     nsHttpChannelAuthProvider();
-    static void InitializePrefs();
 private:
     virtual ~nsHttpChannelAuthProvider();
 
     const char *ProxyHost() const
     { return mProxyInfo ? mProxyInfo->Host().get() : nullptr; }
 
     int32_t     ProxyPort() const
     { return mProxyInfo ? mProxyInfo->Port() : -1; }
@@ -177,21 +176,15 @@ private:
 
     // If a cross-origin sub-resource is being loaded, this flag will be set.
     // In that case, the prompt text will be different to warn users.
     uint32_t                          mCrossOrigin : 1;
     uint32_t                          mConnectionBased : 1;
 
     RefPtr<nsHttpHandler>           mHttpHandler;  // keep gHttpHandler alive
 
-    // A variable holding the preference settings to whether to open HTTP
-    // authentication credentials dialogs for sub-resources and cross-origin
-    // sub-resources.
-    static uint32_t                   sAuthAllowPref;
-    static bool                       sImgCrossOriginAuthAllowPref;
-    static bool                       sNonWebContentTriggeredAuthAllow;
     nsCOMPtr<nsICancelable>           mGenerateCredentialsCancelable;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpChannelAuthProvider_h__
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -464,18 +464,16 @@ nsHttpHandler::Init()
         prefBranch->AddObserver(TCP_FAST_OPEN_ENABLE, this, true);
         prefBranch->AddObserver(TCP_FAST_OPEN_FAILURE_LIMIT, this, true);
         prefBranch->AddObserver(TCP_FAST_OPEN_STALLS_LIMIT, this, true);
         prefBranch->AddObserver(TCP_FAST_OPEN_STALLS_IDLE, this, true);
         prefBranch->AddObserver(TCP_FAST_OPEN_STALLS_TIMEOUT, this, true);
         PrefsChanged(prefBranch, nullptr);
     }
 
-    nsHttpChannelAuthProvider::InitializePrefs();
-
     mMisc.AssignLiteral("rv:" MOZILLA_UAVERSION);
 
     mCompatFirefox.AssignLiteral("Firefox/" MOZILLA_UAVERSION);
 
     nsCOMPtr<nsIXULAppInfo> appInfo =
         do_GetService("@mozilla.org/xre/app-info;1");
 
     mAppName.AssignLiteral(MOZ_APP_UA_NAME);