Bug 1473631: Part 0a - Make preference callbacks typesafe. r=njn
authorKris Maglione <maglione.k@gmail.com>
Fri, 06 Jul 2018 12:24:41 -0700
changeset 426902 bf6b1a5f392d5ba60ef4d36487a3fcdb90a7d04a
parent 426901 2793704b84bb08f95a8fc4e41becede57455e012
child 426903 bf732e81b820724b8a9a099c7b260ebfa90dc977
push id34289
push usertoros@mozilla.com
push dateTue, 17 Jul 2018 21:56:05 +0000
treeherdermozilla-central@afa310dc89be [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1473631
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1473631: Part 0a - Make preference callbacks typesafe. r=njn I initially tried to avoid this, but decided it was necessary given the number of times I had to repeat the same pattern of casting a variable to void*, and then casting it back in a part of code far distant from the original type. This changes our preference callback registration functions to match the type of the callback's closure argument to the actual type of the closure pointer passed, and then casting it to the type of our generic callback function. This ensures that the callback function always gets an argument of the type it's actually expecting without adding any additional runtime memory or QueryInterface overhead for tracking it. MozReview-Commit-ID: 9tLKBe10ddP
dom/base/nsDocument.cpp
dom/encoding/FallbackEncoding.cpp
dom/indexedDB/IndexedDatabaseManager.cpp
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
dom/xul/XULDocument.cpp
dom/xul/XULDocument.h
gfx/thebes/gfxPrefs.cpp
js/xpconnect/src/XPCJSContext.cpp
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/style/nsComputedDOMStyle.cpp
modules/libpref/Preferences.h
modules/libpref/test/gtest/CallbackAndVarCacheOrder.cpp
netwerk/base/nsChannelClassifier.cpp
netwerk/dns/nsHostResolver.cpp
security/manager/ssl/CertBlocklist.cpp
security/manager/ssl/CertBlocklist.h
toolkit/components/places/nsNavHistoryResult.cpp
toolkit/components/places/nsNavHistoryResult.h
widget/windows/WinMouseScrollHandler.h
xpcom/base/nsMemoryInfoDumper.cpp
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12607,19 +12607,19 @@ struct PrefStore
     Preferences::GetCString("urlclassifier.flashAllowTable", mAllowTables);
     Preferences::GetCString("urlclassifier.flashAllowExceptTable", mAllowExceptionsTables);
     Preferences::GetCString("urlclassifier.flashTable", mDenyTables);
     Preferences::GetCString("urlclassifier.flashExceptTable", mDenyExceptionsTables);
     Preferences::GetCString("urlclassifier.flashSubDocTable", mSubDocDenyTables);
     Preferences::GetCString("urlclassifier.flashSubDocExceptTable", mSubDocDenyExceptionsTables);
   }
 
-  static void UpdateStringPrefs(const char*, void* aClosure)
+  static void UpdateStringPrefs(const char*, PrefStore* aSelf)
   {
-    static_cast<PrefStore*>(aClosure)->UpdateStringPrefs();
+    aSelf->UpdateStringPrefs();
   }
 
   bool mFlashBlockEnabled;
   bool mPluginsHttpOnly;
 
   nsCString mAllowTables;
   nsCString mAllowExceptionsTables;
   nsCString mDenyTables;
--- a/dom/encoding/FallbackEncoding.cpp
+++ b/dom/encoding/FallbackEncoding.cpp
@@ -148,18 +148,17 @@ FallbackEncoding::Observe(nsISupports *a
 
 void
 FallbackEncoding::Initialize()
 {
   MOZ_ASSERT(!FallbackEncoding::sInstance,
              "Initializing pre-existing fallback cache.");
   FallbackEncoding::sInstance = new FallbackEncoding;
   Preferences::RegisterCallback(FallbackEncoding::PrefChanged,
-                                "intl.charset.fallback.override",
-                                nullptr);
+                                "intl.charset.fallback.override");
   Preferences::AddBoolVarCache(&sGuessFallbackFromTopLevelDomain,
                                "intl.charset.fallback.tld");
   Preferences::AddBoolVarCache(&sFallbackToUTF8ForFile,
                                "intl.charset.fallback.utf8_for_file");
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->AddObserver(sInstance, "intl:requested-locales-changed", true);
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -242,22 +242,22 @@ private:
   void
   Finish();
 
   void
   UnblockOpen();
 };
 
 void
-AtomicBoolPrefChangedCallback(const char* aPrefName, void* aClosure)
+AtomicBoolPrefChangedCallback(const char* aPrefName, Atomic<bool>* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aClosure);
 
-  *static_cast<Atomic<bool>*>(aClosure) = Preferences::GetBool(aPrefName);
+  *aClosure = Preferences::GetBool(aPrefName);
 }
 
 void
 DataThresholdPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!strcmp(aPrefName, kDataThresholdPref));
   MOZ_ASSERT(!aClosure);
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -507,16 +507,26 @@ PluginModuleChromeParent::LoadModule(con
       FunctionBrokerParent::Create(std::move(brokerParentEnd));
     if (parent->mBrokerParent) {
       parent->SendInitPluginFunctionBroker(std::move(brokerChildEnd));
     }
 #endif
     return parent.forget();
 }
 
+static const char* gCallbackPrefs[] = {
+    kChildTimeoutPref,
+    kParentTimeoutPref,
+#ifdef XP_WIN
+    kHangUITimeoutPref,
+    kHangUIMinDisplayPref,
+#endif
+    nullptr,
+};
+
 void
 PluginModuleChromeParent::OnProcessLaunched(const bool aSucceeded)
 {
     if (!aSucceeded) {
         mShutdown = true;
         OnInitFailure();
         return;
     }
@@ -531,22 +541,18 @@ PluginModuleChromeParent::OnProcessLaunc
 
     // Request Windows message deferral behavior on our channel. This
     // applies to the top level and all sub plugin protocols since they
     // all share the same channel.
     GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 
     TimeoutChanged(CHILD_TIMEOUT_PREF, this);
 
-    Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this);
-    Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this);
-#ifdef XP_WIN
-    Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
-    Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
-#endif
+    Preferences::RegisterCallbacks(TimeoutChanged, gCallbackPrefs,
+                                   static_cast<PluginModuleParent*>(this));
 
     RegisterSettingsCallbacks();
 
     // If this fails, we're having IPC troubles, and we're doomed anyways.
     if (!InitCrashReporter()) {
         mShutdown = true;
         Close();
         OnInitFailure();
@@ -621,22 +627,24 @@ PluginModuleParent::~PluginModuleParent(
         NP_Shutdown(&err);
     }
 }
 
 PluginModuleContentParent::PluginModuleContentParent()
   : PluginModuleParent(false)
   , mPluginId(0)
 {
-  Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref, this);
+  Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref,
+                                static_cast<PluginModuleParent*>(this));
 }
 
 PluginModuleContentParent::~PluginModuleContentParent()
 {
-    Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
+    Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref,
+                                    static_cast<PluginModuleParent*>(this));
 }
 
 PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
                                                    uint32_t aPluginId,
                                                    int32_t aSandboxLevel)
   : PluginModuleParent(true)
   , mSubprocess(new PluginProcessParent(aFilePath))
   , mPluginId(aPluginId)
@@ -697,22 +705,20 @@ PluginModuleChromeParent::~PluginModuleC
     if (mFinishInitTask) {
         // mFinishInitTask will be deleted by the main thread message_loop
         mFinishInitTask->Cancel();
     }
 #endif
 
     UnregisterSettingsCallbacks();
 
-    Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this);
-    Preferences::UnregisterCallback(TimeoutChanged, kParentTimeoutPref, this);
+    Preferences::UnregisterCallbacks(TimeoutChanged, gCallbackPrefs,
+                                     static_cast<PluginModuleParent*>(this));
+
 #ifdef XP_WIN
-    Preferences::UnregisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
-    Preferences::UnregisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
-
     if (mHangUIParent) {
         delete mHangUIParent;
         mHangUIParent = nullptr;
     }
 #endif
 
     mozilla::BackgroundHangMonitor::UnregisterAnnotator(*this);
 }
@@ -763,43 +769,41 @@ void
 PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
 {
     int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
                       MessageChannel::kNoTimeout;
     SetReplyTimeoutMs(timeoutMs);
 }
 
 void
-PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
+PluginModuleParent::TimeoutChanged(const char* aPref, PluginModuleParent* aModule)
 {
-    PluginModuleParent* module = static_cast<PluginModuleParent*>(aModule);
-
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 #ifndef XP_WIN
     if (!strcmp(aPref, kChildTimeoutPref)) {
-      MOZ_ASSERT(module->IsChrome());
+      MOZ_ASSERT(aModule->IsChrome());
       // The timeout value used by the parent for children
       int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
-      module->SetChildTimeout(timeoutSecs);
+      aModule->SetChildTimeout(timeoutSecs);
 #else
     if (!strcmp(aPref, kChildTimeoutPref) ||
         !strcmp(aPref, kHangUIMinDisplayPref) ||
         !strcmp(aPref, kHangUITimeoutPref)) {
-      MOZ_ASSERT(module->IsChrome());
-      static_cast<PluginModuleChromeParent*>(module)->EvaluateHangUIState(true);
+      MOZ_ASSERT(aModule->IsChrome());
+      static_cast<PluginModuleChromeParent*>(aModule)->EvaluateHangUIState(true);
 #endif // XP_WIN
     } else if (!strcmp(aPref, kParentTimeoutPref)) {
       // The timeout value used by the child for its parent
-      MOZ_ASSERT(module->IsChrome());
+      MOZ_ASSERT(aModule->IsChrome());
       int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
-      Unused << static_cast<PluginModuleChromeParent*>(module)->SendSetParentHangTimeout(timeoutSecs);
+      Unused << static_cast<PluginModuleChromeParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
     } else if (!strcmp(aPref, kContentTimeoutPref)) {
-      MOZ_ASSERT(!module->IsChrome());
+      MOZ_ASSERT(!aModule->IsChrome());
       int32_t timeoutSecs = Preferences::GetInt(kContentTimeoutPref, 0);
-      module->SetChildTimeout(timeoutSecs);
+      aModule->SetChildTimeout(timeoutSecs);
     }
 }
 
 void
 PluginModuleChromeParent::CleanupFromTimeout(const bool aFromHangUI)
 {
     if (mShutdown) {
       return;
@@ -2073,20 +2077,19 @@ void
 PluginModuleChromeParent::CachedSettingChanged()
 {
     PluginSettings settings;
     GetSettings(&settings);
     Unused << SendSettingChanged(settings);
 }
 
 /* static */ void
-PluginModuleChromeParent::CachedSettingChanged(const char* aPref, void* aModule)
+PluginModuleChromeParent::CachedSettingChanged(const char* aPref, PluginModuleChromeParent* aModule)
 {
-    PluginModuleChromeParent *module = static_cast<PluginModuleChromeParent*>(aModule);
-    module->CachedSettingChanged();
+    aModule->CachedSettingChanged();
 }
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
 nsresult
 PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -164,17 +164,17 @@ protected:
 
     virtual mozilla::ipc::IPCResult
     AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
                                         const bool& shouldRegister,
                                         NPError* result) override;
 
 protected:
     void SetChildTimeout(const int32_t aChildTimeout);
-    static void TimeoutChanged(const char* aPref, void* aModule);
+    static void TimeoutChanged(const char* aPref, PluginModuleParent* aModule);
 
     virtual void UpdatePluginTimeout() {}
 
     virtual mozilla::ipc::IPCResult RecvNotifyContentModuleDestroyed() override { return IPC_OK(); }
 
     virtual mozilla::ipc::IPCResult RecvReturnClearSiteData(const NPError& aRv,
                                                             const uint64_t& aCallbackId) override;
 
@@ -514,17 +514,17 @@ private:
 
     void RegisterSettingsCallbacks();
     void UnregisterSettingsCallbacks();
 
     bool InitCrashReporter();
 
     virtual mozilla::ipc::IPCResult RecvNotifyContentModuleDestroyed() override;
 
-    static void CachedSettingChanged(const char* aPref, void* aModule);
+    static void CachedSettingChanged(const char* aPref, PluginModuleChromeParent* aModule);
 
     virtual mozilla::ipc::IPCResult
     AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
                                         const bool& shouldRegister,
                                         NPError* result) override;
 
     PluginProcessParent* mSubprocess;
     uint32_t mPluginId;
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -2922,22 +2922,21 @@ XULDocument::IsDocumentRightToLeft()
 
 void
 XULDocument::ResetDocumentDirection()
 {
     DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
 }
 
 void
-XULDocument::DirectionChanged(const char* aPrefName, void* aData)
+XULDocument::DirectionChanged(const char* aPrefName, XULDocument* aDoc)
 {
   // Reset the direction and restyle the document if necessary.
-  XULDocument* doc = (XULDocument *)aData;
-  if (doc) {
-      doc->ResetDocumentDirection();
+  if (aDoc) {
+      aDoc->ResetDocumentDirection();
   }
 }
 
 nsIDocument::DocumentTheme
 XULDocument::GetDocumentLWTheme()
 {
     if (mDocLWTheme == Doc_Theme_Uninitialized) {
         mDocLWTheme = ThreadSafeGetDocumentLWTheme();
--- a/dom/xul/XULDocument.h
+++ b/dom/xul/XULDocument.h
@@ -204,17 +204,17 @@ protected:
 
     nsresult
     ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
                                  Element* aListener,
                                  nsAtom* aAttr);
 
     already_AddRefed<nsPIWindowRoot> GetWindowRoot();
 
-    static void DirectionChanged(const char* aPrefName, void* aData);
+    static void DirectionChanged(const char* aPrefName, XULDocument* aData);
 
     // pseudo constants
     static int32_t gRefCnt;
 
     static LazyLogModule gXULLog;
 
     nsresult
     Persist(mozilla::dom::Element* aElement,
--- a/gfx/thebes/gfxPrefs.cpp
+++ b/gfx/thebes/gfxPrefs.cpp
@@ -268,19 +268,19 @@ void gfxPrefs::PrefSet(const char* aPref
 
 void gfxPrefs::PrefSet(const char* aPref, std::string aValue)
 {
   MOZ_ASSERT(IsPrefsServiceAvailable());
   Preferences::SetCString(aPref, aValue.c_str());
 }
 
 static void
-OnGfxPrefChanged(const char* aPrefname, void* aClosure)
+OnGfxPrefChanged(const char* aPrefname, gfxPrefs::Pref* aPref)
 {
-  reinterpret_cast<gfxPrefs::Pref*>(aClosure)->OnChange();
+  aPref->OnChange();
 }
 
 void gfxPrefs::WatchChanges(const char* aPrefname, Pref* aPref)
 {
   MOZ_ASSERT(IsPrefsServiceAvailable());
   nsCString name;
   name.AssignLiteral(aPrefname, strlen(aPrefname));
   Preferences::RegisterCallback(OnGfxPrefChanged, name, aPref);
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -728,19 +728,18 @@ bool xpc::ExtraWarningsForSystemJS() { r
 #endif
 
 static mozilla::Atomic<bool> sSharedMemoryEnabled(false);
 
 bool
 xpc::SharedMemoryEnabled() { return sSharedMemoryEnabled; }
 
 static void
-ReloadPrefsCallback(const char* pref, void* data)
+ReloadPrefsCallback(const char* pref, XPCJSContext* xpccx)
 {
-    XPCJSContext* xpccx = static_cast<XPCJSContext*>(data);
     JSContext* cx = xpccx->Context();
 
     bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit");
     bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion");
     bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs");
     bool useWasm = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm");
     bool useWasmIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_ionjit");
     bool useWasmBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_baselinejit");
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -145,25 +145,20 @@ nsPresContext::IsDOMPaintEventPending()
     // invalidation doesn't invalidate anything further.
     NotifyInvalidation(drpc->mRefreshDriver->LastTransactionId().Next(), nsRect(0, 0, 0, 0));
     return true;
   }
   return false;
 }
 
 void
-nsPresContext::PrefChangedCallback(const char* aPrefName, void* instance_data)
+nsPresContext::PrefChangedCallback(const char* aPrefName, nsPresContext* aPresContext)
 {
-  RefPtr<nsPresContext>  presContext =
-    static_cast<nsPresContext*>(instance_data);
-
-  NS_ASSERTION(presContext, "bad instance data");
-  if (presContext) {
-    presContext->PreferenceChanged(aPrefName);
-  }
+  NS_ASSERTION(aPresContext, "bad instance data");
+  aPresContext->PreferenceChanged(aPrefName);
 }
 
 void
 nsPresContext::ForceReflowForFontInfoUpdate()
 {
   // We can trigger reflow by pretending a font.* preference has changed;
   // this is the same mechanism as gfxPlatform::ForceGlobalReflow() uses
   // if new fonts are installed during the session, for example.
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1193,17 +1193,17 @@ protected:
   UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument, void* aData);
 
   void SetImgAnimations(nsIContent *aParent, uint16_t aMode);
   void SetSMILAnimations(nsIDocument *aDoc, uint16_t aNewMode,
                                      uint16_t aOldMode);
   void GetDocumentColorPreferences();
 
   void PreferenceChanged(const char* aPrefName);
-  static void PrefChangedCallback(const char*, void*);
+  static void PrefChangedCallback(const char*, nsPresContext*);
 
   void UpdateAfterPreferencesChanged();
   void DispatchPrefChangedRunnableIfNeeded();
 
   void GetUserPreferences();
 
   /**
    * Fetch the user's font preferences for the given aLanguage's
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -5465,19 +5465,19 @@ nsComputedDOMStyle::DoGetAnimationIterat
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DummyGetter()
 {
   MOZ_CRASH("DummyGetter is not supposed to be invoked");
 }
 
 static void
-MarkComputedStyleMapDirty(const char* aPref, void* aData)
-{
-  static_cast<ComputedStyleMap*>(aData)->MarkDirty();
+MarkComputedStyleMapDirty(const char* aPref, ComputedStyleMap* aData)
+{
+  aData->MarkDirty();
 }
 
 void
 nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent)
 {
   NS_ASSERTION(mElement == aContent, "didn't we register mElement?");
   NS_ASSERTION(mResolvedComputedStyle,
                "should have only registered an observer when "
--- a/modules/libpref/Preferences.h
+++ b/modules/libpref/Preferences.h
@@ -29,16 +29,49 @@ class nsIFile;
 // The callback function will get passed the pref name which triggered the call
 // and the void* data which was passed to the registered callback function.
 typedef void (*PrefChangedFunc)(const char* aPref, void* aData);
 
 class nsPrefBranch;
 
 namespace mozilla {
 
+// A typesafe version of PrefChangeFunc, with its data argument type deduced
+// from the type of the argument passed to RegisterCallback.
+//
+// Note: We specify this as a dependent type TypedPrefChangeFunc<T>::SelfType so
+// that it does not participate in argument type deduction. This allows us to
+// use its implicit conversion constructor, and also allows our Register and
+// Unregister methods to accept non-capturing lambdas (which will not match
+// void(*)(const char*, T*) when used in type deduction) as callback functions.
+template<typename T>
+struct TypedPrefChangeFunc
+{
+  using Type = TypedPrefChangeFunc<T>;
+  using CallbackType = void (*)(const char*, T*);
+
+  MOZ_IMPLICIT TypedPrefChangeFunc(CallbackType aCallback)
+    : mCallback(aCallback)
+  {
+  }
+
+  template<typename F>
+  MOZ_IMPLICIT TypedPrefChangeFunc(F&& aLambda)
+    : mCallback(aLambda)
+  {
+  }
+
+  operator PrefChangedFunc() const
+  {
+    return reinterpret_cast<PrefChangedFunc>(mCallback);
+  }
+
+  CallbackType mCallback;
+};
+
 class PreferenceServiceReporter;
 
 namespace dom {
 class Pref;
 class PrefValue;
 } // namespace dom
 
 namespace ipc {
@@ -302,152 +335,178 @@ public:
   // Note: All preference strings *must* be statically-allocated string
   // literals.
   static nsresult AddStrongObservers(nsIObserver* aObserver,
                                      const char** aPrefs);
   static nsresult AddWeakObservers(nsIObserver* aObserver, const char** aPrefs);
   static nsresult RemoveObservers(nsIObserver* aObserver, const char** aPrefs);
 
   // Registers/Unregisters the callback function for the aPref.
-  static nsresult RegisterCallback(PrefChangedFunc aCallback,
-                                   const nsACString& aPref,
-                                   void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return RegisterCallback(aCallback, aPref, aClosure, ExactMatch);
   }
 
-  static nsresult UnregisterCallback(PrefChangedFunc aCallback,
-                                     const nsACString& aPref,
-                                     void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult UnregisterCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return UnregisterCallback(aCallback, aPref, aClosure, ExactMatch);
   }
 
   // Like RegisterCallback, but also calls the callback immediately for
   // initialization.
-  static nsresult RegisterCallbackAndCall(PrefChangedFunc aCallback,
-                                          const nsACString& aPref,
-                                          void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterCallbackAndCall(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return RegisterCallbackAndCall(aCallback, aPref, aClosure, ExactMatch);
   }
 
   // Like RegisterCallback, but registers a callback for a prefix of multiple
   // pref names, not a single pref name.
-  static nsresult RegisterPrefixCallback(PrefChangedFunc aCallback,
-                                         const nsACString& aPref,
-                                         void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterPrefixCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return RegisterCallback(aCallback, aPref, aClosure, PrefixMatch);
   }
 
   // Like RegisterPrefixCallback, but also calls the callback immediately for
   // initialization.
-  static nsresult RegisterPrefixCallbackAndCall(PrefChangedFunc aCallback,
-                                                const nsACString& aPref,
-                                                void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterPrefixCallbackAndCall(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return RegisterCallbackAndCall(aCallback, aPref, aClosure, PrefixMatch);
   }
 
   // Unregister a callback registered with RegisterPrefixCallback or
   // RegisterPrefixCallbackAndCall.
-  static nsresult UnregisterPrefixCallback(PrefChangedFunc aCallback,
-                                           const nsACString& aPref,
-                                           void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult UnregisterPrefixCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return UnregisterCallback(aCallback, aPref, aClosure, PrefixMatch);
   }
 
   // Variants of the above which register a single callback to handle multiple
   // preferences.
   //
   // The array of preference names must be null terminated. It may be
   // dynamically allocated, but the caller is responsible for keeping it alive
   // until the callback is unregistered.
   //
   // Also note that the exact same aPrefs pointer must be passed to the
   // Unregister call as was passed to the Register call.
-  static nsresult RegisterCallbacks(PrefChangedFunc aCallback,
-                                    const char** aPrefs,
-                                    void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterCallbacks(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char** aPrefs,
+    T* aClosure = nullptr)
   {
     return RegisterCallbacks(aCallback, aPrefs, aClosure, ExactMatch);
   }
   static nsresult RegisterCallbacksAndCall(PrefChangedFunc aCallback,
                                            const char** aPrefs,
                                            void* aClosure = nullptr);
-  static nsresult UnregisterCallbacks(PrefChangedFunc aCallback,
-                                      const char** aPrefs,
-                                      void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult UnregisterCallbacks(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char** aPrefs,
+    T* aClosure = nullptr)
   {
     return UnregisterCallbacks(aCallback, aPrefs, aClosure, ExactMatch);
   }
-  static nsresult RegisterPrefixCallbacks(PrefChangedFunc aCallback,
-                                          const char** aPrefs,
-                                          void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterPrefixCallbacks(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char** aPrefs,
+    T* aClosure = nullptr)
   {
     return RegisterCallbacks(aCallback, aPrefs, aClosure, PrefixMatch);
   }
-  static nsresult UnregisterPrefixCallbacks(PrefChangedFunc aCallback,
-                                            const char** aPrefs,
-                                            void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult UnregisterPrefixCallbacks(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char** aPrefs,
+    T* aClosure = nullptr)
   {
     return UnregisterCallbacks(aCallback, aPrefs, aClosure, PrefixMatch);
   }
 
-  template<int N>
-  static nsresult RegisterCallback(PrefChangedFunc aCallback,
-                                   const char (&aPref)[N],
-                                   void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult RegisterCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return RegisterCallback(
       aCallback, nsLiteralCString(aPref), aClosure, ExactMatch);
   }
 
-  template<int N>
-  static nsresult UnregisterCallback(PrefChangedFunc aCallback,
-                                     const char (&aPref)[N],
-                                     void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult UnregisterCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return UnregisterCallback(
       aCallback, nsLiteralCString(aPref), aClosure, ExactMatch);
   }
 
-  template<int N>
-  static nsresult RegisterCallbackAndCall(PrefChangedFunc aCallback,
-                                          const char (&aPref)[N],
-                                          void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult RegisterCallbackAndCall(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return RegisterCallbackAndCall(
       aCallback, nsLiteralCString(aPref), aClosure, ExactMatch);
   }
 
-  template<int N>
-  static nsresult RegisterPrefixCallback(PrefChangedFunc aCallback,
-                                         const char (&aPref)[N],
-                                         void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult RegisterPrefixCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return RegisterCallback(
       aCallback, nsLiteralCString(aPref), aClosure, PrefixMatch);
   }
 
-  template<int N>
-  static nsresult RegisterPrefixCallbackAndCall(PrefChangedFunc aCallback,
-                                                const char (&aPref)[N],
-                                                void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult RegisterPrefixCallbackAndCall(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return RegisterCallbackAndCall(
       aCallback, nsLiteralCString(aPref), aClosure, PrefixMatch);
   }
 
-  template<int N>
-  static nsresult UnregisterPrefixCallback(PrefChangedFunc aCallback,
-                                           const char (&aPref)[N],
-                                           void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult UnregisterPrefixCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return UnregisterCallback(
       aCallback, nsLiteralCString(aPref), aClosure, PrefixMatch);
   }
 
   // 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
--- a/modules/libpref/test/gtest/CallbackAndVarCacheOrder.cpp
+++ b/modules/libpref/test/gtest/CallbackAndVarCacheOrder.cpp
@@ -16,22 +16,21 @@ struct Closure
 {
   U* mLocation;
   T mExpected;
   bool mCalled;
 };
 
 template<typename T, typename U>
 void
-VarChanged(const char* aPrefName, void* aData)
+VarChanged(const char* aPrefName, Closure<T, U>* aClosure)
 {
-  auto closure = static_cast<Closure<T, U>*>(aData);
-  ASSERT_EQ(*closure->mLocation, closure->mExpected);
-  ASSERT_FALSE(closure->mCalled);
-  closure->mCalled = true;
+  ASSERT_EQ(*aClosure->mLocation, aClosure->mExpected);
+  ASSERT_FALSE(aClosure->mCalled);
+  aClosure->mCalled = true;
 }
 
 void
 SetFunc(const nsCString& aPrefName, bool aValue)
 {
   nsresult rv = Preferences::SetBool(aPrefName.get(), aValue);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
 }
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -89,17 +89,17 @@ public:
   void SetTrackingBlackList(const nsACString& aList) { mTrackingBlacklist = aList; }
   nsCString GetTrackingBlackList() { return mTrackingBlacklist; }
 
 private:
   friend class StaticAutoPtr<CachedPrefs>;
   CachedPrefs();
   ~CachedPrefs();
 
-  static void OnPrefsChange(const char* aPrefName, void* );
+  static void OnPrefsChange(const char* aPrefName, CachedPrefs*);
 
   // Whether channels should be annotated as being on the tracking protection
   // list.
   static bool sAnnotateChannelEnabled;
   // Whether the priority of the channels annotated as being on the tracking
   // protection list should be lowered.
   static bool sLowerNetworkPriority;
   static bool sAllowListExample;
@@ -114,34 +114,32 @@ private:
 bool CachedPrefs::sAllowListExample = false;
 bool CachedPrefs::sLowerNetworkPriority = false;
 bool CachedPrefs::sAnnotateChannelEnabled = false;
 
 StaticAutoPtr<CachedPrefs> CachedPrefs::sInstance;
 
 // static
 void
-CachedPrefs::OnPrefsChange(const char* aPref, void* aClosure)
+CachedPrefs::OnPrefsChange(const char* aPref, CachedPrefs* aPrefs)
 {
-  CachedPrefs* prefs = static_cast<CachedPrefs*> (aClosure);
-
   if (!strcmp(aPref, URLCLASSIFIER_SKIP_HOSTNAMES)) {
     nsCString skipHostnames;
     Preferences::GetCString(URLCLASSIFIER_SKIP_HOSTNAMES, skipHostnames);
     ToLowerCase(skipHostnames);
-    prefs->SetSkipHostnames(skipHostnames);
+    aPrefs->SetSkipHostnames(skipHostnames);
   } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_WHITELIST)) {
     nsCString trackingWhitelist;
     Preferences::GetCString(URLCLASSIFIER_TRACKING_WHITELIST,
                             trackingWhitelist);
-    prefs->SetTrackingWhiteList(trackingWhitelist);
+    aPrefs->SetTrackingWhiteList(trackingWhitelist);
   } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_TABLE)) {
     nsCString trackingBlacklist;
     Preferences::GetCString(URLCLASSIFIER_TRACKING_TABLE, trackingBlacklist);
-    prefs->SetTrackingBlackList(trackingBlacklist);
+    aPrefs->SetTrackingBlackList(trackingBlacklist);
   }
 }
 
 void
 CachedPrefs::Init()
 {
   Preferences::AddBoolVarCache(&sAnnotateChannelEnabled,
                                "privacy.trackingprotection.annotate_channels");
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -518,23 +518,22 @@ nsHostRecord::RemoveOrRefresh()
 //----------------------------------------------------------------------------
 
 static const char kPrefGetTtl[] = "network.dns.get-ttl";
 static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost";
 static const char kPrefThreadIdleTime[] = "network.dns.resolver-thread-extra-idle-time-seconds";
 static bool sGetTtlEnabled = false;
 mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
 
-static void DnsPrefChanged(const char* aPref, void* aClosure)
+static void DnsPrefChanged(const char* aPref, nsHostResolver* aSelf)
 {
     MOZ_ASSERT(NS_IsMainThread(),
                "Should be getting pref changed notification on main thread!");
 
-    DebugOnly<nsHostResolver*> self = static_cast<nsHostResolver*>(aClosure);
-    MOZ_ASSERT(self);
+    MOZ_ASSERT(aSelf);
 
     if (!strcmp(aPref, kPrefGetTtl)) {
 #ifdef DNSQUERY_AVAILABLE
         sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
 #endif
     } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) {
         gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost);
     }
--- a/security/manager/ssl/CertBlocklist.cpp
+++ b/security/manager/ssl/CertBlocklist.cpp
@@ -623,21 +623,20 @@ CertBlocklist::IsBlocklistFresh(bool* _r
   MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
          ("CertBlocklist::IsBlocklistFresh ? %s", *_retval ? "true" : "false"));
   return NS_OK;
 }
 
 
 /* static */
 void
-CertBlocklist::PreferenceChanged(const char* aPref, void* aClosure)
+CertBlocklist::PreferenceChanged(const char* aPref, CertBlocklist* aBlocklist)
 
 {
-  auto blocklist = static_cast<CertBlocklist*>(aClosure);
-  MutexAutoLock lock(blocklist->mMutex);
+  MutexAutoLock lock(aBlocklist->mMutex);
 
   MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
          ("CertBlocklist::PreferenceChanged %s changed", aPref));
   if (strcmp(aPref, PREF_BLOCKLIST_ONECRL_CHECKED) == 0) {
     sLastBlocklistUpdate = Preferences::GetUint(PREF_BLOCKLIST_ONECRL_CHECKED,
                                                 uint32_t(0));
   } else if (strcmp(aPref, PREF_MAX_STALENESS_IN_SECONDS) == 0) {
     sMaxStaleness = Preferences::GetUint(PREF_MAX_STALENESS_IN_SECONDS,
--- a/security/manager/ssl/CertBlocklist.h
+++ b/security/manager/ssl/CertBlocklist.h
@@ -74,17 +74,17 @@ private:
   bool mModified;
   bool mBackingFileIsInitialized;
   // call EnsureBackingFileInitialized before operations that read or
   // modify CertBlocklist data
   nsresult EnsureBackingFileInitialized(mozilla::MutexAutoLock& lock);
   nsCOMPtr<nsIFile> mBackingFile;
 
 protected:
-  static void PreferenceChanged(const char* aPref, void* aClosure);
+  static void PreferenceChanged(const char* aPref, CertBlocklist* aBlocklist);
   static uint32_t sLastBlocklistUpdate;
   static uint32_t sLastKintoUpdate;
   static uint32_t sMaxStaleness;
   static bool sUseAMO;
   virtual ~CertBlocklist();
 };
 
 #endif // CertBlocklist_h
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -4706,15 +4706,15 @@ void
 nsNavHistoryResult::OnMobilePrefChanged()
 {
   ENUMERATE_MOBILE_PREF_OBSERVERS(OnMobilePrefChanged(Preferences::GetBool(MOBILE_BOOKMARKS_PREF, false)));
 }
 
 
 void
 nsNavHistoryResult::OnMobilePrefChangedCallback(const char *prefName,
-                                                void *closure)
+                                                nsNavHistoryResult *self)
 {
   MOZ_ASSERT(!strcmp(prefName, MOBILE_BOOKMARKS_PREF),
     "We only expected Mobile Bookmarks pref change.");
 
-  static_cast<nsNavHistoryResult*>(closure)->OnMobilePrefChanged();
+  self->OnMobilePrefChanged();
 }
--- a/toolkit/components/places/nsNavHistoryResult.h
+++ b/toolkit/components/places/nsNavHistoryResult.h
@@ -172,17 +172,17 @@ public:
 
   ContainerObserverList mRefreshParticipants;
   void requestRefresh(nsNavHistoryContainerResultNode* aContainer);
 
   void HandlePlacesEvent(const PlacesEventSequence& aEvents) override;
 
   void OnMobilePrefChanged();
 
-  static void OnMobilePrefChangedCallback(const char* prefName, void* closure);
+  static void OnMobilePrefChangedCallback(const char* prefName, nsNavHistoryResult* self);
 
 protected:
   virtual ~nsNavHistoryResult();
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryResult, NS_NAVHISTORYRESULT_IID)
 
 // nsNavHistoryResultNode
--- a/widget/windows/WinMouseScrollHandler.h
+++ b/widget/windows/WinMouseScrollHandler.h
@@ -375,19 +375,19 @@ private:
     {
       Init();
       return mMouseScrollTransactionTimeout;
     }
 
   private:
     void Init();
 
-    static void OnChange(const char* aPrefName, void* aClosure)
+    static void OnChange(const char* aPrefName, UserPrefs* aClosure)
     {
-      static_cast<UserPrefs*>(aClosure)->MarkDirty();
+      aClosure->MarkDirty();
     }
 
     bool mInitialized;
     bool mScrollMessageHandledAsWheelMessage;
     bool mEnableSystemSettingCache;
     bool mForceEnableSystemSettingCache;
     bool mEmulateToMakeWindowUnderCursorForeground;
     int32_t mOverriddenVerticalScrollAmount;
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -257,18 +257,17 @@ SetupFifo()
 }
 
 void
 OnFifoEnabledChange(const char* /*unused*/, void* /*unused*/)
 {
   LOG("%s changed", FifoWatcher::kPrefName);
   if (SetupFifo()) {
     Preferences::UnregisterCallback(OnFifoEnabledChange,
-                                    FifoWatcher::kPrefName,
-                                    nullptr);
+                                    FifoWatcher::kPrefName);
   }
 }
 
 } // namespace
 #endif // MOZ_SUPPORTS_FIFO }
 
 NS_IMPL_ISUPPORTS(nsMemoryInfoDumper, nsIMemoryInfoDumper)
 
@@ -299,18 +298,17 @@ nsMemoryInfoDumper::Initialize()
 
 #if defined(MOZ_SUPPORTS_FIFO)
   if (!SetupFifo()) {
     // NB: This gets loaded early enough that it's possible there is a user pref
     //     set to enable the fifo watcher that has not been loaded yet. Register
     //     to attempt to initialize if the fifo watcher becomes enabled by
     //     a user pref.
     Preferences::RegisterCallback(OnFifoEnabledChange,
-                                  FifoWatcher::kPrefName,
-                                  nullptr);
+                                  FifoWatcher::kPrefName);
   }
 #endif
 }
 
 static void
 EnsureNonEmptyIdentifier(nsAString& aIdentifier)
 {
   if (!aIdentifier.IsEmpty()) {