bug 1447642 - no more DNSService restarts r?valentin draft
authorDaniel Stenberg <daniel@haxx.se>
Wed, 21 Mar 2018 14:35:53 +0100
changeset 774730 586ee1eea3345f7c5d4498e065d0a48a891b1aff
parent 772787 de32269720d056972b85f4eec5f0a8286de6e3af
push id104484
push userbmo:daniel@haxx.se
push dateThu, 29 Mar 2018 13:27:10 +0000
reviewersvalentin
bugs1447642
milestone61.0a1
bug 1447642 - no more DNSService restarts r?valentin The DNS service was shutdown and restarted again in several scenarios, for example when one of its prefs changed and by nsIOService when going offline/online. The DNSService restart dragged the resolver, TRRService and others with it and they too were thus restarted. Most notably this hurt TRR resolving, as the restart caused short gaps in time when there was no TRRService available and nsHostResolver defaults to TRR Mode "native" if there's no TRRservice up, causing the name resolver to occasionally use the wrong or unexpected resolver even though TRR is enabled. The resolver restart also flushed the DNS cache which is now avoided. It is also a performance gain. MozReview-Commit-ID: pp4Y8bNQJk
netwerk/base/nsIOService.cpp
netwerk/base/nsIOService.h
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsDNSService2.h
netwerk/dns/nsHostResolver.cpp
netwerk/dns/nsHostResolver.h
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -34,17 +34,16 @@
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIProtocolProxyCallback.h"
 #include "nsICancelable.h"
 #include "nsINetworkLinkService.h"
 #include "nsPISocketTransportService.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "nsURLHelper.h"
-#include "nsPIDNSService.h"
 #include "nsIProtocolProxyService2.h"
 #include "MainThreadUtils.h"
 #include "nsINode.h"
 #include "nsIWidget.h"
 #include "nsThreadUtils.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/Services.h"
@@ -199,28 +198,16 @@ nsIOService::nsIOService()
     , mLastNetworkLinkChange(PR_IntervalNow())
     , mNetTearingDownStarted(0)
 {
 }
 
 nsresult
 nsIOService::Init()
 {
-    nsresult rv;
-
-    // We need to get references to the DNS service so that we can shut it
-    // down later. If we wait until the nsIOService is being shut down,
-    // GetService will fail at that point.
-
-    mDNSService = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
-    if (NS_FAILED(rv)) {
-        NS_WARNING("failed to get DNS service");
-        return rv;
-    }
-
     // XXX hack until xpidl supports error info directly (bug 13423)
     nsCOMPtr<nsIErrorService> errorService = do_GetService(NS_ERRORSERVICE_CONTRACTID);
     if (errorService) {
         errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_NETWORK, NECKO_MSGS_URL);
     }
     else
         NS_WARNING("failed to get error service");
 
@@ -1141,20 +1128,16 @@ nsIOService::SetOffline(bool offline)
             mLastOfflineStateChange = PR_IntervalNow();
             if (observerService)
                 observerService->NotifyObservers(subject,
                                                  NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
                                                  u"" NS_IOSERVICE_OFFLINE);
         }
         else if (!offline && mOffline) {
             // go online
-            if (mDNSService) {
-                DebugOnly<nsresult> rv = mDNSService->Init();
-                NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed");
-            }
             InitializeSocketTransportService();
             mOffline = false;    // indicate success only AFTER we've
                                     // brought up the services
 
             mLastOfflineStateChange = PR_IntervalNow();
             // don't care if notification fails
             // Only send the ONLINE notification if there is connectivity
             if (observerService && mConnectivity) {
@@ -1162,22 +1145,16 @@ nsIOService::SetOffline(bool offline)
                                                  NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
                                                  (u"" NS_IOSERVICE_ONLINE));
             }
         }
     }
 
     // Don't notify here, as the above notifications (if used) suffice.
     if ((mShutdown || mOfflineForProfileChange) && mOffline) {
-        // be sure to try and shutdown both (even if the first fails)...
-        // shutdown dns service first, because it has callbacks for socket transport
-        if (mDNSService) {
-            DebugOnly<nsresult> rv = mDNSService->Shutdown();
-            NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service shutdown failed");
-        }
         if (mSocketTransportService) {
             DebugOnly<nsresult> rv = mSocketTransportService->Shutdown(mShutdown);
             NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed");
         }
     }
 
     mSettingOffline = false;
 
--- a/netwerk/base/nsIOService.h
+++ b/netwerk/base/nsIOService.h
@@ -33,17 +33,16 @@
 
 static const char gScheme[][sizeof("moz-safe-about")] =
     {"chrome", "file", "http", "https", "jar", "data", "about", "moz-safe-about", "resource"};
 
 class nsINetworkLinkService;
 class nsIPrefBranch;
 class nsIProtocolProxyService2;
 class nsIProxyInfo;
-class nsPIDNSService;
 class nsPISocketTransportService;
 
 namespace mozilla {
 namespace net {
 class NeckoChild;
 class nsAsyncRedirectVerifyHelper;
 
 class nsIOService final : public nsIIOService
@@ -176,17 +175,16 @@ private:
     // SetOffline() for more details.
     bool                                 mSettingOffline;
     bool                                 mSetOfflineValue;
 
     mozilla::Atomic<bool, mozilla::Relaxed> mShutdown;
     mozilla::Atomic<bool, mozilla::Relaxed> mHttpHandlerAlreadyShutingDown;
 
     nsCOMPtr<nsPISocketTransportService> mSocketTransportService;
-    nsCOMPtr<nsPIDNSService>             mDNSService;
     nsCOMPtr<nsICaptivePortalService>    mCaptivePortalService;
     nsCOMPtr<nsINetworkLinkService>      mNetworkLinkService;
     bool                                 mNetworkLinkServiceInitialized;
 
     // Cached protocol handlers, only accessed on the main thread
     nsWeakPtr                            mWeakHandler[NS_N(gScheme)];
 
     // cached categories
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -21,17 +21,16 @@
 #include "nsDNSPrefetch.h"
 #include "nsThreadUtils.h"
 #include "nsIProtocolProxyService.h"
 #include "prsystem.h"
 #include "prnetdb.h"
 #include "prmon.h"
 #include "prio.h"
 #include "plstr.h"
-#include "nsIOService.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsNetAddr.h"
 #include "nsProxyRelease.h"
 #include "nsIObserverService.h"
 #include "nsINetworkLinkService.h"
 #include "TRRService.h"
 
 #include "mozilla/Attributes.h"
@@ -51,16 +50,17 @@ static const char kPrefDnsCacheGrace[]  
 static const char kPrefIPv4OnlyDomains[]     = "network.dns.ipv4OnlyDomains";
 static const char kPrefDisableIPv6[]         = "network.dns.disableIPv6";
 static const char kPrefDisablePrefetch[]     = "network.dns.disablePrefetch";
 static const char kPrefBlockDotOnion[]       = "network.dns.blockDotOnion";
 static const char kPrefDnsLocalDomains[]     = "network.dns.localDomains";
 static const char kPrefDnsForceResolve[]     = "network.dns.forceResolve";
 static const char kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost";
 static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
+static const char kPrefNetworkProxyType[]    = "network.proxy.type";
 
 //-----------------------------------------------------------------------------
 
 class nsDNSRecord : public nsIDNSRecord
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIDNSRECORD
@@ -490,17 +490,16 @@ private:
 };
 
 //-----------------------------------------------------------------------------
 
 nsDNSService::nsDNSService()
     : mLock("nsDNSServer.mLock")
     , mDisableIPv6(false)
     , mDisablePrefetch(false)
-    , mFirstTime(true)
     , mNotifyResolution(false)
     , mOfflineLocalhost(false)
     , mForceResolveOn(false)
     , mTrrService(nullptr)
 {
 }
 
 nsDNSService::~nsDNSService() = default;
@@ -536,134 +535,170 @@ nsDNSService::GetSingleton()
         } else {
             gDNSService = nullptr;
         }
     }
 
     return do_AddRef(gDNSService);
 }
 
+nsresult
+nsDNSService::ReadPrefs(const char *name)
+{
+    bool tmpbool;
+    uint32_t tmpint;
+    mResolverPrefsUpdated = false;
+
+    // resolver-specific prefs first
+    if(!name || !strcmp(name, kPrefDnsCacheEntries)) {
+        if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheEntries, &tmpint))) {
+            if (!name || (tmpint != mResCacheEntries)) {
+                mResCacheEntries = tmpint;
+                mResolverPrefsUpdated = true;
+            }
+        }
+
+    }
+    if(!name || !strcmp(name, kPrefDnsCacheExpiration)) {
+        if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheExpiration, &tmpint))) {
+            if (!name || (tmpint != mResCacheExpiration)) {
+                mResCacheExpiration = tmpint;
+                mResolverPrefsUpdated = true;
+            }
+        }
+
+    }
+    if(!name || !strcmp(name, kPrefDnsCacheGrace)) {
+        if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheGrace, &tmpint))) {
+            if (!name || (tmpint != mResCacheGrace)) {
+                mResCacheGrace = tmpint;
+                mResolverPrefsUpdated = true;
+            }
+        }
+    }
+
+    // DNSservice prefs
+    if (!name || !strcmp(name, kPrefDisableIPv6)) {
+        if (NS_SUCCEEDED(Preferences::GetBool(kPrefDisableIPv6, &tmpbool))) {
+            mDisableIPv6 = tmpbool;
+        }
+    }
+    if (!name || !strcmp(name, kPrefDnsOfflineLocalhost)) {
+        if (NS_SUCCEEDED(Preferences::GetBool(kPrefDnsOfflineLocalhost, &tmpbool))) {
+            mOfflineLocalhost = tmpbool;
+        }
+    }
+    if (!name || !strcmp(name, kPrefDisablePrefetch)) {
+        if (NS_SUCCEEDED(Preferences::GetBool(kPrefDisablePrefetch, &tmpbool))) {
+            mDisablePrefetch = tmpbool;
+        }
+    }
+    if (!name || !strcmp(name, kPrefBlockDotOnion)) {
+        if (NS_SUCCEEDED(Preferences::GetBool(kPrefBlockDotOnion, &tmpbool))) {
+            mBlockDotOnion = tmpbool;
+        }
+    }
+    if (!name || !strcmp(name, kPrefDnsNotifyResolution)) {
+        if (NS_SUCCEEDED(Preferences::GetBool(kPrefDnsNotifyResolution, &tmpbool))) {
+            mNotifyResolution = tmpbool;
+        }
+    }
+    if (!name || !strcmp(name, kPrefNetworkProxyType)) {
+        if (NS_SUCCEEDED(Preferences::GetUint(kPrefNetworkProxyType, &tmpint))) {
+            mProxyType = tmpint;
+        }
+    }
+    if (!name || !strcmp(name, kPrefIPv4OnlyDomains)) {
+        Preferences::GetCString(kPrefIPv4OnlyDomains, mIPv4OnlyDomains);
+    }
+    if (!name || !strcmp(name, kPrefDnsLocalDomains)) {
+        nsCString localDomains;
+        Preferences::GetCString(kPrefDnsLocalDomains, localDomains);
+        mLocalDomains.Clear();
+        if (!localDomains.IsEmpty()) {
+            nsCCharSeparatedTokenizer tokenizer(localDomains, ',',
+                                                nsCCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
+            while (tokenizer.hasMoreTokens()) {
+                mLocalDomains.PutEntry(tokenizer.nextToken());
+            }
+        }
+    }
+    if (!name || !strcmp(name, kPrefDnsForceResolve)) {
+        Preferences::GetCString(kPrefDnsForceResolve, mForceResolve);
+        mForceResolveOn = !mForceResolve.IsEmpty();
+    }
+
+    if (mProxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL) {
+        // Disable prefetching either by explicit preference or if a
+        // manual proxy is configured
+        mDisablePrefetch = true;
+    }
+    return NS_OK;
+}
+
 NS_IMETHODIMP
 nsDNSService::Init()
 {
-    if (mResolver)
-        return NS_OK;
-    NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
-    // prefs
-    uint32_t maxCacheEntries  = 400;
-    uint32_t defaultCacheLifetime = 120; // seconds
-    uint32_t defaultGracePeriod = 60; // seconds
-    bool     disableIPv6      = false;
-    bool     offlineLocalhost = true;
-    bool     disablePrefetch  = false;
-    bool     blockDotOnion    = true;
-    int      proxyType        = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
-    bool     notifyResolution = false;
-
-    nsAutoCString ipv4OnlyDomains;
-    nsAutoCString localDomains;
-    nsAutoCString forceResolve;
-
-    // read prefs
-    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
-    if (prefs) {
-        int32_t val;
-        if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
-            maxCacheEntries = (uint32_t) val;
-        if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
-            defaultCacheLifetime = val;
-        if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
-            defaultGracePeriod = val;
+    MOZ_ASSERT(!mResolver);
+    MOZ_ASSERT(NS_IsMainThread());
 
-        // ASSUMPTION: pref branch does not modify out params on failure
-        prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
-        prefs->GetCharPref(kPrefIPv4OnlyDomains, ipv4OnlyDomains);
-        prefs->GetCharPref(kPrefDnsLocalDomains, localDomains);
-        prefs->GetCharPref(kPrefDnsForceResolve, forceResolve);
-        prefs->GetBoolPref(kPrefDnsOfflineLocalhost, &offlineLocalhost);
-        prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
-        prefs->GetBoolPref(kPrefBlockDotOnion, &blockDotOnion);
-
-        // If a manual proxy is in use, disable prefetch implicitly
-        prefs->GetIntPref("network.proxy.type", &proxyType);
-        prefs->GetBoolPref(kPrefDnsNotifyResolution, &notifyResolution);
-
-        if (mFirstTime) {
-            mFirstTime = false;
-
-            // register as prefs observer
-            prefs->AddObserver(kPrefDnsCacheEntries, this, false);
-            prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
-            prefs->AddObserver(kPrefDnsCacheGrace, this, false);
-            prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
-            prefs->AddObserver(kPrefDnsLocalDomains, this, false);
-            prefs->AddObserver(kPrefDnsForceResolve, this, false);
-            prefs->AddObserver(kPrefDisableIPv6, this, false);
-            prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false);
-            prefs->AddObserver(kPrefDisablePrefetch, this, false);
-            prefs->AddObserver(kPrefBlockDotOnion, this, false);
-            prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
-
-            // Monitor these to see if there is a change in proxy configuration
-            // If a manual proxy is in use, disable prefetch implicitly
-            prefs->AddObserver("network.proxy.type", this, false);
-        }
-    }
+    ReadPrefs(nullptr);
 
     nsCOMPtr<nsIObserverService> observerService =
         mozilla::services::GetObserverService();
     if (observerService) {
         observerService->AddObserver(this, "last-pb-context-exited", false);
         observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
+        observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     }
 
-    nsDNSPrefetch::Initialize(this);
-
-    nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
-
     RefPtr<nsHostResolver> res;
-    nsresult rv = nsHostResolver::Create(maxCacheEntries,
-                                         defaultCacheLifetime,
-                                         defaultGracePeriod,
+    nsresult rv = nsHostResolver::Create(mResCacheEntries,
+                                         mResCacheExpiration,
+                                         mResCacheGrace,
                                          getter_AddRefs(res));
     if (NS_SUCCEEDED(rv)) {
         // now, set all of our member variables while holding the lock
         MutexAutoLock lock(mLock);
         mResolver = res;
-        mIDN = idn;
-        mIPv4OnlyDomains = ipv4OnlyDomains;
-        mOfflineLocalhost = offlineLocalhost;
-        mDisableIPv6 = disableIPv6;
-        mBlockDotOnion = blockDotOnion;
-        mForceResolve = forceResolve;
-        mForceResolveOn = !mForceResolve.IsEmpty();
-
-        // Disable prefetching either by explicit preference or if a manual proxy is configured
-        mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
+    }
 
-        mLocalDomains.Clear();
-        if (!localDomains.IsVoid()) {
-            nsCCharSeparatedTokenizer tokenizer(localDomains, ',',
-                                                nsCCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
+    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+    if (prefs) {
+        // register as prefs observer
+        prefs->AddObserver(kPrefDnsCacheEntries, this, false);
+        prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
+        prefs->AddObserver(kPrefDnsCacheGrace, this, false);
+        prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
+        prefs->AddObserver(kPrefDnsLocalDomains, this, false);
+        prefs->AddObserver(kPrefDnsForceResolve, this, false);
+        prefs->AddObserver(kPrefDisableIPv6, this, false);
+        prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false);
+        prefs->AddObserver(kPrefDisablePrefetch, this, false);
+        prefs->AddObserver(kPrefBlockDotOnion, this, false);
+        prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
 
-            while (tokenizer.hasMoreTokens()) {
-                mLocalDomains.PutEntry(tokenizer.nextToken());
-            }
-        }
-        mNotifyResolution = notifyResolution;
+        // Monitor these to see if there is a change in proxy configuration
+        // If a manual proxy is in use, disable prefetch implicitly
+        prefs->AddObserver("network.proxy.type", this, false);
     }
 
+    nsDNSPrefetch::Initialize(this);
+
     RegisterWeakMemoryReporter(this);
 
     mTrrService = new TRRService();
     if (NS_FAILED(mTrrService->Init())) {
         mTrrService = nullptr;
     }
 
-    return rv;
+    nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
+    mIDN = idn;
+
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDNSService::Shutdown()
 {
     UnregisterWeakMemoryReporter(this);
 
     RefPtr<nsHostResolver> res;
@@ -676,16 +711,17 @@ nsDNSService::Shutdown()
         res->Shutdown();
     }
 
     nsCOMPtr<nsIObserverService> observerService =
         mozilla::services::GetObserverService();
     if (observerService) {
         observerService->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
         observerService->RemoveObserver(this, "last-pb-context-exited");
+        observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     }
 
     return NS_OK;
 }
 
 bool
 nsDNSService::GetOffline() const
 {
@@ -1108,50 +1144,40 @@ nsDNSService::GetMyHostName(nsACString &
         return NS_OK;
     }
     return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *data)
 {
-    // We are only getting called if a preference has changed or there's a
-    // network link event.
-    NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 ||
-                 strcmp(topic, "last-pb-context-exited") == 0 ||
-                 strcmp(topic, NS_NETWORK_LINK_TOPIC) == 0,
-                 "unexpected observe call");
-
     bool flushCache = false;
     if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
         nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
         if (mResolver && !strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
             flushCache = true;
         }
     } else if (!strcmp(topic, "last-pb-context-exited")) {
         flushCache = true;
+    } else if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+        ReadPrefs(NS_ConvertUTF16toUTF8(data).get());
+        NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
+        if (mResolverPrefsUpdated && mResolver) {
+            mResolver->SetCacheLimits(mResCacheEntries, mResCacheExpiration,
+                                      mResCacheGrace);
+        }
+    } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+        Shutdown();
     }
+
     if (flushCache) {
         mResolver->FlushCache();
         return NS_OK;
     }
 
-    //
-    // Shutdown and this function are both only called on the UI thread, so we don't
-    // have to worry about mResolver being cleared out from under us.
-    //
-    // NOTE Shutting down and reinitializing the service like this is obviously
-    // suboptimal if Observe gets called several times in a row, but we don't
-    // expect that to be the case.
-    //
-
-    if (mResolver) {
-        Shutdown();
-    }
-    Init();
     return NS_OK;
 }
 
 uint16_t
 nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags)
 {
     if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6))
         return PR_AF_INET;
@@ -1239,9 +1265,8 @@ nsDNSService::CollectReports(nsIHandleRe
 {
     MOZ_COLLECT_REPORT(
         "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
         SizeOfIncludingThis(DNSServiceMallocSizeOf),
         "Memory used for the DNS service.");
 
     return NS_OK;
 }
-
--- a/netwerk/dns/nsDNSService2.h
+++ b/netwerk/dns/nsDNSService2.h
@@ -46,16 +46,17 @@ protected:
 
     nsresult DeprecatedSyncResolve(const nsACString &aHostname,
                                    uint32_t flags,
                                    const mozilla::OriginAttributes &aOriginAttributes,
                                    nsIDNSRecord **result);
 private:
     ~nsDNSService();
 
+    nsresult ReadPrefs(const char *name);
     static already_AddRefed<nsDNSService> GetSingleton();
 
     uint16_t GetAFForLookup(const nsACString &host, uint32_t flags);
 
     nsresult PreprocessHostname(bool              aLocalDomain,
                                 const nsACString &aInput,
                                 nsIIDNService    *aIDN,
                                 nsACString       &aACE);
@@ -74,17 +75,22 @@ private:
     // mIPv4OnlyDomains is a comma-separated list of domains for which only
     // IPv4 DNS lookups are performed. This allows the user to disable IPv6 on
     // a per-domain basis and work around broken DNS servers. See bug 68796.
     nsCString                                 mIPv4OnlyDomains;
     nsCString                                 mForceResolve;
     bool                                      mDisableIPv6;
     bool                                      mDisablePrefetch;
     bool                                      mBlockDotOnion;
-    bool                                      mFirstTime;
     bool                                      mNotifyResolution;
     bool                                      mOfflineLocalhost;
     bool                                      mForceResolveOn;
+    uint32_t                                  mProxyType;
     nsTHashtable<nsCStringHashKey>            mLocalDomains;
     RefPtr<mozilla::net::TRRService>          mTrrService;
+
+    uint32_t                                  mResCacheEntries;
+    uint32_t                                  mResCacheExpiration;
+    uint32_t                                  mResCacheGrace;
+    bool                                      mResolverPrefsUpdated;
 };
 
 #endif //nsDNSService2_h__
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -1860,16 +1860,27 @@ nsHostResolver::ThreadFunc(void *arg)
         }
     } while(true);
 
     resolver->mThreadCount--;
     resolver = nullptr;
     LOG(("DNS lookup thread - queue empty, thread finished.\n"));
 }
 
+void
+nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries,
+                               uint32_t aDefaultCacheEntryLifetime,
+                               uint32_t aDefaultGracePeriod)
+{
+    MutexAutoLock lock(mLock);
+    mMaxCacheEntries = aMaxCacheEntries;
+    mDefaultCacheLifetime = aDefaultCacheEntryLifetime;
+    mDefaultGracePeriod = aDefaultGracePeriod;
+}
+
 nsresult
 nsHostResolver::Create(uint32_t maxCacheEntries,
                        uint32_t defaultCacheEntryLifetime,
                        uint32_t defaultGracePeriod,
                        nsHostResolver **result)
 {
     auto *res = new nsHostResolver(maxCacheEntries, defaultCacheEntryLifetime,
                                    defaultGracePeriod);
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -305,16 +305,23 @@ public:
      * creates an addref'd instance of a nsHostResolver object.
      */
     static nsresult Create(uint32_t maxCacheEntries, // zero disables cache
                            uint32_t defaultCacheEntryLifetime, // seconds
                            uint32_t defaultGracePeriod, // seconds
                            nsHostResolver **resolver);
 
     /**
+     * Set (new) cache limits.
+     */
+    void SetCacheLimits(uint32_t maxCacheEntries, // zero disables cache
+                        uint32_t defaultCacheEntryLifetime, // seconds
+                        uint32_t defaultGracePeriod); // seconds
+
+    /**
      * puts the resolver in the shutdown state, which will cause any pending
      * callbacks to be detached.  any future calls to ResolveHost will fail.
      */
     void Shutdown();
 
     /**
      * resolve the given hostname and originAttributes asynchronously.  the caller
      * can synthesize a synchronous host lookup using a lock and a cvar.  as noted