Bug 1552176 - Add nsIRequest.set/getTRRMode r=dragana
☠☠ backed out by 0f89eae60891 ☠ ☠
authorValentin Gosu <valentin.gosu@gmail.com>
Fri, 08 Nov 2019 17:13:05 +0000
changeset 501340 ee9911acfcd4d46f67e59153df8513d444f7c4ac
parent 501339 652b3bd6848d4593a14f3095f06daa17f51addce
child 501341 7ce9e220cdb9985131305b61019fa9e2c2e7ab77
push id114168
push userdluca@mozilla.com
push dateSun, 10 Nov 2019 03:08:55 +0000
treeherdermozilla-inbound@33f64c1ef3e4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1552176
milestone72.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 1552176 - Add nsIRequest.set/getTRRMode r=dragana * Makes it possible to selectively enable TRR for pbmode/container/window/etc Differential Revision: https://phabricator.services.mozilla.com/D48363
dom/base/Document.cpp
dom/ipc/BrowserParent.cpp
dom/ipc/RemoteWebProgressRequest.cpp
dom/jsurl/nsJSProtocolHandler.cpp
dom/presentation/PresentationConnection.cpp
dom/websocket/WebSocket.cpp
image/decoders/icon/mac/nsIconChannelCocoa.mm
image/decoders/icon/win/nsIconChannel.cpp
image/imgRequestProxy.cpp
modules/libjar/nsJARChannel.cpp
netwerk/base/NetworkConnectivityService.cpp
netwerk/base/nsAsyncStreamCopier.cpp
netwerk/base/nsBaseChannel.cpp
netwerk/base/nsIRequest.idl
netwerk/base/nsISocketTransport.idl
netwerk/base/nsIncrementalDownload.cpp
netwerk/base/nsInputStreamPump.cpp
netwerk/base/nsLoadGroup.cpp
netwerk/base/nsSocketTransport2.cpp
netwerk/dns/TRR.cpp
netwerk/dns/TRRService.cpp
netwerk/dns/TRRService.h
netwerk/dns/nsHostResolver.cpp
netwerk/dns/nsHostResolver.h
netwerk/dns/nsIDNSService.idl
netwerk/protocol/http/ClassifierDummyChannel.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/NullHttpChannel.cpp
netwerk/protocol/http/nsHttp.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpConnectionInfo.cpp
netwerk/protocol/http/nsHttpConnectionInfo.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
netwerk/streamconv/converters/nsMultiMixedConv.cpp
toolkit/components/captivedetect/CaptiveDetect.jsm
toolkit/components/extensions/webrequest/StreamFilterParent.cpp
uriloader/exthandler/ExternalHelperAppParent.cpp
uriloader/exthandler/nsExternalProtocolHandler.cpp
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -693,16 +693,26 @@ OnloadBlocker::SetLoadGroup(nsILoadGroup
 
 NS_IMETHODIMP
 OnloadBlocker::GetLoadFlags(nsLoadFlags* aLoadFlags) {
   *aLoadFlags = nsIRequest::LOAD_NORMAL;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+OnloadBlocker::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+OnloadBlocker::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 OnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
 
 // ==================================================================
 
 namespace dom {
 
 ExternalResourceMap::ExternalResourceMap() : mHaveShutDown(false) {}
 
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -3580,16 +3580,18 @@ class FakeChannel final : public nsIChan
   NS_IMETHOD GetStatus(nsresult*) NO_IMPL;
   NS_IMETHOD Cancel(nsresult) NO_IMPL;
   NS_IMETHOD Suspend() NO_IMPL;
   NS_IMETHOD Resume() NO_IMPL;
   NS_IMETHOD GetLoadGroup(nsILoadGroup**) NO_IMPL;
   NS_IMETHOD SetLoadGroup(nsILoadGroup*) NO_IMPL;
   NS_IMETHOD SetLoadFlags(nsLoadFlags) NO_IMPL;
   NS_IMETHOD GetLoadFlags(nsLoadFlags*) NO_IMPL;
+  NS_IMETHOD GetTRRMode(nsIRequest::TRRMode* aTRRMode) NO_IMPL;
+  NS_IMETHOD SetTRRMode(nsIRequest::TRRMode aMode) NO_IMPL;
   NS_IMETHOD GetIsDocument(bool*) NO_IMPL;
   NS_IMETHOD GetOriginalURI(nsIURI**) NO_IMPL;
   NS_IMETHOD SetOriginalURI(nsIURI*) NO_IMPL;
   NS_IMETHOD GetURI(nsIURI** aUri) override {
     nsCOMPtr<nsIURI> copy = mUri;
     copy.forget(aUri);
     return NS_OK;
   }
--- a/dom/ipc/RemoteWebProgressRequest.cpp
+++ b/dom/ipc/RemoteWebProgressRequest.cpp
@@ -218,16 +218,26 @@ NS_IMETHODIMP RemoteWebProgressRequest::
 NS_IMETHODIMP RemoteWebProgressRequest::SetLoadGroup(nsILoadGroup* aLoadGroup) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP RemoteWebProgressRequest::GetLoadFlags(nsLoadFlags* aLoadFlags) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP RemoteWebProgressRequest::GetTRRMode(
+    nsIRequest::TRRMode* aTRRMode) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetTRRMode(
+    nsIRequest::TRRMode aTRRMode) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 NS_IMETHODIMP RemoteWebProgressRequest::SetLoadFlags(nsLoadFlags aLoadFlags) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 RemoteWebProgressRequest::IsTrackingResource(bool* aIsTrackingResource) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -829,16 +829,26 @@ nsJSChannel::SetLoadFlags(nsLoadFlags aL
   // ... but the underlying stream channel should get this bit, if
   // set, since that'll be the real document channel if the
   // javascript: URL generated data.
 
   return mStreamChannel->SetLoadFlags(aLoadFlags);
 }
 
 NS_IMETHODIMP
+nsJSChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsJSChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsJSChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
   return mStreamChannel->GetLoadGroup(aLoadGroup);
 }
 
 NS_IMETHODIMP
 nsJSChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
   if (aLoadGroup) {
     bool streamPending;
--- a/dom/presentation/PresentationConnection.cpp
+++ b/dom/presentation/PresentationConnection.cpp
@@ -671,16 +671,26 @@ NS_IMETHODIMP
 PresentationConnection::GetLoadFlags(nsLoadFlags* aLoadFlags) {
   *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationConnection::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
 
+NS_IMETHODIMP
+PresentationConnection::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+PresentationConnection::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
 nsresult PresentationConnection::AddIntoLoadGroup() {
   // Avoid adding to loadgroup multiple times
   if (mWeakLoadGroup) {
     return NS_OK;
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   nsresult rv = GetLoadGroup(getter_AddRefs(loadGroup));
--- a/dom/websocket/WebSocket.cpp
+++ b/dom/websocket/WebSocket.cpp
@@ -2541,16 +2541,26 @@ WebSocketImpl::GetLoadFlags(nsLoadFlags*
 NS_IMETHODIMP
 WebSocketImpl::SetLoadFlags(nsLoadFlags aLoadFlags) {
   AssertIsOnMainThread();
 
   // we won't change the load flags at all.
   return NS_OK;
 }
 
+NS_IMETHODIMP
+WebSocketImpl::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+WebSocketImpl::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
 namespace {
 
 class WorkerRunnableDispatcher final : public WorkerRunnable {
   RefPtr<WebSocketImpl> mWebSocketImpl;
 
  public:
   WorkerRunnableDispatcher(WebSocketImpl* aImpl,
                            ThreadSafeWorkerRef* aWorkerRef,
--- a/image/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -340,16 +340,22 @@ nsIconChannel::GetLoadFlags(uint32_t* aL
 }
 
 NS_IMETHODIMP
 nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes) {
   return mPump->SetLoadFlags(aLoadAttributes);
 }
 
 NS_IMETHODIMP
+nsIconChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { return GetTRRModeImpl(aTRRMode); }
+
+NS_IMETHODIMP
+nsIconChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) { return SetTRRModeImpl(aTRRMode); }
+
+NS_IMETHODIMP
 nsIconChannel::GetIsDocument(bool* aIsDocument) {
   return NS_GetIsDocumentChannel(this, aIsDocument);
 }
 
 NS_IMETHODIMP
 nsIconChannel::GetContentType(nsACString& aContentType) {
   aContentType.AssignLiteral(IMAGE_ICON_MS);
   return NS_OK;
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -227,16 +227,26 @@ nsIconChannel::GetLoadFlags(uint32_t* aL
 }
 
 NS_IMETHODIMP
 nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes) {
   return mPump->SetLoadFlags(aLoadAttributes);
 }
 
 NS_IMETHODIMP
+nsIconChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsIconChannel::GetIsDocument(bool* aIsDocument) {
   return NS_GetIsDocumentChannel(this, aIsDocument);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIChannel methods:
 
 NS_IMETHODIMP
--- a/image/imgRequestProxy.cpp
+++ b/image/imgRequestProxy.cpp
@@ -656,16 +656,26 @@ imgRequestProxy::GetLoadFlags(nsLoadFlag
   return NS_OK;
 }
 NS_IMETHODIMP
 imgRequestProxy::SetLoadFlags(nsLoadFlags flags) {
   mLoadFlags = flags;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+imgRequestProxy::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+imgRequestProxy::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
 /**  imgIRequest methods **/
 
 NS_IMETHODIMP
 imgRequestProxy::GetImage(imgIContainer** aImage) {
   NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
   // It's possible that our owner has an image but hasn't notified us of it -
   // that'll happen if we get Canceled before the owner instantiates its image
   // (because Canceling unregisters us as a listener on mOwner). If we're
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -610,16 +610,26 @@ nsJARChannel::GetLoadFlags(nsLoadFlags* 
 
 NS_IMETHODIMP
 nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
   mLoadFlags = aLoadFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsJARChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsJARChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsJARChannel::GetIsDocument(bool* aIsDocument) {
   return NS_GetIsDocumentChannel(this, aIsDocument);
 }
 
 NS_IMETHODIMP
 nsJARChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
   NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
   return NS_OK;
--- a/netwerk/base/NetworkConnectivityService.cpp
+++ b/netwerk/base/NetworkConnectivityService.cpp
@@ -196,20 +196,21 @@ static inline already_AddRefed<nsIChanne
   rv = NS_NewChannel(
       getter_AddRefs(channel), uri, nsContentUtils::GetSystemPrincipal(),
       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
       nsIContentPolicy::TYPE_OTHER,
       nullptr,  // nsICookieSettings
       nullptr,  // aPerformanceStorage
       nullptr,  // aLoadGroup
       nullptr,
-      nsIRequest::LOAD_BYPASS_CACHE |     // don't read from the cache
-          nsIRequest::INHIBIT_CACHING |   // don't write the response to cache
-          nsIRequest::LOAD_DISABLE_TRR |  // check network capabilities not TRR
-          nsIRequest::LOAD_ANONYMOUS);    // prevent privacy leaks
+      nsIRequest::LOAD_BYPASS_CACHE |    // don't read from the cache
+          nsIRequest::INHIBIT_CACHING |  // don't write the response to cache
+          nsIRequest::LOAD_ANONYMOUS);   // prevent privacy leaks
+
+  channel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
 
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(channel);
   NS_ENSURE_TRUE(internalChan, nullptr);
 
   if (ipv4) {
     internalChan->SetIPv6Disabled();
--- a/netwerk/base/nsAsyncStreamCopier.cpp
+++ b/netwerk/base/nsAsyncStreamCopier.cpp
@@ -198,16 +198,26 @@ nsAsyncStreamCopier::GetLoadFlags(nsLoad
   *aLoadFlags = LOAD_NORMAL;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAsyncStreamCopier::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
 
 NS_IMETHODIMP
+nsAsyncStreamCopier::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return nsIAsyncStreamCopier::GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsAsyncStreamCopier::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return nsIAsyncStreamCopier::SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsAsyncStreamCopier::GetLoadGroup(nsILoadGroup** aLoadGroup) {
   *aLoadGroup = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAsyncStreamCopier::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
 
--- a/netwerk/base/nsBaseChannel.cpp
+++ b/netwerk/base/nsBaseChannel.cpp
@@ -426,16 +426,26 @@ nsBaseChannel::GetLoadFlags(nsLoadFlags*
 
 NS_IMETHODIMP
 nsBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
   mLoadFlags = aLoadFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsBaseChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsBaseChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsBaseChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
   NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsBaseChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
   if (!CanSetLoadGroup(aLoadGroup)) {
--- a/netwerk/base/nsIRequest.idl
+++ b/netwerk/base/nsIRequest.idl
@@ -132,21 +132,78 @@ interface nsIRequest : nsISupports
     const unsigned long LOAD_HTML_OBJECT_DATA = 1 << 1;
 
     /**
      * This flag marks the request as belonging to a document that requires access
      * to the document.cookies API.
      */
     const unsigned long LOAD_DOCUMENT_NEEDS_COOKIE = 1 << 2;
 
+    cenum TRRMode : 8 {
+        TRR_DEFAULT_MODE = 0,
+        TRR_DISABLED_MODE = 1,
+        TRR_FIRST_MODE = 2,
+        TRR_ONLY_MODE = 3
+    };
 
     /**
-     * Set this flag to disable TRR for this request.
+     * These methods encode/decode the TRR mode to/from the loadFlags.
+     * Helper methods Get/SetTRRModeImpl are provided so implementations don't
+     * need to duplicate code.
+     *
+     * Requests with TRR_DEFAULT_MODE will use the mode indicated by the pref
+     *   - see network.trr.mode in all.js
+     * Requests with TRR_DISABLED_MODE will always use native DNS, even if the
+     *   pref is set to mode3 (TRR-only).
+     * Requests with TRR_DISABLED_MODE will first use TRR then fallback to
+     *   regular DNS, unless TRR is disabled by setting the pref to mode5,
+     *   parental control being enabled, or the domain being in the exclusion
+     *   list.
+     * Requests with TRR_ONLY_MODE will only use TRR, unless not allowed by
+     *   the same conditions mentioned above.
      */
-    const unsigned long LOAD_DISABLE_TRR = 1 << 3;
+    nsIRequest_TRRMode getTRRMode();
+    void setTRRMode(in nsIRequest_TRRMode mode);
+
+    %{C++
+        TRRMode GetTRRMode() {
+            TRRMode mode = TRR_DEFAULT_MODE;
+            GetTRRMode(&mode);
+            return mode;
+        }
+
+        nsresult GetTRRModeImpl(nsIRequest::TRRMode* aTRRMode) {
+          NS_ENSURE_ARG_POINTER(aTRRMode);
+          nsLoadFlags flags = nsIRequest::LOAD_NORMAL;
+          nsresult rv = GetLoadFlags(&flags);
+          if (NS_FAILED(rv)) {
+            return rv;
+          }
+          *aTRRMode = static_cast<nsIRequest::TRRMode>(
+              (flags & nsIRequest::LOAD_TRR_MASK) >> 3);
+          return NS_OK;
+        }
+
+        nsresult SetTRRModeImpl(nsIRequest::TRRMode aTRRMode) {
+          MOZ_ASSERT(aTRRMode <= 3, "invalid value");
+          nsLoadFlags flags = nsIRequest::LOAD_NORMAL;
+          nsresult rv = GetLoadFlags(&flags);
+          if (NS_FAILED(rv)) {
+            return rv;
+          }
+          flags = (flags & ~nsIRequest::LOAD_TRR_MASK) | (aTRRMode << 3);
+          return SetLoadFlags(flags);
+        }
+    %}
+
+    /**
+     * These two bits encode the TRR mode.
+     * Do not get/set manually, rather use the getTRRMode/setTRRMode methods.
+     */
+    const unsigned long LOAD_TRR_MASK = (1 << 3) | (1 << 4);
 
     /**************************************************************************
      * The following flags control the flow of data into the cache.
      */
 
     /**
      * This flag prevents caching of any kind.  It does not, however, prevent
      * cached content from being used to satisfy this request.
--- a/netwerk/base/nsISocketTransport.idl
+++ b/netwerk/base/nsISocketTransport.idl
@@ -1,14 +1,15 @@
 /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsITransport.idl"
+#include "nsIRequest.idl"
 
 interface nsIInterfaceRequestor;
 interface nsINetAddr;
 
 %{ C++
 #include "mozilla/BasePrincipal.h"
 namespace mozilla {
 namespace net {
@@ -254,16 +255,33 @@ interface nsISocketTransport : nsITransp
 
     /**
      * If we know that a server speaks only tls <1.3 there is no need to try
      * to use esni and query dns for esni keys.
      */
     const unsigned long DONT_TRY_ESNI = (1 << 10);
 
     /**
+     * These two bits encode the TRR mode of the request.
+     * Use the static helper methods convert between the TRR mode and flags.
+     */
+    const unsigned long TRR_MODE_FLAGS = (1 << 11) | (1 << 12);
+
+%{C++
+
+    static uint32_t GetFlagsFromTRRMode(nsIRequest::TRRMode aMode) {
+        return static_cast<uint32_t>(aMode) << 11;
+    }
+
+    static nsIRequest::TRRMode GetTRRModeFromFlags(uint32_t aFlags) {
+        return static_cast<nsIRequest::TRRMode>((aFlags & TRR_MODE_FLAGS) >> 11);
+    }
+%}
+
+    /**
      * An opaque flags for non-standard behavior of the TLS system.
      * It is unlikely this will need to be set outside of telemetry studies
      * relating to the TLS implementation.
      */
     attribute unsigned long tlsFlags;
 
     /**
      * Socket QoS/ToS markings. Valid values are IPTOS_DSCP_AFxx or
--- a/netwerk/base/nsIncrementalDownload.cpp
+++ b/netwerk/base/nsIncrementalDownload.cpp
@@ -367,16 +367,26 @@ nsIncrementalDownload::GetLoadFlags(nsLo
 
 NS_IMETHODIMP
 nsIncrementalDownload::SetLoadFlags(nsLoadFlags loadFlags) {
   mLoadFlags = loadFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsIncrementalDownload::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsIncrementalDownload::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsIncrementalDownload::GetLoadGroup(nsILoadGroup** loadGroup) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsIncrementalDownload::SetLoadGroup(nsILoadGroup* loadGroup) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/netwerk/base/nsInputStreamPump.cpp
+++ b/netwerk/base/nsInputStreamPump.cpp
@@ -240,16 +240,26 @@ NS_IMETHODIMP
 nsInputStreamPump::SetLoadFlags(nsLoadFlags aLoadFlags) {
   RecursiveMutexAutoLock lock(mMutex);
 
   mLoadFlags = aLoadFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsInputStreamPump::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsInputStreamPump::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsInputStreamPump::GetLoadGroup(nsILoadGroup** aLoadGroup) {
   RecursiveMutexAutoLock lock(mMutex);
 
   NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/nsLoadGroup.cpp
+++ b/netwerk/base/nsLoadGroup.cpp
@@ -340,16 +340,26 @@ nsLoadGroup::GetLoadFlags(uint32_t* aLoa
 
 NS_IMETHODIMP
 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags) {
   mLoadFlags = aLoadFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsLoadGroup::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsLoadGroup::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsLoadGroup::GetLoadGroup(nsILoadGroup** loadGroup) {
   nsCOMPtr<nsILoadGroup> result = mLoadGroup;
   result.forget(loadGroup);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLoadGroup::SetLoadGroup(nsILoadGroup* loadGroup) {
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -1034,16 +1034,19 @@ nsresult nsSocketTransport::ResolveHost(
     dnsFlags = nsIDNSService::RESOLVE_REFRESH_CACHE;
   if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6)
     dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
   if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4)
     dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
   if (mConnectionFlags & nsSocketTransport::DISABLE_TRR)
     dnsFlags |= nsIDNSService::RESOLVE_DISABLE_TRR;
 
+  dnsFlags |= nsIDNSService::GetFlagsFromTRRMode(
+      nsISocketTransport::GetTRRModeFromFlags(mConnectionFlags));
+
   NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
                    !(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
                "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
 
   SendStatus(NS_NET_STATUS_RESOLVING_HOST);
 
   if (!SocketHost().Equals(mOriginHost)) {
     SOCKET_LOG(("nsSocketTransport %p origin %s doing dns for %s\n", this,
--- a/netwerk/dns/TRR.cpp
+++ b/netwerk/dns/TRR.cpp
@@ -168,18 +168,20 @@ nsresult TRR::SendHTTPRequest() {
 
   if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
       (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT)) {
     // limit the calling interface because nsHostResolver has explicit slots for
     // these types
     return NS_ERROR_FAILURE;
   }
 
-  if ((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) {
+  if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) &&
+      mRec->EffectiveTRRMode() != nsIRequest::TRR_ONLY_MODE) {
     // let NS resolves skip the blacklist check
+    // we also don't check the blacklist for TRR only requests
     MOZ_ASSERT(mRec);
 
     if (gTRRService->IsTRRBlacklisted(mHost, mOriginSuffix, mPB, true)) {
       if (mType == TRRTYPE_A) {
         // count only blacklist for A records to avoid double counts
         Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED, true);
       }
       // not really an error but no TRR is issued
@@ -249,16 +251,20 @@ nsresult TRR::SendHTTPRequest() {
     return rv;
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   if (!httpChannel) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  // This connection should not use TRR
+  rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   rv = httpChannel->SetRequestHeader(
       NS_LITERAL_CSTRING("Accept"),
       NS_LITERAL_CSTRING("application/dns-message"), false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString cred;
   gTRRService->GetCredentials(cred);
   if (!cred.IsEmpty()) {
--- a/netwerk/dns/TRRService.cpp
+++ b/netwerk/dns/TRRService.cpp
@@ -17,16 +17,18 @@
 #include "mozilla/StaticPrefs_network.h"
 #include "mozilla/Tokenizer.h"
 
 static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
 static const char kClearPrivateData[] = "clear-private-data";
 static const char kPurge[] = "browser:purge-session-history";
 static const char kDisableIpv6Pref[] = "network.dns.disableIPv6";
 static const char kCaptivedetectCanonicalURL[] = "captivedetect.canonicalURL";
+static const char kPrefSkipTRRParentalControl[] =
+    "network.dns.skipTRR-when-parental-control-enabled";
 
 #define TRR_PREF_PREFIX "network.trr."
 #define TRR_PREF(x) TRR_PREF_PREFIX x
 
 namespace mozilla {
 namespace net {
 
 #undef LOG
@@ -43,16 +45,17 @@ TRRService::TRRService()
       mTRRBlacklistExpireTime(72 * 3600),
       mLock("trrservice"),
       mConfirmationNS(NS_LITERAL_CSTRING("example.com")),
       mWaitForCaptive(true),
       mRfc1918(false),
       mCaptiveIsPassed(false),
       mUseGET(false),
       mDisableECS(true),
+      mSkipTRRWhenParentalControlEnabled(true),
       mDisableAfterFails(5),
       mClearTRRBLStorage(false),
       mConfirmationState(CONFIRM_INIT),
       mRetryConfirmInterval(1000),
       mTRRFailures(0),
       mParentalControlEnabled(false) {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
 }
@@ -74,16 +77,17 @@ nsresult TRRService::Init() {
     observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
   }
   nsCOMPtr<nsIPrefBranch> prefBranch;
   GetPrefBranch(getter_AddRefs(prefBranch));
   if (prefBranch) {
     prefBranch->AddObserver(TRR_PREF_PREFIX, this, true);
     prefBranch->AddObserver(kDisableIpv6Pref, this, true);
     prefBranch->AddObserver(kCaptivedetectCanonicalURL, this, true);
+    prefBranch->AddObserver(kPrefSkipTRRParentalControl, this, true);
   }
   nsCOMPtr<nsICaptivePortalService> captivePortalService =
       do_GetService(NS_CAPTIVEPORTAL_CID);
   if (captivePortalService) {
     int32_t captiveState;
     MOZ_ALWAYS_SUCCEEDS(captivePortalService->GetState(&captiveState));
 
     if ((captiveState == nsICaptivePortalService::UNLOCKED_PORTAL) ||
@@ -109,19 +113,23 @@ void TRRService::GetParentalControlEnabl
       do_CreateInstance("@mozilla.org/parental-controls-service;1");
   if (pc) {
     pc->GetParentalControlsEnabled(&mParentalControlEnabled);
     LOG(("TRRService::GetParentalControlEnabledInternal=%d\n",
          mParentalControlEnabled));
   }
 }
 
-bool TRRService::Enabled() {
+bool TRRService::Enabled(nsIRequest::TRRMode aMode) {
+  if (mMode == MODE_TRROFF) {
+    return false;
+  }
   if (mConfirmationState == CONFIRM_INIT &&
-      (!mWaitForCaptive || mCaptiveIsPassed || (mMode == MODE_TRRONLY))) {
+      (!mWaitForCaptive || mCaptiveIsPassed ||
+       (mMode == MODE_TRRONLY || aMode == nsIRequest::TRR_ONLY_MODE))) {
     LOG(("TRRService::Enabled => CONFIRM_TRYING\n"));
     mConfirmationState = CONFIRM_TRYING;
   }
 
   if (mConfirmationState == CONFIRM_TRYING) {
     LOG(("TRRService::Enabled MaybeConfirm()\n"));
     MaybeConfirm();
   }
@@ -337,16 +345,23 @@ nsresult TRRService::ReadPrefs(const cha
     if (NS_SUCCEEDED(rv)) {
       nsAutoCString host;
       uri->GetHost(host);
       LOG(("TRRService::ReadPrefs captive portal URL:[%s]\n", host.get()));
       mExcludedDomains.PutEntry(host);
     }
   }
 
+  if (!name || !strcmp(name, kPrefSkipTRRParentalControl)) {
+    bool tmp;
+    if (NS_SUCCEEDED(Preferences::GetBool(kPrefSkipTRRParentalControl, &tmp))) {
+      mSkipTRRWhenParentalControlEnabled = tmp;
+    }
+  }
+
   // if name is null, then we're just now initializing. In that case we don't
   // need to clear the cache.
   if (name && clearEntireCache) {
     bool tmp;
     if (NS_SUCCEEDED(Preferences::GetBool(
             TRR_PREF("clear-cache-on-pref-change"), &tmp)) &&
         tmp) {
       nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
@@ -429,27 +444,30 @@ TRRService::Observe(nsISupports* aSubjec
           if (mTRRBLStorage) {
             mTRRBLStorage->Clear();
           }
           mClearTRRBLStorage = false;
         }
       }
     }
 
-    if (!mCaptiveIsPassed) {
-      if (mConfirmationState != CONFIRM_OK) {
-        mConfirmationState = CONFIRM_TRYING;
-        MaybeConfirm();
+    // We should avoid doing calling MaybeConfirm in response to a pref change
+    // unless the service is in a TRR=enabled mode.
+    if (mMode == MODE_TRRFIRST || mMode == MODE_TRRONLY) {
+      if (!mCaptiveIsPassed) {
+        if (mConfirmationState != CONFIRM_OK) {
+          mConfirmationState = CONFIRM_TRYING;
+          MaybeConfirm();
+        }
+      } else {
+        LOG(("TRRservice CP clear when already up!\n"));
       }
-    } else {
-      LOG(("TRRservice CP clear when already up!\n"));
+      mCaptiveIsPassed = true;
     }
 
-    mCaptiveIsPassed = true;
-
   } else if (!strcmp(aTopic, kClearPrivateData) || !strcmp(aTopic, kPurge)) {
     // flush the TRR blacklist, both in-memory and on-disk
     if (mTRRBLStorage) {
       mTRRBLStorage->Clear();
     }
   } else if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) {
     mDNSSuffixDomains.Clear();
     nsAutoCString data = NS_ConvertUTF16toUTF8(aData);
@@ -471,17 +489,17 @@ TRRService::Observe(nsISupports* aSubjec
 
 void TRRService::MaybeConfirm() {
   MutexAutoLock lock(mLock);
   MaybeConfirm_locked();
 }
 
 void TRRService::MaybeConfirm_locked() {
   mLock.AssertCurrentThreadOwns();
-  if (TRR_DISABLED(mMode) || mConfirmer ||
+  if (mMode == MODE_TRROFF || mConfirmer ||
       mConfirmationState != CONFIRM_TRYING) {
     LOG(
         ("TRRService:MaybeConfirm mode=%d, mConfirmer=%p "
          "mConfirmationState=%d\n",
          (int)mMode, (void*)mConfirmer, (int)mConfirmationState));
     return;
   }
 
@@ -496,17 +514,17 @@ void TRRService::MaybeConfirm_locked() {
         new TRR(this, mConfirmationNS, TRRTYPE_NS, EmptyCString(), false);
     NS_DispatchToMainThread(mConfirmer);
   }
 }
 
 bool TRRService::MaybeBootstrap(const nsACString& aPossible,
                                 nsACString& aResult) {
   MutexAutoLock lock(mLock);
-  if (TRR_DISABLED(mMode) || mBootstrapAddr.IsEmpty()) {
+  if (mMode == MODE_TRROFF || mBootstrapAddr.IsEmpty()) {
     return false;
   }
 
   nsCOMPtr<nsIURI> url;
   nsresult rv =
       NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
           .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init,
                                   nsIStandardURL::URLTYPE_STANDARD, 443,
@@ -553,17 +571,17 @@ bool TRRService::IsTRRBlacklisted(const 
     LOG(("Host [%s] is TRR blacklisted via pref\n", aHost.BeginReading()));
     return true;
   }
   if (mDNSSuffixDomains.GetEntry(aHost)) {
     LOG(("Host [%s] is TRR blacklisted dns suffix\n", aHost.BeginReading()));
     return true;
   }
 
-  if (!Enabled()) {
+  if (!Enabled(nsIRequest::TRR_DEFAULT_MODE)) {
     return true;
   }
 
   int32_t dot = aHost.FindChar('.');
   if ((dot == kNotFound) && aParentsToo) {
     // Only if a full host name. Domains can be dotless to be able to
     // blacklist entire TLDs
     return true;
--- a/netwerk/dns/TRRService.h
+++ b/netwerk/dns/TRRService.h
@@ -25,26 +25,29 @@ class TRRService : public nsIObserver,
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITIMERCALLBACK
 
   TRRService();
   nsresult Init();
   nsresult Start();
-  bool Enabled();
+  bool Enabled(nsIRequest::TRRMode aMode);
 
   uint32_t Mode() { return mMode; }
   bool AllowRFC1918() { return mRfc1918; }
   bool UseGET() { return mUseGET; }
   bool EarlyAAAA() { return mEarlyAAAA; }
   bool CheckIPv6Connectivity() { return mCheckIPv6Connectivity; }
   bool WaitForAllResponses() { return mWaitForAllResponses; }
   bool DisableIPv6() { return mDisableIPv6; }
   bool DisableECS() { return mDisableECS; }
+  bool SkipTRRWhenParentalControlEnabled() {
+    return mSkipTRRWhenParentalControlEnabled;
+  }
   nsresult GetURI(nsCString& result);
   nsresult GetCredentials(nsCString& result);
   uint32_t GetRequestTimeout();
 
   LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*,
                               bool pb,
                               const nsACString& aOriginSuffix) override;
   LookupStatus CompleteLookupByType(nsHostRecord*, nsresult,
@@ -89,16 +92,17 @@ class TRRService : public nsIObserver,
   Atomic<bool, Relaxed>
       mCaptiveIsPassed;           // set when captive portal check is passed
   Atomic<bool, Relaxed> mUseGET;  // do DOH using GET requests (instead of POST)
   Atomic<bool, Relaxed> mEarlyAAAA;  // allow use of AAAA results before A is in
   Atomic<bool, Relaxed> mCheckIPv6Connectivity;  // check IPv6 connectivity
   Atomic<bool, Relaxed> mWaitForAllResponses;  // Don't notify until all are in
   Atomic<bool, Relaxed> mDisableIPv6;          // don't even try
   Atomic<bool, Relaxed> mDisableECS;  // disable EDNS Client Subnet in requests
+  Atomic<bool, Relaxed> mSkipTRRWhenParentalControlEnabled;
   Atomic<uint32_t, Relaxed>
       mDisableAfterFails;  // this many fails in a row means failed TRR service
 
   // TRR Blacklist storage
   // mTRRBLStorage is only modified on the main thread, but we query whether it
   // is initialized or not off the main thread as well. Therefore we need to
   // lock while creating it and while accessing it off the main thread.
   RefPtr<DataStorage> mTRRBLStorage;
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -147,35 +147,31 @@ static inline bool IsMediumPriority(uint
 static inline bool IsLowPriority(uint16_t flags) {
   return flags & nsHostResolver::RES_PRIORITY_LOW;
 }
 
 //----------------------------------------------------------------------------
 // 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 RES_KEY_FLAGS(_f)                                                     \
+  ((_f) & (nsHostResolver::RES_CANON_NAME | nsHostResolver::RES_DISABLE_TRR | \
+           nsIDNSService::RESOLVE_TRR_MODE_MASK))
 
 #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) {
-  if (TRR_DISABLED(gTRRService->Mode())) {
-    // When not using TRR, lookup all answers as TRR-disabled
-    flags |= nsHostResolver::RES_DISABLE_TRR;
-  }
-}
+      originSuffix(aOriginsuffix) {}
 
 bool nsHostKey::operator==(const nsHostKey& other) const {
   return host == other.host && type == other.type &&
          RES_KEY_FLAGS(flags) == RES_KEY_FLAGS(other.flags) && af == other.af &&
          originSuffix == other.originSuffix;
 }
 
 PLDHashNumber nsHostKey::Hash() const {
@@ -542,18 +538,16 @@ void TypeHostRecord::Cancel() {
 }
 
 //----------------------------------------------------------------------------
 
 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 const char kPrefSkipTRRParentalControl[] =
-    "network.dns.skipTRR-when-parental-control-enabled";
 static bool sGetTtlEnabled = false;
 mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
 
 static void DnsPrefChanged(const char* aPref, void* aSelf) {
   MOZ_ASSERT(NS_IsMainThread(),
              "Should be getting pref changed notification on main thread!");
 
   MOZ_ASSERT(aSelf);
@@ -573,17 +567,16 @@ nsHostResolver::nsHostResolver(uint32_t 
                                uint32_t defaultCacheEntryLifetime,
                                uint32_t defaultGracePeriod)
     : mMaxCacheEntries(maxCacheEntries),
       mDefaultCacheLifetime(defaultCacheEntryLifetime),
       mDefaultGracePeriod(defaultGracePeriod),
       mLock("nsHostResolver.mLock"),
       mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV"),
       mEvictionQSize(0),
-      mSkipTRRWhenParentalControlEnabled(true),
       mShutdown(true),
       mNumIdleTasks(0),
       mActiveTaskCount(0),
       mActiveAnyThreadCount(0),
       mPendingCount(0) {
   mCreationTime = PR_Now();
 
   mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
@@ -597,18 +590,16 @@ nsresult nsHostResolver::Init() {
   if (NS_FAILED(GetAddrInfoInit())) {
     return NS_ERROR_FAILURE;
   }
 
   LOG(("nsHostResolver::Init this=%p", this));
 
   mShutdown = false;
   mNCS = NetworkConnectivityService::GetSingleton();
-  mSkipTRRWhenParentalControlEnabled =
-      Preferences::GetBool(kPrefSkipTRRParentalControl, true);
 
   // The preferences probably haven't been loaded from the disk yet, so we
   // need to register a callback that will set up the experiment once they
   // are. We also need to explicitly set a value for the props otherwise the
   // callback won't be called.
   {
     DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
         &DnsPrefChanged, kPrefGetTtl, this);
@@ -824,17 +815,17 @@ nsresult nsHostResolver::ResolveHost(con
        flags & RES_REFRESH_CACHE ? " - refresh cache" : "", 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())) {
+  if (IS_OTHER_TYPE(type) && Mode() == MODE_TRROFF) {
     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));
@@ -1190,16 +1181,20 @@ nsresult nsHostResolver::ConditionallyCr
 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) {
+  if (Mode() == MODE_TRROFF) {
+    return NS_ERROR_UNKNOWN_HOST;
+  }
+
   RefPtr<nsHostRecord> rec(aRec);
   mLock.AssertCurrentThreadOwns();
 
   RefPtr<AddrHostRecord> addrRec;
   RefPtr<TypeHostRecord> typeRec;
 
   if (rec->IsAddrRecord()) {
     addrRec = do_QueryObject(rec);
@@ -1212,17 +1207,18 @@ nsresult nsHostResolver::TrrLookup(nsHos
 #ifdef DEBUG
   if (rec->IsAddrRecord()) {
     MutexAutoLock trrlock(addrRec->mTrrLock);
     MOZ_ASSERT(!TRROutstanding());
   }
 #endif
   MOZ_ASSERT(!rec->mResolving);
 
-  if (!gTRRService || !gTRRService->Enabled()) {
+  nsIRequest::TRRMode reqMode = rec->EffectiveTRRMode();
+  if (!gTRRService || !gTRRService->Enabled(reqMode)) {
     LOG(("TrrLookup:: %s service not enabled\n", rec->host.get()));
     return NS_ERROR_UNKNOWN_HOST;
   }
 
   if (rec->isInList()) {
     // we're already on the eviction queue. This is a renewal
     MOZ_ASSERT(mEvictionQSize);
     AssertOnQ(rec, mEvictionQ);
@@ -1373,64 +1369,92 @@ nsresult nsHostResolver::NativeLookup(ns
 ResolverMode nsHostResolver::Mode() {
   if (gTRRService) {
     return static_cast<ResolverMode>(gTRRService->Mode());
   }
 
   return MODE_NATIVEONLY;
 }
 
+nsIRequest::TRRMode nsHostRecord::TRRMode() {
+  return nsIDNSService::GetTRRModeFromFlags(flags);
+}
+
+nsIRequest::TRRMode nsHostRecord::EffectiveTRRMode() {
+  // For domains that are excluded from TRR or when parental control is enabled,
+  // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
+  // localhost and local are excluded (so we cover *.local hosts) See the
+  // network.trr.excluded-domains pref.
+  bool skipTRR = true;
+  if (gTRRService) {
+    skipTRR = gTRRService->IsExcludedFromTRR(host) ||
+              (gTRRService->SkipTRRWhenParentalControlEnabled() &&
+               gTRRService->ParentalControlEnabled());
+  }
+
+  nsIRequest::TRRMode aRequestMode = TRRMode();
+  if (!gTRRService) {
+    return aRequestMode;
+  }
+
+  ResolverMode aResolverMode = static_cast<ResolverMode>(gTRRService->Mode());
+
+  if (skipTRR || aResolverMode == MODE_TRROFF ||
+      aRequestMode == nsIRequest::TRR_DISABLED_MODE ||
+      (aRequestMode == nsIRequest::TRR_DEFAULT_MODE &&
+       aResolverMode == MODE_NATIVEONLY)) {
+    return nsIRequest::TRR_DISABLED_MODE;
+  }
+
+  if (aRequestMode == nsIRequest::TRR_DEFAULT_MODE &&
+      aResolverMode == MODE_TRRFIRST) {
+    return nsIRequest::TRR_FIRST_MODE;
+  }
+
+  if (aRequestMode == nsIRequest::TRR_DEFAULT_MODE &&
+      aResolverMode == MODE_TRRONLY) {
+    return nsIRequest::TRR_ONLY_MODE;
+  }
+
+  return aRequestMode;
+}
+
 // Kick-off a name resolve operation, using native resolver and/or TRR
 nsresult nsHostResolver::NameLookup(nsHostRecord* rec) {
   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();
-  MOZ_ASSERT(mode != MODE_RESERVED1);
+  rec->mResolverMode = Mode();
+  MOZ_ASSERT(rec->mResolverMode != MODE_RESERVED1);
 
   if (rec->IsAddrRecord()) {
     RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
     MOZ_ASSERT(addrRec);
 
     addrRec->mNativeUsed = false;
     addrRec->mTRRUsed = false;
     addrRec->mNativeSuccess = false;
     addrRec->mTRRSuccess = 0;
     addrRec->mDidCallbacks = false;
     addrRec->mTrrAUsed = AddrHostRecord::INIT;
     addrRec->mTrrAAAAUsed = AddrHostRecord::INIT;
   }
 
-  // For domains that are excluded from TRR or when parental control is enabled,
-  // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
-  // localhost and local are excluded (so we cover *.local hosts) See the
-  // network.trr.excluded-domains pref.
-  bool skipTRR = true;
-  if (gTRRService) {
-    skipTRR = gTRRService->IsExcludedFromTRR(rec->host) ||
-              (mSkipTRRWhenParentalControlEnabled &&
-               gTRRService->ParentalControlEnabled());
-  }
-
-  if (rec->flags & RES_DISABLE_TRR) {
-    if (mode == MODE_TRRONLY && !skipTRR) {
-      return rv;
-    }
-    mode = MODE_NATIVEONLY;
-  }
-
-  if (!TRR_DISABLED(mode) && !skipTRR) {
+  nsIRequest::TRRMode effectiveRequestMode = rec->EffectiveTRRMode();
+  if (effectiveRequestMode != nsIRequest::TRR_DISABLED_MODE &&
+      !((rec->flags & RES_DISABLE_TRR))) {
     rv = TrrLookup(rec);
   }
 
-  if (TRR_DISABLED(mode) || ((mode == MODE_TRRFIRST) && NS_FAILED(rv)) ||
-      (mode == MODE_TRRONLY && skipTRR)) {
+  if (effectiveRequestMode == nsIRequest::TRR_DISABLED_MODE ||
+      (effectiveRequestMode == nsIRequest::TRR_FIRST_MODE &&
+       (rec->flags & RES_DISABLE_TRR) && NS_FAILED(rv))) {
     if (!rec->IsAddrRecord()) {
       return rv;
     }
     rv = NativeLookup(rec);
   }
 
   return rv;
 }
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -76,16 +76,22 @@ class nsHostRecord : public mozilla::Lin
                      public nsISupports {
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     return 0;
   }
 
+  // Returns the TRR mode encoded by the flags
+  nsIRequest::TRRMode TRRMode();
+  // Returns a TRR mode that takes into account if the TRR service is disabled,
+  // parental controls are on, domain matches exclusion list, etc.
+  nsIRequest::TRRMode EffectiveTRRMode();
+
  protected:
   friend class nsHostResolver;
 
   explicit nsHostRecord(const nsHostKey& key);
   virtual ~nsHostRecord() = default;
 
   // Mark hostrecord as not usable
   void Invalidate();
@@ -546,17 +552,16 @@ class nsHostResolver : public nsISupport
   mozilla::LinkedList<RefPtr<nsHostRecord>> mHighQ;
   mozilla::LinkedList<RefPtr<nsHostRecord>> mMediumQ;
   mozilla::LinkedList<RefPtr<nsHostRecord>> mLowQ;
   mozilla::LinkedList<RefPtr<nsHostRecord>> mEvictionQ;
   uint32_t mEvictionQSize;
   PRTime mCreationTime;
   mozilla::TimeDuration mLongIdleTimeout;
   mozilla::TimeDuration mShortIdleTimeout;
-  bool mSkipTRRWhenParentalControlEnabled;
 
   RefPtr<nsIThreadPool> mResolverThreads;
   RefPtr<mozilla::net::NetworkConnectivityService> mNCS;
 
   mozilla::Atomic<bool> mShutdown;
   mozilla::Atomic<uint32_t> mNumIdleTasks;
   mozilla::Atomic<uint32_t> mActiveTaskCount;
   mozilla::Atomic<uint32_t> mActiveAnyThreadCount;
--- a/netwerk/dns/nsIDNSService.idl
+++ b/netwerk/dns/nsIDNSService.idl
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
+#include "nsIRequest.idl"
 
 %{ C++
 #include "mozilla/BasePrincipal.h"
 %}
 
 interface nsICancelable;
 interface nsIEventTarget;
 interface nsIDNSRecord;
@@ -290,15 +291,31 @@ interface nsIDNSService : nsISupports
 
     /**
      * if set (together with RESOLVE_BYPASS_CACHE), invalidate the DNS
      * existing cache entry first (if existing) then make a new resolve.
      */
     const unsigned long RESOLVE_REFRESH_CACHE = (1 << 10);
 
     /**
+     * These two bits encode the TRR mode of the request.
+     * Use the static helper methods convert between the TRR mode and flags.
+     */
+    const unsigned long RESOLVE_TRR_MODE_MASK = (1 << 11) | (1 << 12);
+
+%{C++
+    static uint32_t GetFlagsFromTRRMode(nsIRequest::TRRMode aMode) {
+        return static_cast<uint32_t>(aMode) << 11;
+    }
+
+    static nsIRequest::TRRMode GetTRRModeFromFlags(uint32_t aFlags) {
+        return static_cast<nsIRequest::TRRMode>((aFlags & RESOLVE_TRR_MODE_MASK) >> 11);
+    }
+%}
+
+    /**
      * This ure dns request types that are currently supported.
      * RESOLVE_TYPE_DEFAULT is standard A/AAAA lookup
      * The others (currently only TXT supported) are wireformat types
      */
     const unsigned long RESOLVE_TYPE_DEFAULT = 0;
     const unsigned long RESOLVE_TYPE_TXT = 16;
 };
--- a/netwerk/protocol/http/ClassifierDummyChannel.cpp
+++ b/netwerk/protocol/http/ClassifierDummyChannel.cpp
@@ -292,16 +292,26 @@ ClassifierDummyChannel::SetLoadGroup(nsI
 }
 
 NS_IMETHODIMP
 ClassifierDummyChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+ClassifierDummyChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ClassifierDummyChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 ClassifierDummyChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 ClassifierDummyChannel::GetIsDocument(bool* aIsDocument) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -480,16 +480,26 @@ HttpBaseChannel::GetLoadFlags(nsLoadFlag
 
 NS_IMETHODIMP
 HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
   mLoadFlags = aLoadFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HttpBaseChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::SetDocshellUserAgentOverride() {
   // This sets the docshell specific user agent override
   nsresult rv;
   nsCOMPtr<nsILoadContext> loadContext;
   NS_QueryNotificationCallbacks(this, loadContext);
   if (!loadContext) {
     return NS_OK;
   }
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -136,16 +136,18 @@ class HttpBaseChannel : public nsHashPro
   // nsIRequest
   NS_IMETHOD GetName(nsACString& aName) override;
   NS_IMETHOD IsPending(bool* aIsPending) override;
   NS_IMETHOD GetStatus(nsresult* aStatus) override;
   NS_IMETHOD GetLoadGroup(nsILoadGroup** aLoadGroup) override;
   NS_IMETHOD SetLoadGroup(nsILoadGroup* aLoadGroup) override;
   NS_IMETHOD GetLoadFlags(nsLoadFlags* aLoadFlags) override;
   NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags) override;
+  NS_IMETHOD GetTRRMode(nsIRequest::TRRMode* aTRRMode) override;
+  NS_IMETHOD SetTRRMode(nsIRequest::TRRMode aTRRMode) override;
   NS_IMETHOD SetDocshellUserAgentOverride();
 
   // nsIChannel
   NS_IMETHOD GetOriginalURI(nsIURI** aOriginalURI) override;
   NS_IMETHOD SetOriginalURI(nsIURI* aOriginalURI) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetOwner(nsISupports** aOwner) override;
   NS_IMETHOD SetOwner(nsISupports* aOwner) override;
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -445,16 +445,26 @@ NullHttpChannel::SetLoadGroup(nsILoadGro
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+NullHttpChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetIsDocument(bool* aIsDocument) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -106,36 +106,40 @@ const char kHttp3VersionHEX[] = "ff00000
 
 // Transactions with this flag should be processed first.
 #define NS_HTTP_URGENT_START (1 << 12)
 
 // A sticky connection of the transaction is explicitly allowed to be restarted
 // on ERROR_NET_RESET.
 #define NS_HTTP_CONNECTION_RESTARTABLE (1 << 13)
 
-// Disallow name resolutions for this transaction to use TRR - primarily
-// for use with TRR implementations themselves
-#define NS_HTTP_DISABLE_TRR (1 << 14)
-
 // Allow re-using a spdy/http2 connection with NS_HTTP_ALLOW_KEEPALIVE not set.
 // This is primarily used to allow connection sharing for websockets over http/2
 // without accidentally allowing it for websockets not over http/2
 #define NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE (1 << 15)
 
 // Only permit CONNECTing to a proxy. A channel with this flag will not send an
 // http request after CONNECT or setup tls. An http upgrade handler MUST be
 // set. An ALPN header is set using the upgrade protocol.
 #define NS_HTTP_CONNECT_ONLY (1 << 16)
 
 // The connection should not use IPv4.
 #define NS_HTTP_DISABLE_IPV4 (1 << 17)
 
 // The connection should not use IPv6
 #define NS_HTTP_DISABLE_IPV6 (1 << 18)
 
+// Encodes the TRR mode.
+#define NS_HTTP_TRR_MODE_MASK ((1 << 19) | (1 << 20))
+
+#define NS_HTTP_TRR_FLAGS_FROM_MODE(x) ((static_cast<uint32_t>(x) & 3) << 19)
+
+#define NS_HTTP_TRR_MODE_FROM_FLAGS(x) \
+  (static_cast<nsIRequest::TRRMode>((((x)&NS_HTTP_TRR_MODE_MASK) >> 19) & 3))
+
 //-----------------------------------------------------------------------------
 // some default values
 //-----------------------------------------------------------------------------
 
 #define NS_HTTP_DEFAULT_PORT 80
 #define NS_HTTPS_DEFAULT_PORT 443
 
 #define NS_HTTP_HEADER_SEPS ", \t"
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -615,33 +615,31 @@ nsresult nsHttpChannel::ContinueOnBefore
       // we absolutely do want to disable keepalive).
       mCaps |= NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE;
     } else {
       mCaps |= NS_HTTP_DISALLOW_SPDY;
     }
   }
 
   if (mIsTRRServiceChannel) {
-    mCaps |= NS_HTTP_LARGE_KEEPALIVE | NS_HTTP_DISABLE_TRR;
-  }
-
-  if (mLoadFlags & LOAD_DISABLE_TRR) {
-    mCaps |= NS_HTTP_DISABLE_TRR;
-  }
+    mCaps |= NS_HTTP_LARGE_KEEPALIVE;
+  }
+
+  mCaps |= NS_HTTP_TRR_FLAGS_FROM_MODE(nsIRequest::GetTRRMode());
 
   // Finalize ConnectionInfo flags before SpeculativeConnect
   mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
   mConnectionInfo->SetPrivate(mPrivateBrowsing);
   mConnectionInfo->SetIsolated(IsIsolated());
   mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
   mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) ||
                                      mBeConservative);
   mConnectionInfo->SetTlsFlags(mTlsFlags);
   mConnectionInfo->SetIsTrrServiceChannel(mIsTRRServiceChannel);
-  mConnectionInfo->SetTrrDisabled(mCaps & NS_HTTP_DISABLE_TRR);
+  mConnectionInfo->SetTRRMode(nsIRequest::GetTRRMode());
   mConnectionInfo->SetIPv4Disabled(mCaps & NS_HTTP_DISABLE_IPV4);
   mConnectionInfo->SetIPv6Disabled(mCaps & NS_HTTP_DISABLE_IPV6);
 
   // notify "http-on-before-connect" observers
   gHttpHandler->OnBeforeConnect(this);
 
   // Check if request was cancelled during http-on-before-connect.
   if (mCanceled) {
@@ -904,17 +902,17 @@ void nsHttpChannel::SpeculativeConnect()
 
   nsCOMPtr<nsIInterfaceRequestor> callbacks;
   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
                                          getter_AddRefs(callbacks));
   if (!callbacks) return;
 
   Unused << gHttpHandler->SpeculativeConnect(
       mConnectionInfo, callbacks,
-      mCaps & (NS_HTTP_DISALLOW_SPDY | NS_HTTP_DISABLE_TRR |
+      mCaps & (NS_HTTP_DISALLOW_SPDY | NS_HTTP_TRR_MODE_MASK |
                NS_HTTP_DISABLE_IPV4 | NS_HTTP_DISABLE_IPV6));
 }
 
 void nsHttpChannel::DoNotifyListenerCleanup() {
   // We don't need this info anymore
   CleanRedirectCacheChainIfNecessary();
 }
 
--- a/netwerk/protocol/http/nsHttpConnectionInfo.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.cpp
@@ -101,17 +101,17 @@ void nsHttpConnectionInfo::Init(const ns
   mProxyInfo = proxyInfo;
   mEndToEndSSL = e2eSSL;
   mUsingConnect = false;
   mNPNToken = npnToken;
   mIsHttp3 = aIsHttp3;
   mOriginAttributes = originAttributes;
   mTlsFlags = 0x0;
   mIsTrrServiceChannel = false;
-  mTrrDisabled = false;
+  mTRRMode = nsIRequest::TRR_DEFAULT_MODE;
   mIPv4Disabled = false;
   mIPv6Disabled = false;
 
   mUsingHttpsProxy = (proxyInfo && proxyInfo->IsHTTPS());
   mUsingHttpProxy = mUsingHttpsProxy || (proxyInfo && proxyInfo->IsHTTP());
 
   if (mUsingHttpProxy) {
     mUsingConnect = mEndToEndSSL;  // SSL always uses CONNECT
@@ -229,21 +229,23 @@ void nsHttpConnectionInfo::BuildHashKey(
   }
 
   if (!mNPNToken.IsEmpty()) {
     mHashKey.AppendLiteral(" {NPN-TOKEN ");
     mHashKey.Append(mNPNToken);
     mHashKey.AppendLiteral("}");
   }
 
-  if (GetTrrDisabled()) {
-    // When connecting with TRR disabled, we enforce a separate connection
+  if (GetTRRMode() != nsIRequest::TRR_DEFAULT_MODE) {
+    // When connecting with another TRR mode, we enforce a separate connection
     // hashkey so that we also can trigger a fresh DNS resolver that then
     // doesn't use TRR as the previous connection might have.
-    mHashKey.AppendLiteral("[NOTRR]");
+    mHashKey.AppendLiteral("[TRR:");
+    mHashKey.AppendInt(GetTRRMode());
+    mHashKey.AppendLiteral("]");
   }
 
   if (GetIPv4Disabled()) {
     mHashKey.AppendLiteral("[!v4]");
   }
 
   if (GetIPv6Disabled()) {
     mHashKey.AppendLiteral("[!v6]");
@@ -320,17 +322,17 @@ already_AddRefed<nsHttpConnectionInfo> n
   // Make sure the anonymous, insecure-scheme, and private flags are transferred
   clone->SetAnonymous(GetAnonymous());
   clone->SetPrivate(GetPrivate());
   clone->SetInsecureScheme(GetInsecureScheme());
   clone->SetNoSpdy(GetNoSpdy());
   clone->SetBeConservative(GetBeConservative());
   clone->SetTlsFlags(GetTlsFlags());
   clone->SetIsTrrServiceChannel(GetIsTrrServiceChannel());
-  clone->SetTrrDisabled(GetTrrDisabled());
+  clone->SetTRRMode(GetTRRMode());
   clone->SetIPv4Disabled(GetIPv4Disabled());
   clone->SetIPv6Disabled(GetIPv6Disabled());
   MOZ_ASSERT(clone->Equals(this));
 
   return clone.forget();
 }
 
 void nsHttpConnectionInfo::CloneAsDirectRoute(nsHttpConnectionInfo** outCI) {
@@ -346,17 +348,17 @@ void nsHttpConnectionInfo::CloneAsDirect
   // Make sure the anonymous, insecure-scheme, and private flags are transferred
   clone->SetAnonymous(GetAnonymous());
   clone->SetPrivate(GetPrivate());
   clone->SetInsecureScheme(GetInsecureScheme());
   clone->SetNoSpdy(GetNoSpdy());
   clone->SetBeConservative(GetBeConservative());
   clone->SetTlsFlags(GetTlsFlags());
   clone->SetIsTrrServiceChannel(GetIsTrrServiceChannel());
-  clone->SetTrrDisabled(GetTrrDisabled());
+  clone->SetTRRMode(GetTRRMode());
   clone->SetIPv4Disabled(GetIPv4Disabled());
   clone->SetIPv6Disabled(GetIPv6Disabled());
 
   clone.forget(outCI);
 }
 
 nsresult nsHttpConnectionInfo::CreateWildCard(nsHttpConnectionInfo** outParam) {
   // T???mozilla.org:443 (https:proxy.ducksong.com:3128) [specifc form]
@@ -373,19 +375,19 @@ nsresult nsHttpConnectionInfo::CreateWil
                                    mOriginAttributes, true, mIsHttp3);
   // Make sure the anonymous and private flags are transferred!
   clone->SetAnonymous(GetAnonymous());
   clone->SetPrivate(GetPrivate());
   clone.forget(outParam);
   return NS_OK;
 }
 
-void nsHttpConnectionInfo::SetTrrDisabled(bool aNoTrr) {
-  if (mTrrDisabled != aNoTrr) {
-    mTrrDisabled = aNoTrr;
+void nsHttpConnectionInfo::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  if (mTRRMode != aTRRMode) {
+    mTRRMode = aTRRMode;
     RebuildHashKey();
   }
 }
 
 void nsHttpConnectionInfo::SetIPv4Disabled(bool aNoIPv4) {
   if (mIPv4Disabled != aNoIPv4) {
     mIPv4Disabled = aNoIPv4;
     RebuildHashKey();
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -144,20 +144,18 @@ class nsHttpConnectionInfo final : publi
 
   // IsTrrServiceChannel means that this connection is used to send TRR requests
   // over
   void SetIsTrrServiceChannel(bool aIsTRRChannel) {
     mIsTrrServiceChannel = aIsTRRChannel;
   }
   bool GetIsTrrServiceChannel() const { return mIsTrrServiceChannel; }
 
-  // SetTrrDisabled means don't use TRR to resolve host names for this
-  // connection
-  void SetTrrDisabled(bool aNoTrr);
-  bool GetTrrDisabled() const { return mTrrDisabled; }
+  void SetTRRMode(nsIRequest::TRRMode aTRRMode);
+  nsIRequest::TRRMode GetTRRMode() const { return mTRRMode; }
 
   void SetIPv4Disabled(bool aNoIPv4);
   bool GetIPv4Disabled() const { return mIPv4Disabled; }
 
   void SetIPv6Disabled(bool aNoIPv6);
   bool GetIPv6Disabled() const { return mIPv6Disabled; }
 
   const nsCString& GetNPNToken() { return mNPNToken; }
@@ -228,21 +226,21 @@ class nsHttpConnectionInfo final : publi
   nsCString mTopWindowOrigin;
   nsCOMPtr<nsProxyInfo> mProxyInfo;
   bool mUsingHttpProxy;
   bool mUsingHttpsProxy;
   bool mEndToEndSSL;
   bool mUsingConnect;  // if will use CONNECT with http proxy
   nsCString mNPNToken;
   OriginAttributes mOriginAttributes;
+  nsIRequest::TRRMode mTRRMode;
 
   uint32_t mTlsFlags;
   uint16_t mIsolated : 1;
   uint16_t mIsTrrServiceChannel : 1;
-  uint16_t mTrrDisabled : 1;
   uint16_t mIPv4Disabled : 1;
   uint16_t mIPv6Disabled : 1;
 
   bool mLessThanTls13;  // This will be set to true if we negotiate less than
                         // tls1.3. If the tls version is till not know or it
                         // is 1.3 or greater the value will be false.
   bool mIsHttp3;
 
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -4094,19 +4094,18 @@ nsresult nsHttpConnectionMgr::nsHalfOpen
     rv = sts->CreateTransport(socketTypes, ci->GetOrigin(), ci->OriginPort(),
                               ci->ProxyInfo(), getter_AddRefs(socketTransport));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t tmpFlags = 0;
   if (mCaps & NS_HTTP_REFRESH_DNS) tmpFlags = nsISocketTransport::BYPASS_CACHE;
 
-  if (mCaps & NS_HTTP_DISABLE_TRR) {
-    tmpFlags = nsISocketTransport::DISABLE_TRR;
-  }
+  tmpFlags |= nsISocketTransport::GetFlagsFromTRRMode(
+      NS_HTTP_TRR_MODE_FROM_FLAGS(mCaps));
 
   if (mCaps & NS_HTTP_LOAD_ANONYMOUS)
     tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
 
   if (ci->GetPrivate() || ci->GetIsolated()) {
     tmpFlags |= nsISocketTransport::NO_PERMANENT_STORAGE;
   }
 
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -373,16 +373,26 @@ nsViewSourceChannel::SetLoadFlags(uint32
         aLoadFlags & ::nsIChannel::LOAD_DOCUMENT_URI);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return nsIViewSourceChannel::GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsViewSourceChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return nsIViewSourceChannel::SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::GetContentType(nsACString& aContentType) {
   NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
 
   aContentType.Truncate();
 
   if (mContentType.IsEmpty()) {
     // Get the current content type
     nsresult rv;
--- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp
@@ -194,16 +194,26 @@ nsPartChannel::GetLoadFlags(nsLoadFlags*
 
 NS_IMETHODIMP
 nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
   mLoadFlags = aLoadFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPartChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsPartChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 nsPartChannel::GetIsDocument(bool* aIsDocument) {
   return NS_GetIsDocumentChannel(this, aIsDocument);
 }
 
 NS_IMETHODIMP
 nsPartChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
   *aLoadGroup = mLoadGroup;
   NS_IF_ADDREF(*aLoadGroup);
--- a/toolkit/components/captivedetect/CaptiveDetect.jsm
+++ b/toolkit/components/captivedetect/CaptiveDetect.jsm
@@ -29,17 +29,17 @@ function URLFetcher(url, timeout) {
   xhr.open("GET", url, true);
   // Prevent the request from reading from the cache.
   xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
   // Prevent the request from writing to the cache.
   xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
   // Prevent privacy leaks
   xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_ANONYMOUS;
   // Use the system's resolver for this check
-  xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_DISABLE_TRR;
+  xhr.channel.setTRRMode(Ci.nsIRequest.TRR_DISABLED_MODE);
   // We except this from being classified
   xhr.channel.loadFlags |= Ci.nsIChannel.LOAD_BYPASS_URL_CLASSIFIER;
 
   // We don't want to follow _any_ redirects
   xhr.channel.QueryInterface(Ci.nsIHttpChannel).redirectionLimit = 0;
 
   // The Cache-Control header is only interpreted by proxies and the
   // final destination. It does not help if a resource is already
--- a/toolkit/components/extensions/webrequest/StreamFilterParent.cpp
+++ b/toolkit/components/extensions/webrequest/StreamFilterParent.cpp
@@ -442,16 +442,26 @@ StreamFilterParent::GetLoadFlags(nsLoadF
 }
 
 NS_IMETHODIMP
 StreamFilterParent::SetLoadFlags(nsLoadFlags aLoadFlags) {
   MOZ_ASSERT(mChannel);
   return mChannel->SetLoadFlags(aLoadFlags);
 }
 
+NS_IMETHODIMP
+StreamFilterParent::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+StreamFilterParent::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
 /*****************************************************************************
  * nsIStreamListener
  *****************************************************************************/
 
 NS_IMETHODIMP
 StreamFilterParent::OnStartRequest(nsIRequest* aRequest) {
   AssertIsMainThread();
 
--- a/uriloader/exthandler/ExternalHelperAppParent.cpp
+++ b/uriloader/exthandler/ExternalHelperAppParent.cpp
@@ -289,16 +289,26 @@ ExternalHelperAppParent::GetLoadFlags(ns
 
 NS_IMETHODIMP
 ExternalHelperAppParent::SetLoadFlags(nsLoadFlags aLoadFlags) {
   mLoadFlags = aLoadFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+ExternalHelperAppParent::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+ExternalHelperAppParent::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
 ExternalHelperAppParent::GetIsDocument(bool* aIsDocument) {
   return NS_GetIsDocumentChannel(this, aIsDocument);
 }
 
 NS_IMETHODIMP
 ExternalHelperAppParent::GetLoadGroup(nsILoadGroup** aLoadGroup) {
   *aLoadGroup = nullptr;
   return NS_OK;
--- a/uriloader/exthandler/nsExternalProtocolHandler.cpp
+++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp
@@ -233,16 +233,24 @@ NS_IMETHODIMP nsExtProtocolChannel::GetL
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
   mLoadFlags = aLoadFlags;
   return NS_OK;
 }
 
+NS_IMETHODIMP nsExtProtocolChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+  return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP nsExtProtocolChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+  return SetTRRModeImpl(aTRRMode);
+}
+
 NS_IMETHODIMP nsExtProtocolChannel::GetIsDocument(bool* aIsDocument) {
   return NS_GetIsDocumentChannel(this, aIsDocument);
 }
 
 NS_IMETHODIMP nsExtProtocolChannel::GetContentType(nsACString& aContentType) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }