Bug 1580416 - P1. Use a different fragment method when the URI is a pairwiseWhitelistedURI. r=Ehsan
authorDimiL <dlee@mozilla.com>
Fri, 27 Sep 2019 13:22:18 +0000
changeset 495601 a1237d4bad3deefcd6e7d9a27422403da1477e31
parent 495511 09ea1fb930062b862b7d133c61903a7d16d7e613
child 495602 843690981959e08dc2587d29e14148773a34a312
push id114140
push userdvarga@mozilla.com
push dateWed, 02 Oct 2019 18:04:51 +0000
treeherdermozilla-inbound@32eb0ea893f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersEhsan
bugs1580416
milestone71.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 1580416 - P1. Use a different fragment method when the URI is a pairwiseWhitelistedURI. r=Ehsan This patch does the following to support matching a whitelisted URI when its domain is eTLD+1: 1. add an URIType to indicate whether a URI is generated by UrlClassifierCommoon::CreatePairwiseWhiteListURI(), which crafts a whitelist URL like "toplevel.page/?resource=third.party.domain" 2. call LookupCache::GetLookupWhitelistFragments() if URIType is nsIUrlClassifierFeature::pairwiseWhitelistedURI before initiating a lookup. 3. implement LookupCache::GetLookupWhitelistFragments() which creates an additional fragment by removing the leading component of third.party.domain Differential Revision: https://phabricator.services.mozilla.com/D47212
dom/ipc/URLClassifierParent.cpp
netwerk/url-classifier/AsyncUrlChannelClassifier.cpp
netwerk/url-classifier/UrlClassifierFeatureCryptominingAnnotation.cpp
netwerk/url-classifier/UrlClassifierFeatureCryptominingAnnotation.h
netwerk/url-classifier/UrlClassifierFeatureCryptominingProtection.cpp
netwerk/url-classifier/UrlClassifierFeatureCryptominingProtection.h
netwerk/url-classifier/UrlClassifierFeatureCustomTables.cpp
netwerk/url-classifier/UrlClassifierFeatureFingerprintingAnnotation.cpp
netwerk/url-classifier/UrlClassifierFeatureFingerprintingAnnotation.h
netwerk/url-classifier/UrlClassifierFeatureFingerprintingProtection.cpp
netwerk/url-classifier/UrlClassifierFeatureFingerprintingProtection.h
netwerk/url-classifier/UrlClassifierFeatureFlash.cpp
netwerk/url-classifier/UrlClassifierFeatureFlash.h
netwerk/url-classifier/UrlClassifierFeatureLoginReputation.cpp
netwerk/url-classifier/UrlClassifierFeatureLoginReputation.h
netwerk/url-classifier/UrlClassifierFeaturePhishingProtection.cpp
netwerk/url-classifier/UrlClassifierFeaturePhishingProtection.h
netwerk/url-classifier/UrlClassifierFeatureSocialTrackingAnnotation.cpp
netwerk/url-classifier/UrlClassifierFeatureSocialTrackingAnnotation.h
netwerk/url-classifier/UrlClassifierFeatureSocialTrackingProtection.cpp
netwerk/url-classifier/UrlClassifierFeatureSocialTrackingProtection.h
netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.h
netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.h
netwerk/url-classifier/nsIUrlClassifierFeature.idl
toolkit/components/url-classifier/LookupCache.cpp
toolkit/components/url-classifier/LookupCache.h
--- a/dom/ipc/URLClassifierParent.cpp
+++ b/dom/ipc/URLClassifierParent.cpp
@@ -102,22 +102,26 @@ class IPCFeature final : public nsIUrlCl
 
     // Nothing to do here.
     return NS_OK;
   }
 
   NS_IMETHOD
   GetURIByListType(nsIChannel* aChannel,
                    nsIUrlClassifierFeature::listType aListType,
+                   nsIUrlClassifierFeature::URIType* aURIType,
                    nsIURI** aURI) override {
     NS_ENSURE_ARG_POINTER(aURI);
 
     // This method should not be called, but we have a URI, let's return it.
     nsCOMPtr<nsIURI> uri = mURI;
     uri.forget(aURI);
+    *aURIType = aListType == nsIUrlClassifierFeature::blacklist
+                    ? nsIUrlClassifierFeature::URIType::blacklistURI
+                    : nsIUrlClassifierFeature::URIType::whitelistURI;
     return NS_OK;
   }
 
  private:
   ~IPCFeature() = default;
 
   nsCOMPtr<nsIURI> mURI;
   IPCURLClassifierFeature mIPCFeature;
--- a/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp
+++ b/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp
@@ -61,41 +61,47 @@ namespace {
 // -----------------------------------------------------------------------------
 
 // In order to avoid multiple URI parsing, we have this class which contains
 // nsIURI and its fragments.
 class URIData {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URIData);
 
-  static nsresult Create(nsIURI* aURI, nsIURI* aInnermostURI, URIData** aData);
+  static nsresult Create(nsIURI* aURI, nsIURI* aInnermostURI,
+                         nsIUrlClassifierFeature::URIType aURIType,
+                         URIData** aData);
 
   bool IsEqual(nsIURI* aURI) const;
 
   const nsTArray<nsCString>& Fragments();
 
   nsIURI* URI() const;
 
  private:
   URIData();
   ~URIData();
 
   nsCOMPtr<nsIURI> mURI;
   nsCString mURISpec;
   nsTArray<nsCString> mFragments;
+  nsIUrlClassifierFeature::URIType mURIType;
 };
 
 /* static */
-nsresult URIData::Create(nsIURI* aURI, nsIURI* aInnermostURI, URIData** aData) {
+nsresult URIData::Create(nsIURI* aURI, nsIURI* aInnermostURI,
+                         nsIUrlClassifierFeature::URIType aURIType,
+                         URIData** aData) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aURI);
   MOZ_ASSERT(aInnermostURI);
 
   RefPtr<URIData> data = new URIData();
   data->mURI = aURI;
+  data->mURIType = aURIType;
 
   nsUrlClassifierUtils* utilsService = nsUrlClassifierUtils::GetInstance();
   if (NS_WARN_IF(!utilsService)) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = utilsService->GetKeyForURI(aInnermostURI, data->mURISpec);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -127,17 +133,24 @@ bool URIData::IsEqual(nsIURI* aURI) cons
 
   return isEqual;
 }
 
 const nsTArray<nsCString>& URIData::Fragments() {
   MOZ_ASSERT(!NS_IsMainThread());
 
   if (mFragments.IsEmpty()) {
-    nsresult rv = LookupCache::GetLookupFragments(mURISpec, &mFragments);
+    nsresult rv;
+
+    if (mURIType == nsIUrlClassifierFeature::pairwiseWhitelistURI) {
+      rv = LookupCache::GetLookupWhitelistFragments(mURISpec, &mFragments);
+    } else {
+      rv = LookupCache::GetLookupFragments(mURISpec, &mFragments);
+    }
+
     Unused << NS_WARN_IF(NS_FAILED(rv));
   }
 
   return mFragments;
 }
 
 nsIURI* URIData::URI() const {
   MOZ_ASSERT(NS_IsMainThread());
@@ -501,16 +514,17 @@ class FeatureTask {
 
   // Called on the classifier thread.
   void DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier);
 
   // Called on the main-thread to process the channel.
   void CompleteClassification();
 
   nsresult GetOrCreateURIData(nsIURI* aURI, nsIURI* aInnermostURI,
+                              nsIUrlClassifierFeature::URIType aURIType,
                               URIData** aData);
 
   nsresult GetOrCreateTableData(URIData* aURIData, const nsACString& aTable,
                                 TableData** aData);
 
  private:
   FeatureTask(nsIChannel* aChannel, std::function<void()>&& aCallback);
   ~FeatureTask();
@@ -572,18 +586,19 @@ FeatureTask::FeatureTask(nsIChannel* aCh
 }
 
 FeatureTask::~FeatureTask() {
   NS_ReleaseOnMainThreadSystemGroup("FeatureTask::mChannel", mChannel.forget());
   NS_ReleaseOnMainThreadSystemGroup("FeatureTask::mCallbackHolder",
                                     mCallbackHolder.forget());
 }
 
-nsresult FeatureTask::GetOrCreateURIData(nsIURI* aURI, nsIURI* aInnermostURI,
-                                         URIData** aData) {
+nsresult FeatureTask::GetOrCreateURIData(
+    nsIURI* aURI, nsIURI* aInnermostURI,
+    nsIUrlClassifierFeature::URIType aURIType, URIData** aData) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aURI);
   MOZ_ASSERT(aInnermostURI);
   MOZ_ASSERT(aData);
 
   UC_LOG(
       ("FeatureTask::GetOrCreateURIData[%p] - Checking if a URIData must be "
        "created",
@@ -596,17 +611,18 @@ nsresult FeatureTask::GetOrCreateURIData
 
       RefPtr<URIData> uriData = data;
       uriData.forget(aData);
       return NS_OK;
     }
   }
 
   RefPtr<URIData> data;
-  nsresult rv = URIData::Create(aURI, aInnermostURI, getter_AddRefs(data));
+  nsresult rv =
+      URIData::Create(aURI, aInnermostURI, aURIType, getter_AddRefs(data));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mURIs.AppendElement(data);
 
   UC_LOG(("FeatureTask::GetOrCreateURIData[%p] - Create new URIData %p", this,
           data.get()));
@@ -683,18 +699,19 @@ nsresult FeatureData::InitializeList(
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aTask);
   MOZ_ASSERT(aChannel);
 
   UC_LOG(("FeatureData::InitializeList[%p] - Initialize list %d for channel %p",
           this, aListType, aChannel));
 
   nsCOMPtr<nsIURI> uri;
-  nsresult rv =
-      mFeature->GetURIByListType(aChannel, aListType, getter_AddRefs(uri));
+  nsIUrlClassifierFeature::URIType URIType;
+  nsresult rv = mFeature->GetURIByListType(aChannel, aListType, &URIType,
+                                           getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
     if (UC_LOG_ENABLED()) {
       nsAutoCString errorName;
       GetErrorName(rv, errorName);
       UC_LOG(("FeatureData::InitializeList got an unexpected error (rv=%s)",
               errorName.get()));
     }
     return rv;
@@ -718,17 +735,18 @@ nsresult FeatureData::InitializeList(
     return rv;
   }
 
   if (found) {
     mHostInPrefTables[aListType] = tableName;
   }
 
   RefPtr<URIData> uriData;
-  rv = aTask->GetOrCreateURIData(uri, innermostURI, getter_AddRefs(uriData));
+  rv = aTask->GetOrCreateURIData(uri, innermostURI, URIType,
+                                 getter_AddRefs(uriData));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(uriData);
 
   nsTArray<nsCString> tables;
   rv = mFeature->GetTables(aListType, tables);
--- a/netwerk/url-classifier/UrlClassifierFeatureCryptominingAnnotation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureCryptominingAnnotation.cpp
@@ -147,22 +147,26 @@ UrlClassifierFeatureCryptominingAnnotati
       aChannel, flags,
       nsIWebProgressListener::STATE_LOADED_CRYPTOMINING_CONTENT);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureCryptominingAnnotation::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
 
   if (aListType == nsIUrlClassifierFeature::blacklist) {
+    *aURIType = nsIUrlClassifierFeature::blacklistURI;
     return aChannel->GetURI(aURI);
   }
 
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+
+  *aURIType = nsIUrlClassifierFeature::pairwiseWhitelistURI;
   return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureCryptominingAnnotation.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureCryptominingAnnotation.h
@@ -29,16 +29,17 @@ class UrlClassifierFeatureCryptominingAn
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel,
                             const nsTArray<nsCString>& aList,
                             const nsTArray<nsCString>& aHashes,
                             bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   UrlClassifierFeatureCryptominingAnnotation();
 
   static void MaybeInitialize();
 };
 
--- a/netwerk/url-classifier/UrlClassifierFeatureCryptominingProtection.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureCryptominingProtection.cpp
@@ -175,22 +175,26 @@ UrlClassifierFeatureCryptominingProtecti
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureCryptominingProtection::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
 
   if (aListType == nsIUrlClassifierFeature::blacklist) {
+    *aURIType = nsIUrlClassifierFeature::blacklistURI;
     return aChannel->GetURI(aURI);
   }
 
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+
+  *aURIType = nsIUrlClassifierFeature::pairwiseWhitelistURI;
   return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureCryptominingProtection.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureCryptominingProtection.h
@@ -29,16 +29,17 @@ class UrlClassifierFeatureCryptominingPr
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel,
                             const nsTArray<nsCString>& aList,
                             const nsTArray<nsCString>& aHashes,
                             bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   UrlClassifierFeatureCryptominingProtection();
 
   static void MaybeInitialize();
 };
 
--- a/netwerk/url-classifier/UrlClassifierFeatureCustomTables.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureCustomTables.cpp
@@ -91,13 +91,13 @@ UrlClassifierFeatureCustomTables::Proces
   *aShouldContinue = true;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureCustomTables::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureFingerprintingAnnotation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureFingerprintingAnnotation.cpp
@@ -150,22 +150,26 @@ UrlClassifierFeatureFingerprintingAnnota
       nsIWebProgressListener::STATE_LOADED_FINGERPRINTING_CONTENT);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureFingerprintingAnnotation::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
 
   if (aListType == nsIUrlClassifierFeature::blacklist) {
+    *aURIType = nsIUrlClassifierFeature::blacklistURI;
     return aChannel->GetURI(aURI);
   }
 
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+
+  *aURIType = nsIUrlClassifierFeature::pairwiseWhitelistURI;
   return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureFingerprintingAnnotation.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureFingerprintingAnnotation.h
@@ -29,16 +29,17 @@ class UrlClassifierFeatureFingerprinting
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel,
                             const nsTArray<nsCString>& aList,
                             const nsTArray<nsCString>& aHashes,
                             bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   UrlClassifierFeatureFingerprintingAnnotation();
 
   static void MaybeInitialize();
 };
 
--- a/netwerk/url-classifier/UrlClassifierFeatureFingerprintingProtection.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureFingerprintingProtection.cpp
@@ -179,22 +179,26 @@ UrlClassifierFeatureFingerprintingProtec
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureFingerprintingProtection::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
 
   if (aListType == nsIUrlClassifierFeature::blacklist) {
+    *aURIType = nsIUrlClassifierFeature::blacklistURI;
     return aChannel->GetURI(aURI);
   }
 
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+
+  *aURIType = nsIUrlClassifierFeature::pairwiseWhitelistURI;
   return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureFingerprintingProtection.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureFingerprintingProtection.h
@@ -29,16 +29,17 @@ class UrlClassifierFeatureFingerprinting
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel,
                             const nsTArray<nsCString>& aList,
                             const nsTArray<nsCString>& aHashes,
                             bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   UrlClassifierFeatureFingerprintingProtection();
 
   static void MaybeInitialize();
 };
 
--- a/netwerk/url-classifier/UrlClassifierFeatureFlash.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureFlash.cpp
@@ -174,18 +174,22 @@ UrlClassifierFeatureFlash::ProcessChanne
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureFlash::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
 
   // Here we return the channel's URI always.
+  *aURIType = aListType == nsIUrlClassifierFeature::blacklist
+                  ? nsIUrlClassifierFeature::URIType::blacklistURI
+                  : nsIUrlClassifierFeature::URIType::whitelistURI;
   return aChannel->GetURI(aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureFlash.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureFlash.h
@@ -29,16 +29,17 @@ class UrlClassifierFeatureFlash final : 
 
   NS_IMETHOD
   ProcessChannel(nsIChannel* aChannel, const nsTArray<nsCString>& aList,
                  const nsTArray<nsCString>& aHashes,
                  bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   explicit UrlClassifierFeatureFlash(const FlashFeature& aFlashFeature);
 
   static void MaybeInitialize();
 
   nsIHttpChannel::FlashPluginState mFlashPluginState;
--- a/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.cpp
@@ -83,20 +83,21 @@ UrlClassifierFeatureLoginReputation::Pro
       "UrlClassifierFeatureLoginReputation::ProcessChannel should never be "
       "called");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureLoginReputation::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist,
              "UrlClassifierFeatureLoginReputation is meant to be used just to "
              "whitelist URLs");
-
+  *aURIType = nsIUrlClassifierFeature::URIType::whitelistURI;
   return aChannel->GetURI(aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.h
@@ -28,16 +28,17 @@ class UrlClassifierFeatureLoginReputatio
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel,
                             const nsTArray<nsCString>& aList,
                             const nsTArray<nsCString>& aHashes,
                             bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   UrlClassifierFeatureLoginReputation();
 };
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeaturePhishingProtection.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeaturePhishingProtection.cpp
@@ -113,14 +113,14 @@ UrlClassifierFeaturePhishingProtection::
     nsIChannel* aChannel, const nsTArray<nsCString>& aList,
     const nsTArray<nsCString>& aHashes, bool* aShouldContinue) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeaturePhishingProtection::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeaturePhishingProtection.h
+++ b/netwerk/url-classifier/UrlClassifierFeaturePhishingProtection.h
@@ -28,16 +28,17 @@ class UrlClassifierFeaturePhishingProtec
 
   NS_IMETHOD
   ProcessChannel(nsIChannel* aChannel, const nsTArray<nsCString>& aList,
                  const nsTArray<nsCString>& aHashes,
                  bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   explicit UrlClassifierFeaturePhishingProtection(
       const PhishingProtectionFeature& aFeature);
 
   static void MaybeInitialize();
 };
--- a/netwerk/url-classifier/UrlClassifierFeatureSocialTrackingAnnotation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureSocialTrackingAnnotation.cpp
@@ -154,22 +154,26 @@ UrlClassifierFeatureSocialTrackingAnnota
       nsIWebProgressListener::STATE_LOADED_SOCIALTRACKING_CONTENT);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureSocialTrackingAnnotation::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
 
   if (aListType == nsIUrlClassifierFeature::blacklist) {
+    *aURIType = nsIUrlClassifierFeature::blacklistURI;
     return aChannel->GetURI(aURI);
   }
 
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+
+  *aURIType = nsIUrlClassifierFeature::pairwiseWhitelistURI;
   return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureSocialTrackingAnnotation.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureSocialTrackingAnnotation.h
@@ -29,16 +29,17 @@ class UrlClassifierFeatureSocialTracking
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel,
                             const nsTArray<nsCString>& aList,
                             const nsTArray<nsCString>& aHashes,
                             bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   UrlClassifierFeatureSocialTrackingAnnotation();
 
   static void MaybeInitialize();
 };
 
--- a/netwerk/url-classifier/UrlClassifierFeatureSocialTrackingProtection.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureSocialTrackingProtection.cpp
@@ -179,22 +179,26 @@ UrlClassifierFeatureSocialTrackingProtec
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureSocialTrackingProtection::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
 
   if (aListType == nsIUrlClassifierFeature::blacklist) {
+    *aURIType = nsIUrlClassifierFeature::blacklistURI;
     return aChannel->GetURI(aURI);
   }
 
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+
+  *aURIType = nsIUrlClassifierFeature::pairwiseWhitelistURI;
   return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureSocialTrackingProtection.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureSocialTrackingProtection.h
@@ -29,16 +29,17 @@ class UrlClassifierFeatureSocialTracking
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel,
                             const nsTArray<nsCString>& aList,
                             const nsTArray<nsCString>& aHashes,
                             bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   UrlClassifierFeatureSocialTrackingProtection();
 
   static void MaybeInitialize();
 };
 
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
@@ -146,22 +146,26 @@ UrlClassifierFeatureTrackingAnnotation::
       aChannel, flags, nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureTrackingAnnotation::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
 
   if (aListType == nsIUrlClassifierFeature::blacklist) {
+    *aURIType = nsIUrlClassifierFeature::blacklistURI;
     return aChannel->GetURI(aURI);
   }
 
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+
+  *aURIType = nsIUrlClassifierFeature::pairwiseWhitelistURI;
   return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.h
@@ -29,16 +29,17 @@ class UrlClassifierFeatureTrackingAnnota
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel,
                             const nsTArray<nsCString>& aList,
                             const nsTArray<nsCString>& aHashes,
                             bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   UrlClassifierFeatureTrackingAnnotation();
 
   static void MaybeInitialize();
 };
 
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
@@ -173,22 +173,26 @@ UrlClassifierFeatureTrackingProtection::
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureTrackingProtection::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
-    nsIURI** aURI) {
+    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
   NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURIType);
   NS_ENSURE_ARG_POINTER(aURI);
 
   if (aListType == nsIUrlClassifierFeature::blacklist) {
+    *aURIType = nsIUrlClassifierFeature::blacklistURI;
     return aChannel->GetURI(aURI);
   }
 
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+
+  *aURIType = nsIUrlClassifierFeature::pairwiseWhitelistURI;
   return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.h
@@ -29,16 +29,17 @@ class UrlClassifierFeatureTrackingProtec
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel,
                             const nsTArray<nsCString>& aList,
                             const nsTArray<nsCString>& aHashes,
                             bool* aShouldContinue) override;
 
   NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
                               nsIUrlClassifierFeature::listType aListType,
+                              nsIUrlClassifierFeature::URIType* aURIType,
                               nsIURI** aURI) override;
 
  private:
   UrlClassifierFeatureTrackingProtection();
 
   static void MaybeInitialize();
 };
 
--- a/netwerk/url-classifier/nsIUrlClassifierFeature.idl
+++ b/netwerk/url-classifier/nsIUrlClassifierFeature.idl
@@ -20,16 +20,22 @@ interface nsIURI;
 [builtinclass, scriptable, uuid(a6c9b24e-b4f1-426e-af58-2c976c3943a8)]
 interface nsIUrlClassifierFeature : nsISupports
 {
   cenum listType: 8 {
     blacklist = 0,
     whitelist = 1,
   };
 
+  cenum URIType: 8 {
+    blacklistURI = 0,
+    whitelistURI = 1,
+    pairwiseWhitelistURI = 2,
+  };
+
   /**
    * The feature name
    */
   readonly attribute ACString name;
 
   /**
    * Returns the tables for one of the possible lists.
    */
@@ -66,19 +72,22 @@ interface nsIUrlClassifierFeature : nsIS
   [noscript] boolean processChannel(in nsIChannel aChannel,
                                     in ConstStringArrayRef aList,
                                     in ConstStringArrayRef aHashes);
 
   /**
    * Features can work with different URLs from a channel (channel url, or
    * top-level, or something else). This method returns what we need to use for
    * the current list.
+   * If the returned URI is created by CreatePairwiseWhiteListURI(), the URIType
+   * is pairwiseWhitelistURI. Otherwise, it depends on the listType.
    */
   [noscript] nsIURI getURIByListType(in nsIChannel channel,
-                                     in nsIUrlClassifierFeature_listType listType);
+                                     in nsIUrlClassifierFeature_listType listType,
+                                     out nsIUrlClassifierFeature_URIType URIType);
 };
 
 /**
  * The result of the classifier operation is this interface.
  * See asyncClassifyLocalWithFeatures() in nsIURIClassifier.idl.
  */
 [builtinclass, scriptable, uuid(ccb88140-5d66-4873-9815-a1b98d6cdc92)]
 interface nsIUrlClassifierFeatureResult : nsISupports
--- a/toolkit/components/url-classifier/LookupCache.cpp
+++ b/toolkit/components/url-classifier/LookupCache.cpp
@@ -406,16 +406,106 @@ bool LookupCache::IsCanonicalizedIP(cons
   if (PR_sscanf(PromiseFlatCString(aHost).get(), "%u.%u.%u.%u%c", &i1, &i2, &i3,
                 &i4, &c) == 4) {
     return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF);
   }
 
   return false;
 }
 
+// This is used when the URL is created by CreatePairwiseWhiteListURI(),
+// which returns an URI like "toplevel.page/?resource=third.party.domain"
+// The fragment rule for the hostname(toplevel.page) is still the same
+// as Safe Browsing protocol.
+// The difference is that we always keep the path and query string and
+// generate an additional fragment by removing the leading component of
+// third.party.domain. This is to make sure we can find a match when a
+// whitelisted domain is eTLD.
+/* static */
+nsresult LookupCache::GetLookupWhitelistFragments(
+    const nsACString& aSpec, nsTArray<nsCString>* aFragments) {
+  aFragments->Clear();
+
+  nsACString::const_iterator begin, end, iter, iter_end;
+  aSpec.BeginReading(begin);
+  aSpec.EndReading(end);
+
+  iter = begin;
+  iter_end = end;
+
+  // Fallback to use default fragment rule when the URL doesn't contain
+  // "/?resoruce=" because this means the URL is not generated in
+  // CreatePairwiseWhiteListURI()
+  if (!FindInReadable(NS_LITERAL_CSTRING("/?resource="), iter, iter_end)) {
+    return GetLookupFragments(aSpec, aFragments);
+  }
+
+  const nsACString& topLevelURL = Substring(begin, iter++);
+  const nsACString& thirdPartyURL = Substring(iter_end, end);
+
+  /**
+   * For the top-level URL, we follow the host fragment rule defined
+   * in the Safe Browsing protocol.
+   */
+  nsTArray<nsCString> topLevelURLs;
+  topLevelURLs.AppendElement(topLevelURL);
+
+  MOZ_ASSERT(!IsCanonicalizedIP(topLevelURL));
+
+  topLevelURL.BeginReading(begin);
+  topLevelURL.EndReading(end);
+  int numTopLevelURLComponents = 0;
+  while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) &&
+         numTopLevelURLComponents < MAX_HOST_COMPONENTS) {
+    // don't bother checking toplevel domains
+    if (++numTopLevelURLComponents >= 2) {
+      topLevelURL.EndReading(iter);
+      topLevelURLs.AppendElement(Substring(end, iter));
+    }
+    end = begin;
+    topLevelURL.BeginReading(begin);
+  }
+
+  /**
+   * The whiltelisted domain in the entity list may be eTLD or eTLD+1.
+   * Since the number of the domain name part in the third-party URL searching
+   * is always less than or equal to eTLD+1, we remove the leading
+   * component from the third-party domain to make sure we can find a match
+   * if the whitelisted domain stoed in the entity list is eTLD.
+   */
+  nsTArray<nsCString> thirdPartyURLs;
+  thirdPartyURLs.AppendElement(thirdPartyURL);
+
+  thirdPartyURL.BeginReading(iter);
+  thirdPartyURL.EndReading(end);
+  if (FindCharInReadable('.', iter, end)) {
+    iter++;
+    nsAutoCString thirdPartyURLToAdd;
+    thirdPartyURLToAdd.Assign(Substring(iter++, end));
+
+    // don't bother checking toplevel domains
+    if (FindCharInReadable('.', iter, end)) {
+      thirdPartyURLs.AppendElement(thirdPartyURLToAdd);
+    }
+  }
+
+  for (size_t i = 0; i < topLevelURLs.Length(); i++) {
+    for (size_t j = 0; j < thirdPartyURLs.Length(); j++) {
+      nsAutoCString key;
+      key.Assign(topLevelURLs[i]);
+      key.Append("/?resource=");
+      key.Append(thirdPartyURLs[j]);
+
+      aFragments->AppendElement(key);
+    }
+  }
+
+  return NS_OK;
+}
+
 /* static */
 nsresult LookupCache::GetLookupFragments(const nsACString& aSpec,
                                          nsTArray<nsCString>* aFragments)
 
 {
   aFragments->Clear();
 
   nsACString::const_iterator begin, end, iter;
--- a/toolkit/components/url-classifier/LookupCache.h
+++ b/toolkit/components/url-classifier/LookupCache.h
@@ -178,16 +178,19 @@ class LookupCache {
   static bool IsCanonicalizedIP(const nsACString& aHost);
 
   // take a lookup string (www.hostname.com/path/to/resource.html) and
   // expand it into the set of fragments that should be searched for in an
   // entry
   static nsresult GetLookupFragments(const nsACString& aSpec,
                                      nsTArray<nsCString>* aFragments);
 
+  static nsresult GetLookupWhitelistFragments(const nsACString& aSpec,
+                                              nsTArray<nsCString>* aFragments);
+
   LookupCache(const nsACString& aTableName, const nsACString& aProvider,
               nsCOMPtr<nsIFile>& aStoreFile);
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LookupCache);
 
   const nsCString& TableName() const { return mTableName; }
 
   // The directory handle where we operate will