Bug 1481251 - Optimize non-A/AAAA type DNS records. r=bagder
☠☠ backed out by d58b290f0bd7 ☠ ☠
authorDragana Damjanovic <dd.mozilla@gmail.com>
Wed, 26 Sep 2018 20:10:30 +0000
changeset 496850 72d92155fb642884e7295ca9be6923f8f75d30b3
parent 496828 fa1831cceeaee74c580a73f85f96b624b407cc72
child 496851 d58b290f0bd7177bb8c23c28504890025a1515ea
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbagder
bugs1481251
milestone64.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 1481251 - Optimize non-A/AAAA type DNS records. r=bagder Split nsHostRecord into AddrHostRecord and TypeHostRecord for standard address dns queries and queries by-type. Differential Revision: https://phabricator.services.mozilla.com/D6130
netwerk/dns/TRR.cpp
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsHostResolver.cpp
netwerk/dns/nsHostResolver.h
toolkit/components/telemetry/Histograms.json
--- a/netwerk/dns/TRR.cpp
+++ b/netwerk/dns/TRR.cpp
@@ -427,16 +427,17 @@ TRR::ReceivePush(nsIHttpChannel *pushed,
       (PR_StringToNetAddr(mHost.get(), &tempAddr) == PR_SUCCESS)) { // literal
     LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get()));
     return NS_ERROR_UNEXPECTED;
   }
 
   RefPtr<nsHostRecord> hostRecord;
   nsresult rv;
   rv = mHostResolver->GetHostRecord(mHost,
+                                    pushedRec->type,
                                     pushedRec->flags, pushedRec->af,
                                     pushedRec->pb,
                                     pushedRec->originSuffix,
                                     getter_AddRefs(hostRecord));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -62,30 +62,32 @@ static const char kPrefNetworkProxyType[
 
 class nsDNSRecord : public nsIDNSRecord
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIDNSRECORD
 
     explicit nsDNSRecord(nsHostRecord *hostRecord)
-        : mHostRecord(hostRecord)
-        , mIter(nullptr)
+        : mIter(nullptr)
         , mIterGenCnt(-1)
-        , mDone(false) {}
+        , mDone(false)
+    {
+        mHostRecord = do_QueryInterface(hostRecord);
+    }
 
 private:
     virtual ~nsDNSRecord() = default;
 
-    RefPtr<nsHostRecord>  mHostRecord;
-    NetAddrElement         *mIter;
-    int                     mIterGenCnt; // the generation count of
-                                         // mHostRecord->addr_info when we
-                                         // start iterating
-    bool                    mDone;
+    nsCOMPtr<AddrHostRecord>  mHostRecord;
+    NetAddrElement            *mIter;
+    int                        mIterGenCnt; // the generation count of
+                                            // mHostRecord->addr_info when we
+                                            // start iterating
+    bool                       mDone;
 };
 
 NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
 
 NS_IMETHODIMP
 nsDNSRecord::GetCanonicalName(nsACString &result)
 {
     // this method should only be called if we have a CNAME
@@ -309,44 +311,40 @@ nsDNSRecord::ReportUnusable(uint16_t aPo
 
 class nsDNSByTypeRecord : public nsIDNSByTypeRecord
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIDNSBYTYPERECORD
 
     explicit nsDNSByTypeRecord(nsHostRecord *hostRecord)
-      : mHostRecord(hostRecord)
-    {}
+    {
+        mHostRecord = do_QueryInterface(hostRecord);
+    }
 
 private:
     virtual ~nsDNSByTypeRecord() = default;
-    RefPtr<nsHostRecord>  mHostRecord;
+    nsCOMPtr<TypeHostRecord>  mHostRecord;
 };
 
 NS_IMPL_ISUPPORTS(nsDNSByTypeRecord, nsIDNSByTypeRecord)
 
 NS_IMETHODIMP
 nsDNSByTypeRecord::GetRecords(nsTArray<nsCString> &aRecords)
 {
    // deep copy
-   MutexAutoLock lock(mHostRecord->mRequestByTypeResultLock);
-   aRecords = mHostRecord->mRequestByTypeResult;
+   mHostRecord->GetRecords(aRecords);
    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDNSByTypeRecord::GetRecordsAsOneString(nsACString &aRecords)
 {
   // deep copy
-  MutexAutoLock lock(mHostRecord->mRequestByTypeResultLock);
-
-  for (uint32_t i = 0; i < mHostRecord->mRequestByTypeResult.Length(); i++) {
-    aRecords.Append(mHostRecord->mRequestByTypeResult[i]);
-  }
+  mHostRecord->GetRecordsAsOneString(aRecords);
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
 class nsDNSAsyncRequest final : public nsResolveHostCallback
                               , public nsICancelable
 {
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -151,16 +151,19 @@ IsLowPriority(uint16_t flags)
 
 //----------------------------------------------------------------------------
 // this macro filters out any flags that are not used when constructing the
 // host key.  the significant flags are those that would affect the resulting
 // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
 #define RES_KEY_FLAGS(_f) ((_f) & (nsHostResolver::RES_CANON_NAME |     \
                                    nsHostResolver::RES_DISABLE_TRR))
 
+#define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
+#define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
+
 nsHostKey::nsHostKey(const nsACString& aHost, uint16_t aType, uint16_t aFlags,
                      uint16_t aAf, bool aPb, const nsACString& aOriginsuffix)
   : host(aHost)
   , type(aType)
   , flags(aFlags)
   , af(aAf)
   , pb(aPb)
   , originSuffix(aOriginsuffix)
@@ -194,67 +197,48 @@ nsHostKey::SizeOfExcludingThis(mozilla::
     size_t n = 0;
     n += host.SizeOfExcludingThisIfUnshared(mallocSizeOf);
     n += originSuffix.SizeOfExcludingThisIfUnshared(mallocSizeOf);
     return n;
 }
 
 nsHostRecord::nsHostRecord(const nsHostKey& key)
     : nsHostKey(key)
-    , addr_info_lock("nsHostRecord.addr_info_lock")
-    , addr_info_gencnt(0)
-    , addr_info(nullptr)
-    , addr(nullptr)
-    , negative(false)
     , mResolverMode(MODE_NATIVEONLY)
-    , mRequestByTypeResultLock("nsHostRecord.mRequestByTypeResultLock")
-    , mFirstTRRresult(NS_OK)
     , mResolving(0)
-    , mTRRSuccess(0)
-    , mNativeSuccess(0)
-    , mNative(false)
-    , mTRRUsed(false)
-    , mNativeUsed(false)
-    , onQueue(false)
-    , usingAnyThread(false)
+    , negative(false)
     , mDoomed(false)
-    , mDidCallbacks(false)
-    , mGetTtl(false)
-    , mResolveAgain(false)
-    , mTrrAUsed(INIT)
-    , mTrrAAAAUsed(INIT)
-    , mTrrLock("nsHostRecord.mTrrLock")
-    , mBlacklistedCount(0)
 {
 }
 
-void
-nsHostRecord::Cancel()
+nsHostRecord::~nsHostRecord()
 {
-    MutexAutoLock trrlock(mTrrLock);
-    if (mTrrA) {
-        mTrrA->Cancel();
-        mTrrA = nullptr;
-    }
-    if (mTrrAAAA) {
-        mTrrAAAA->Cancel();
-        mTrrAAAA = nullptr;
-    }
-    if (mTrrTxt) {
-        mTrrTxt->Cancel();
-        mTrrTxt = nullptr;
-    }
+    mCallbacks.clear();
 }
 
 void
 nsHostRecord::Invalidate()
 {
     mDoomed = true;
 }
 
+nsHostRecord::ExpirationStatus
+nsHostRecord::CheckExpiration(const mozilla::TimeStamp& now) const
+{
+    if (!mGraceStart.IsNull() && now >= mGraceStart
+            && !mValidEnd.IsNull() && now < mValidEnd) {
+        return nsHostRecord::EXP_GRACE;
+    }
+    if (!mValidEnd.IsNull() && now < mValidEnd) {
+        return nsHostRecord::EXP_VALID;
+    }
+
+    return nsHostRecord::EXP_EXPIRED;
+}
+
 void
 nsHostRecord::SetExpiration(const mozilla::TimeStamp& now, unsigned int valid, unsigned int grace)
 {
     mValidStart = now;
     mGraceStart = now + TimeDuration::FromSeconds(valid);
     mValidEnd = now + TimeDuration::FromSeconds(valid + grace);
 }
 
@@ -264,18 +248,204 @@ nsHostRecord::CopyExpirationTimesAndFlag
     // This is used to copy information from a cache entry to a record. All
     // information necessary for HasUsableRecord needs to be copied.
     mValidStart = aFromHostRecord->mValidStart;
     mValidEnd = aFromHostRecord->mValidEnd;
     mGraceStart = aFromHostRecord->mGraceStart;
     mDoomed = aFromHostRecord->mDoomed;
 }
 
+bool
+nsHostRecord::HasUsableResult(const mozilla::TimeStamp& now, uint16_t queryFlags) const
+{
+    if (mDoomed) {
+        return false;
+    }
+
+    // don't use cached negative results for high priority queries.
+    if (negative && IsHighPriority(queryFlags)) {
+        return false;
+    }
+
+    if (CheckExpiration(now) == EXP_EXPIRED) {
+        return false;
+    }
+
+    if (negative) {
+        return true;
+    }
+
+    return HasUsableResultInternal();
+}
+
+static size_t
+SizeOfResolveHostCallbackListExcludingHead(const mozilla::LinkedList<RefPtr<nsResolveHostCallback>>& aCallbacks,
+                                           MallocSizeOf mallocSizeOf)
+{
+    size_t n = aCallbacks.sizeOfExcludingThis(mallocSizeOf);
+
+    for (const nsResolveHostCallback* t = aCallbacks.getFirst(); t; t = t->getNext()) {
+      n += t->SizeOfIncludingThis(mallocSizeOf);
+    }
+
+    return n;
+}
+
+NS_IMPL_ISUPPORTS(AddrHostRecord, nsISupports, AddrHostRecord, nsHostRecord)
+
+AddrHostRecord::AddrHostRecord(const nsHostKey& key)
+    : nsHostRecord(key)
+    , addr_info_lock("AddrHostRecord.addr_info_lock")
+    , addr_info_gencnt(0)
+    , addr_info(nullptr)
+    , addr(nullptr)
+    , mFirstTRRresult(NS_OK)
+    , mTRRSuccess(0)
+    , mNativeSuccess(0)
+    , mNative(false)
+    , mTRRUsed(false)
+    , mNativeUsed(false)
+    , onQueue(false)
+    , usingAnyThread(false)
+    , mDidCallbacks(false)
+    , mGetTtl(false)
+    , mResolveAgain(false)
+    , mTrrAUsed(INIT)
+    , mTrrAAAAUsed(INIT)
+    , mTrrLock("AddrHostRecord.mTrrLock")
+    , mBlacklistedCount(0)
+{
+}
+
+AddrHostRecord::~AddrHostRecord()
+{
+
+    Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mBlacklistedCount);
+    delete addr_info;
+}
+
+bool
+AddrHostRecord::Blacklisted(NetAddr *aQuery)
+{
+    // must call locked
+    LOG(("Checking blacklist for host [%s], host record [%p].\n",
+         host.get(), this));
+
+    // skip the string conversion for the common case of no blacklist
+    if (!mBlacklistedItems.Length()) {
+        return false;
+    }
+
+    char buf[kIPv6CStrBufSize];
+    if (!NetAddrToString(aQuery, buf, sizeof(buf))) {
+        return false;
+    }
+    nsDependentCString strQuery(buf);
+
+    for (uint32_t i = 0; i < mBlacklistedItems.Length(); i++) {
+        if (mBlacklistedItems.ElementAt(i).Equals(strQuery)) {
+            LOG(("Address [%s] is blacklisted for host [%s].\n", buf, host.get()));
+            return true;
+        }
+    }
+
+    return false;
+}
+
 void
-nsHostRecord::ResolveComplete()
+AddrHostRecord::ReportUnusable(NetAddr *aAddress)
+{
+    // must call locked
+    LOG(("Adding address to blacklist for host [%s], host record [%p]."
+         "used trr=%d\n", host.get(), this, mTRRSuccess));
+
+    ++mBlacklistedCount;
+
+    char buf[kIPv6CStrBufSize];
+    if (NetAddrToString(aAddress, buf, sizeof(buf))) {
+        LOG(("Successfully adding address [%s] to blacklist for host "
+             "[%s].\n", buf, host.get()));
+        mBlacklistedItems.AppendElement(nsCString(buf));
+    }
+}
+
+void
+AddrHostRecord::ResetBlacklist()
+{
+    // must call locked
+    LOG(("Resetting blacklist for host [%s], host record [%p].\n",
+         host.get(), this));
+    mBlacklistedItems.Clear();
+}
+
+size_t
+AddrHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
+{
+    size_t n = mallocSizeOf(this);
+
+    n += nsHostKey::SizeOfExcludingThis(mallocSizeOf);
+    n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf);
+
+    n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0;
+    n += mallocSizeOf(addr.get());
+
+    n += mBlacklistedItems.ShallowSizeOfExcludingThis(mallocSizeOf);
+    for (size_t i = 0; i < mBlacklistedItems.Length(); i++) {
+        n += mBlacklistedItems[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
+    }
+    return n;
+}
+
+bool
+AddrHostRecord::HasUsableResultInternal() const
+{
+    return addr_info || addr;
+}
+
+void
+AddrHostRecord::Cancel()
+{
+    MutexAutoLock trrlock(mTrrLock);
+    if (mTrrA) {
+        mTrrA->Cancel();
+        mTrrA = nullptr;
+    }
+    if (mTrrAAAA) {
+        mTrrAAAA->Cancel();
+        mTrrAAAA = nullptr;
+    }
+}
+
+// Returns true if the entry can be removed, or false if it should be left.
+// Sets mResolveAgain true for entries being resolved right now.
+bool
+AddrHostRecord::RemoveOrRefresh()
+{
+    // no need to flush TRRed names, they're not resolved "locally"
+    MutexAutoLock lock(addr_info_lock);
+    if (addr_info && addr_info->IsTRR()) {
+        return false;
+    }
+    if (mNative) {
+        if (!onQueue) {
+            // The request has been passed to the OS resolver. The resultant DNS
+            // record should be considered stale and not trusted; set a flag to
+            // ensure it is called again.
+            mResolveAgain = true;
+        }
+        // if Onqueue is true, the host entry is already added to the cache
+        // but is still pending to get resolved: just leave it in hash.
+        return false;
+    }
+    // Already resolved; not in a pending state; remove from cache
+    return true;
+}
+
+void
+AddrHostRecord::ResolveComplete()
 {
     if (mNativeUsed) {
         if (mNativeSuccess) {
             uint32_t millis = static_cast<uint32_t>(mNativeDuration.ToMilliseconds());
             Telemetry::Accumulate(Telemetry::DNS_NATIVE_LOOKUP_TIME, millis);
         }
         AccumulateCategorical(mNativeSuccess ?
                               Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::osOK :
@@ -367,180 +537,83 @@ nsHostRecord::ResolveComplete()
         break;
     }
 
     if (mTRRUsed && !mTRRSuccess && mNativeSuccess && gTRRService) {
         gTRRService->TRRBlacklist(nsCString(host), pb, true);
     }
 }
 
-nsHostRecord::~nsHostRecord()
+AddrHostRecord::DnsPriority
+AddrHostRecord::GetPriority(uint16_t aFlags)
 {
-    mCallbacks.clear();
-
-    Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mBlacklistedCount);
-    delete addr_info;
-}
-
-bool
-nsHostRecord::Blacklisted(NetAddr *aQuery)
-{
-    // must call locked
-    LOG(("Checking blacklist for host [%s], host record [%p].\n",
-         host.get(), this));
-
-    // skip the string conversion for the common case of no blacklist
-    if (!mBlacklistedItems.Length()) {
-        return false;
+    if (IsHighPriority(aFlags)){
+        return AddrHostRecord::DNS_PRIORITY_HIGH;
+    }
+    if (IsMediumPriority(aFlags)) {
+        return AddrHostRecord::DNS_PRIORITY_MEDIUM;
     }
 
-    char buf[kIPv6CStrBufSize];
-    if (!NetAddrToString(aQuery, buf, sizeof(buf))) {
-        return false;
-    }
-    nsDependentCString strQuery(buf);
-
-    for (uint32_t i = 0; i < mBlacklistedItems.Length(); i++) {
-        if (mBlacklistedItems.ElementAt(i).Equals(strQuery)) {
-            LOG(("Address [%s] is blacklisted for host [%s].\n", buf, host.get()));
-            return true;
-        }
-    }
-
-    return false;
+    return AddrHostRecord::DNS_PRIORITY_LOW;
 }
 
-void
-nsHostRecord::ReportUnusable(NetAddr *aAddress)
-{
-    // must call locked
-    LOG(("Adding address to blacklist for host [%s], host record [%p]."
-         "used trr=%d\n", host.get(), this, mTRRSuccess));
-
-    ++mBlacklistedCount;
+NS_IMPL_ISUPPORTS(TypeHostRecord, nsISupports, TypeHostRecord, nsHostRecord)
 
-    if (negative)
-        mDoomed = true;
+TypeHostRecord::TypeHostRecord(const nsHostKey& key)
+    : nsHostRecord(key)
+    , mTrrLock("TypeHostRecord.mTrrLock")
+    , mResultsLock("TypeHostRecord.mResultsLock")
+{
+}
 
-    char buf[kIPv6CStrBufSize];
-    if (NetAddrToString(aAddress, buf, sizeof(buf))) {
-        LOG(("Successfully adding address [%s] to blacklist for host "
-             "[%s].\n", buf, host.get()));
-        mBlacklistedItems.AppendElement(nsCString(buf));
-    }
+TypeHostRecord::~TypeHostRecord() = default;
+
+bool
+TypeHostRecord::HasUsableResultInternal() const
+{
+    return !mResults.IsEmpty();
 }
 
 void
-nsHostRecord::ResetBlacklist()
+TypeHostRecord::GetRecords(nsTArray<nsCString> &aRecords)
 {
-    // must call locked
-    LOG(("Resetting blacklist for host [%s], host record [%p].\n",
-         host.get(), this));
-    mBlacklistedItems.Clear();
-}
-
-nsHostRecord::ExpirationStatus
-nsHostRecord::CheckExpiration(const mozilla::TimeStamp& now) const {
-    if (!mGraceStart.IsNull() && now >= mGraceStart
-            && !mValidEnd.IsNull() && now < mValidEnd) {
-        return nsHostRecord::EXP_GRACE;
-    }
-    if (!mValidEnd.IsNull() && now < mValidEnd) {
-        return nsHostRecord::EXP_VALID;
-    }
-
-    return nsHostRecord::EXP_EXPIRED;
+    // deep copy
+    MutexAutoLock lock(mResultsLock);
+    aRecords = mResults;
 }
 
-
-bool
-nsHostRecord::HasUsableResult(const mozilla::TimeStamp& now, uint16_t queryFlags) const
+void
+TypeHostRecord::GetRecordsAsOneString(nsACString &aRecords)
 {
-    if (mDoomed) {
-        return false;
-    }
-
-    // don't use cached negative results for high priority queries.
-    if (negative && IsHighPriority(queryFlags)) {
-        return false;
-    }
+  // deep copy
+  MutexAutoLock lock(mResultsLock);
 
-    if (CheckExpiration(now) == EXP_EXPIRED) {
-        return false;
-    }
-
-    return addr_info || addr || negative;
-}
-
-static size_t
-SizeOfResolveHostCallbackListExcludingHead(const mozilla::LinkedList<RefPtr<nsResolveHostCallback>>& aCallbacks,
-                                           MallocSizeOf mallocSizeOf)
-{
-    size_t n = aCallbacks.sizeOfExcludingThis(mallocSizeOf);
-
-    for (const nsResolveHostCallback* t = aCallbacks.getFirst(); t; t = t->getNext()) {
-      n += t->SizeOfIncludingThis(mallocSizeOf);
-    }
-
-    return n;
+  for (uint32_t i = 0; i < mResults.Length(); i++) {
+    aRecords.Append(mResults[i]);
+  }
 }
 
 size_t
-nsHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
+TypeHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
 {
     size_t n = mallocSizeOf(this);
 
     n += nsHostKey::SizeOfExcludingThis(mallocSizeOf);
     n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf);
-    n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0;
-    n += mallocSizeOf(addr.get());
 
-    n += mBlacklistedItems.ShallowSizeOfExcludingThis(mallocSizeOf);
-    for (size_t i = 0; i < mBlacklistedItems.Length(); i++) {
-        n += mBlacklistedItems[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
-    }
     return n;
 }
 
-nsHostRecord::DnsPriority
-nsHostRecord::GetPriority(uint16_t aFlags)
-{
-    if (IsHighPriority(aFlags)){
-        return nsHostRecord::DNS_PRIORITY_HIGH;
-    }
-    if (IsMediumPriority(aFlags)) {
-        return nsHostRecord::DNS_PRIORITY_MEDIUM;
-    }
-
-    return nsHostRecord::DNS_PRIORITY_LOW;
-}
-
-// Returns true if the entry can be removed, or false if it should be left.
-// Sets mResolveAgain true for entries being resolved right now.
-bool
-nsHostRecord::RemoveOrRefresh()
+void
+TypeHostRecord::Cancel()
 {
-    // no need to flush TRRed names, they're not resolved "locally"
-    MutexAutoLock lock(addr_info_lock);
-    if (addr_info && addr_info->IsTRR()) {
-        return false;
+    if (mTrr) {
+        mTrr->Cancel();
+        mTrr = nullptr;
     }
-    if (mNative) {
-        if (!onQueue) {
-            // The request has been passed to the OS resolver. The resultant DNS
-            // record should be considered stale and not trusted; set a flag to
-            // ensure it is called again.
-            mResolveAgain = true;
-        }
-        // if Onqueue is true, the host entry is already added to the cache
-        // but is still pending to get resolved: just leave it in hash.
-        return false;
-    }
-    // Already resolved; not in a pending state; remove from cache
-    return true;
 }
 
 //----------------------------------------------------------------------------
 
 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;
@@ -653,17 +726,17 @@ nsHostResolver::Init()
 
 void
 nsHostResolver::ClearPendingQueue(LinkedList<RefPtr<nsHostRecord>>& aPendingQ)
 {
     // loop through pending queue, erroring out pending lookups.
     if (!aPendingQ.isEmpty()) {
         for (RefPtr<nsHostRecord> rec : aPendingQ) {
             rec->Cancel();
-            if (rec->type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
+            if (rec->IsAddrRecord()) {
                 CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb);
             } else {
                 CompleteLookupByType(rec, NS_ERROR_ABORT, nullptr, 0, rec->pb);
             }
         }
     }
 }
 
@@ -692,21 +765,27 @@ nsHostResolver::FlushCache()
         }
         mEvictionQ.clear();
     }
 
     // Refresh the cache entries that are resolving RIGHT now, remove the rest.
     for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
         nsHostRecord* record = iter.UserData();
         // Try to remove the record, or mark it for refresh.
-        if (record->RemoveOrRefresh()) {
-            if (record->isInList()) {
-                record->remove();
+        // By-type records are from TRR. We do not need to flush those entry
+        // when the network has change, because they are not local.
+        if (record->IsAddrRecord()) {
+            nsCOMPtr<AddrHostRecord> addrRec = do_QueryInterface(record);
+            MOZ_ASSERT(addrRec);
+            if (addrRec->RemoveOrRefresh()) {
+                if (record->isInList()) {
+                    record->remove();
+                }
+                iter.Remove();
             }
-            iter.Remove();
         }
     }
 }
 
 void
 nsHostResolver::Shutdown()
 {
     LOG(("Shutting down host resolver.\n"));
@@ -779,219 +858,259 @@ nsHostResolver::Shutdown()
         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                              "Failed to shutdown GetAddrInfo");
     }
 
     mResolverThreads->Shutdown();
 }
 
 nsresult
-nsHostResolver::GetHostRecord(const nsACString &host,
+nsHostResolver::GetHostRecord(const nsACString &host, uint16_t type,
                               uint16_t flags, uint16_t af, bool pb,
                               const nsCString &originSuffix,
                               nsHostRecord **result)
 {
     MutexAutoLock lock(mLock);
-    nsHostKey key(host, nsIDNSService::RESOLVE_TYPE_DEFAULT, flags, af, pb,
-                  originSuffix);
+    nsHostKey key(host, type, flags, af, pb, originSuffix);
 
     RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
     if (!entry) {
-        entry = new nsHostRecord(key);
+        if (IS_ADDR_TYPE(type)) {
+            entry = new AddrHostRecord(key);
+        } else {
+            entry = new TypeHostRecord(key);
+        }
     }
 
     RefPtr<nsHostRecord> rec = entry;
-    if (rec->addr) {
-        return NS_ERROR_FAILURE;
+    if (rec->IsAddrRecord()) {
+        nsCOMPtr<AddrHostRecord> addrRec = do_QueryInterface(rec);
+        if (addrRec->addr) {
+            return NS_ERROR_FAILURE;
+        }
     }
+
     if (rec->mResolving) {
         return NS_ERROR_FAILURE;
     }
     *result = rec.forget().take();
     return NS_OK;
 }
 
 nsresult
-nsHostResolver::ResolveHost(const nsACString &aHost,
+nsHostResolver::ResolveHost(const nsACString       &aHost,
                             uint16_t                type,
                             const OriginAttributes &aOriginAttributes,
                             uint16_t                flags,
                             uint16_t                af,
                             nsResolveHostCallback  *aCallback)
 {
     nsAutoCString host(aHost);
     NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED);
 
-    LOG(("Resolving host [%s]%s%s type %d.\n", host.get(),
+    LOG(("Resolving host [%s]%s%s type %d. [this=%p]\n", host.get(),
          flags & RES_BYPASS_CACHE ? " - bypassing cache" : "",
          flags & RES_REFRESH_CACHE ? " - refresh cache" : "",
-         type));
+         type, this));
 
     // ensure that we are working with a valid hostname before proceeding.  see
     // bug 304904 for details.
     if (!net_IsValidHostName(host))
         return NS_ERROR_UNKNOWN_HOST;
 
+    // By-Type requests use only TRR. If TRR is disabled we can return
+    // immediately.
+    if (IS_OTHER_TYPE(type) && TRR_DISABLED(Mode())) {
+        return NS_ERROR_UNKNOWN_HOST;
+    }
+
+    // Used to try to parse to an IP address literal.
+    PRNetAddr tempAddr;
+    // Unfortunately, PR_StringToNetAddr does not properly initialize
+    // the output buffer in the case of IPv6 input. See bug 223145.
+    memset(&tempAddr, 0, sizeof(PRNetAddr));
+
+    if (IS_OTHER_TYPE(type) &&
+        (PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS)) {
+        // For by-type queries the host cannot be IP literal.
+        return NS_ERROR_UNKNOWN_HOST;
+    }
+    memset(&tempAddr, 0, sizeof(PRNetAddr));
+
     RefPtr<nsResolveHostCallback> callback(aCallback);
     // if result is set inside the lock, then we need to issue the
     // callback before returning.
     RefPtr<nsHostRecord> result;
     nsresult status = NS_OK, rv = NS_OK;
     {
         MutexAutoLock lock(mLock);
 
         if (mShutdown) {
             rv = NS_ERROR_NOT_INITIALIZED;
         } else {
-            // Used to try to parse to an IP address literal.
-            PRNetAddr tempAddr;
-            // Unfortunately, PR_StringToNetAddr does not properly initialize
-            // the output buffer in the case of IPv6 input. See bug 223145.
-            memset(&tempAddr, 0, sizeof(PRNetAddr));
 
             // check to see if there is already an entry for this |host|
             // in the hash table.  if so, then check to see if we can't
             // just reuse the lookup result.  otherwise, if there are
             // any pending callbacks, then add to pending callbacks queue,
             // and return.  otherwise, add ourselves as first pending
             // callback, and proceed to do the lookup.
             nsAutoCString originSuffix;
             aOriginAttributes.CreateSuffix(originSuffix);
 
             nsHostKey key(host, type, flags, af,
                           (aOriginAttributes.mPrivateBrowsingId > 0),
                           originSuffix);
             RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
             if (!entry) {
-                entry = new nsHostRecord(key);
+                if (IS_ADDR_TYPE(type)) {
+                    entry = new AddrHostRecord(key);
+                } else {
+                    entry = new TypeHostRecord(key);
+                }
             }
 
             RefPtr<nsHostRecord> rec = entry;
+            nsCOMPtr<AddrHostRecord> addrRec = do_QueryInterface(rec);
             MOZ_ASSERT(rec, "Record should not be null");
+            MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) ||
+                       (IS_OTHER_TYPE(type) && !rec->IsAddrRecord()));
+
+            // Check if the entry is vaild.
             if (!(flags & RES_BYPASS_CACHE) &&
                 rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
                 LOG(("  Using cached record for host [%s].\n", host.get()));
                 // put reference to host record on stack...
                 result = rec;
-                if (type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
+                if (IS_ADDR_TYPE(type)) {
                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
                 }
 
                 // For entries that are in the grace period
                 // or all cached negative entries, use the cache but start a new
                 // lookup in the background
                 ConditionallyRefreshRecord(rec, host);
 
                 if (rec->negative) {
                     LOG(("  Negative cache entry for host [%s].\n", host.get()));
-                    if (type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
+                    if (IS_ADDR_TYPE(type)) {
                         Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
                                               METHOD_NEGATIVE_HIT);
                     }
                     status = NS_ERROR_UNKNOWN_HOST;
                 }
-            } else if (rec->addr) {
-                if (type != nsIDNSService::RESOLVE_TYPE_DEFAULT) {
-                    // do not send a query with type for ip literals.
-                    rv = NS_ERROR_UNKNOWN_HOST;
-                } else {
-                    // if the host name is an IP address literal and has been
-                    // parsed, go ahead and use it.
-                    LOG(("  Using cached address for IP Literal [%s].\n",
-                         host.get()));
-                    Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
-                                          METHOD_LITERAL);
-                    result = rec;
-                }
-            } else if (PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS) {
+
+            // Check whether host is a IP address for A/AAAA queries.
+            // For by-type records we have already checked at the beginning of
+            // this function.
+            } else if (addrRec && addrRec->addr) {
+                // if the host name is an IP address literal and has been
+                // parsed, go ahead and use it.
+                LOG(("  Using cached address for IP Literal [%s].\n",
+                     host.get()));
+                Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
+                                      METHOD_LITERAL);
+                result = rec;
+            } else if (addrRec &&
+                       PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS) {
                 // try parsing the host name as an IP address literal to short
                 // circuit full host resolution.  (this is necessary on some
                 // platforms like Win9x.  see bug 219376 for more details.)
                 LOG(("  Host is IP Literal [%s].\n", host.get()));
-                if (type != nsIDNSService::RESOLVE_TYPE_DEFAULT) {
-                    // do not send a query with type for ip literals.
-                    rv = NS_ERROR_UNKNOWN_HOST;
-                } else {
-                    // ok, just copy the result into the host record, and be
-                    // done with it! ;-)
-                    rec->addr = MakeUnique<NetAddr>();
-                    PRNetAddrToNetAddr(&tempAddr, rec->addr.get());
-                    // put reference to host record on stack...
-                    Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
-                                          METHOD_LITERAL);
-                    result = rec;
-                }
+
+                // ok, just copy the result into the host record, and be
+                // done with it! ;-)
+                addrRec->addr = MakeUnique<NetAddr>();
+                PRNetAddrToNetAddr(&tempAddr, addrRec->addr.get());
+                // put reference to host record on stack...
+                Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
+                                      METHOD_LITERAL);
+                result = rec;
+
+            // Check if we have received too many requests.
             } else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
                        !IsHighPriority(flags) &&
                        !rec->mResolving) {
                 LOG(("  Lookup queue full: dropping %s priority request for "
                      "host [%s].\n",
                      IsMediumPriority(flags) ? "medium" : "low", host.get()));
-                if (type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
+                if (IS_ADDR_TYPE(type)) {
                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
                                           METHOD_OVERFLOW);
                 }
                 // This is a lower priority request and we are swamped, so refuse it.
                 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+
+            // Check if the offline flag is set.
             } else if (flags & RES_OFFLINE) {
                 LOG(("  Offline request for host [%s]; ignoring.\n", host.get()));
                 rv = NS_ERROR_OFFLINE;
+
+            // We do not have a valid result till here.
+            // A/AAAA request can check for an alternative entry like AF_UNSPEC.
+            // Otherwise we need to start a new query.
             } else if (!rec->mResolving) {
+
                 // If this is an IPV4 or IPV6 specific request, check if there is
                 // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
-
-                if (!(flags & RES_BYPASS_CACHE) &&
+                if (addrRec &&
+                    !(flags & RES_BYPASS_CACHE) &&
                     ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
-                     MOZ_ASSERT(type == nsIDNSService::RESOLVE_TYPE_DEFAULT);
-                    // First, search for an entry with AF_UNSPEC
+                    // Check for an AF_UNSPEC entry.
+
                     const nsHostKey unspecKey(host,
                                               nsIDNSService::RESOLVE_TYPE_DEFAULT,
                                               flags, PR_AF_UNSPEC,
                                               (aOriginAttributes.mPrivateBrowsingId > 0),
                                               originSuffix);
                     RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
 
                     TimeStamp now = TimeStamp::NowLoRes();
                     if (unspecRec && unspecRec->HasUsableResult(now, flags)) {
+                        MOZ_ASSERT(unspecRec->IsAddrRecord());
 
-                        MOZ_ASSERT(unspecRec->addr_info || unspecRec->negative,
+                        nsCOMPtr<AddrHostRecord> addrUnspecRec =
+                            do_QueryInterface(unspecRec);
+                        MOZ_ASSERT(addrUnspecRec);
+                        MOZ_ASSERT(addrUnspecRec->addr_info ||
+                                   addrUnspecRec->negative,
                                    "Entry should be resolved or negative.");
 
                         LOG(("  Trying AF_UNSPEC entry for host [%s] af: %s.\n", host.get(),
                              (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
 
                         // We need to lock in case any other thread is reading
                         // addr_info.
-                        MutexAutoLock lock(rec->addr_info_lock);
+                        MutexAutoLock lock(addrRec->addr_info_lock);
 
                         // XXX: note that this actually leaks addr_info.
                         // For some reason, freeing the memory causes a crash in
                         // nsDNSRecord::GetNextAddr - see bug 1422173
-                        rec->addr_info = nullptr;
+                        addrRec->addr_info = nullptr;
                         if (unspecRec->negative) {
                             rec->negative = unspecRec->negative;
                             rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
-                        } else if (unspecRec->addr_info) {
+                        } else if (addrUnspecRec->addr_info) {
                             // Search for any valid address in the AF_UNSPEC entry
                             // in the cache (not blacklisted and from the right
                             // family).
                             NetAddrElement *addrIter =
-                                unspecRec->addr_info->mAddresses.getFirst();
+                                addrUnspecRec->addr_info->mAddresses.getFirst();
                             while (addrIter) {
                                 if ((af == addrIter->mAddress.inet.family) &&
-                                     !unspecRec->Blacklisted(&addrIter->mAddress)) {
-                                    if (!rec->addr_info) {
-                                        rec->addr_info = new AddrInfo(
-                                            unspecRec->addr_info->mHostName,
-                                            unspecRec->addr_info->mCanonicalName,
-                                            unspecRec->addr_info->IsTRR()
+                                     !addrUnspecRec->Blacklisted(&addrIter->mAddress)) {
+                                    if (!addrRec->addr_info) {
+                                        addrRec->addr_info = new AddrInfo(
+                                            addrUnspecRec->addr_info->mHostName,
+                                            addrUnspecRec->addr_info->mCanonicalName,
+                                            addrUnspecRec->addr_info->IsTRR()
                                           );
                                         rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
                                     }
-                                    rec->addr_info->AddAddress(
+                                    addrRec->addr_info->AddAddress(
                                         new NetAddrElement(*addrIter));
                                 }
                                 addrIter = addrIter->getNext();
                             }
                         }
                         // Now check if we have a new record.
                         if (rec->HasUsableResult(now, flags)) {
                             result = rec;
@@ -1011,77 +1130,84 @@ nsHostResolver::ResolveHost(const nsACSt
                             result = rec;
                             rec->negative = true;
                             status = NS_ERROR_UNKNOWN_HOST;
                             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
                                                   METHOD_NEGATIVE_HIT);
                         }
                     }
                 }
-                // If no valid address was found in the cache or this is an
-                // AF_UNSPEC request, then start a new lookup.
+
+                // If this is a by-type request or if no valid record was found
+                // in the cache or this is an AF_UNSPEC request, then start a
+                // new lookup.
                 if (!result) {
-                    LOG(("  No usable address in cache for host [%s].", host.get()));
+                    LOG(("  No usable record in cache for host [%s] type %d.",
+                         host.get(), type));
 
                     if (flags & RES_REFRESH_CACHE) {
                         rec->Invalidate();
                     }
 
                     // Add callback to the list of pending callbacks.
                     rec->mCallbacks.insertBack(callback);
                     rec->flags = flags;
                     rv = NameLookup(rec);
-                    if (type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
+                    if (IS_ADDR_TYPE(type)) {
                         Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
                                               METHOD_NETWORK_FIRST);
                     }
                     if (NS_FAILED(rv) && callback->isInList()) {
                         callback->remove();
                     } else {
                         LOG(("  DNS lookup for host [%s] blocking "
-                             "pending 'getaddrinfo' query: callback [%p]",
+                             "pending 'getaddrinfo' or trr query: "
+                             "callback [%p]",
                              host.get(), callback.get()));
                     }
                 }
-            } else if (rec->mDidCallbacks) {
+
+            } else if (addrRec && addrRec->mDidCallbacks) {
+                // This is only for A/AAAA query.
                 // record is still pending more (TRR) data; make the callback
                 // at once
                 result = rec;
                 // make it count as a hit
-                if (type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
-                    Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
-                }
+                Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
+
                 LOG(("  Host [%s] re-using early TRR resolve data\n", host.get()));
             } else {
                 LOG(("  Host [%s] is being resolved. Appending callback "
                      "[%p].", host.get(), callback.get()));
 
                 rec->mCallbacks.insertBack(callback);
-                if (rec->onQueue) {
-                    if (type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
-                        Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
-                                              METHOD_NETWORK_SHARED);
-                    }
+
+                // Only A/AAAA records are place in a queue. The queues are for
+                // the native resolver, therefore by-type request are never put
+                // into a queue.
+                if (addrRec && addrRec->onQueue) {
+                    Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
+                                          METHOD_NETWORK_SHARED);
 
                     // Consider the case where we are on a pending queue of
                     // lower priority than the request is being made at.
                     // In that case we should upgrade to the higher queue.
 
                     if (IsHighPriority(flags) &&
                         !IsHighPriority(rec->flags)) {
                         // Move from (low|med) to high.
-                        NS_ASSERTION(rec->onQueue, "Moving Host Record Not Currently Queued");
+                        NS_ASSERTION(addrRec->onQueue, "Moving Host Record Not Currently Queued");
                         rec->remove();
                         mHighQ.insertBack(rec);
                         rec->flags = flags;
                         ConditionallyCreateThread(rec);
                     } else if (IsMediumPriority(flags) &&
                                IsLowPriority(rec->flags)) {
                         // Move from low to med.
-                        NS_ASSERTION(rec->onQueue, "Moving Host Record Not Currently Queued");
+                        NS_ASSERTION(addrRec->onQueue, "Moving Host Record Not Currently Queued");
                         rec->remove();
                         mMediumQ.insertBack(rec);
                         rec->flags = flags;
                         mIdleTaskCV.Notify();
                     }
                 }
             }
         }
@@ -1161,35 +1287,47 @@ nsHostResolver::ConditionallyCreateThrea
     }
     else {
         LOG(("  Unable to find a thread for looking up host [%s].\n", rec->host.get()));
     }
     return NS_OK;
 }
 
 // make sure the mTrrLock is held when this is used!
-#define TRROutstanding() ((rec->mTrrA || rec->mTrrAAAA))
+#define TRROutstanding() ((addrRec->mTrrA || addrRec->mTrrAAAA))
 
 nsresult
 nsHostResolver::TrrLookup_unlocked(nsHostRecord *rec, TRR *pushedTRR)
 {
     MutexAutoLock lock(mLock);
     return TrrLookup(rec, pushedTRR);
 }
 
 // returns error if no TRR resolve is issued
 // it is impt this is not called while a native lookup is going on
 nsresult
 nsHostResolver::TrrLookup(nsHostRecord *aRec, TRR *pushedTRR)
 {
     RefPtr<nsHostRecord> rec(aRec);
     mLock.AssertCurrentThreadOwns();
+
+    nsCOMPtr<AddrHostRecord> addrRec;
+    nsCOMPtr<TypeHostRecord> typeRec;
+
+    if (rec->IsAddrRecord()) {
+        addrRec = do_QueryInterface(rec);
+        MOZ_ASSERT(addrRec);
+    } else {
+        typeRec = do_QueryInterface(rec);
+        MOZ_ASSERT(typeRec);
+    }
+
 #ifdef DEBUG
-    if (rec->type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
-        MutexAutoLock trrlock(rec->mTrrLock);
+    if (rec->IsAddrRecord()) {
+        MutexAutoLock trrlock(addrRec->mTrrLock);
         MOZ_ASSERT(!TRROutstanding());
     }
 #endif
     MOZ_ASSERT(!rec->mResolving);
 
     if (!gTRRService || !gTRRService->Enabled()) {
         LOG(("TrrLookup:: %s service not enabled\n", rec->host.get()));
         return NS_ERROR_UNKNOWN_HOST;
@@ -1199,72 +1337,85 @@ nsHostResolver::TrrLookup(nsHostRecord *
         // we're already on the eviction queue. This is a renewal
         MOZ_ASSERT(mEvictionQSize);
         AssertOnQ(rec, mEvictionQ);
 
         rec->remove();
         mEvictionQSize--;
     }
 
-    rec->mTRRSuccess = 0; // bump for each successful TRR response
-    rec->mTrrStart = TimeStamp::Now();
-    rec->mTRRUsed = true; // this record gets TRR treatment
+    bool madeQuery = false;
 
-    enum TrrType rectype;
-
-    if (rec->type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
-        rec->mTrrAUsed = nsHostRecord::INIT;
-        rec->mTrrAAAAUsed = nsHostRecord::INIT;
+    if (addrRec) {
+        addrRec->mTRRSuccess = 0; // bump for each successful TRR response
+        addrRec->mTrrStart = TimeStamp::Now();
+        addrRec->mTRRUsed = true; // this record gets TRR treatment
+        addrRec->mTrrAUsed = AddrHostRecord::INIT;
+        addrRec->mTrrAAAAUsed = AddrHostRecord::INIT;
 
         // If asking for AF_UNSPEC, issue both A and AAAA.
         // If asking for AF_INET6 or AF_INET, do only that single type
-        rectype = (rec->af == AF_INET6)? TRRTYPE_AAAA : TRRTYPE_A;
-    } else {
-        rectype = TRRTYPE_TXT;
-    }
+        enum TrrType rectype = (rec->af == AF_INET6)? TRRTYPE_AAAA : TRRTYPE_A;
 
-    if (pushedTRR) {
-        rectype = pushedTRR->Type();
-    }
-    bool sendAgain;
+        if (pushedTRR) {
+            rectype = pushedTRR->Type();
+        }
+        bool sendAgain;
 
-    bool madeQuery = false;
-    do {
-        sendAgain = false;
-        if ((TRRTYPE_AAAA == rectype) && gTRRService && gTRRService->DisableIPv6()) {
-            break;
+        do {
+            sendAgain = false;
+            if ((TRRTYPE_AAAA == rectype) && gTRRService && gTRRService->DisableIPv6()) {
+                break;
+            }
+            LOG(("TRR Resolve %s type %d\n",
+                 addrRec->host.get(), (int)rectype));
+            RefPtr<TRR> trr;
+            MutexAutoLock trrlock(addrRec->mTrrLock);
+            trr = pushedTRR ? pushedTRR : new TRR(this, rec, rectype);
+            if (pushedTRR || NS_SUCCEEDED(NS_DispatchToMainThread(trr))) {
+                addrRec->mResolving++;
+                if (rectype == TRRTYPE_A) {
+                    MOZ_ASSERT(!addrRec->mTrrA);
+                    addrRec->mTrrA = trr;
+                    addrRec->mTrrAUsed = AddrHostRecord::STARTED;
+                } else if (rectype == TRRTYPE_AAAA) {
+                    MOZ_ASSERT(!addrRec->mTrrAAAA);
+                    addrRec->mTrrAAAA = trr;
+                    addrRec->mTrrAAAAUsed = AddrHostRecord::STARTED;
+                } else {
+                    LOG(("TrrLookup called with bad type set: %d\n", rectype));
+                    MOZ_ASSERT(0);
+                }
+                madeQuery = true;
+                if (!pushedTRR && (rec->af == AF_UNSPEC) && (rectype == TRRTYPE_A)) {
+                    rectype = TRRTYPE_AAAA;
+                    sendAgain = true;
+                }
+            }
+        } while (sendAgain);
+    } else {
+        typeRec->mStart = TimeStamp::Now();
+        enum TrrType rectype = TRRTYPE_TXT;
+
+        if (pushedTRR) {
+            rectype = pushedTRR->Type();
         }
-        LOG(("TRR Resolve %s type %d\n", rec->host.get(), (int)rectype));
+
+        LOG(("TRR Resolve %s type %d\n",
+             typeRec->host.get(), (int)rectype));
         RefPtr<TRR> trr;
-        MutexAutoLock trrlock(rec->mTrrLock);
+        MutexAutoLock trrlock(typeRec->mTrrLock);
         trr = pushedTRR ? pushedTRR : new TRR(this, rec, rectype);
         if (pushedTRR || NS_SUCCEEDED(NS_DispatchToMainThread(trr))) {
-            rec->mResolving++;
-            if (rectype == TRRTYPE_A) {
-                MOZ_ASSERT(!rec->mTrrA);
-                rec->mTrrA = trr;
-                rec->mTrrAUsed = nsHostRecord::STARTED;
-            } else if (rectype == TRRTYPE_AAAA) {
-                MOZ_ASSERT(!rec->mTrrAAAA);
-                rec->mTrrAAAA = trr;
-                rec->mTrrAAAAUsed = nsHostRecord::STARTED;
-            } else if (rectype == TRRTYPE_TXT) {
-                MOZ_ASSERT(!rec->mTrrTxt);
-                rec->mTrrTxt = trr;
-            } else {
-                LOG(("TrrLookup called with bad type set: %d\n", rectype));
-                MOZ_ASSERT(0);
-            }
+            typeRec->mResolving++;
+            MOZ_ASSERT(!typeRec->mTrr);
+            typeRec->mTrr = trr;
             madeQuery = true;
-            if (!pushedTRR && (rec->af == AF_UNSPEC) && (rectype == TRRTYPE_A)) {
-                rectype = TRRTYPE_AAAA;
-                sendAgain = true;
-            }
         }
-    } while (sendAgain);
+    }
 
     return madeQuery ? NS_OK : NS_ERROR_UNKNOWN_HOST;
 }
 
 void
 nsHostResolver::AssertOnQ(nsHostRecord *rec, LinkedList<RefPtr<nsHostRecord>>& q)
 {
 #ifdef DEBUG
@@ -1277,51 +1428,54 @@ nsHostResolver::AssertOnQ(nsHostRecord *
     }
     MOZ_ASSERT(false, "Did not find element");
 #endif
 }
 
 nsresult
 nsHostResolver::NativeLookup(nsHostRecord *aRec)
 {
+    // Only A/AAAA request are resolve natively.
+    MOZ_ASSERT(aRec->IsAddrRecord());
     mLock.AssertCurrentThreadOwns();
-    if (aRec->type != nsIDNSService::RESOLVE_TYPE_DEFAULT) {
-        return NS_ERROR_UNKNOWN_HOST;
-    }
+
     RefPtr<nsHostRecord> rec(aRec);
+    nsCOMPtr<AddrHostRecord> addrRec;
+    addrRec = do_QueryInterface(rec);
+    MOZ_ASSERT(addrRec);
 
-    rec->mNativeStart = TimeStamp::Now();
+    addrRec->mNativeStart = TimeStamp::Now();
 
     // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
     if (rec->isInList()) {
         MOZ_ASSERT(mEvictionQSize);
         AssertOnQ(rec, mEvictionQ);
         rec->remove(); // was on the eviction queue
         mEvictionQSize--;
     }
 
-    switch (nsHostRecord::GetPriority(rec->flags)) {
-        case nsHostRecord::DNS_PRIORITY_HIGH:
+    switch (AddrHostRecord::GetPriority(rec->flags)) {
+        case AddrHostRecord::DNS_PRIORITY_HIGH:
             mHighQ.insertBack(rec);
             break;
 
-        case nsHostRecord::DNS_PRIORITY_MEDIUM:
+        case AddrHostRecord::DNS_PRIORITY_MEDIUM:
             mMediumQ.insertBack(rec);
             break;
 
-        case nsHostRecord::DNS_PRIORITY_LOW:
+        case AddrHostRecord::DNS_PRIORITY_LOW:
             mLowQ.insertBack(rec);
             break;
     }
     mPendingCount++;
 
-    rec->mNative = true;
-    rec->mNativeUsed = true;
-    rec->onQueue = true;
-    rec->mResolving++;
+    addrRec->mNative = true;
+    addrRec->mNativeUsed = true;
+    addrRec->onQueue = true;
+    addrRec->mResolving++;
 
     nsresult rv = ConditionallyCreateThread(rec);
 
     LOG (("  DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
           static_cast<uint32_t>(mActiveTaskCount),
           static_cast<uint32_t>(mActiveAnyThreadCount),
           static_cast<uint32_t>(mNumIdleTasks),
           static_cast<uint32_t>(mPendingCount)));
@@ -1346,25 +1500,28 @@ nsHostResolver::NameLookup(nsHostRecord 
     nsresult rv = NS_ERROR_UNKNOWN_HOST;
     if (rec->mResolving) {
         LOG(("NameLookup %s while already resolving\n", rec->host.get()));
         return NS_OK;
     }
 
     ResolverMode mode = rec->mResolverMode = Mode();
 
-    rec->mNativeUsed = false;
-    rec->mTRRUsed = false;
-    rec->mNativeSuccess = false;
-    rec->mTRRSuccess = 0;
-    rec->mDidCallbacks = false;
+    if (rec->IsAddrRecord()) {
+        nsCOMPtr<AddrHostRecord> addrRec;
+        addrRec = do_QueryInterface(rec);
+        MOZ_ASSERT(addrRec);
 
-    if (rec->type == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
-        rec->mTrrAUsed = nsHostRecord::INIT;
-        rec->mTrrAAAAUsed = nsHostRecord::INIT;
+        addrRec->mNativeUsed = false;
+        addrRec->mTRRUsed = false;
+        addrRec->mNativeSuccess = false;
+        addrRec->mTRRSuccess = 0;
+        addrRec->mDidCallbacks = false;
+        addrRec->mTrrAUsed = AddrHostRecord::INIT;
+        addrRec->mTrrAAAAUsed = AddrHostRecord::INIT;
     }
 
     if (rec->flags & RES_DISABLE_TRR) {
         if (mode == MODE_TRRONLY) {
             return rv;
         }
         mode = MODE_NATIVEONLY;
     }
@@ -1372,17 +1529,17 @@ nsHostResolver::NameLookup(nsHostRecord 
     if (!TRR_DISABLED(mode)) {
         rv = TrrLookup(rec);
     }
 
     if ((mode == MODE_PARALLEL) ||
         TRR_DISABLED(mode) ||
         (mode == MODE_SHADOW) ||
         ((mode == MODE_TRRFIRST) && NS_FAILED(rv))) {
-        if (rec->type != nsIDNSService::RESOLVE_TYPE_DEFAULT) {
+        if (!rec->IsAddrRecord()) {
           return rv;
         }
         rv = NativeLookup(rec);
     }
 
     return rv;
 }
 
@@ -1390,37 +1547,42 @@ nsresult
 nsHostResolver::ConditionallyRefreshRecord(nsHostRecord *rec, const nsACString &host)
 {
     if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID
             || rec->negative) && !rec->mResolving) {
         LOG(("  Using %s cache entry for host [%s] but starting async renewal.",
             rec->negative ? "negative" :"positive", host.BeginReading()));
         NameLookup(rec);
 
-        if (!rec->negative) {
+        if (rec->IsAddrRecord() && !rec->negative) {
             // negative entries are constantly being refreshed, only
             // track positive grace period induced renewals
             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
-                METHOD_RENEWAL);
+                                  METHOD_RENEWAL);
         }
     }
     return NS_OK;
 }
 
 void
-nsHostResolver::DeQueue(LinkedList<RefPtr<nsHostRecord>>& aQ, nsHostRecord **aResult)
+nsHostResolver::DeQueue(LinkedList<RefPtr<nsHostRecord>>& aQ,
+                        AddrHostRecord **aResult)
 {
     RefPtr<nsHostRecord> rec = aQ.popFirst();
     mPendingCount--;
-    rec.forget(aResult);
-    (*aResult)->onQueue = false;
+    MOZ_ASSERT(rec->IsAddrRecord());
+    nsCOMPtr<AddrHostRecord> addrRec;
+    addrRec = do_QueryInterface(rec);
+    MOZ_ASSERT(addrRec);
+    addrRec->onQueue = false;
+    addrRec.forget(aResult);
 }
 
 bool
-nsHostResolver::GetHostToLookup(nsHostRecord **result)
+nsHostResolver::GetHostToLookup(AddrHostRecord **result)
 {
     bool timedOut = false;
     TimeDuration timeout;
     TimeStamp epoch, now;
 
     MutexAutoLock lock(mLock);
 
     timeout = (mNumIdleTasks >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
@@ -1483,17 +1645,17 @@ nsHostResolver::GetHostToLookup(nsHostRe
         }
     }
 
     // tell thread to exit...
     return false;
 }
 
 void
-nsHostResolver::PrepareRecordExpiration(nsHostRecord* rec) const
+nsHostResolver::PrepareRecordExpirationAddrRecord(AddrHostRecord* rec) const
 {
     // NOTE: rec->addr_info_lock is already held by parent
     MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
     mLock.AssertCurrentThreadOwns();
     if (!rec->addr_info) {
         rec->SetExpiration(TimeStamp::NowLoRes(),
                            NEGATIVE_RECORD_LIFETIME, 0);
         LOG(("Caching host [%s] negative record for %u seconds.\n",
@@ -1594,250 +1756,272 @@ nsHostResolver::AddToEvictionQ(nsHostRec
     } else {
         // remove first element on mEvictionQ
         RefPtr<nsHostRecord> head = mEvictionQ.popFirst();
         mRecordDB.Remove(*static_cast<nsHostKey *>(head.get()));
 
         if (!head->negative) {
             // record the age of the entry upon eviction.
             TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
-            Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
-                                  static_cast<uint32_t>(age.ToSeconds() / 60));
+            if (rec->IsAddrRecord()) {
+                Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
+                                      static_cast<uint32_t>(age.ToSeconds() / 60));
+            } else {
+                Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_CLEANUP_AGE,
+                                      static_cast<uint32_t>(age.ToSeconds() / 60));
+            }
             if (head->CheckExpiration(TimeStamp::Now()) !=
                 nsHostRecord::EXP_EXPIRED) {
-                Telemetry::Accumulate(Telemetry::DNS_PREMATURE_EVICTION,
-                                      static_cast<uint32_t>(age.ToSeconds() / 60));
+                if (rec->IsAddrRecord()) {
+                    Telemetry::Accumulate(Telemetry::DNS_PREMATURE_EVICTION,
+                                          static_cast<uint32_t>(age.ToSeconds() / 60));
+                } else {
+                    Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_PREMATURE_EVICTION,
+                                          static_cast<uint32_t>(age.ToSeconds() / 60));
+                }
             }
         }
     }
 }
 
 //
 // CompleteLookup() checks if the resolving should be redone and if so it
 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
 // takes ownership of AddrInfo parameter
 nsHostResolver::LookupStatus
 nsHostResolver::CompleteLookup(nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb)
 {
     MutexAutoLock lock(mLock);
     MOZ_ASSERT(rec);
     MOZ_ASSERT(rec->pb == pb);
+    MOZ_ASSERT(rec->IsAddrRecord());
+
+    nsCOMPtr<AddrHostRecord> addrRec = do_QueryInterface(rec);
+    MOZ_ASSERT(addrRec);
 
     // newRRSet needs to be taken into the hostrecord (which will then own it)
     // or deleted on early return.
     nsAutoPtr<AddrInfo> newRRSet(aNewRRSet);
 
     bool trrResult = newRRSet && newRRSet->IsTRR();
 
-    if (rec->mResolveAgain && (status != NS_ERROR_ABORT) && !trrResult) {
-        LOG(("nsHostResolver record %p resolve again due to flushcache\n", rec));
-        rec->mResolveAgain = false;
+    if (addrRec->mResolveAgain && (status != NS_ERROR_ABORT) &&
+        !trrResult) {
+        LOG(("nsHostResolver record %p resolve again due to flushcache\n",
+             addrRec.get()));
+        addrRec->mResolveAgain = false;
         return LOOKUP_RESOLVEAGAIN;
     }
 
-    MOZ_ASSERT(rec->mResolving);
-    rec->mResolving--;
+    MOZ_ASSERT(addrRec->mResolving);
+    addrRec->mResolving--;
     LOG(("nsHostResolver::CompleteLookup %s %p %X trr=%d stillResolving=%d\n",
-         rec->host.get(), aNewRRSet, (unsigned int)status,
-         aNewRRSet ? aNewRRSet->IsTRR() : 0, rec->mResolving));
+         addrRec->host.get(), aNewRRSet, (unsigned int)status,
+         aNewRRSet ? aNewRRSet->IsTRR() : 0, addrRec->mResolving));
 
     if (trrResult) {
-        MutexAutoLock trrlock(rec->mTrrLock);
+        MutexAutoLock trrlock(addrRec->mTrrLock);
         LOG(("TRR lookup Complete (%d) %s %s\n",
              newRRSet->IsTRR(), newRRSet->mHostName.get(),
              NS_SUCCEEDED(status) ? "OK" : "FAILED"));
         MOZ_ASSERT(TRROutstanding());
         if (newRRSet->IsTRR() == TRRTYPE_A) {
-            MOZ_ASSERT(rec->mTrrA);
-            rec->mTrrA = nullptr;
-            rec->mTrrAUsed = NS_SUCCEEDED(status) ? nsHostRecord::OK : nsHostRecord::FAILED;
+            MOZ_ASSERT(addrRec->mTrrA);
+            addrRec->mTrrA = nullptr;
+            addrRec->mTrrAUsed = NS_SUCCEEDED(status) ? AddrHostRecord::OK
+                                                       : AddrHostRecord::FAILED;
         } else if (newRRSet->IsTRR() == TRRTYPE_AAAA) {
-            MOZ_ASSERT(rec->mTrrAAAA);
-            rec->mTrrAAAA = nullptr;
-            rec->mTrrAAAAUsed = NS_SUCCEEDED(status) ? nsHostRecord::OK : nsHostRecord::FAILED;
+            MOZ_ASSERT(addrRec->mTrrAAAA);
+            addrRec->mTrrAAAA = nullptr;
+            addrRec->mTrrAAAAUsed = NS_SUCCEEDED(status) ? AddrHostRecord::OK
+                                                          : AddrHostRecord::FAILED;
         } else {
             MOZ_ASSERT(0);
         }
 
         if (NS_SUCCEEDED(status)) {
-            rec->mTRRSuccess++;
-            if (rec->mTRRSuccess == 1) {
+            addrRec->mTRRSuccess++;
+            if (addrRec->mTRRSuccess == 1) {
                 // Store the duration on first succesful TRR response.  We
                 // don't know that there will be a second response nor can we
                 // tell which of two has useful data, especially in
                 // MODE_SHADOW where the actual results are discarded.
-                rec->mTrrDuration = TimeStamp::Now() - rec->mTrrStart;
+                addrRec->mTrrDuration = TimeStamp::Now() - addrRec->mTrrStart;
             }
         }
         if (TRROutstanding()) {
-            rec->mFirstTRRresult = status;
+            addrRec->mFirstTRRresult = status;
             if (NS_FAILED(status)) {
                 return LOOKUP_OK; // wait for outstanding
             }
 
             // There's another TRR complete pending. Wait for it and keep
             // this RRset around until then.
-            MOZ_ASSERT(!rec->mFirstTRR && newRRSet);
-            rec->mFirstTRR = newRRSet; // autoPtr.swap()
-            MOZ_ASSERT(rec->mFirstTRR && !newRRSet);
+            MOZ_ASSERT(!addrRec->mFirstTRR && newRRSet);
+            addrRec->mFirstTRR = newRRSet; // autoPtr.swap()
+            MOZ_ASSERT(addrRec->mFirstTRR && !newRRSet);
 
-            if (rec->mDidCallbacks || rec->mResolverMode == MODE_SHADOW) {
+            if (addrRec->mDidCallbacks ||
+                addrRec->mResolverMode == MODE_SHADOW) {
                 return LOOKUP_OK;
             }
 
-            if (rec->mTrrA && (!gTRRService || !gTRRService->EarlyAAAA())) {
+            if (addrRec->mTrrA && (!gTRRService ||
+                !gTRRService->EarlyAAAA())) {
                 // This is an early AAAA with a pending A response. Allowed
                 // only by pref.
                 LOG(("CompleteLookup: avoiding early use of TRR AAAA!\n"));
                 return LOOKUP_OK;
             }
 
             // we can do some callbacks with this partial result which requires
             // a deep copy
-            newRRSet = new AddrInfo(rec->mFirstTRR);
-            MOZ_ASSERT(rec->mFirstTRR && newRRSet);
+            newRRSet = new AddrInfo(addrRec->mFirstTRR);
+            MOZ_ASSERT(addrRec->mFirstTRR && newRRSet);
 
         } else {
             // no more outstanding TRRs
             // If mFirstTRR is set, merge those addresses into current set!
-            if (rec->mFirstTRR) {
+            if (addrRec->mFirstTRR) {
                 if (NS_SUCCEEDED(status)) {
-                    merge_rrset(newRRSet, rec->mFirstTRR);
+                    merge_rrset(newRRSet, addrRec->mFirstTRR);
                 }
                 else {
-                    newRRSet = rec->mFirstTRR; // transfers
+                    newRRSet = addrRec->mFirstTRR; // transfers
                 }
-                rec->mFirstTRR = nullptr;
+                addrRec->mFirstTRR = nullptr;
             }
 
-            if (NS_FAILED(rec->mFirstTRRresult) &&
+            if (NS_FAILED(addrRec->mFirstTRRresult) &&
                 NS_FAILED(status) &&
-                (rec->mFirstTRRresult != NS_ERROR_UNKNOWN_HOST) &&
+                (addrRec->mFirstTRRresult != NS_ERROR_UNKNOWN_HOST) &&
                 (status != NS_ERROR_UNKNOWN_HOST)) {
                 // the errors are not failed resolves, that means
                 // something else failed, consider this as *TRR not used*
                 // for actually trying to resolve the host
-                rec->mTRRUsed = false;
+                addrRec->mTRRUsed = false;
             }
 
-            if (!rec->mTRRSuccess) {
+            if (!addrRec->mTRRSuccess) {
                 // no TRR success
                 newRRSet = nullptr;
                 status = NS_ERROR_UNKNOWN_HOST;
             }
 
-            if (!rec->mTRRSuccess && rec->mResolverMode == MODE_TRRFIRST) {
-                MOZ_ASSERT(!rec->mResolving);
-                NativeLookup(rec);
-                MOZ_ASSERT(rec->mResolving);
+            if (!addrRec->mTRRSuccess && addrRec->mResolverMode == MODE_TRRFIRST) {
+                MOZ_ASSERT(!addrRec->mResolving);
+                NativeLookup(addrRec);
+                MOZ_ASSERT(addrRec->mResolving);
                 return LOOKUP_OK;
             }
 
             // continue
         }
 
     } else { // native resolve completed
-        if (rec->usingAnyThread) {
+        if (addrRec->usingAnyThread) {
             mActiveAnyThreadCount--;
-            rec->usingAnyThread = false;
+            addrRec->usingAnyThread = false;
         }
 
-        rec->mNative = false;
-        rec->mNativeSuccess = newRRSet ? true : false;
-        if (rec->mNativeSuccess) {
-            rec->mNativeDuration = TimeStamp::Now() - rec->mNativeStart;
+        addrRec->mNative = false;
+        addrRec->mNativeSuccess = newRRSet ? true : false;
+        if (addrRec->mNativeSuccess) {
+            addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart;
         }
     }
 
-    // update record fields.  We might have a rec->addr_info already if a
+    // update record fields.  We might have a addrRec->addr_info already if a
     // previous lookup result expired and we're reresolving it or we get
     // a late second TRR response.
     // note that we don't update the addr_info if this is trr shadow results
     if (!mShutdown &&
-        !(trrResult && rec->mResolverMode == MODE_SHADOW)) {
-        MutexAutoLock lock(rec->addr_info_lock);
+        !(trrResult && addrRec->mResolverMode == MODE_SHADOW)) {
+        MutexAutoLock lock(addrRec->addr_info_lock);
         nsAutoPtr<AddrInfo> old_addr_info;
-        if (different_rrset(rec->addr_info, newRRSet)) {
-            LOG(("nsHostResolver record %p new gencnt\n", rec));
-            old_addr_info = rec->addr_info;
-            rec->addr_info = newRRSet.forget();
-            rec->addr_info_gencnt++;
+        if (different_rrset(addrRec->addr_info, newRRSet)) {
+            LOG(("nsHostResolver record %p new gencnt\n", addrRec.get()));
+            old_addr_info = addrRec->addr_info;
+            addrRec->addr_info = newRRSet.forget();
+            addrRec->addr_info_gencnt++;
         } else {
-            if (rec->addr_info && newRRSet) {
-                rec->addr_info->ttl = newRRSet->ttl;
+            if (addrRec->addr_info && newRRSet) {
+                addrRec->addr_info->ttl = newRRSet->ttl;
             }
             old_addr_info = newRRSet.forget();
         }
-        rec->negative = !rec->addr_info;
-        PrepareRecordExpiration(rec);
+        addrRec->negative = !addrRec->addr_info;
+        PrepareRecordExpirationAddrRecord(addrRec);
     }
 
     bool doCallbacks = true;
 
-    if (trrResult && (rec->mResolverMode == MODE_SHADOW) && !rec->mDidCallbacks) {
+    if (trrResult && (addrRec->mResolverMode == MODE_SHADOW) &&
+        !addrRec->mDidCallbacks) {
         // don't report result based only on suppressed TRR info
         doCallbacks = false;
         LOG(("nsHostResolver Suppressing TRR %s because it is first shadow result\n",
-             rec->host.get()));
-    } else if(trrResult && rec->mDidCallbacks) {
+             addrRec->host.get()));
+    } else if(trrResult && addrRec->mDidCallbacks) {
         // already callback'ed on the first TRR response
         LOG(("nsHostResolver Suppressing callback for second TRR response for %s\n",
-             rec->host.get()));
+             addrRec->host.get()));
         doCallbacks = false;
     }
 
 
     if (LOG_ENABLED()) {
-        MutexAutoLock lock(rec->addr_info_lock);
+        MutexAutoLock lock(addrRec->addr_info_lock);
         NetAddrElement *element;
-        if (rec->addr_info) {
-            for (element = rec->addr_info->mAddresses.getFirst();
+        if (addrRec->addr_info) {
+            for (element = addrRec->addr_info->mAddresses.getFirst();
                  element; element = element->getNext()) {
                 char buf[128];
                 NetAddrToString(&element->mAddress, buf, sizeof(buf));
-                LOG(("CompleteLookup: %s has %s\n", rec->host.get(), buf));
+                LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf));
             }
         } else {
-            LOG(("CompleteLookup: %s has NO address\n", rec->host.get()));
+            LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get()));
         }
     }
 
     if (doCallbacks) {
         // get the list of pending callbacks for this lookup, and notify
         // them that the lookup is complete.
         mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = std::move(rec->mCallbacks);
 
-        LOG(("nsHostResolver record %p calling back dns users\n", rec));
+        LOG(("nsHostResolver record %p calling back dns users\n",
+             addrRec.get()));
 
         for (nsResolveHostCallback* c = cbs.getFirst(); c; c = c->removeAndGetNext()) {
             c->OnResolveHostComplete(this, rec, status);
         }
-        rec->mDidCallbacks = true;
+        addrRec->mDidCallbacks = true;
     }
 
-    if (!rec->mResolving && !mShutdown) {
-        rec->ResolveComplete();
+    if (!addrRec->mResolving && !mShutdown) {
+        addrRec->ResolveComplete();
 
         AddToEvictionQ(rec);
     }
 
 #ifdef DNSQUERY_AVAILABLE
     // Unless the result is from TRR, resolve again to get TTL
     bool fromTRR = false;
     {
-        MutexAutoLock lock(rec->addr_info_lock);
-        if(rec->addr_info && rec->addr_info->IsTRR()) {
+        MutexAutoLock lock(addrRec->addr_info_lock);
+        if(addrRec->addr_info && addrRec->addr_info->IsTRR()) {
             fromTRR = true;
         }
     }
     if (!fromTRR &&
-        !mShutdown && !rec->mGetTtl && !rec->mResolving && sGetTtlEnabled) {
-        LOG(("Issuing second async lookup for TTL for host [%s].", rec->host.get()));
-        rec->flags =
-            (rec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW |
+        !mShutdown && !addrRec->mGetTtl && !rec->mResolving && sGetTtlEnabled) {
+        LOG(("Issuing second async lookup for TTL for host [%s].", addrRec->host.get()));
+        addrRec->flags =
+            (addrRec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW |
             RES_DISABLE_TRR;
         DebugOnly<nsresult> rv = NameLookup(rec);
         NS_WARNING_ASSERTION(
             NS_SUCCEEDED(rv),
             "Could not issue second async lookup for TTL.");
     }
 #endif
     return LOOKUP_OK;
@@ -1846,40 +2030,57 @@ nsHostResolver::CompleteLookup(nsHostRec
 nsHostResolver::LookupStatus
 nsHostResolver::CompleteLookupByType(nsHostRecord* rec, nsresult status,
                                      const nsTArray<nsCString> *aResult,
                                      uint32_t aTtl, bool pb)
 {
     MutexAutoLock lock(mLock);
     MOZ_ASSERT(rec);
     MOZ_ASSERT(rec->pb == pb);
+    MOZ_ASSERT(!rec->IsAddrRecord());
 
-    MOZ_ASSERT(rec->mResolving);
-    rec->mResolving--;
+    nsCOMPtr<TypeHostRecord> typeRec;
+    typeRec = do_QueryInterface(rec);
+    MOZ_ASSERT(typeRec);
 
-    MutexAutoLock trrlock(rec->mTrrLock);
-    rec->mTrrTxt = nullptr;
+    MOZ_ASSERT(typeRec->mResolving);
+    typeRec->mResolving--;
+
+    MutexAutoLock trrlock(typeRec->mTrrLock);
+    typeRec->mTrr = nullptr;
+
+    uint32_t duration = static_cast<uint32_t>((TimeStamp::Now() - typeRec->mStart).ToMilliseconds());
 
     if (NS_FAILED(status)) {
-        rec->SetExpiration(TimeStamp::NowLoRes(),
-                           NEGATIVE_RECORD_LIFETIME, 0);
+        LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n",
+             typeRec.get(), typeRec->host.get(), (unsigned int)status));
+        typeRec->SetExpiration(TimeStamp::NowLoRes(),
+                               NEGATIVE_RECORD_LIFETIME, 0);
         MOZ_ASSERT(!aResult);
         status = NS_ERROR_UNKNOWN_HOST;
-        rec->negative = true;
+        typeRec->negative = true;
+        Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_FAILED_LOOKUP_TIME, duration);
     } else {
         MOZ_ASSERT(aResult);
-        MutexAutoLock byTypeLock(rec->mRequestByTypeResultLock);
-        rec->mRequestByTypeResult = *aResult;
-        rec->SetExpiration(TimeStamp::NowLoRes(), aTtl, mDefaultGracePeriod);
-        rec->negative = false;
+        LOG(("nsHostResolver::CompleteLookupByType record %p [%s], number of "
+             "records %zu\n", typeRec.get(), typeRec->host.get(),
+             aResult->Length()));
+        MutexAutoLock typeLock(typeRec->mResultsLock);
+        typeRec->mResults = *aResult;
+        typeRec->SetExpiration(TimeStamp::NowLoRes(), aTtl,
+                                 mDefaultGracePeriod);
+        typeRec->negative = false;
+        Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_SUCCEEDED_LOOKUP_TIME, duration);
     }
 
-    mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = std::move(rec->mCallbacks);
+    mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
+        std::move(typeRec->mCallbacks);
 
-    LOG(("nsHostResolver record %p calling back dns users\n", rec));
+    LOG(("nsHostResolver::CompleteLookupByType record %p calling back dns "
+         "users\n", typeRec.get()));
 
     for (nsResolveHostCallback* c = cbs.getFirst(); c; c = c->removeAndGetNext()) {
         c->OnResolveHostComplete(this, rec, status);
     }
 
     AddToEvictionQ(rec);
     return LOOKUP_OK;
 }
@@ -1952,22 +2153,22 @@ nsHostResolver::SizeOfIncludingThis(Mall
 void
 nsHostResolver::ThreadFunc()
 {
     LOG(("DNS lookup thread - starting execution.\n"));
 
 #if defined(RES_RETRY_ON_FAILURE)
     nsResState rs;
 #endif
-    RefPtr<nsHostRecord> rec;
+    nsCOMPtr<AddrHostRecord> rec;
     AddrInfo *ai = nullptr;
 
     do {
         if (!rec) {
-            RefPtr<nsHostRecord> tmpRec;
+            nsCOMPtr<AddrHostRecord> tmpRec;
             if (!GetHostToLookup(getter_AddRefs(tmpRec))) {
                 break; // thread shutdown signal
             }
             // GetHostToLookup() returns an owning reference
             MOZ_ASSERT(tmpRec);
             rec.swap(tmpRec);
         }
 
@@ -2065,50 +2266,62 @@ void
 nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries> *args)
 {
     MutexAutoLock lock(mLock);
     for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
         // We don't pay attention to address literals, only resolved domains.
         // Also require a host.
         nsHostRecord* rec = iter.UserData();
         MOZ_ASSERT(rec, "rec should never be null here!");
-        if (!rec || !rec->addr_info) {
+
+        if (!rec) {
+            continue;
+        }
+
+        // For now we only show A/AAAA records.
+        if (!rec->IsAddrRecord()) {
+            continue;
+        }
+
+        nsCOMPtr<AddrHostRecord> addrRec = do_QueryInterface(rec);
+        MOZ_ASSERT(addrRec);
+        if (!addrRec && !addrRec->addr_info) {
             continue;
         }
 
         DNSCacheEntries info;
         info.hostname = rec->host;
         info.family = rec->af;
         info.expiration =
             (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
         if (info.expiration <= 0) {
             // We only need valid DNS cache entries
             continue;
         }
 
         {
-            MutexAutoLock lock(rec->addr_info_lock);
+            MutexAutoLock lock(addrRec->addr_info_lock);
 
             NetAddr *addr = nullptr;
-            NetAddrElement *addrElement = rec->addr_info->mAddresses.getFirst();
+            NetAddrElement *addrElement = addrRec->addr_info->mAddresses.getFirst();
             if (addrElement) {
                 addr = &addrElement->mAddress;
             }
             while (addr) {
                 char buf[kIPv6CStrBufSize];
                 if (NetAddrToString(addr, buf, sizeof(buf))) {
                     info.hostaddr.AppendElement(buf);
                 }
                 addr = nullptr;
                 addrElement = addrElement->getNext();
                 if (addrElement) {
                     addr = &addrElement->mAddress;
                 }
             }
-            info.TRR = rec->addr_info->IsTRR();
+            info.TRR = addrRec->addr_info->IsTRR();
         }
 
         args->AppendElement(info);
     }
 }
 
 #undef LOG
 #undef LOG_ENABLED
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -60,27 +60,113 @@ struct nsHostKey
     const nsCString originSuffix;
     explicit nsHostKey(const nsACString& host, uint16_t  type, uint16_t flags,
                        uint16_t af, bool pb, const nsACString& originSuffix);
     bool operator==(const nsHostKey& other) const;
     size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
     PLDHashNumber Hash() const;
 };
 
+// 9c29024a-e7ea-48b0-945e-058a8687247b
+#define NS_HOSTRECORD_IID \
+{ 0x9c29024a, 0xe7ea, 0x48b0, {0x94, 0x5e, 0x05, 0x8a, 0x86, 0x87, 0x24, 0x7b }}
+
 /**
  * nsHostRecord - ref counted object type stored in host resolver cache.
  */
 class nsHostRecord :
     public mozilla::LinkedListElement<RefPtr<nsHostRecord>>,
-    public nsHostKey
+    public nsHostKey,
+    public nsISupports
+{
+public:
+    NS_DECLARE_STATIC_IID_ACCESSOR(NS_HOSTRECORD_IID)
+
+    virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const = 0;
+
+protected:
+    friend class nsHostResolver;
+
+    explicit nsHostRecord(const nsHostKey& key);
+    virtual ~nsHostRecord();
+
+    // Mark hostrecord as not usable
+    void Invalidate();
+
+    enum ExpirationStatus {
+        EXP_VALID,
+        EXP_GRACE,
+        EXP_EXPIRED,
+    };
+
+    ExpirationStatus CheckExpiration(const mozilla::TimeStamp& now) const;
+
+    // Convenience function for setting the timestamps above (mValidStart,
+    // mValidEnd, and mGraceStart). valid and grace are durations in seconds.
+    void SetExpiration(const mozilla::TimeStamp& now, unsigned int valid,
+                       unsigned int grace);
+    void CopyExpirationTimesAndFlagsFrom(const nsHostRecord *aFromHostRecord);
+
+    // Checks if the record is usable (not expired and has a value)
+    bool HasUsableResult(const mozilla::TimeStamp& now, uint16_t queryFlags = 0) const;
+
+    enum DnsPriority {
+        DNS_PRIORITY_LOW,
+        DNS_PRIORITY_MEDIUM,
+        DNS_PRIORITY_HIGH,
+    };
+    static DnsPriority GetPriority(uint16_t aFlags);
+
+    virtual void Cancel() = 0;
+
+    virtual bool HasUsableResultInternal() const = 0;
+
+    mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks;
+
+    bool IsAddrRecord() const {
+        return type == nsIDNSService::RESOLVE_TYPE_DEFAULT;
+    }
+
+    // When the record began being valid. Used mainly for bookkeeping.
+    mozilla::TimeStamp mValidStart;
+
+    // When the record is no longer valid (it's time of expiration)
+    mozilla::TimeStamp mValidEnd;
+
+    // When the record enters its grace period. This must be before mValidEnd.
+    // If a record is in its grace period (and not expired), it will be used
+    // but a request to refresh it will be made.
+    mozilla::TimeStamp mGraceStart;
+
+    const nsCString mOriginSuffix;
+
+    mozilla::net::ResolverMode mResolverMode;
+
+    uint16_t  mResolving;  // counter of outstanding resolving calls
+
+    uint8_t negative : 1;   /* True if this record is a cache of a failed lookup.
+                               Negative cache entries are valid just like any other
+                               (though never for more than 60 seconds), but a use
+                               of that negative entry forces an asynchronous refresh. */
+    uint8_t mDoomed : 1;    // explicitly expired
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsHostRecord, NS_HOSTRECORD_IID)
+
+// b020e996-f6ab-45e5-9bf5-1da71dd0053a
+#define ADDRHOSTRECORD_IID \
+{ 0xb020e996, 0xf6ab, 0x45e5, {0x9b, 0xf5, 0x1d, 0xa7, 0x1d, 0xd0, 0x05, 0x3a }}
+
+class AddrHostRecord final : public nsHostRecord
 {
     typedef mozilla::Mutex Mutex;
 
 public:
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHostRecord)
+    NS_DECLARE_STATIC_IID_ACCESSOR(ADDRHOSTRECORD_IID)
+    NS_DECL_THREADSAFE_ISUPPORTS
 
     /* a fully resolved host record has either a non-null |addr_info| or |addr|
      * field.  if |addr_info| is null, it implies that the |host| is an IP
      * address literal.  in which case, |addr| contains the parsed address.
      * otherwise, if |addr_info| is non-null, then it contains one or many
      * IP addresses corresponding to the given host name.  if both |addr_info|
      * and |addr| are null, then the given host has not yet been fully resolved.
      * |af| is the address family of the record we are querying for.
@@ -93,131 +179,138 @@ public:
      * nsHostRecord (and only in nsHostResolver::CompleteLookup);
      * the other threads just read it.  therefore the resolver worker
      * thread doesn't need to lock when reading |addr_info|.
      */
     Mutex        addr_info_lock;
     int          addr_info_gencnt; /* generation count of |addr_info| */
     mozilla::net::AddrInfo *addr_info;
     mozilla::UniquePtr<mozilla::net::NetAddr> addr;
-    bool         negative;   /* True if this record is a cache of a failed lookup.
-                                Negative cache entries are valid just like any other
-                                (though never for more than 60 seconds), but a use
-                                of that negative entry forces an asynchronous refresh. */
-
-    enum ExpirationStatus {
-        EXP_VALID,
-        EXP_GRACE,
-        EXP_EXPIRED,
-    };
-
-    ExpirationStatus CheckExpiration(const mozilla::TimeStamp& now) const;
-
-    // When the record began being valid. Used mainly for bookkeeping.
-    mozilla::TimeStamp mValidStart;
-
-    // When the record is no longer valid (it's time of expiration)
-    mozilla::TimeStamp mValidEnd;
-
-    // When the record enters its grace period. This must be before mValidEnd.
-    // If a record is in its grace period (and not expired), it will be used
-    // but a request to refresh it will be made.
-    mozilla::TimeStamp mGraceStart;
-
-    // When the lookups of this record started and their durations
-    mozilla::TimeStamp mTrrStart;
-    mozilla::TimeStamp mNativeStart;
-    mozilla::TimeDuration mTrrDuration;
-    mozilla::TimeDuration mNativeDuration;
-
-    // Convenience function for setting the timestamps above (mValidStart,
-    // mValidEnd, and mGraceStart). valid and grace are durations in seconds.
-    void SetExpiration(const mozilla::TimeStamp& now, unsigned int valid,
-                       unsigned int grace);
-    void CopyExpirationTimesAndFlagsFrom(const nsHostRecord *aFromHostRecord);
-
-    // Checks if the record is usable (not expired and has a value)
-    bool HasUsableResult(const mozilla::TimeStamp& now, uint16_t queryFlags = 0) const;
-
-    // Mark hostrecord as not usable
-    void Invalidate();
 
     // hold addr_info_lock when calling the blacklist functions
     bool   Blacklisted(mozilla::net::NetAddr *query);
     void   ResetBlacklist();
     void   ReportUnusable(mozilla::net::NetAddr *addr);
 
-    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+
+    bool IsTRR() { return mTRRUsed; }
+
+private:
+    friend class nsHostResolver;
+
+    explicit AddrHostRecord(const nsHostKey& key);
+    ~AddrHostRecord();
+
+    // Checks if the record is usable (not expired and has a value)
+    bool HasUsableResultInternal() const override;
+
+    void Cancel() override;
+
+    bool RemoveOrRefresh(); // Mark records currently being resolved as needed
+                            // to resolve again.
+
+    void ResolveComplete();
 
     enum DnsPriority {
         DNS_PRIORITY_LOW,
         DNS_PRIORITY_MEDIUM,
         DNS_PRIORITY_HIGH,
     };
     static DnsPriority GetPriority(uint16_t aFlags);
 
-    bool RemoveOrRefresh(); // Mark records currently being resolved as needed
-                            // to resolve again.
-    bool IsTRR() { return mTRRUsed; }
-    void ResolveComplete();
-    void Cancel();
-
-    mozilla::net::ResolverMode mResolverMode;
+    // When the lookups of this record started and their durations
+    mozilla::TimeStamp mTrrStart;
+    mozilla::TimeStamp mNativeStart;
+    mozilla::TimeDuration mTrrDuration;
+    mozilla::TimeDuration mNativeDuration;
 
-    nsTArray<nsCString> mRequestByTypeResult;
-    Mutex mRequestByTypeResultLock;
-
-private:
-    friend class nsHostResolver;
-
-    explicit nsHostRecord(const nsHostKey& key);
-    mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks;
     nsAutoPtr<mozilla::net::AddrInfo> mFirstTRR; // partial TRR storage
     nsresult mFirstTRRresult;
 
-    uint16_t  mResolving;  // counter of outstanding resolving calls
     uint8_t   mTRRSuccess; // number of successful TRR responses
     uint8_t   mNativeSuccess; // number of native lookup responses
 
     uint16_t    mNative : 1;     // true if this record is being resolved "natively",
                                  // which means that it is either on the pending queue
                                  // or owned by one of the worker threads. */
     uint16_t    mTRRUsed : 1;    // TRR was used on this record
     uint16_t    mNativeUsed : 1;
     uint16_t    onQueue : 1;    // true if pending and on the queue (not yet given to getaddrinfo())
     uint16_t    usingAnyThread : 1; // true if off queue and contributing to mActiveAnyThreadCount
-    uint16_t    mDoomed : 1;    // explicitly expired
     uint16_t    mDidCallbacks : 1;
     uint16_t    mGetTtl : 1;
 
     // when the results from this resolve is returned, it is not to be
     // trusted, but instead a new resolve must be made!
     uint16_t    mResolveAgain : 1;
 
     enum {
         INIT, STARTED, OK, FAILED
     } mTrrAUsed, mTrrAAAAUsed;
 
     Mutex mTrrLock; // lock when accessing the mTrrA[AAA] pointers
     RefPtr<mozilla::net::TRR> mTrrA;
     RefPtr<mozilla::net::TRR> mTrrAAAA;
-    RefPtr<mozilla::net::TRR> mTrrTxt;
 
     // The number of times ReportUnusable() has been called in the record's
     // lifetime.
     uint32_t mBlacklistedCount;
 
     // a list of addresses associated with this record that have been reported
     // as unusable. the list is kept as a set of strings to make it independent
     // of gencnt.
     nsTArray<nsCString> mBlacklistedItems;
 
-   ~nsHostRecord();
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(AddrHostRecord, ADDRHOSTRECORD_IID)
+
+// 77b786a7-04be-44f2-987c-ab8aa96676e0
+#define TYPEHOSTRECORD_IID \
+{ 0x77b786a7, 0x04be, 0x44f2, {0x98, 0x7c, 0xab, 0x8a, 0xa9, 0x66, 0x76, 0xe0 }}
+
+class TypeHostRecord final : public nsHostRecord
+{
+public:
+    NS_DECLARE_STATIC_IID_ACCESSOR(TYPEHOSTRECORD_IID)
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+    void GetRecords(nsTArray<nsCString> &aRecords);
+    void GetRecordsAsOneString(nsACString &aRecords);
+
+    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+private:
+    friend class nsHostResolver;
+
+    explicit TypeHostRecord(const nsHostKey& key);
+    ~TypeHostRecord();
+
+    // Checks if the record is usable (not expired and has a value)
+    bool HasUsableResultInternal() const override;
+
+    void Cancel() override;
+
+    bool HasUsableResult();
+
+    mozilla::Mutex mTrrLock; // lock when accessing the mTrr pointer
+    RefPtr<mozilla::net::TRR> mTrr;
+
+    nsTArray<nsCString> mResults;
+    mozilla::Mutex mResultsLock;
+
+    // When the lookups of this record started (for telemetry).
+    mozilla::TimeStamp mStart;
+
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(TypeHostRecord, TYPEHOSTRECORD_IID)
+
 /**
  * This class is used to notify listeners when a ResolveHost operation is
  * complete. Classes that derive it must implement threadsafe nsISupports
  * to be able to use RefPtr with this class.
  */
 class nsResolveHostCallback
     : public mozilla::LinkedListElement<RefPtr<nsResolveHostCallback>>
     , public nsISupports
@@ -274,17 +367,17 @@ public:
         LOOKUP_OK,
         LOOKUP_RESOLVEAGAIN,
     };
 
     virtual LookupStatus CompleteLookup(nsHostRecord *, nsresult, mozilla::net::AddrInfo *, bool pb) = 0;
     virtual LookupStatus CompleteLookupByType(nsHostRecord *, nsresult,
                                               const nsTArray<nsCString> *aResult,
                                               uint32_t aTtl, bool pb) = 0;
-    virtual nsresult GetHostRecord(const nsACString &host,
+    virtual nsresult GetHostRecord(const nsACString &host, uint16_t type,
                                    uint16_t flags, uint16_t af, bool pb,
                                    const nsCString &originSuffix,
                                    nsHostRecord **result)
     {
         return NS_ERROR_FAILURE;
     }
     virtual nsresult TrrLookup_unlocked(nsHostRecord *, mozilla::net::TRR *pushedTRR = nullptr)
     {
@@ -393,17 +486,17 @@ public:
      * Flush the DNS cache.
      */
     void FlushCache();
 
     LookupStatus CompleteLookup(nsHostRecord *, nsresult, mozilla::net::AddrInfo *, bool pb) override;
     LookupStatus CompleteLookupByType(nsHostRecord *, nsresult,
                                       const nsTArray<nsCString> *aResult,
                                       uint32_t aTtl, bool pb) override;
-    nsresult GetHostRecord(const nsACString &host,
+    nsresult GetHostRecord(const nsACString &host, uint16_t type,
                            uint16_t flags, uint16_t af, bool pb,
                            const nsCString &originSuffix,
                            nsHostRecord **result) override;
     nsresult TrrLookup_unlocked(nsHostRecord *, mozilla::net::TRR *pushedTRR = nullptr) override;
 
 private:
    explicit nsHostResolver(uint32_t maxCacheEntries,
                            uint32_t defaultCacheEntryLifetime,
@@ -414,21 +507,21 @@ private:
     // In debug builds it asserts that the element is in the list.
     void AssertOnQ(nsHostRecord *, mozilla::LinkedList<RefPtr<nsHostRecord>>&);
     mozilla::net::ResolverMode Mode();
     nsresult NativeLookup(nsHostRecord *);
     nsresult TrrLookup(nsHostRecord *, mozilla::net::TRR *pushedTRR = nullptr);
 
     // Kick-off a name resolve operation, using native resolver and/or TRR
     nsresult NameLookup(nsHostRecord *);
-    bool     GetHostToLookup(nsHostRecord **m);
+    bool     GetHostToLookup(AddrHostRecord **m);
 
     // Removes the first element from the list and returns it AddRef-ed in aResult
     // Should not be called for an empty linked list.
-    void     DeQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aQ, nsHostRecord **aResult);
+    void     DeQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aQ, AddrHostRecord **aResult);
     // Cancels host records in the pending queue and also
     // calls CompleteLookup with the NS_ERROR_ABORT result code.
     void     ClearPendingQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aPendingQ);
     nsresult ConditionallyCreateThread(nsHostRecord *rec);
 
     /**
      * Starts a new lookup in the background for entries that are in the grace
      * period with a failed connect or all cached entries are negative.
@@ -468,17 +561,17 @@ private:
 
     mozilla::Atomic<bool>     mShutdown;
     mozilla::Atomic<uint32_t> mNumIdleTasks;
     mozilla::Atomic<uint32_t> mActiveTaskCount;
     mozilla::Atomic<uint32_t> mActiveAnyThreadCount;
     mozilla::Atomic<uint32_t> mPendingCount;
 
     // Set the expiration time stamps appropriately.
-    void PrepareRecordExpiration(nsHostRecord* rec) const;
+    void PrepareRecordExpirationAddrRecord(AddrHostRecord* rec) const;
 
 public:
     /*
      * Called by the networking dashboard via the DnsService2
      */
     void GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *);
 };
 
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -3255,27 +3255,49 @@
   "DNS_CLEANUP_AGE": {
     "record_in_processes": ["main"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 1440,
     "n_buckets": 50,
     "description": "DNS Cache Entry Age at Removal Time (minutes)"
   },
+  "DNS_BY_TYPE_CLEANUP_AGE": {
+    "record_in_processes": ["main"],
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1440,
+    "alert_emails": ["necko@mozilla.com", "ddamjanovic@mozilla.com"],
+    "bug_numbers": [1481251],
+    "releaseChannelCollection": "opt-out",
+    "n_buckets": 50,
+    "description": "DNS Cache Entry Age for by-type queries at Removal Time (minutes)"
+  },
   "DNS_PREMATURE_EVICTION": {
     "record_in_processes": ["main"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 1440,
     "releaseChannelCollection": "opt-out",
     "alert_emails": ["necko@mozilla.com", "dstenberg@mozilla.com"],
     "bug_numbers": [1460305],
     "n_buckets": 50,
     "description": "DNS Cache Entry Age at Removal Time of non-expired entries (minutes)"
   },
+  "DNS_BY_TYPE_PREMATURE_EVICTION": {
+    "record_in_processes": ["main"],
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1440,
+    "releaseChannelCollection": "opt-out",
+    "alert_emails": ["necko@mozilla.com", "ddamjanovic@mozilla.com"],
+    "bug_numbers": [1481251],
+    "n_buckets": 50,
+    "description": "DNS Cache Entry Age for by-type queries at Removal Time of non-expired entries (minutes)"
+  },
   "DNS_LOOKUP_TIME": {
     "record_in_processes": ["main"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 60000,
     "releaseChannelCollection": "opt-out",
     "alert_emails": ["necko@mozilla.com", "pmcmanus@mozilla.com"],
     "n_buckets": 50,
@@ -3298,16 +3320,38 @@
     "kind": "exponential",
     "high": 60000,
     "releaseChannelCollection": "opt-out",
     "alert_emails": ["necko@mozilla.com", "dstenberg@mozilla.com"],
     "bug_numbers": [1434852],
     "n_buckets": 50,
     "description": "Time for a completed native name resolution (msec)"
   },
+  "DNS_BY_TYPE_FAILED_LOOKUP_TIME": {
+    "record_in_processes": ["main"],
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 60000,
+    "releaseChannelCollection": "opt-out",
+    "alert_emails": ["necko@mozilla.com", "ddamjanovic@mozilla.com"],
+    "bug_numbers": [1481251],
+    "n_buckets": 50,
+    "description": "Time for a completed by-type resolution that has a negative answer (msec)."
+  },
+  "DNS_BY_TYPE_SUCCEEDED_LOOKUP_TIME": {
+    "record_in_processes": ["main"],
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 60000,
+    "releaseChannelCollection": "opt-out",
+    "alert_emails": ["necko@mozilla.com", "ddamjanovic@mozilla.com"],
+    "bug_numbers": [1481251],
+    "n_buckets": 50,
+    "description": "Time for a completed by-type resolution that succeeded (msec)."
+  },
   "DNS_NATIVE_QUEUING": {
     "record_in_processes": ["main"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 60000,
     "releaseChannelCollection": "opt-out",
     "alert_emails": ["necko@mozilla.com", "dstenberg@mozilla.com"],
     "bug_numbers": [1470215],