Bug 1360581 - Part 2: Do channel annotation a bit earlier. r=francois
☠☠ backed out by 78aa5a83314a ☠ ☠
authorKershaw Chang <kechang@mozilla.com>
Tue, 06 Jun 2017 02:30:00 -0400
changeset 410705 d1c5998858a36a1a07afeb9fa8d3c52f95a7cae5
parent 410704 4d81e7dfd02068a3c5f2da58449d3f8c55f93ed0
child 410706 3825fc840a68526654858e782d9f8fed6d68d78c
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfrancois
bugs1360581
milestone55.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 1360581 - Part 2: Do channel annotation a bit earlier. r=francois The current channel annotation is happened at nsChannelClassifier::OnClassifyComplete and this is too late because this channel might be already hit the network. This patch adds a new API CheckIsTrackerWithLocalTable in nsChannelClassifier to check if the URI is in local blacklist and whitelist before calling BeginConnectActual. Please note that whitelist will be checked only when TP is disabled.
netwerk/base/nsChannelClassifier.cpp
netwerk/base/nsChannelClassifier.h
netwerk/protocol/http/nsHttpChannel.cpp
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -51,16 +51,17 @@ static LazyLogModule gChannelClassifierL
 
 
 #undef LOG
 #define LOG(args)     MOZ_LOG(gChannelClassifierLog, LogLevel::Debug, args)
 #define LOG_ENABLED() MOZ_LOG_TEST(gChannelClassifierLog, LogLevel::Debug)
 
 #define URLCLASSIFIER_SKIP_HOSTNAMES       "urlclassifier.skipHostnames"
 #define URLCLASSIFIER_TRACKING_WHITELIST   "urlclassifier.trackingWhitelistTable"
+#define URLCLASSIFIER_TRACKING_TABLE       "urlclassifier.trackingTable"
 
 // Put CachedPrefs in anonymous namespace to avoid any collision from outside of
 // this file.
 namespace {
 
 /**
  * It is not recommended to read from Preference everytime a channel is
  * connected.
@@ -74,16 +75,18 @@ public:
   void Init();
   bool IsAllowListExample() { return sAllowListExample;}
   bool IsLowerNetworkPriority() { return sLowerNetworkPriority;}
   bool IsAnnotateChannelEnabled() { return sAnnotateChannelEnabled;}
   nsCString GetTrackingWhiteList() { return mTrackingWhitelist; }
   void SetTrackingWhiteList(const nsACString& aList) { mTrackingWhitelist = aList; }
   nsCString GetSkipHostnames() { return mSkipHostnames; }
   void SetSkipHostnames(const nsACString& aHostnames) { mSkipHostnames = aHostnames; }
+  void SetTrackingBlackList(const nsACString& aList) { mTrackingBlacklist = aList; }
+  nsCString GetTrackingBlackList() { return mTrackingBlacklist; }
 
 private:
   friend class StaticAutoPtr<CachedPrefs>;
   CachedPrefs();
   ~CachedPrefs();
 
   static void OnPrefsChange(const char* aPrefName, void* );
 
@@ -92,16 +95,17 @@ private:
   static bool sAnnotateChannelEnabled;
   // Whether the priority of the channels annotated as being on the tracking
   // protection list should be lowered.
   static bool sLowerNetworkPriority;
   static bool sAllowListExample;
 
   nsCString mTrackingWhitelist;
   nsCString mSkipHostnames;
+  nsCString mTrackingBlacklist;
 
   static StaticAutoPtr<CachedPrefs> sInstance;
 };
 
 bool CachedPrefs::sAllowListExample = false;
 bool CachedPrefs::sLowerNetworkPriority = false;
 bool CachedPrefs::sAnnotateChannelEnabled = false;
 
@@ -115,32 +119,37 @@ CachedPrefs::OnPrefsChange(const char* a
 
   if (!strcmp(aPref, URLCLASSIFIER_SKIP_HOSTNAMES)) {
     nsCString skipHostnames = Preferences::GetCString(URLCLASSIFIER_SKIP_HOSTNAMES);
     ToLowerCase(skipHostnames);
     prefs->SetSkipHostnames(skipHostnames);
   } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_WHITELIST)) {
     nsCString trackingWhitelist = Preferences::GetCString(URLCLASSIFIER_TRACKING_WHITELIST);
     prefs->SetTrackingWhiteList(trackingWhitelist);
+  } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_TABLE)) {
+    nsCString trackingBlacklist = Preferences::GetCString(URLCLASSIFIER_TRACKING_TABLE);
+    prefs->SetTrackingBlackList(trackingBlacklist);
   }
 }
 
 void
 CachedPrefs::Init()
 {
   Preferences::AddBoolVarCache(&sAnnotateChannelEnabled,
                                "privacy.trackingprotection.annotate_channels");
   Preferences::AddBoolVarCache(&sLowerNetworkPriority,
                                "privacy.trackingprotection.lower_network_priority");
   Preferences::AddBoolVarCache(&sAllowListExample,
                                "channelclassifier.allowlist_example");
   Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
                                        URLCLASSIFIER_SKIP_HOSTNAMES, this);
   Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
                                        URLCLASSIFIER_TRACKING_WHITELIST, this);
+  Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
+                                       URLCLASSIFIER_TRACKING_TABLE, this);
 
 }
 
 // static
 CachedPrefs*
 CachedPrefs::GetInstance()
 {
   if (!sInstance) {
@@ -161,32 +170,67 @@ CachedPrefs::~CachedPrefs()
 {
   MOZ_COUNT_DTOR(CachedPrefs);
 
   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_SKIP_HOSTNAMES, this);
   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_WHITELIST, this);
 }
 } // anonymous namespace
 
+static void
+SetIsTrackingResourceHelper(nsIChannel* aChannel)
+{
+  MOZ_ASSERT(aChannel);
+
+  nsCOMPtr<nsIParentChannel> parentChannel;
+  NS_QueryNotificationCallbacks(aChannel, parentChannel);
+  if (parentChannel) {
+    // This channel is a parent-process proxy for a child process
+    // request. We should notify the child process as well.
+    parentChannel->NotifyTrackingResource();
+  }
+
+  RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
+  if (httpChannel) {
+    httpChannel->SetIsTrackingResource();
+  }
+}
+
+static void
+LowerPriorityHelper(nsIChannel* aChannel)
+{
+  MOZ_ASSERT(aChannel);
+
+  nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aChannel);
+  if (p) {
+    p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
+  }
+}
+
 NS_IMPL_ISUPPORTS(nsChannelClassifier,
                   nsIURIClassifierCallback,
                   nsIObserver)
 
 nsChannelClassifier::nsChannelClassifier(nsIChannel *aChannel)
   : mIsAllowListed(false),
     mSuspendedChannel(false),
     mChannel(aChannel),
     mTrackingProtectionEnabled(Nothing())
 {
   MOZ_ASSERT(mChannel);
 }
 
 nsresult
 nsChannelClassifier::ShouldEnableTrackingProtection(bool *result)
 {
+  if (mTrackingProtectionEnabled) {
+    *result = mTrackingProtectionEnabled.value();
+    return NS_OK;
+  }
+
   nsresult rv = ShouldEnableTrackingProtectionInternal(mChannel, result);
   mTrackingProtectionEnabled = Some(*result);
   return rv;
 }
 
 nsresult
 nsChannelClassifier::ShouldEnableTrackingProtectionInternal(nsIChannel *aChannel,
                                                             bool *result)
@@ -491,34 +535,30 @@ nsChannelClassifier::StartInternal()
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIPrincipal> principal;
     rv = securityManager->GetChannelURIPrincipal(mChannel, getter_AddRefs(principal));
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool expectCallback;
     bool trackingProtectionEnabled = false;
-    if (mTrackingProtectionEnabled.isNothing()) {
-      (void)ShouldEnableTrackingProtection(&trackingProtectionEnabled);
-    } else {
-      trackingProtectionEnabled = mTrackingProtectionEnabled.value();
-    }
+    rv = ShouldEnableTrackingProtection(&trackingProtectionEnabled);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     if (LOG_ENABLED()) {
       nsCOMPtr<nsIURI> principalURI;
       principal->GetURI(getter_AddRefs(principalURI));
       LOG(("nsChannelClassifier[%p]: Classifying principal %s on channel with "
            "uri %s", this, principalURI->GetSpecOrDefault().get(),
            uri->GetSpecOrDefault().get()));
     }
     // The classify is running in parent process, no need to give a valid event
     // target
     rv = uriClassifier->Classify(principal, nullptr,
-                                 CachedPrefs::GetInstance()->IsAnnotateChannelEnabled() ||
-                                   trackingProtectionEnabled,
+                                 trackingProtectionEnabled,
                                  this, &expectCallback);
     if (NS_FAILED(rv)) {
         return rv;
     }
 
     if (expectCallback) {
         // Suspend the channel, it will be resumed when we get the classifier
         // callback.
@@ -765,143 +805,306 @@ nsChannelClassifier::SetBlockedContent(n
                                   message,
                                   params, ArrayLength(params));
 
   return NS_OK;
 }
 
 namespace {
 
-class IsTrackerWhitelistedCallback final : public nsIURIClassifierCallback {
+// The purpose of this class is only for implementing all nsISupports methods.
+// This is a workaround for template derived class.
+class URIClassifierCallbackBase : public nsIURIClassifierCallback {
 public:
-  explicit IsTrackerWhitelistedCallback(nsChannelClassifier* aClosure,
+  URIClassifierCallbackBase() = default;
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+protected:
+  virtual ~URIClassifierCallbackBase() = default;
+};
+
+NS_IMPL_ISUPPORTS(URIClassifierCallbackBase, nsIURIClassifierCallback)
+
+// A template class for reusing the code.
+// OnClassifyCompleteInternal will be called to pass the result.
+template<class T>
+class IsTrackerWhitelistedCallback final : public URIClassifierCallbackBase {
+public:
+  explicit IsTrackerWhitelistedCallback(T* aClosure,
                                         const nsACString& aList,
                                         const nsACString& aProvider,
                                         const nsACString& aPrefix,
-                                        const nsACString& aWhitelistEntry)
+                                        nsIURI* aWhitelistURI)
     : mClosure(aClosure)
-    , mWhitelistEntry(aWhitelistEntry)
+    , mWhitelistURI(aWhitelistURI)
     , mList(aList)
     , mProvider(aProvider)
     , mPrefix(aPrefix)
   {
   }
 
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIURICLASSIFIERCALLBACK
+  NS_IMETHOD OnClassifyComplete(nsresult /*aErrorCode*/,
+                                const nsACString& aLists, // Only this matters.
+                                const nsACString& /*aProvider*/,
+                                const nsACString& /*aPrefix*/) override
+  {
+    nsresult rv;
+    if (aLists.IsEmpty()) {
+      if (LOG_ENABLED()) {
+        MOZ_ASSERT(mWhitelistURI);
+
+        LOG(("nsChannelClassifier[%p]: %s is not in the whitelist",
+             mClosure.get(), mWhitelistURI->GetSpecOrDefault().get()));
+      }
+      rv = NS_ERROR_TRACKING_URI;
+    } else {
+      LOG(("nsChannelClassifier[%p]:OnClassifyComplete tracker found "
+           "in whitelist so we won't block it", mClosure.get()));
+      rv = NS_OK;
+    }
+
+    rv = mClosure->OnClassifyCompleteInternal(rv, mList, mProvider, mPrefix);
+    mClosure = nullptr;
+    return rv;
+  }
 
 private:
   ~IsTrackerWhitelistedCallback() = default;
 
-  RefPtr<nsChannelClassifier> mClosure;
-  nsCString mWhitelistEntry;
+  RefPtr<T> mClosure;
+  nsCOMPtr<nsIURI> mWhitelistURI;
 
   // The following 3 values are for forwarding the callback.
   nsCString mList;
   nsCString mProvider;
   nsCString mPrefix;
 };
 
-NS_IMPL_ISUPPORTS(IsTrackerWhitelistedCallback, nsIURIClassifierCallback)
+// This class is designed to get the results of checking blacklist and whitelist.
+// 1. The result of local blacklist will be sent back via
+//    OnClassifyComplete, which is called by nsIURIClassifier service.
+// 2. The result of local whitelist is got via OnClassifyCompleteInternal,
+//    which is called by IsTrackerWhitelistedCallback::OnClassifyComplete.
+class IsTrackerBlacklistedCallback final : public nsIURIClassifierCallback {
+public:
+  explicit IsTrackerBlacklistedCallback(nsChannelClassifier* aChannelClassifier,
+                                        nsIURIClassifierCallback* aCallback)
+    : mChannelClassifier(aChannelClassifier)
+    , mChannelCallback(aCallback)
+  {
+  }
 
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIURICLASSIFIERCALLBACK
+
+  nsresult OnClassifyCompleteInternal(nsresult aErrorCode,
+                                      const nsACString& aList,
+                                      const nsACString& aProvider,
+                                      const nsACString& aPrefix);
+
+private:
+  ~IsTrackerBlacklistedCallback() = default;
+
+  RefPtr<nsChannelClassifier> mChannelClassifier;
+  nsCOMPtr<nsIURIClassifierCallback> mChannelCallback;
+};
+
+NS_IMPL_ISUPPORTS(IsTrackerBlacklistedCallback, nsIURIClassifierCallback)
 
 /*virtual*/ nsresult
-IsTrackerWhitelistedCallback::OnClassifyComplete(nsresult /*aErrorCode*/,
-                                                 const nsACString& aLists, // Only this matters.
-                                                 const nsACString& /*aProvider*/,
-                                                 const nsACString& /*aPrefix*/)
+IsTrackerBlacklistedCallback::OnClassifyComplete(nsresult aErrorCode,
+                                                 const nsACString& aLists,
+                                                 const nsACString& aProvider,
+                                                 const nsACString& aPrefix)
 {
-  nsresult rv;
-  if (aLists.IsEmpty()) {
-    LOG(("nsChannelClassifier[%p]: %s is not in the whitelist",
-       mClosure.get(), mWhitelistEntry.get()));
-    rv = NS_ERROR_TRACKING_URI;
-  } else {
-    LOG(("nsChannelClassifier[%p]:OnClassifyComplete tracker found "
-         "in whitelist so we won't block it", mClosure.get()));
-    rv = NS_OK;
+  nsresult status = aLists.IsEmpty() ? NS_OK : NS_ERROR_TRACKING_URI;
+  bool tpEnabled = false;
+  mChannelClassifier->ShouldEnableTrackingProtection(&tpEnabled);
+
+  LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyComplete "
+       " status=0x%" PRIx32 ", tpEnabled=%d",
+       mChannelClassifier.get(), static_cast<uint32_t>(status), tpEnabled));
+
+  // If this is not in local blacklist or tracking protection is enabled,
+  // directly send the status back.
+  // The whitelist will be checked at nsChannelClassifier::OnClassifyComplete
+  // when tracking protection is enabled, so we can just return here.
+  if (NS_SUCCEEDED(status) || tpEnabled) {
+    return mChannelCallback->OnClassifyComplete(
+      status, aLists, aProvider, aPrefix);
+  }
+
+  nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel();
+  if (LOG_ENABLED()) {
+    nsCOMPtr<nsIURI> uri;
+    channel->GetURI(getter_AddRefs(uri));
+    LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyComplete channel [%p] "
+         "uri=%s, is in blacklist. Start checking whitelist.",
+         mChannelClassifier.get(), channel.get(),
+         uri->GetSpecOrDefault().get()));
   }
 
-  return mClosure->OnClassifyCompleteInternal(rv, mList, mProvider, mPrefix);
+  nsCOMPtr<nsIURI> whitelistURI = mChannelClassifier->CreateWhiteListURI();
+  nsCOMPtr<nsIURIClassifierCallback> callback =
+    new IsTrackerWhitelistedCallback<IsTrackerBlacklistedCallback>(
+      this, aLists, aProvider, aPrefix, whitelistURI);
+
+  // If IsTrackerWhitelisted has failed, it means the uri is not in whitelist.
+  if (NS_FAILED(mChannelClassifier->IsTrackerWhitelisted(whitelistURI, callback))) {
+    LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyComplete channel [%p] "
+         "IsTrackerWhitelisted has failed.",
+         mChannelClassifier.get(), channel.get()));
+
+    MOZ_ASSERT(CachedPrefs::GetInstance()->IsAnnotateChannelEnabled());
+
+    SetIsTrackingResourceHelper(channel);
+    if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
+      LowerPriorityHelper(channel);
+    }
+
+    // We don't want to disable speculative connection when tracking protection
+    // is disabled. So, change the status to NS_OK.
+    status = NS_OK;
+
+    return mChannelCallback->OnClassifyComplete(
+      status, aLists, aProvider, aPrefix);
+  }
+
+  // OnClassifyCompleteInternal() will be called once we know
+  // if the tracker is whitelisted.
+  return NS_OK;
+}
+
+nsresult
+IsTrackerBlacklistedCallback::OnClassifyCompleteInternal(nsresult aErrorCode,
+                                                         const nsACString& aLists,
+                                                         const nsACString& aProvider,
+                                                         const nsACString& aPrefix)
+{
+  LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyCompleteInternal"
+       " status=0x%" PRIx32,
+       mChannelClassifier.get(), static_cast<uint32_t>(aErrorCode)));
+
+  if (NS_SUCCEEDED(aErrorCode)) {
+    return mChannelCallback->OnClassifyComplete(
+      aErrorCode, aLists, aProvider, aPrefix);
+  }
+
+  MOZ_ASSERT(CachedPrefs::GetInstance()->IsAnnotateChannelEnabled());
+  MOZ_ASSERT(aErrorCode == NS_ERROR_TRACKING_URI);
+
+  nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel();
+  if (LOG_ENABLED()) {
+    nsCOMPtr<nsIURI> uri;
+    channel->GetURI(getter_AddRefs(uri));
+    LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyCompleteInternal "
+         "channel [%p] uri=%s, is not in whitelist",
+         mChannelClassifier.get(), channel.get(),
+         uri->GetSpecOrDefault().get()));
+  }
+
+  SetIsTrackingResourceHelper(channel);
+  if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
+    LowerPriorityHelper(channel);
+  }
+
+  return mChannelCallback->OnClassifyComplete(
+      NS_OK, aLists, aProvider, aPrefix);
 }
 
 } // end of unnamed namespace/
 
+already_AddRefed<nsIURI>
+nsChannelClassifier::CreateWhiteListURI() const
+{
+  nsresult rv;
+  nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(mChannel, &rv);
+  if (!chan) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIURI> topWinURI;
+  rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+  if (!topWinURI) {
+    LOG(("nsChannelClassifier[%p]: No window URI", this));
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIScriptSecurityManager> securityManager =
+    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+  nsCOMPtr<nsIPrincipal> chanPrincipal;
+  rv = securityManager->GetChannelURIPrincipal(mChannel,
+                                               getter_AddRefs(chanPrincipal));
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+
+  // Craft a whitelist URL like "toplevel.page/?resource=third.party.domain"
+  nsAutoCString pageHostname, resourceDomain;
+  rv = topWinURI->GetHost(pageHostname);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+  rv = chanPrincipal->GetBaseDomain(resourceDomain);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+  nsAutoCString whitelistEntry = NS_LITERAL_CSTRING("http://") +
+    pageHostname + NS_LITERAL_CSTRING("/?resource=") + resourceDomain;
+  LOG(("nsChannelClassifier[%p]: Looking for %s in the whitelist",
+       this, whitelistEntry.get()));
+
+  nsCOMPtr<nsIURI> whitelistURI;
+  rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
+
+  return NS_SUCCEEDED(rv) ? whitelistURI.forget() : nullptr;
+}
+
 nsresult
-nsChannelClassifier::IsTrackerWhitelisted(const nsACString& aList,
-                                          const nsACString& aProvider,
-                                          const nsACString& aPrefix)
+nsChannelClassifier::IsTrackerWhitelisted(nsIURI* aWhiteListURI,
+                                          nsIURIClassifierCallback *aCallback)
 {
+  if (!aCallback || !aWhiteListURI) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
   nsresult rv;
   nsCOMPtr<nsIURIClassifier> uriClassifier =
     do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCString trackingWhitelist = CachedPrefs::GetInstance()->GetTrackingWhiteList();
   if (trackingWhitelist.IsEmpty()) {
     LOG(("nsChannelClassifier[%p]:IsTrackerWhitelisted whitelist disabled",
          this));
     return NS_ERROR_TRACKING_URI;
   }
 
-  nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(mChannel, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIURI> topWinURI;
-  rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!topWinURI) {
-    LOG(("nsChannelClassifier[%p]: No window URI", this));
-    return NS_ERROR_TRACKING_URI;
-  }
-
-  nsCOMPtr<nsIScriptSecurityManager> securityManager =
-    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIPrincipal> chanPrincipal;
-  rv = securityManager->GetChannelURIPrincipal(mChannel,
-                                               getter_AddRefs(chanPrincipal));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Craft a whitelist URL like "toplevel.page/?resource=third.party.domain"
-  nsAutoCString pageHostname, resourceDomain;
-  rv = topWinURI->GetHost(pageHostname);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = chanPrincipal->GetBaseDomain(resourceDomain);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsAutoCString whitelistEntry = NS_LITERAL_CSTRING("http://") +
-    pageHostname + NS_LITERAL_CSTRING("/?resource=") + resourceDomain;
-  LOG(("nsChannelClassifier[%p]: Looking for %s in the whitelist",
-       this, whitelistEntry.get()));
-
-  nsCOMPtr<nsIURI> whitelistURI;
-  rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  RefPtr<IsTrackerWhitelistedCallback> cb =
-    new IsTrackerWhitelistedCallback(this, aList, aProvider, aPrefix,
-                                     whitelistEntry);
-
-  return uriClassifier->AsyncClassifyLocalWithTables(whitelistURI, trackingWhitelist, cb);
+  return uriClassifier->AsyncClassifyLocalWithTables(aWhiteListURI, trackingWhitelist, aCallback);
 }
 
 NS_IMETHODIMP
 nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode,
                                         const nsACString& aList,
                                         const nsACString& aProvider,
                                         const nsACString& aPrefix)
 {
   // Should only be called in the parent process.
   MOZ_ASSERT(XRE_IsParentProcess());
 
-  if (aErrorCode == NS_ERROR_TRACKING_URI &&
-      NS_SUCCEEDED(IsTrackerWhitelisted(aList, aProvider, aPrefix))) {
-    // OnClassifyCompleteInternal() will be called once we know
-    // if the tracker is whitelisted.
-    return NS_OK;
+  if (aErrorCode == NS_ERROR_TRACKING_URI) {
+    nsCOMPtr<nsIURI> whitelistURI = CreateWhiteListURI();
+    nsCOMPtr<nsIURIClassifierCallback> callback =
+      new IsTrackerWhitelistedCallback<nsChannelClassifier>(
+        this, aList, aProvider, aPrefix, whitelistURI);
+    if (whitelistURI &&
+        NS_SUCCEEDED(IsTrackerWhitelisted(whitelistURI, callback))) {
+      // OnClassifyCompleteInternal() will be called once we know
+      // if the tracker is whitelisted.
+      return NS_OK;
+    }
   }
 
   return OnClassifyCompleteInternal(aErrorCode, aList, aProvider, aPrefix);
 }
 
 nsresult
 nsChannelClassifier::OnClassifyCompleteInternal(nsresult aErrorCode,
                                                 const nsACString& aList,
@@ -912,53 +1115,16 @@ nsChannelClassifier::OnClassifyCompleteI
       nsAutoCString errorName;
       if (LOG_ENABLED()) {
         GetErrorName(aErrorCode, errorName);
         LOG(("nsChannelClassifier[%p]:OnClassifyComplete %s (suspended channel)",
              this, errorName.get()));
       }
       MarkEntryClassified(aErrorCode);
 
-      // The value of |mTrackingProtectionEnabled| should be assigned at
-      // |ShouldEnableTrackingProtection| before.
-      MOZ_ASSERT(mTrackingProtectionEnabled, "Should contain a value.");
-
-      if (aErrorCode == NS_ERROR_TRACKING_URI &&
-          !mTrackingProtectionEnabled.valueOr(false)) {
-        if (CachedPrefs::GetInstance()->IsAnnotateChannelEnabled()) {
-          nsCOMPtr<nsIParentChannel> parentChannel;
-          NS_QueryNotificationCallbacks(mChannel, parentChannel);
-          if (parentChannel) {
-            // This channel is a parent-process proxy for a child process
-            // request. We should notify the child process as well.
-            parentChannel->NotifyTrackingResource();
-          }
-          RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(mChannel);
-          if (httpChannel) {
-            httpChannel->SetIsTrackingResource();
-          }
-        }
-
-        if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
-          if (LOG_ENABLED()) {
-            nsCOMPtr<nsIURI> uri;
-            mChannel->GetURI(getter_AddRefs(uri));
-            LOG(("nsChannelClassifier[%p]: lower the priority of channel %p"
-                 ", since %s is a tracker", this, mChannel.get(),
-                 uri->GetSpecOrDefault().get()));
-          }
-          nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
-          if (p) {
-            p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
-          }
-        }
-
-        aErrorCode = NS_OK;
-      }
-
       if (NS_FAILED(aErrorCode)) {
         if (LOG_ENABLED()) {
           nsCOMPtr<nsIURI> uri;
           mChannel->GetURI(getter_AddRefs(uri));
           LOG(("nsChannelClassifier[%p]: cancelling channel %p for %s "
                "with error code %s", this, mChannel.get(),
                uri->GetSpecOrDefault().get(), errorName.get()));
         }
@@ -977,16 +1143,71 @@ nsChannelClassifier::OnClassifyCompleteI
     }
 
     mChannel = nullptr;
     RemoveShutdownObserver();
 
     return NS_OK;
 }
 
+nsresult
+nsChannelClassifier::CheckIsTrackerWithLocalTable(nsIURIClassifierCallback* aCallback)
+{
+  nsresult rv;
+
+  if (!aCallback) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  bool trackingProtectionEnabled = false;
+  rv = ShouldEnableTrackingProtection(&trackingProtectionEnabled);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (!trackingProtectionEnabled &&
+      !CachedPrefs::GetInstance()->IsAnnotateChannelEnabled()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIURIClassifier> uriClassifier =
+    do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  rv = mChannel->GetURI(getter_AddRefs(uri));
+  if (NS_FAILED(rv) || !uri) {
+    return rv;
+  }
+
+  nsCString trackingBlacklist =
+    CachedPrefs::GetInstance()->GetTrackingBlackList();
+  if (trackingBlacklist.IsEmpty()) {
+    LOG(("nsChannelClassifier[%p]:CheckIsTrackerWithLocalTable blacklist is empty",
+         this));
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIURIClassifierCallback> callback =
+    new IsTrackerBlacklistedCallback(this, aCallback);
+
+  return uriClassifier->AsyncClassifyLocalWithTables(uri,
+                                                     trackingBlacklist,
+                                                     callback);
+}
+
+already_AddRefed<nsIChannel>
+nsChannelClassifier::GetChannel()
+{
+  nsCOMPtr<nsIChannel> channel = mChannel;
+  return channel.forget();
+}
+
 void
 nsChannelClassifier::AddShutdownObserver()
 {
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   if (observerService) {
     observerService->AddObserver(this, "profile-change-net-teardown", false);
   }
 }
@@ -1009,16 +1230,17 @@ nsChannelClassifier::Observe(nsISupports
   if (!strcmp(aTopic, "profile-change-net-teardown")) {
     // If we aren't getting a callback for any reason, make sure
     // we resume the channel.
 
     if (mChannel && mSuspendedChannel) {
       mSuspendedChannel = false;
       mChannel->Cancel(NS_ERROR_ABORT);
       mChannel->Resume();
+      mChannel = nullptr;
     }
 
     RemoveShutdownObserver();
   }
 
   return NS_OK;
 }
 
--- a/netwerk/base/nsChannelClassifier.h
+++ b/netwerk/base/nsChannelClassifier.h
@@ -29,43 +29,53 @@ public:
     NS_DECL_NSIOBSERVER
 
     // Calls nsIURIClassifier.Classify with the principal of the given channel,
     // and cancels the channel on a bad verdict.
     void Start();
     // Whether or not tracking protection should be enabled on this channel.
     nsresult ShouldEnableTrackingProtection(bool *result);
 
+    // Helper function to check a tracking URI against the whitelist
+    nsresult IsTrackerWhitelisted(nsIURI* aWhiteListURI,
+                                  nsIURIClassifierCallback* aCallback);
+
     // Called once we actually classified an URI. (An additional whitelist
     // check will be done if the classifier reports the URI is a tracker.)
     nsresult OnClassifyCompleteInternal(nsresult aErrorCode,
                                         const nsACString& aList,
                                         const nsACString& aProvider,
                                         const nsACString& aPrefix);
 
+    // Check a tracking URI against the local blacklist and whitelist.
+    // Returning NS_OK means the check will be processed
+    // and the caller should wait for the result.
+    nsresult CheckIsTrackerWithLocalTable(nsIURIClassifierCallback* aCallback);
+
+    // Helper function to create a whitelist URL.
+    already_AddRefed<nsIURI> CreateWhiteListURI() const;
+
+    already_AddRefed<nsIChannel> GetChannel();
+
 private:
     // True if the channel is on the allow list.
     bool mIsAllowListed;
     // True if the channel has been suspended.
     bool mSuspendedChannel;
     nsCOMPtr<nsIChannel> mChannel;
     Maybe<bool> mTrackingProtectionEnabled;
 
     ~nsChannelClassifier() {}
     // Caches good classifications for the channel principal.
     void MarkEntryClassified(nsresult status);
     bool HasBeenClassified(nsIChannel *aChannel);
     // Helper function so that we ensure we call ContinueBeginConnect once
     // Start is called. Returns NS_OK if and only if we will get a callback
     // from the classifier service.
     nsresult StartInternal();
-    // Helper function to check a tracking URI against the whitelist
-    nsresult IsTrackerWhitelisted(const nsACString& aList,
-                                  const nsACString& aProvider,
-                                  const nsACString& aPrefix);
     // Helper function to check a URI against the hostname whitelist
     bool IsHostnameWhitelisted(nsIURI *aUri, const nsACString &aWhitelisted);
     // Checks that the channel was loaded by the URI currently loaded in aDoc
     static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel);
 
     nsresult ShouldEnableTrackingProtectionInternal(nsIChannel *aChannel,
                                                     bool *result);
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6089,22 +6089,22 @@ private:
   ~InitLocalBlockListXpcCallback() = default;
 
   CallbackType mCallback;
 };
 
 NS_IMPL_ISUPPORTS(InitLocalBlockListXpcCallback, nsIURIClassifierCallback)
 
 /*virtual*/ nsresult
-InitLocalBlockListXpcCallback::OnClassifyComplete(nsresult /*aErrorCode*/,
-                                               const nsACString& aLists, // Only this matters.
+InitLocalBlockListXpcCallback::OnClassifyComplete(nsresult aErrorCode, // Only this matters.
+                                               const nsACString& /*aLists*/,
                                                const nsACString& /*aProvider*/,
                                                const nsACString& /*aPrefix*/)
 {
-    bool localBlockList = !aLists.IsEmpty();
+    bool localBlockList = aErrorCode == NS_ERROR_TRACKING_URI;
     mCallback(localBlockList);
     return NS_OK;
 }
 
 } // end of unnamed namespace/
 
 already_AddRefed<nsChannelClassifier>
 nsHttpChannel::GetOrCreateChannelClassifier()
@@ -6124,44 +6124,27 @@ nsHttpChannel::InitLocalBlockList(const 
 {
     mLocalBlocklist = false;
 
     if (!(mLoadFlags & LOAD_CLASSIFY_URI)) {
         return false;
     }
 
     // Check to see if this principal exists on local blocklists.
-    nsCOMPtr<nsIURIClassifier> classifier(services::GetURIClassifier());
     RefPtr<nsChannelClassifier> channelClassifier =
         GetOrCreateChannelClassifier();
-    bool tpEnabled = false;
-    channelClassifier->ShouldEnableTrackingProtection(&tpEnabled);
-    if (!classifier || !tpEnabled) {
-        return false;
-    }
 
     // We skip speculative connections by setting mLocalBlocklist only
     // when tracking protection is enabled. Though we could do this for
     // both phishing and malware, it is not necessary for correctness,
     // since no network events will be received while the
     // nsChannelClassifier is in progress. See bug 1122691.
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv = GetURI(getter_AddRefs(uri));
-    if (NS_FAILED(rv) || !uri) {
-        return false;
-    }
-
-    nsAutoCString tables;
-    Preferences::GetCString("urlclassifier.trackingTable", &tables);
-    nsTArray<nsCString> results;
-
     RefPtr<InitLocalBlockListXpcCallback> xpcCallback
         = new InitLocalBlockListXpcCallback(aCallback);
-    rv = classifier->AsyncClassifyLocalWithTables(uri, tables, xpcCallback);
-    if (NS_FAILED(rv)) {
+    if (NS_FAILED(channelClassifier->CheckIsTrackerWithLocalTable(xpcCallback))) {
         return false;
     }
 
     return true;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::AsyncOpen2(nsIStreamListener *aListener)