Bug 1514202 - Port flash url-classifier to nsIUrlClassifierFeature - part 1 - Flash feature, r=dimi, r=edgar, r=valentin
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 04 Jan 2019 14:45:42 +0100
changeset 509689 ddd69d7ae1a0fc874b7037981f90e48abee73f39
parent 509643 86afdee4cff987a8de0a1e97005ede4f9fba011c
child 509690 b8155ef9f71faa4a380080a1253c5118b8b20979
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdimi, edgar, valentin
bugs1514202
milestone66.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 1514202 - Port flash url-classifier to nsIUrlClassifierFeature - part 1 - Flash feature, r=dimi, r=edgar, r=valentin
dom/ipc/PURLClassifierLocal.ipdl
dom/ipc/URLClassifierChild.h
dom/ipc/URLClassifierParent.cpp
modules/libpref/init/StaticPrefList.h
modules/libpref/init/all.js
netwerk/base/SimpleChannelParent.cpp
netwerk/base/nsIParentChannel.idl
netwerk/ipc/NeckoMessageUtils.h
netwerk/protocol/data/DataChannelParent.cpp
netwerk/protocol/file/FileChannelParent.cpp
netwerk/protocol/ftp/FTPChannelParent.cpp
netwerk/protocol/http/HttpBackgroundChannelChild.cpp
netwerk/protocol/http/HttpBackgroundChannelChild.h
netwerk/protocol/http/HttpBackgroundChannelParent.cpp
netwerk/protocol/http/HttpBackgroundChannelParent.h
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/NullHttpChannel.cpp
netwerk/protocol/http/PHttpBackgroundChannel.ipdl
netwerk/protocol/http/nsIHttpChannel.idl
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
netwerk/url-classifier/AsyncUrlChannelClassifier.cpp
netwerk/url-classifier/UrlClassifierCommon.cpp
netwerk/url-classifier/UrlClassifierCommon.h
netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
netwerk/url-classifier/UrlClassifierFeatureFlash.cpp
netwerk/url-classifier/UrlClassifierFeatureFlash.h
netwerk/url-classifier/UrlClassifierFeatureLoginReputation.cpp
netwerk/url-classifier/UrlClassifierFeatureLoginReputation.h
netwerk/url-classifier/UrlClassifierFeatureResult.cpp
netwerk/url-classifier/UrlClassifierFeatureResult.h
netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.h
netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.h
netwerk/url-classifier/moz.build
netwerk/url-classifier/nsIUrlClassifierFeature.idl
toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
uriloader/exthandler/nsExternalProtocolHandler.cpp
--- a/dom/ipc/PURLClassifierLocal.ipdl
+++ b/dom/ipc/PURLClassifierLocal.ipdl
@@ -1,23 +1,27 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* 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 protocol PContent;
+
 include PURLClassifierInfo;
 
+using refcounted class nsIURI from "mozilla/ipc/URIUtils.h";
+
 namespace mozilla {
 namespace dom {
 
 struct URLClassifierLocalResult
 {
+  nsIURI uri;
   nsCString featureName;
   nsCString matchingList;
 };
 
 protocol PURLClassifierLocal
 {
   manager PContent;
 
--- a/dom/ipc/URLClassifierChild.h
+++ b/dom/ipc/URLClassifierChild.h
@@ -4,16 +4,17 @@
  * 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/. */
 
 #ifndef mozilla_dom_URLClassifierChild_h
 #define mozilla_dom_URLClassifierChild_h
 
 #include "mozilla/dom/PURLClassifierChild.h"
 #include "mozilla/dom/PURLClassifierLocalChild.h"
+#include "mozilla/ipc/URIUtils.h"
 #include "mozilla/net/UrlClassifierFeatureResult.h"
 #include "nsIURIClassifier.h"
 #include "nsIUrlClassifierFeature.h"
 
 namespace mozilla {
 namespace dom {
 
 class URLClassifierChild : public PURLClassifierChild {
@@ -58,18 +59,24 @@ class URLClassifierLocalChild : public P
         if (NS_WARN_IF(NS_FAILED(rv))) {
           continue;
         }
 
         if (result.featureName() != name) {
           continue;
         }
 
+        RefPtr<nsIURI> uri = result.uri();
+        if (NS_WARN_IF(!uri)) {
+          continue;
+        }
+
         RefPtr<net::UrlClassifierFeatureResult> r =
-            new net::UrlClassifierFeatureResult(feature, result.matchingList());
+            new net::UrlClassifierFeatureResult(uri, feature,
+                                                result.matchingList());
         finalResults.AppendElement(r);
         break;
       }
     }
 
     mCallback->OnClassifyComplete(finalResults);
     return IPC_OK();
   }
--- a/dom/ipc/URLClassifierParent.cpp
+++ b/dom/ipc/URLClassifierParent.cpp
@@ -48,18 +48,18 @@ mozilla::ipc::IPCResult URLClassifierPar
 namespace {
 
 // This class implements a nsIUrlClassifierFeature on the parent side, starting
 // from an IPC data struct.
 class IPCFeature final : public nsIUrlClassifierFeature {
  public:
   NS_DECL_ISUPPORTS
 
-  explicit IPCFeature(const IPCURLClassifierFeature& aFeature)
-      : mIPCFeature(aFeature) {}
+  IPCFeature(nsIURI* aURI, const IPCURLClassifierFeature& aFeature)
+      : mURI(aURI), mIPCFeature(aFeature) {}
 
   NS_IMETHOD
   GetName(nsACString& aName) override {
     aName = mIPCFeature.featureName();
     return NS_OK;
   }
 
   NS_IMETHOD
@@ -97,19 +97,32 @@ class IPCFeature final : public nsIUrlCl
                  bool* aShouldContinue) override {
     NS_ENSURE_ARG_POINTER(aShouldContinue);
     *aShouldContinue = true;
 
     // Nothing to do here.
     return NS_OK;
   }
 
+  NS_IMETHOD
+  GetURIByListType(nsIChannel* aChannel,
+                   nsIUrlClassifierFeature::listType aListType,
+                   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);
+    return NS_OK;
+  }
+
  private:
   ~IPCFeature() = default;
 
+  nsCOMPtr<nsIURI> mURI;
   IPCURLClassifierFeature mIPCFeature;
 };
 
 NS_IMPL_ISUPPORTS(IPCFeature, nsIUrlClassifierFeature)
 
 }  // namespace
 
 NS_IMPL_ISUPPORTS(URLClassifierLocalParent, nsIUrlClassifierFeatureCallback)
@@ -125,17 +138,17 @@ mozilla::ipc::IPCResult URLClassifierLoc
       do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     OnClassifyComplete(nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>());
     return IPC_OK();
   }
 
   nsTArray<RefPtr<nsIUrlClassifierFeature>> features;
   for (const IPCURLClassifierFeature& feature : aFeatures) {
-    features.AppendElement(new IPCFeature(feature));
+    features.AppendElement(new IPCFeature(aURI, feature));
   }
 
   // Doesn't matter if we pass blacklist, whitelist or any other list.
   // IPCFeature returns always the same values.
   rv = uriClassifier->AsyncClassifyLocalWithFeatures(
       aURI, features, nsIUrlClassifierFeature::blacklist, this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     OnClassifyComplete(nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>());
@@ -149,15 +162,17 @@ NS_IMETHODIMP
 URLClassifierLocalParent::OnClassifyComplete(
     const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aResults) {
   nsTArray<URLClassifierLocalResult> ipcResults;
   for (nsIUrlClassifierFeatureResult* result : aResults) {
     URLClassifierLocalResult* ipcResult = ipcResults.AppendElement();
 
     net::UrlClassifierFeatureResult* r =
         static_cast<net::UrlClassifierFeatureResult*>(result);
+
+    ipcResult->uri() = r->URI();
     r->Feature()->GetName(ipcResult->featureName());
     ipcResult->matchingList() = r->List();
   }
 
   Unused << Send__delete__(this, ipcResults);
   return NS_OK;
 }
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1936,16 +1936,32 @@ VARCACHE_PREF(
 VARCACHE_PREF(
   "dom.security.featurePolicy.webidl.enabled",
    dom_security_featurePolicy_webidl_enabled,
   bool, PREF_VALUE
 )
 #undef PREF_VALUE
 
 //---------------------------------------------------------------------------
+// Plugins prefs
+//---------------------------------------------------------------------------
+
+VARCACHE_PREF(
+  "plugins.flashBlock.enabled",
+   plugins_flashBlock_enabled,
+  bool, false
+)
+
+VARCACHE_PREF(
+  "plugins.http_https_only",
+   plugins_http_https_only,
+  bool, true
+)
+
+//---------------------------------------------------------------------------
 // Reporting API
 //---------------------------------------------------------------------------
 
 #ifdef NIGHTLY_BUILD
 # define PREF_VALUE true
 #else
 # define PREF_VALUE false
 #endif
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5650,19 +5650,16 @@ pref("urlclassifier.blockedTable", "test
 pref("urlclassifier.flashAllowTable", "allow-flashallow-digest256");
 pref("urlclassifier.flashAllowExceptTable", "except-flashallow-digest256");
 pref("urlclassifier.flashTable", "block-flash-digest256");
 pref("urlclassifier.flashExceptTable", "except-flash-digest256");
 pref("urlclassifier.flashSubDocTable", "block-flashsubdoc-digest256");
 pref("urlclassifier.flashSubDocExceptTable", "except-flashsubdoc-digest256");
 pref("urlclassifier.flashInfobarTable", "except-flashinfobar-digest256");
 
-pref("plugins.http_https_only", true);
-pref("plugins.flashBlock.enabled", false);
-
 // Turn off Spatial navigation by default.
 pref("snav.enabled", false);
 
 // Wakelock is disabled by default.
 pref("dom.wakelock.enabled", false);
 
 // The URL of the Firefox Accounts auth server backend
 pref("identity.fxaccounts.auth.uri", "https://api.accounts.firefox.com/v1");
--- a/netwerk/base/SimpleChannelParent.cpp
+++ b/netwerk/base/SimpleChannelParent.cpp
@@ -48,16 +48,23 @@ SimpleChannelParent::NotifyTrackingCooki
 
 NS_IMETHODIMP
 SimpleChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
+SimpleChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  // Nothing to do.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 SimpleChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                               const nsACString& aProvider,
                                               const nsACString& aPrefix) {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/nsIParentChannel.idl
+++ b/netwerk/base/nsIParentChannel.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 "nsIStreamListener.idl"
+#include "nsIHttpChannel.idl"
 
 interface nsITabParent;
 
 %{C++
 namespace mozilla {
 namespace net {
 class HttpChannelParentListener;
 }
@@ -41,16 +42,21 @@ interface nsIParentChannel : nsIStreamLi
   [noscript] void notifyCookieAllowed();
 
   /**
    * Called to notify the HttpChannelChild that cookie has been blocked for
    * this load.
    */
   [noscript] void notifyTrackingCookieBlocked(in uint32_t aRejectedReason);
 
+  /**
+   * Called to notify the HttpChannelChild that flash plugin state has changed.
+   */
+  [noscript] void notifyFlashPluginStateChanged(in nsIHttpChannel_FlashPluginState aState);
+
    /**
    * Called to set matched information when URL matches SafeBrowsing list.
    * @param aList
    *        Name of the list that matched
    * @param aProvider
    *        Name of provider that matched
    * @param aFullHash
    *        String represents full hash that matched
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -5,16 +5,17 @@
 
 #ifndef mozilla_net_NeckoMessageUtils_h
 #define mozilla_net_NeckoMessageUtils_h
 
 #include "mozilla/DebugOnly.h"
 
 #include "ipc/IPCMessageUtils.h"
 #include "nsExceptionHandler.h"
+#include "nsIHttpChannel.h"
 #include "nsPrintfCString.h"
 #include "nsString.h"
 #include "prio.h"
 #include "mozilla/net/DNS.h"
 #include "TimingStruct.h"
 
 namespace IPC {
 
@@ -174,11 +175,17 @@ struct ParamTraits<mozilla::net::Resourc
            ReadParam(aMsg, aIter, &aResult->transferSize) &&
            ReadParam(aMsg, aIter, &aResult->encodedBodySize) &&
            ReadParam(aMsg, aIter, &aResult->protocolVersion) &&
            ReadParam(aMsg, aIter, &aResult->cacheReadStart) &&
            ReadParam(aMsg, aIter, &aResult->cacheReadEnd);
   }
 };
 
+template <>
+struct ParamTraits<nsIHttpChannel::FlashPluginState>
+    : public ContiguousEnumSerializerInclusive<
+          nsIHttpChannel::FlashPluginState, nsIHttpChannel::FlashPluginUnknown,
+          nsIHttpChannel::FlashPluginLastValue> {};
+
 }  // namespace IPC
 
 #endif  // mozilla_net_NeckoMessageUtils_h
--- a/netwerk/protocol/data/DataChannelParent.cpp
+++ b/netwerk/protocol/data/DataChannelParent.cpp
@@ -48,16 +48,23 @@ DataChannelParent::NotifyTrackingCookieB
 
 NS_IMETHODIMP
 DataChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
+DataChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  // Nothing to do.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 DataChannelParent::SetClassifierMatchedInfo(const nsACString &aList,
                                             const nsACString &aProvider,
                                             const nsACString &aFullHash) {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/file/FileChannelParent.cpp
+++ b/netwerk/protocol/file/FileChannelParent.cpp
@@ -48,16 +48,23 @@ FileChannelParent::NotifyTrackingCookieB
 
 NS_IMETHODIMP
 FileChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
+FileChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  // Nothing to do.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 FileChannelParent::SetClassifierMatchedInfo(const nsACString &aList,
                                             const nsACString &aProvider,
                                             const nsACString &aFullHash) {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -519,16 +519,23 @@ FTPChannelParent::NotifyTrackingCookieBl
 
 NS_IMETHODIMP
 FTPChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // One day, this should probably be filled in.
   return NS_OK;
 }
 
 NS_IMETHODIMP
+FTPChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  // One day, this should probably be filled in.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 FTPChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                            const nsACString& aProvider,
                                            const nsACString& aFullHash) {
   // One day, this should probably be filled in.
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
@@ -353,16 +353,35 @@ IPCResult HttpBackgroundChannelChild::Re
 
   // NotifyTrackingResource has no order dependency to OnStartRequest.
   // It this be handled as soon as possible
   mChannelChild->ProcessNotifyTrackingResource(aIsThirdParty);
 
   return IPC_OK();
 }
 
+IPCResult HttpBackgroundChannelChild::RecvNotifyFlashPluginStateChanged(
+    const nsIHttpChannel::FlashPluginState& aState) {
+  LOG(
+      ("HttpBackgroundChannelChild::RecvNotifyFlashPluginStateChanged "
+       "[this=%p]\n",
+       this));
+  MOZ_ASSERT(OnSocketThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  // NotifyFlashPluginStateChanged has no order dependency to OnStartRequest.
+  // It this be handled as soon as possible
+  mChannelChild->ProcessNotifyFlashPluginStateChanged(aState);
+
+  return IPC_OK();
+}
+
 IPCResult HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(
     const ClassifierInfo& info) {
   LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n",
        this));
   MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.h
@@ -67,16 +67,19 @@ class HttpBackgroundChannelChild final :
 
   IPCResult RecvNotifyCookieAllowed() override;
 
   IPCResult RecvNotifyTrackingCookieBlocked(
       const uint32_t& aRejectedReason) override;
 
   IPCResult RecvNotifyTrackingResource(const bool& aIsThirdParty) override;
 
+  IPCResult RecvNotifyFlashPluginStateChanged(
+      const nsIHttpChannel::FlashPluginState& aState) override;
+
   IPCResult RecvSetClassifierMatchedInfo(const ClassifierInfo& info) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
  private:
   virtual ~HttpBackgroundChannelChild();
 
   // Initiate the creation of the PBckground IPC channel.
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -421,16 +421,47 @@ bool HttpBackgroundChannelParent::OnNoti
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
 
     return NS_SUCCEEDED(rv);
   }
 
   return SendNotifyTrackingResource(aIsThirdParty);
 }
 
+bool HttpBackgroundChannelParent::OnNotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  LOG(
+      ("HttpBackgroundChannelParent::OnNotifyFlashPluginStateChanged "
+       "[this=%p]\n",
+       this));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    MutexAutoLock lock(mBgThreadMutex);
+    RefPtr<HttpBackgroundChannelParent> self = this;
+    nsresult rv = mBackgroundThread->Dispatch(
+        NS_NewRunnableFunction(
+            "net::HttpBackgroundChannelParent::OnNotifyFlashPluginStateChanged",
+            [self, aState]() {
+              self->OnNotifyFlashPluginStateChanged(aState);
+            }),
+        NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  return SendNotifyFlashPluginStateChanged(aState);
+}
+
 bool HttpBackgroundChannelParent::OnSetClassifierMatchedInfo(
     const nsACString& aList, const nsACString& aProvider,
     const nsACString& aFullHash) {
   LOG(("HttpBackgroundChannelParent::OnSetClassifierMatchedInfo [this=%p]\n",
        this));
   AssertIsInMainProcess();
 
   if (NS_WARN_IF(!mIPCOpened)) {
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.h
@@ -70,16 +70,19 @@ class HttpBackgroundChannelParent final 
   bool OnNotifyCookieAllowed();
 
   // To send NotifyTrackingCookieBlocked message over background channel.
   bool OnNotifyTrackingCookieBlocked(uint32_t aRejectedReason);
 
   // To send NotifyTrackingResource message over background channel.
   bool OnNotifyTrackingResource(bool aIsThirdParty);
 
+  // To send NotifyFlashPluginStateChanged message over background channel.
+  bool OnNotifyFlashPluginStateChanged(nsIHttpChannel::FlashPluginState aState);
+
   // To send SetClassifierMatchedInfo message over background channel.
   bool OnSetClassifierMatchedInfo(const nsACString& aList,
                                   const nsACString& aProvider,
                                   const nsACString& aFullHash);
 
  protected:
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -163,16 +163,17 @@ HttpBaseChannel::HttpBaseChannel()
       mTopLevelOuterContentWindowId(0),
       mAltDataLength(0),
       mChannelId(0),
       mReqContentLength(0U),
       mStatus(NS_OK),
       mCanceled(false),
       mIsFirstPartyTrackingResource(false),
       mIsThirdPartyTrackingResource(false),
+      mFlashPluginState(nsIHttpChannel::FlashPluginUnknown),
       mLoadFlags(LOAD_NORMAL),
       mCaps(0),
       mClassOfService(0),
       mUpgradeToSecure(false),
       mApplyConversion(true),
       mIsPending(false),
       mWasOpened(false),
       mRequestObserversCalled(false),
@@ -314,16 +315,22 @@ void HttpBaseChannel::SetIsTrackingResou
     MOZ_ASSERT(!mIsFirstPartyTrackingResource);
     mIsThirdPartyTrackingResource = true;
   } else {
     MOZ_ASSERT(!mIsThirdPartyTrackingResource);
     mIsFirstPartyTrackingResource = true;
   }
 }
 
+void HttpBaseChannel::SetFlashPluginState(
+    nsIHttpChannel::FlashPluginState aState) {
+  LOG(("HttpBaseChannel::SetFlashPluginState %p", this));
+  mFlashPluginState = aState;
+}
+
 nsresult HttpBaseChannel::Init(nsIURI* aURI, uint32_t aCaps,
                                nsProxyInfo* aProxyInfo,
                                uint32_t aProxyResolveFlags, nsIURI* aProxyURI,
                                uint64_t aChannelId) {
   LOG1(("HttpBaseChannel::Init [this=%p]\n", this));
 
   MOZ_ASSERT(aURI, "null uri");
 
@@ -1481,16 +1488,23 @@ HttpBaseChannel::GetIsTrackingResource(b
 NS_IMETHODIMP
 HttpBaseChannel::GetIsThirdPartyTrackingResource(bool* aIsTrackingResource) {
   MOZ_ASSERT(!(mIsFirstPartyTrackingResource && mIsThirdPartyTrackingResource));
   *aIsTrackingResource = mIsThirdPartyTrackingResource;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HttpBaseChannel::GetFlashPluginState(nsIHttpChannel::FlashPluginState* aState) {
+  uint32_t flashPluginState = mFlashPluginState;
+  *aState = (nsIHttpChannel::FlashPluginState)flashPluginState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::OverrideTrackingFlagsForDocumentCookieAccessor(
     nsIHttpChannel* aDocumentChannel) {
   LOG(
       ("HttpBaseChannel::OverrideTrackingFlagsForDocumentCookieAccessor() %p "
        "mIsFirstPartyTrackingResource=%d  mIsThirdPartyTrackingResource=%d",
        this, static_cast<int>(mIsFirstPartyTrackingResource),
        static_cast<int>(mIsThirdPartyTrackingResource)));
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -233,16 +233,18 @@ class HttpBaseChannel : public nsHashPro
   NS_IMETHOD SetTopLevelContentWindowId(uint64_t aContentWindowId) override;
   NS_IMETHOD GetTopLevelOuterContentWindowId(uint64_t *aWindowId) override;
   NS_IMETHOD SetTopLevelOuterContentWindowId(uint64_t aWindowId) override;
   NS_IMETHOD GetIsTrackingResource(bool *aIsTrackingResource) override;
   NS_IMETHOD GetIsThirdPartyTrackingResource(
       bool *aIsTrackingResource) override;
   NS_IMETHOD OverrideTrackingFlagsForDocumentCookieAccessor(
       nsIHttpChannel *aDocumentChannel) override;
+  NS_IMETHOD GetFlashPluginState(
+      nsIHttpChannel::FlashPluginState *aState) override;
 
   // nsIHttpChannelInternal
   NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI) override;
   NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI) override;
   NS_IMETHOD GetRequestVersion(uint32_t *major, uint32_t *minor) override;
   NS_IMETHOD GetResponseVersion(uint32_t *major, uint32_t *minor) override;
   NS_IMETHOD SetCookie(const char *aCookieHeader) override;
   NS_IMETHOD GetThirdPartyFlags(uint32_t *aForce) override;
@@ -408,16 +410,18 @@ class HttpBaseChannel : public nsHashPro
 
   // Callback on STS thread called by CopyComplete when NS_AsyncCopy()
   // is finished. This function works as a proxy function to dispatch
   // |EnsureUploadStreamIsCloneableComplete| to main thread.
   virtual void OnCopyComplete(nsresult aStatus);
 
   void SetIsTrackingResource(bool aIsThirdParty);
 
+  void SetFlashPluginState(nsIHttpChannel::FlashPluginState aState);
+
   const uint64_t &ChannelId() const { return mChannelId; }
 
   void InternalSetUploadStream(nsIInputStream *uploadStream) {
     mUploadStream = uploadStream;
   }
 
   void InternalSetUploadStreamLength(uint64_t aLength) {
     mReqContentLength = aLength;
@@ -642,16 +646,17 @@ class HttpBaseChannel : public nsHashPro
 
   Atomic<nsresult, ReleaseAcquire> mStatus;
 
   // Use Release-Acquire ordering to ensure the OMT ODA is ignored while channel
   // is canceled on main thread.
   Atomic<bool, ReleaseAcquire> mCanceled;
   Atomic<bool, ReleaseAcquire> mIsFirstPartyTrackingResource;
   Atomic<bool, ReleaseAcquire> mIsThirdPartyTrackingResource;
+  Atomic<uint32_t, ReleaseAcquire> mFlashPluginState;
 
   uint32_t mLoadFlags;
   uint32_t mCaps;
   uint32_t mClassOfService;
 
   uint32_t mUpgradeToSecure : 1;
   uint32_t mApplyConversion : 1;
   uint32_t mIsPending : 1;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1857,16 +1857,25 @@ void HttpChannelChild::ProcessNotifyTrac
       ("HttpChannelChild::ProcessNotifyTrackingResource thirdparty=%d "
        "[this=%p]\n",
        static_cast<int>(aIsThirdParty), this));
   MOZ_ASSERT(OnSocketThread());
 
   SetIsTrackingResource(aIsThirdParty);
 }
 
+void HttpChannelChild::ProcessNotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  LOG(("HttpChannelChild::ProcessNotifyFlashPluginStateChanged [this=%p]\n",
+       this));
+  MOZ_ASSERT(OnSocketThread());
+
+  SetFlashPluginState(aState);
+}
+
 void HttpChannelChild::FlushedForDiversion() {
   LOG(("HttpChannelChild::FlushedForDiversion [this=%p]\n", this));
   MOZ_RELEASE_ASSERT(mDivertingToParent);
 
   // Once this is set, it should not be unset before HttpChannelChild is taken
   // down. After it is set, no OnStart/OnData/OnStop callbacks should be
   // received from the parent channel, nor dequeued from the ChannelEventQueue.
   mFlushedForDiversion = true;
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -260,16 +260,18 @@ class HttpChannelChild final : public PH
   void ProcessOnProgress(const int64_t& aProgress, const int64_t& aProgressMax);
   void ProcessOnStatus(const nsresult& aStatus);
   void ProcessFlushedForDiversion();
   void ProcessDivertMessages();
   void ProcessNotifyTrackingProtectionDisabled();
   void ProcessNotifyCookieAllowed();
   void ProcessNotifyTrackingCookieBlocked(uint32_t aRejectedReason);
   void ProcessNotifyTrackingResource(bool aIsThirdParty);
+  void ProcessNotifyFlashPluginStateChanged(
+      nsIHttpChannel::FlashPluginState aState);
   void ProcessSetClassifierMatchedInfo(const nsCString& aList,
                                        const nsCString& aProvider,
                                        const nsCString& aFullHash);
 
   // Return true if we need to tell the parent the size of unreported received
   // data
   bool NeedToReportBytesRead();
   int32_t mUnreportBytesRead = 0;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1822,16 +1822,27 @@ HttpChannelParent::NotifyTrackingResourc
   if (!mIPCClosed) {
     MOZ_ASSERT(mBgParent);
     Unused << mBgParent->OnNotifyTrackingResource(aIsThirdParty);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HttpChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  LOG(("HttpChannelParent::NotifyFlashPluginStateChanged [this=%p]\n", this));
+  if (!mIPCClosed) {
+    MOZ_ASSERT(mBgParent);
+    Unused << mBgParent->OnNotifyFlashPluginStateChanged(aState);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HttpChannelParent::Delete() {
   if (!mIPCClosed) Unused << DoSendDeleteSelf();
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIParentRedirectingChannel
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -84,16 +84,22 @@ NullHttpChannel::SetTopLevelOuterContent
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetIsTrackingResource(bool *aIsTrackingResource) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+NullHttpChannel::GetFlashPluginState(
+    nsIHttpChannel::FlashPluginState *aResult) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::GetIsThirdPartyTrackingResource(bool *aIsTrackingResource) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::OverrideTrackingFlagsForDocumentCookieAccessor(
     nsIHttpChannel *aDocumentChannel) {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/netwerk/protocol/http/PHttpBackgroundChannel.ipdl
+++ b/netwerk/protocol/http/PHttpBackgroundChannel.ipdl
@@ -8,16 +8,17 @@
 include protocol PBackground;
 include NeckoChannelParams;
 include PURLClassifierInfo;
 
 include "mozilla/net/NeckoMessageUtils.h";
 
 using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
 using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
+using nsIHttpChannel::FlashPluginState from "mozilla/net/NeckoMessageUtils.h";
 
 namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
 async protocol PHttpBackgroundChannel
 {
   manager PBackground;
@@ -61,16 +62,20 @@ child:
 
   // Tell the child that tracking cookies are blocked for this load.
   async NotifyTrackingCookieBlocked(uint32_t aRejectedReason);
 
   // Tell the child that the resource being loaded is on the tracking
   // protection list.
   async NotifyTrackingResource(bool aIsThirdParty);
 
+  // Tell the child that the current channel's document is not allowed to load
+  // flash content.
+  async NotifyFlashPluginStateChanged(FlashPluginState aState);
+
   // Tell the child information of matched URL againts SafeBrowsing list
   async SetClassifierMatchedInfo(ClassifierInfo info);
 
   async __delete__();
 
 };
 
 
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -491,16 +491,30 @@ interface nsIHttpChannel : nsIChannel
      * protection list and is considered third-party with the top window URI.
      * This is only available if the privacy.trackingprotection.annotate_channels
      * pref is set and its value should only be relied on after the channel has
      * established a connection.
      */
     [infallible] readonly attribute boolean isThirdPartyTrackingResource;
 
     /**
+     * Returns the allowing status for flash plugin for this channel.
+     */
+    cenum FlashPluginState : 8 {
+      FlashPluginUnknown = 0,
+      FlashPluginAllowed = 1,
+      FlashPluginDenied = 2,
+      FlashPluginDeniedInSubdocuments = 3,
+
+      // Keep this equal to the last value.
+      FlashPluginLastValue = 3,
+    };
+    [infallible] readonly attribute nsIHttpChannel_FlashPluginState flashPluginState;
+
+    /**
      * This method is used by the document.cookie call site in order
      * to override the tracking status of an HTTP channel. This should
      * only be called by Gecko under certain circumstances when Gecko
      * can guarantee that the channel classifier service will not be
      * determining the tracking status of the channel.
      *
      * @param aDocumentChannel
      *        The document channel from which to copy the tracking flags
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -761,16 +761,23 @@ nsViewSourceChannel::SetTopLevelOuterCon
 NS_IMETHODIMP
 nsViewSourceChannel::GetIsTrackingResource(bool *aIsTrackingResource) {
   return !mHttpChannel
              ? NS_ERROR_NULL_POINTER
              : mHttpChannel->GetIsTrackingResource(aIsTrackingResource);
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::GetFlashPluginState(
+    nsIHttpChannel::FlashPluginState *aResult) {
+  return !mHttpChannel ? NS_ERROR_NULL_POINTER
+                       : mHttpChannel->GetFlashPluginState(aResult);
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::GetIsThirdPartyTrackingResource(
     bool *aIsTrackingResource) {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER
                        : mHttpChannel->GetIsThirdPartyTrackingResource(
                              aIsTrackingResource);
 }
 
 NS_IMETHODIMP
--- a/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp
+++ b/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp
@@ -19,16 +19,84 @@
 #include "nsServiceManagerUtils.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
 namespace net {
 
 namespace {
 
+// When we do blacklist/whitelist classification, from a list of features, we
+// need to aggregate them per URI, because not all the features work with the
+// same channel's URI.
+// This struct contains only the features able to deal with a particular URI.
+// See more in GetFeatureTasks().
+struct FeatureTask {
+  nsCOMPtr<nsIURI> mURI;
+  // Let's use RefPtr<> here, because this needs to be used with methods which
+  // require it.
+  nsTArray<RefPtr<nsIUrlClassifierFeature>> mFeatures;
+};
+
+// Features are able to classify particular URIs from a channel. For instance,
+// tracking-annotation feature uses the top-level URI to whitelist the current
+// channel's URI; flash feature always uses the channel's URI.  Because of
+// this, this function aggregates feature per URI in an array of FeatureTask
+// object.
+nsresult GetFeatureTasks(
+    nsIChannel* aChannel,
+    const nsTArray<nsCOMPtr<nsIUrlClassifierFeature>>& aFeatures,
+    nsIUrlClassifierFeature::listType aListType,
+    nsTArray<FeatureTask>& aTasks) {
+  MOZ_ASSERT(!aFeatures.IsEmpty());
+
+  // Let's unify features per nsIURI.
+  for (nsIUrlClassifierFeature* feature : aFeatures) {
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv =
+        feature->GetURIByListType(aChannel, aListType, getter_AddRefs(uri));
+    if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
+      if (UC_LOG_ENABLED()) {
+        nsAutoCString errorName;
+        GetErrorName(rv, errorName);
+        UC_LOG(
+            ("GetFeatureTasks got an unexpected error (rv=%s) while trying to "
+             "create a whitelist URI. Allowing tracker.",
+             errorName.get()));
+      }
+      return rv;
+    }
+
+    MOZ_ASSERT(uri);
+
+    bool found = false;
+    for (FeatureTask& task : aTasks) {
+      bool equal = false;
+      rv = task.mURI->Equals(uri, &equal);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      if (equal) {
+        task.mFeatures.AppendElement(feature);
+        found = true;
+        break;
+      }
+    }
+
+    if (!found) {
+      FeatureTask* task = aTasks.AppendElement();
+      task->mURI = uri;
+      task->mFeatures.AppendElement(feature);
+    }
+  }
+
+  return NS_OK;
+}
+
 nsresult TrackerFound(
     const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aResults,
     nsIChannel* aChannel, const std::function<void()>& aCallback) {
   // Let's ask the features to do the magic.
   for (nsIUrlClassifierFeatureResult* result : aResults) {
     UrlClassifierFeatureResult* r =
         static_cast<UrlClassifierFeatureResult*>(result);
 
@@ -43,137 +111,86 @@ nsresult TrackerFound(
       break;
     }
   }
 
   aCallback();
   return NS_OK;
 }
 
-nsresult CreateWhiteListURI(nsIChannel* aChannel, nsIURI** aURI) {
-  MOZ_ASSERT(aChannel);
-  MOZ_ASSERT(aURI);
-
-  nsresult rv;
-  nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!chan) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIURI> topWinURI;
-  rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!topWinURI) {
-    if (UC_LOG_ENABLED()) {
-      nsresult rv;
-      nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(aChannel, &rv);
-      nsCOMPtr<nsIURI> uri;
-      rv = httpChan->GetURI(getter_AddRefs(uri));
-      nsAutoCString spec;
-      uri->GetAsciiSpec(spec);
-      spec.Truncate(
-          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
-      UC_LOG(
-          ("CreateWhiteListURI: No window URI associated with %s", spec.get()));
-    }
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIScriptSecurityManager> securityManager =
-      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIPrincipal> chanPrincipal;
-  rv = securityManager->GetChannelURIPrincipal(aChannel,
-                                               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;
-  UC_LOG(("CreateWhiteListURI: Looking for %s in the whitelist (channel=%p)",
-          whitelistEntry.get(), aChannel));
-
-  nsCOMPtr<nsIURI> whitelistURI;
-  rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  whitelistURI.forget(aURI);
-  return NS_OK;
-}
-
-nsresult IsTrackerWhitelisted(
-    nsIURI* aWhiteListURI,
-    const nsTArray<RefPtr<nsIUrlClassifierFeature>>& aFeatures,
-    nsIUrlClassifierFeatureCallback* aCallback) {
-  MOZ_ASSERT(aWhiteListURI);
-  MOZ_ASSERT(!aFeatures.IsEmpty());
-  MOZ_ASSERT(aCallback);
-
-  nsresult rv;
-  nsCOMPtr<nsIURIClassifier> uriClassifier =
-      do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return uriClassifier->AsyncClassifyLocalWithFeatures(
-      aWhiteListURI, aFeatures, nsIUrlClassifierFeature::whitelist, aCallback);
-}
-
 // This class is designed to get the results of checking whitelist.
 class WhitelistClassifierCallback final
     : public nsIUrlClassifierFeatureCallback {
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIURLCLASSIFIERFEATURECALLBACK
 
   WhitelistClassifierCallback(
-      nsIChannel* aChannel, nsIURI* aURI,
+      nsIChannel* aChannel,
       const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aBlacklistResults,
       std::function<void()>& aCallback)
       : mChannel(aChannel),
-        mURI(aURI),
+        mTaskCount(0),
         mBlacklistResults(aBlacklistResults),
         mChannelCallback(aCallback) {
     MOZ_ASSERT(mChannel);
-    MOZ_ASSERT(mURI);
     MOZ_ASSERT(!mBlacklistResults.IsEmpty());
   }
 
+  void SetTaskCount(uint32_t aTaskCount) {
+    MOZ_ASSERT(aTaskCount > 0);
+    mTaskCount = aTaskCount;
+  }
+
  private:
   ~WhitelistClassifierCallback() = default;
 
+  nsresult OnClassifyCompleteInternal();
+
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsIURI> mURI;
+  uint32_t mTaskCount;
   nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> mBlacklistResults;
   std::function<void()> mChannelCallback;
+
+  nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> mWhitelistResults;
 };
 
 NS_IMPL_ISUPPORTS(WhitelistClassifierCallback, nsIUrlClassifierFeatureCallback)
 
 NS_IMETHODIMP
 WhitelistClassifierCallback::OnClassifyComplete(
     const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aWhitelistResults) {
+  MOZ_ASSERT(mTaskCount > 0);
+
   UC_LOG(("WhitelistClassifierCallback[%p]:OnClassifyComplete channel=%p", this,
           mChannel.get()));
 
+  mWhitelistResults.AppendElements(aWhitelistResults);
+
+  if (--mTaskCount) {
+    // More callbacks will come.
+    return NS_OK;
+  }
+
+  return OnClassifyCompleteInternal();
+}
+
+nsresult WhitelistClassifierCallback::OnClassifyCompleteInternal() {
   nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> remainingResults;
 
   for (nsIUrlClassifierFeatureResult* blacklistResult : mBlacklistResults) {
-    nsIUrlClassifierFeature* blacklistFeature =
-        static_cast<UrlClassifierFeatureResult*>(blacklistResult)->Feature();
+    UrlClassifierFeatureResult* result =
+        static_cast<UrlClassifierFeatureResult*>(blacklistResult);
+
+    nsIUrlClassifierFeature* blacklistFeature = result->Feature();
     MOZ_ASSERT(blacklistFeature);
 
     bool found = false;
-    for (nsIUrlClassifierFeatureResult* whitelistResult : aWhitelistResults) {
+    for (nsIUrlClassifierFeatureResult* whitelistResult : mWhitelistResults) {
       // We can do pointer comparison because Features are singletons.
       if (static_cast<UrlClassifierFeatureResult*>(whitelistResult)
               ->Feature() == blacklistFeature) {
         found = true;
         break;
       }
     }
 
@@ -183,211 +200,260 @@ WhitelistClassifierCallback::OnClassifyC
 
     // Maybe we have to skip this host
     nsAutoCString skipList;
     nsresult rv = blacklistFeature->GetSkipHostList(skipList);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       continue;
     }
 
-    if (nsContentUtils::IsURIInList(mURI, skipList)) {
+    if (nsContentUtils::IsURIInList(result->URI(), skipList)) {
       if (UC_LOG_ENABLED()) {
-        nsCString spec = mURI->GetSpecOrDefault();
         UC_LOG(
-            ("WhitelistClassifierCallback[%p]::OnClassifyComplete uri %s found "
-             "in skiplist",
-             this, spec.get()));
+            ("WhitelistClassifierCallback[%p]::OnClassifyComplete uri found in "
+             "skiplist",
+             this));
       }
 
       continue;
     }
 
     remainingResults.AppendElement(blacklistResult);
   }
 
   // Whitelist lookup results
 
   if (remainingResults.IsEmpty()) {
     if (UC_LOG_ENABLED()) {
-      nsCString spec = mURI->GetSpecOrDefault();
-      spec.Truncate(
-          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
       UC_LOG(
-          ("WhitelistClassifierCallback[%p]::OnClassifyComplete uri %s fully "
+          ("WhitelistClassifierCallback[%p]::OnClassifyComplete uri fully "
            "whitelisted",
-           this, spec.get()));
+           this));
     }
 
     mChannelCallback();
     return NS_OK;
   }
 
   if (UC_LOG_ENABLED()) {
-    nsCString spec = mURI->GetSpecOrDefault();
-    spec.Truncate(std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
     UC_LOG(
-        ("WhitelistClassifierCallback[%p]::OnClassifyComplete "
-         "channel[%p] uri=%s, should not be whitelisted",
-         this, mChannel.get(), spec.get()));
+        ("WhitelistClassifierCallback[%p]::OnClassifyComplete channel[%p] "
+         "should not be whitelisted",
+         this, mChannel.get()));
   }
 
   return TrackerFound(remainingResults, mChannel, mChannelCallback);
 }
 
 // This class is designed to get the results of checking blacklist.
 class BlacklistClassifierCallback final
     : public nsIUrlClassifierFeatureCallback {
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIURLCLASSIFIERFEATURECALLBACK
 
-  BlacklistClassifierCallback(nsIChannel* aChannel, nsIURI* aURI,
+  BlacklistClassifierCallback(nsIChannel* aChannel,
                               std::function<void()>&& aCallback)
-      : mChannel(aChannel), mURI(aURI), mChannelCallback(std::move(aCallback)) {
+      : mChannel(aChannel),
+        mTaskCount(0),
+        mChannelCallback(std::move(aCallback)) {
     MOZ_ASSERT(mChannel);
-    MOZ_ASSERT(mURI);
+  }
+
+  void SetTaskCount(uint32_t aTaskCount) {
+    MOZ_ASSERT(aTaskCount > 0);
+    mTaskCount = aTaskCount;
   }
 
  private:
   ~BlacklistClassifierCallback() = default;
 
+  nsresult OnClassifyCompleteInternal();
+
   nsCOMPtr<nsIChannel> mChannel;
-  nsCOMPtr<nsIURI> mURI;
+  uint32_t mTaskCount;
   std::function<void()> mChannelCallback;
+
+  nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> mResults;
 };
 
 NS_IMPL_ISUPPORTS(BlacklistClassifierCallback, nsIUrlClassifierFeatureCallback)
 
 NS_IMETHODIMP
 BlacklistClassifierCallback::OnClassifyComplete(
     const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aResults) {
-  UC_LOG(("BlacklistClassifierCallback[%p]:OnClassifyComplete", this));
+  MOZ_ASSERT(mTaskCount > 0);
+
+  UC_LOG(("BlacklistClassifierCallback[%p]:OnClassifyComplete - remaining %d",
+          this, mTaskCount));
+
+  mResults.AppendElements(aResults);
 
+  if (--mTaskCount) {
+    // More callbacks will come.
+    return NS_OK;
+  }
+
+  return OnClassifyCompleteInternal();
+}
+
+nsresult BlacklistClassifierCallback::OnClassifyCompleteInternal() {
   // All good! The URL has not been classified.
-  if (aResults.IsEmpty()) {
+  if (mResults.IsEmpty()) {
     if (UC_LOG_ENABLED()) {
-      nsCString spec = mURI->GetSpecOrDefault();
-      spec.Truncate(
-          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
       UC_LOG(
-          ("BlacklistClassifierCallback[%p]::OnClassifyComplete uri %s not "
-           "found in blacklist",
-           this, spec.get()));
+          ("BlacklistClassifierCallback[%p]::OnClassifyComplete uri not found "
+           "in blacklist",
+           this));
     }
 
     mChannelCallback();
     return NS_OK;
   }
 
   if (UC_LOG_ENABLED()) {
-    nsCString spec = mURI->GetSpecOrDefault();
-    spec.Truncate(std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
     UC_LOG(
-        ("BlacklistClassifierCallback[%p]::OnClassifyComplete uri %s is in "
+        ("BlacklistClassifierCallback[%p]::OnClassifyComplete uri is in "
          "blacklist. Start checking whitelist.",
-         this, spec.get()));
+         this));
   }
 
-  nsCOMPtr<nsIURI> whitelistURI;
-  nsresult rv = CreateWhiteListURI(mChannel, getter_AddRefs(whitelistURI));
-  if (NS_FAILED(rv)) {
-    nsAutoCString errorName;
-    GetErrorName(rv, errorName);
-    NS_WARNING(
-        nsPrintfCString("BlacklistClassifierCallback[%p]:OnClassifyComplete "
-                        "got an unexpected error (rv=%s) while trying to "
-                        "create a whitelist URI. Allowing tracker.",
-                        this, errorName.get())
-            .get());
-    return TrackerFound(aResults, mChannel, mChannelCallback);
+  nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> features;
+  for (nsIUrlClassifierFeatureResult* result : mResults) {
+    features.AppendElement(
+        static_cast<UrlClassifierFeatureResult*>(result)->Feature());
   }
 
-  if (!whitelistURI) {
+  nsTArray<FeatureTask> tasks;
+  nsresult rv = GetFeatureTasks(mChannel, features,
+                                nsIUrlClassifierFeature::whitelist, tasks);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return TrackerFound(mResults, mChannel, mChannelCallback);
+  }
+
+  if (tasks.IsEmpty()) {
     UC_LOG(
         ("BlacklistClassifierCallback[%p]:OnClassifyComplete could not create "
          "a whitelist URI. Ignoring whitelist.",
          this));
 
-    return TrackerFound(aResults, mChannel, mChannelCallback);
+    return TrackerFound(mResults, mChannel, mChannelCallback);
   }
 
-  nsCOMPtr<nsIUrlClassifierFeatureCallback> callback =
-      new WhitelistClassifierCallback(mChannel, mURI, aResults,
-                                      mChannelCallback);
+  RefPtr<WhitelistClassifierCallback> callback =
+      new WhitelistClassifierCallback(mChannel, mResults, mChannelCallback);
+
+  nsCOMPtr<nsIURIClassifier> uriClassifier =
+      do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t pendingCallbacks = 0;
+  for (FeatureTask& task : tasks) {
+    rv = uriClassifier->AsyncClassifyLocalWithFeatures(
+        task.mURI, task.mFeatures, nsIUrlClassifierFeature::whitelist,
+        callback);
 
-  // xpcom parser creates array of interfaces using RefPtr<>.
-  nsTArray<RefPtr<nsIUrlClassifierFeature>> refPtrFeatures;
-  for (nsIUrlClassifierFeatureResult* result : aResults) {
-    refPtrFeatures.AppendElement(
-        static_cast<UrlClassifierFeatureResult*>(result)->Feature());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      if (UC_LOG_ENABLED()) {
+        nsAutoCString errorName;
+        GetErrorName(rv, errorName);
+        UC_LOG((
+            "BlacklistClassifierCallback[%p]:OnClassifyComplete Failed "
+            "calling AsyncClassifyLocalWithFeatures with rv=%s. Let's move on.",
+            this, errorName.get()));
+      }
+
+      continue;
+    }
+
+    ++pendingCallbacks;
   }
 
-  rv = IsTrackerWhitelisted(whitelistURI, refPtrFeatures, callback);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
+  // All the AsyncClassifyLocalWithFeatures() calls return error. We do not
+  // expect callbacks.
+  if (pendingCallbacks == 0) {
     if (UC_LOG_ENABLED()) {
-      nsAutoCString errorName;
-      GetErrorName(rv, errorName);
       UC_LOG(
-          ("BlacklistClassifierCallback[%p]:OnClassifyComplete "
-           "IsTrackerWhitelisted has failed with rv=%s.",
-           this, errorName.get()));
+          ("BlacklistClassifierCallback[%p]:OnClassifyComplete All "
+           "AsyncClassifyLocalWithFeatures() calls return errors. We cannot "
+           "continue.",
+           this));
     }
 
-    return TrackerFound(aResults, mChannel, mChannelCallback);
+    return TrackerFound(mResults, mChannel, mChannelCallback);
   }
 
   // Nothing else do here. Let's wait for the WhitelistClassifierCallback.
+  callback->SetTaskCount(pendingCallbacks);
   return NS_OK;
 }
 
 }  // namespace
 
 /* static */ nsresult AsyncUrlChannelClassifier::CheckChannel(
     nsIChannel* aChannel, std::function<void()>&& aCallback) {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(aChannel);
 
   if (!aCallback) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
-  if (NS_FAILED(rv) || !uri) {
+  // We need to obtain the list of nsIUrlClassifierFeature objects able to
+  // classify this channel. If the list is empty, we do an early return.
+  nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> features;
+  UrlClassifierFeatureFactory::GetFeaturesFromChannel(aChannel, features);
+  if (features.IsEmpty()) {
+    UC_LOG(
+        ("AsyncUrlChannelClassifier: Nothing to do for channel %p", aChannel));
+    return NS_ERROR_FAILURE;
+  }
+
+  nsTArray<FeatureTask> tasks;
+  nsresult rv = GetFeatureTasks(aChannel, features,
+                                nsIUrlClassifierFeature::blacklist, tasks);
+  if (NS_WARN_IF(NS_FAILED(rv)) || tasks.IsEmpty()) {
     return rv;
   }
 
-  nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> features;
-  UrlClassifierFeatureFactory::GetFeaturesFromChannel(aChannel, features);
-  if (features.IsEmpty()) {
-    UC_LOG(("AsyncUrlChannelClassifier: Feature list is empty for channel %p",
-            aChannel));
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_ASSERT(!tasks.IsEmpty());
 
   nsCOMPtr<nsIURIClassifier> uriClassifier =
       do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  nsCOMPtr<nsIUrlClassifierFeatureCallback> callback =
-      new BlacklistClassifierCallback(aChannel, uri, std::move(aCallback));
+  RefPtr<BlacklistClassifierCallback> callback =
+      new BlacklistClassifierCallback(aChannel, std::move(aCallback));
 
-  if (UC_LOG_ENABLED()) {
-    nsCString spec = uri->GetSpecOrDefault();
-    spec.Truncate(std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
-    UC_LOG(("AsyncUrlChannelClassifier: Checking blacklist for uri=%s\n",
-            spec.get()));
+  uint32_t pendingCallbacks = 0;
+  for (FeatureTask& task : tasks) {
+    if (UC_LOG_ENABLED()) {
+      nsCString spec = task.mURI->GetSpecOrDefault();
+      spec.Truncate(
+          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
+      UC_LOG(("AsyncUrlChannelClassifier: Checking blacklist for uri=%s\n",
+              spec.get()));
+    }
+
+    rv = uriClassifier->AsyncClassifyLocalWithFeatures(
+        task.mURI, task.mFeatures, nsIUrlClassifierFeature::blacklist,
+        callback);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    ++pendingCallbacks;
   }
 
-  // xpcom parser creates array of interfaces using RefPtr<>.
-  nsTArray<RefPtr<nsIUrlClassifierFeature>> refPtrFeatures;
-  for (nsIUrlClassifierFeature* feature : features) {
-    refPtrFeatures.AppendElement(feature);
+  // All the AsyncClassifyLocalWithFeatures() calls return error. We do not
+  // expect callbacks.
+  if (pendingCallbacks == 0) {
+    return NS_ERROR_FAILURE;
   }
 
-  return uriClassifier->AsyncClassifyLocalWithFeatures(
-      uri, refPtrFeatures, nsIUrlClassifierFeature::blacklist, callback);
+  callback->SetTaskCount(pendingCallbacks);
+  return NS_OK;
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierCommon.cpp
+++ b/netwerk/url-classifier/UrlClassifierCommon.cpp
@@ -239,10 +239,71 @@ UrlClassifierCommon::ShouldEnableTrackin
 
   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, category, doc,
                                   nsContentUtils::eNECKO_PROPERTIES, message,
                                   params, ArrayLength(params));
 
   return NS_OK;
 }
 
+/* static */ nsresult UrlClassifierCommon::CreatePairwiseWhiteListURI(
+    nsIChannel* aChannel, nsIURI** aURI) {
+  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(aURI);
+
+  nsresult rv;
+  nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!chan) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIURI> topWinURI;
+  rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!topWinURI) {
+    if (UC_LOG_ENABLED()) {
+      nsresult rv;
+      nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(aChannel, &rv);
+      nsCOMPtr<nsIURI> uri;
+      rv = httpChan->GetURI(getter_AddRefs(uri));
+      nsAutoCString spec;
+      uri->GetAsciiSpec(spec);
+      spec.Truncate(
+          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
+      UC_LOG(("CreatePairwiseWhiteListURI: No window URI associated with %s",
+              spec.get()));
+    }
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIScriptSecurityManager> securityManager =
+      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIPrincipal> chanPrincipal;
+  rv = securityManager->GetChannelURIPrincipal(aChannel,
+                                               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;
+  UC_LOG(
+      ("CreatePairwiseWhiteListURI: Looking for %s in the whitelist "
+       "(channel=%p)",
+       whitelistEntry.get(), aChannel));
+
+  nsCOMPtr<nsIURI> whitelistURI;
+  rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  whitelistURI.forget(aURI);
+  return NS_OK;
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierCommon.h
+++ b/netwerk/url-classifier/UrlClassifierCommon.h
@@ -36,14 +36,19 @@ class UrlClassifierCommon final {
   static bool ShouldEnableTrackingProtectionOrAnnotation(
       nsIChannel* aChannel,
       AntiTrackingCommon::ContentBlockingAllowListPurpose aBlockingPurpose);
 
   static nsresult SetBlockedContent(nsIChannel* channel, nsresult aErrorCode,
                                     const nsACString& aList,
                                     const nsACString& aProvider,
                                     const nsACString& aFullHash);
+
+  // Use this function only when you are looking for a pairwise whitelist uri
+  // with the format: http://toplevel.page/?resource=channel.uri.domain
+  static nsresult CreatePairwiseWhiteListURI(nsIChannel* aChannel,
+                                             nsIURI** aURI);
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierCommon_h
--- a/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
@@ -2,41 +2,44 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/net/UrlClassifierFeatureFactory.h"
 
 // List of Features
+#include "UrlClassifierFeatureFlash.h"
 #include "UrlClassifierFeatureLoginReputation.h"
 #include "UrlClassifierFeatureTrackingProtection.h"
 #include "UrlClassifierFeatureTrackingAnnotation.h"
 
 #include "nsAppRunner.h"
 
 namespace mozilla {
 namespace net {
 
 /* static */ void UrlClassifierFeatureFactory::Initialize() {
   // We want to expose Features only in the parent process.
   if (!XRE_IsParentProcess()) {
     return;
   }
 
+  UrlClassifierFeatureFlash::Initialize();
   UrlClassifierFeatureTrackingAnnotation::Initialize();
   UrlClassifierFeatureTrackingProtection::Initialize();
 }
 
 /* static */ void UrlClassifierFeatureFactory::Shutdown() {
   // We want to expose Features only in the parent process.
   if (!XRE_IsParentProcess()) {
     return;
   }
 
+  UrlClassifierFeatureFlash::Shutdown();
   UrlClassifierFeatureLoginReputation::MaybeShutdown();
   UrlClassifierFeatureTrackingAnnotation::Shutdown();
   UrlClassifierFeatureTrackingProtection::Shutdown();
 }
 
 /* static */ void UrlClassifierFeatureFactory::GetFeaturesFromChannel(
     nsIChannel* aChannel,
     nsTArray<nsCOMPtr<nsIUrlClassifierFeature>>& aFeatures) {
@@ -56,16 +59,21 @@ namespace net {
     aFeatures.AppendElement(feature);
   }
 
   // Tracking Annotation
   feature = UrlClassifierFeatureTrackingAnnotation::MaybeCreate(aChannel);
   if (feature) {
     aFeatures.AppendElement(feature);
   }
+
+  // Flash
+  nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> flashFeatures;
+  UrlClassifierFeatureFlash::MaybeCreate(aChannel, flashFeatures);
+  aFeatures.AppendElements(flashFeatures);
 }
 
 /* static */
 nsIUrlClassifierFeature*
 UrlClassifierFeatureFactory::GetFeatureLoginReputation() {
   return UrlClassifierFeatureLoginReputation::MaybeGetOrCreate();
 }
 
new file mode 100644
--- /dev/null
+++ b/netwerk/url-classifier/UrlClassifierFeatureFlash.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "UrlClassifierFeatureFlash.h"
+#include "mozilla/net/HttpBaseChannel.h"
+#include "nsScriptSecurityManager.h"
+#include "nsQueryObject.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+struct FlashFeatures {
+  const char* mName;
+  const char* mBlacklistPrefTables;
+  const char* mWhitelistPrefTables;
+  bool mSubdocumentOnly;
+  nsIHttpChannel::FlashPluginState mFlashPluginState;
+  RefPtr<UrlClassifierFeatureFlash> mFeature;
+};
+
+static FlashFeatures sFlashFeaturesMap[] = {
+    {"flash-deny", "urlclassifier.flashTable", "urlclassifier.flashExceptTable",
+     false, nsIHttpChannel::FlashPluginDenied},
+    {"flash-allow", "urlclassifier.flashAllowTable",
+     "urlclassifier.flashAllowExceptTable", false,
+     nsIHttpChannel::FlashPluginAllowed},
+    {"flash-deny-subdoc", "urlclassifier.flashSubDocTable",
+     "urlclassifier.flashSubDocExceptTable", true,
+     nsIHttpChannel::FlashPluginDeniedInSubdocuments},
+};
+
+}  // namespace
+
+UrlClassifierFeatureFlash::UrlClassifierFeatureFlash(uint32_t aId)
+    : UrlClassifierFeatureBase(
+          nsDependentCString(sFlashFeaturesMap[aId].mName),
+          nsDependentCString(sFlashFeaturesMap[aId].mBlacklistPrefTables),
+          nsDependentCString(sFlashFeaturesMap[aId].mWhitelistPrefTables),
+          EmptyCString(),  // aPrefBlacklistHosts
+          EmptyCString(),  // aPrefWhitelistHosts
+          EmptyCString(),  // aPrefBlacklistTableName
+          EmptyCString(),  // aPrefWhitelistTableName
+          EmptyCString())  // aPrefSkipHosts
+      ,
+      mFlashPluginState(sFlashFeaturesMap[aId].mFlashPluginState) {
+  static_assert(nsIHttpChannel::FlashPluginDeniedInSubdocuments ==
+                    nsIHttpChannel::FlashPluginLastValue,
+                "nsIHttpChannel::FlashPluginLastValue is out-of-sync!");
+}
+
+/* static */ void UrlClassifierFeatureFlash::Initialize() {
+  uint32_t numFeatures =
+      (sizeof(sFlashFeaturesMap) / sizeof(sFlashFeaturesMap[0]));
+  for (uint32_t i = 0; i < numFeatures; ++i) {
+    MOZ_ASSERT(!sFlashFeaturesMap[i].mFeature);
+    sFlashFeaturesMap[i].mFeature = new UrlClassifierFeatureFlash(i);
+    sFlashFeaturesMap[i].mFeature->InitializePreferences();
+  }
+}
+
+/* static */ void UrlClassifierFeatureFlash::Shutdown() {
+  uint32_t numFeatures =
+      (sizeof(sFlashFeaturesMap) / sizeof(sFlashFeaturesMap[0]));
+  for (uint32_t i = 0; i < numFeatures; ++i) {
+    MOZ_ASSERT(sFlashFeaturesMap[i].mFeature);
+    sFlashFeaturesMap[i].mFeature->ShutdownPreferences();
+    sFlashFeaturesMap[i].mFeature = nullptr;
+  }
+}
+
+/* static */ void UrlClassifierFeatureFlash::MaybeCreate(
+    nsIChannel* aChannel,
+    nsTArray<nsCOMPtr<nsIUrlClassifierFeature>>& aFeatures) {
+  // All disabled.
+  if (!StaticPrefs::plugins_flashBlock_enabled()) {
+    return;
+  }
+
+  // We use Flash feature just for document loading.
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  nsContentPolicyType contentPolicyType =
+      loadInfo ? loadInfo->GetExternalContentPolicyType()
+               : nsIContentPolicy::TYPE_INVALID;
+
+  if (contentPolicyType != nsIContentPolicy::TYPE_DOCUMENT &&
+      contentPolicyType != nsIContentPolicy::TYPE_SUBDOCUMENT) {
+    return;
+  }
+
+  // Only allow plugins for documents from an HTTP/HTTPS origin.
+  if (StaticPrefs::plugins_http_https_only()) {
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+    if (!httpChannel) {
+      return;
+    }
+  }
+
+  uint32_t numFeatures =
+      (sizeof(sFlashFeaturesMap) / sizeof(sFlashFeaturesMap[0]));
+  for (uint32_t i = 0; i < numFeatures; ++i) {
+    MOZ_ASSERT(sFlashFeaturesMap[i].mFeature);
+    if (!sFlashFeaturesMap[i].mSubdocumentOnly ||
+        contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
+      aFeatures.AppendElement(sFlashFeaturesMap[i].mFeature);
+    }
+  }
+}
+
+NS_IMETHODIMP
+UrlClassifierFeatureFlash::ProcessChannel(nsIChannel* aChannel,
+                                          const nsACString& aList,
+                                          bool* aShouldContinue) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aShouldContinue);
+
+  // This is not a blocking feature.
+  *aShouldContinue = true;
+
+  UC_LOG(("UrlClassifierFeatureFlash::ProcessChannel, annotating channel[%p]",
+          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->NotifyFlashPluginStateChanged(mFlashPluginState);
+  }
+
+  RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
+  if (httpChannel) {
+    httpChannel->SetFlashPluginState(mFlashPluginState);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierFeatureFlash::GetURIByListType(
+    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
+    nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  // Here we return the channel's URI always.
+  return aChannel->GetURI(aURI);
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/url-classifier/UrlClassifierFeatureFlash.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_UrlClassifierFeatureFlash_h
+#define mozilla_UrlClassifierFeatureFlash_h
+
+#include "UrlClassifierFeatureBase.h"
+
+namespace mozilla {
+namespace net {
+
+class UrlClassifierFeatureFlash final : public UrlClassifierFeatureBase {
+ public:
+  static void Initialize();
+  static void Shutdown();
+
+  static void MaybeCreate(
+      nsIChannel* aChannel,
+      nsTArray<nsCOMPtr<nsIUrlClassifierFeature>>& aFeatures);
+
+  NS_IMETHOD
+  ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
+                 bool* aShouldContinue) override;
+
+  NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
+                              nsIUrlClassifierFeature::listType aListType,
+                              nsIURI** aURI) override;
+
+ private:
+  explicit UrlClassifierFeatureFlash(uint32_t aId);
+
+  nsIHttpChannel::FlashPluginState mFlashPluginState;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_UrlClassifierFeatureFlash_h
--- a/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.cpp
@@ -88,10 +88,23 @@ UrlClassifierFeatureLoginReputation::Has
     nsACString& aPrefTableName, bool* aResult) {
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist,
              "UrlClassifierFeatureLoginReputation is meant to be used just to "
              "whitelist URLs");
   return UrlClassifierFeatureBase::HasHostInPreferences(
       aHost, aListType, aPrefTableName, aResult);
 }
 
+NS_IMETHODIMP
+UrlClassifierFeatureLoginReputation::GetURIByListType(
+    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
+    nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURI);
+  MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist,
+             "UrlClassifierFeatureLoginReputation is meant to be used just to "
+             "whitelist URLs");
+
+  return aChannel->GetURI(aURI);
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.h
@@ -32,16 +32,20 @@ class UrlClassifierFeatureLoginReputatio
   NS_IMETHOD
   HasHostInPreferences(const nsACString& aHost,
                        nsIUrlClassifierFeature::listType aListType,
                        nsACString& aPrefTableName, bool* aResult) override;
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
                             bool* aShouldContinue) override;
 
+  NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
+                              nsIUrlClassifierFeature::listType aListType,
+                              nsIURI** aURI) override;
+
  private:
   UrlClassifierFeatureLoginReputation();
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_UrlClassifierFeatureLoginReputation_h
--- a/netwerk/url-classifier/UrlClassifierFeatureResult.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureResult.cpp
@@ -5,22 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/net/UrlClassifierFeatureResult.h"
 
 namespace mozilla {
 namespace net {
 
 UrlClassifierFeatureResult::UrlClassifierFeatureResult(
-    nsIUrlClassifierFeature* aFeature, const nsACString& aList)
-    : mFeature(aFeature), mList(aList) {}
+    nsIURI* aURI, nsIUrlClassifierFeature* aFeature, const nsACString& aList)
+    : mURI(aURI), mFeature(aFeature), mList(aList) {}
 
 UrlClassifierFeatureResult::~UrlClassifierFeatureResult() = default;
 
 NS_IMETHODIMP
+UrlClassifierFeatureResult::GetUri(nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aURI);
+  nsCOMPtr<nsIURI> uri = mURI;
+  uri.forget(aURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 UrlClassifierFeatureResult::GetFeature(nsIUrlClassifierFeature** aFeature) {
   NS_ENSURE_ARG_POINTER(aFeature);
   nsCOMPtr<nsIUrlClassifierFeature> feature = mFeature;
   feature.forget(aFeature);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/url-classifier/UrlClassifierFeatureResult.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureResult.h
@@ -5,36 +5,41 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_net_UrlClassifierFeatureResult_h
 #define mozilla_net_UrlClassifierFeatureResult_h
 
 #include "nsIUrlClassifierFeature.h"
 #include "nsString.h"
 
+class nsIURI;
+
 namespace mozilla {
 namespace net {
 
 class UrlClassifierFeatureResult final : public nsIUrlClassifierFeatureResult {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIURLCLASSIFIERFEATURERESULT
 
-  UrlClassifierFeatureResult(nsIUrlClassifierFeature* aFeature,
+  UrlClassifierFeatureResult(nsIURI* aURI, nsIUrlClassifierFeature* aFeature,
                              const nsACString& aList);
 
+  nsIURI* URI() const { return mURI; }
+
   nsIUrlClassifierFeature* Feature() const { return mFeature; }
 
   // Comma separated list of tables.
   const nsCString& List() const { return mList; }
 
  protected:
   ~UrlClassifierFeatureResult();
 
  private:
+  nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIUrlClassifierFeature> mFeature;
   const nsCString mList;
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierFeatureResult_h
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
@@ -196,10 +196,25 @@ UrlClassifierFeatureTrackingAnnotation::
     if (StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
       LowerPriorityHelper(aChannel);
     }
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+UrlClassifierFeatureTrackingAnnotation::GetURIByListType(
+    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
+    nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  if (aListType == nsIUrlClassifierFeature::blacklist) {
+    return aChannel->GetURI(aURI);
+  }
+
+  MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+  return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.h
@@ -22,16 +22,20 @@ class UrlClassifierFeatureTrackingAnnota
   static void Shutdown();
 
   static already_AddRefed<UrlClassifierFeatureTrackingAnnotation> MaybeCreate(
       nsIChannel* aChannel);
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
                             bool* aShouldContinue) override;
 
+  NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
+                              nsIUrlClassifierFeature::listType aListType,
+                              nsIURI** aURI) override;
+
  private:
   UrlClassifierFeatureTrackingAnnotation();
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierFeatureTrackingAnnotation_h
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
@@ -125,10 +125,25 @@ UrlClassifierFeatureTrackingProtection::
     Unused << httpChannel->CancelForTrackingProtection();
   } else {
     Unused << aChannel->Cancel(NS_ERROR_TRACKING_URI);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+UrlClassifierFeatureTrackingProtection::GetURIByListType(
+    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
+    nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  if (aListType == nsIUrlClassifierFeature::blacklist) {
+    return aChannel->GetURI(aURI);
+  }
+
+  MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+  return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.h
@@ -22,16 +22,20 @@ class UrlClassifierFeatureTrackingProtec
   static void Shutdown();
 
   static already_AddRefed<UrlClassifierFeatureTrackingProtection> MaybeCreate(
       nsIChannel* aChannel);
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
                             bool* aShouldContinue) override;
 
+  NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
+                              nsIUrlClassifierFeature::listType aListType,
+                              nsIURI** aURI) override;
+
  private:
   UrlClassifierFeatureTrackingProtection();
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierFeatureTrackingProtection_h
--- a/netwerk/url-classifier/moz.build
+++ b/netwerk/url-classifier/moz.build
@@ -18,16 +18,17 @@ DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = Tru
 DEFINES['GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER'] = True
 
 UNIFIED_SOURCES += [
     'AsyncUrlChannelClassifier.cpp',
     'nsChannelClassifier.cpp',
     'UrlClassifierCommon.cpp',
     'UrlClassifierFeatureBase.cpp',
     'UrlClassifierFeatureFactory.cpp',
+    'UrlClassifierFeatureFlash.cpp',
     'UrlClassifierFeatureLoginReputation.cpp',
     'UrlClassifierFeatureResult.cpp',
     'UrlClassifierFeatureTrackingAnnotation.cpp',
     'UrlClassifierFeatureTrackingProtection.cpp',
 ]
 
 EXPORTS.mozilla.net += [
     'AsyncUrlChannelClassifier.h',
--- a/netwerk/url-classifier/nsIUrlClassifierFeature.idl
+++ b/netwerk/url-classifier/nsIUrlClassifierFeature.idl
@@ -6,16 +6,17 @@
 
 %{C++
 #include "nsStringFwd.h"
 #include "nsTArrayForwardDeclare.h"
 %}
 [ref] native StringArrayRef(nsTArray<nsCString>);
 
 interface nsIChannel;
+interface nsIURI;
 
 /**
  * A single URLClassifier feature.
  */
 [builtinclass, scriptable, uuid(a6c9b24e-b4f1-426e-af58-2c976c3943a8)]
 interface nsIUrlClassifierFeature : nsISupports
 {
   cenum listType: 8 {
@@ -57,25 +58,35 @@ interface nsIUrlClassifierFeature : nsIS
    * 'something' on the channel. For instance, a tracking-annotation feature
    * would mark the channel as tracker, a tracking-protection feature would
    * cancel the channel.
    * Returns if we should process other feature results or not. For instance,
    * tracking-protection cancel the channel, and after that we should stop
    * processing other features.
    */
   [noscript] boolean processChannel(in nsIChannel aChannel, in ACString aList);
+
+  /**
+   * 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.
+   */
+  [noscript] nsIURI getURIByListType(in nsIChannel channel,
+                                     in nsIUrlClassifierFeature_listType listType);
 };
 
 /**
  * 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
 {
+  readonly attribute nsIURI uri;
+
   readonly attribute nsIUrlClassifierFeature feature;
 
   // Comma separate tables or preferences.
   readonly attribute ACString list;
 };
 
 /**
  * Callback function for nsIURIClassifier lookups.
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -122,21 +122,22 @@ class FeatureHolder final {
   };
 
   struct FeatureData {
     RefPtr<nsIUrlClassifierFeature> mFeature;
     nsTArray<RefPtr<TableData>> mTables;
   };
 
   static already_AddRefed<FeatureHolder> Create(
-      const nsTArray<RefPtr<nsIUrlClassifierFeature>>& aFeatures,
+      nsIURI* aURI, const nsTArray<RefPtr<nsIUrlClassifierFeature>>& aFeatures,
       nsIUrlClassifierFeature::listType aListType) {
     MOZ_ASSERT(NS_IsMainThread());
-
-    RefPtr<FeatureHolder> holder = new FeatureHolder();
+    MOZ_ASSERT(aURI);
+
+    RefPtr<FeatureHolder> holder = new FeatureHolder(aURI);
 
     for (nsIUrlClassifierFeature* feature : aFeatures) {
       FeatureData* featureData = holder->mFeatureData.AppendElement();
       MOZ_ASSERT(featureData);
 
       featureData->mFeature = feature;
       nsTArray<nsCString> tables;
       nsresult rv = feature->GetTables(aListType, tables);
@@ -197,44 +198,49 @@ class FeatureHolder final {
         }
       }
 
       if (list.IsEmpty()) {
         continue;
       }
 
       RefPtr<mozilla::net::UrlClassifierFeatureResult> result =
-          new mozilla::net::UrlClassifierFeatureResult(featureData.mFeature,
-                                                       list);
+          new mozilla::net::UrlClassifierFeatureResult(
+              mURI, featureData.mFeature, list);
       aResults.AppendElement(result);
     }
   }
 
  private:
-  FeatureHolder() { MOZ_ASSERT(NS_IsMainThread()); }
+  explicit FeatureHolder(nsIURI* aURI) : mURI(aURI) {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
 
   ~FeatureHolder() {
     for (FeatureData& featureData : mFeatureData) {
       NS_ReleaseOnMainThreadSystemGroup("FeatureHolder:mFeatureData",
                                         featureData.mFeature.forget());
     }
+
+    NS_ReleaseOnMainThreadSystemGroup("FeatureHolder:mURI", mURI.forget());
   }
 
   TableData* GetOrCreateTableData(const nsACString& aTable) {
     for (TableData* tableData : mTableData) {
       if (tableData->mTable == aTable) {
         return tableData;
       }
     }
 
     RefPtr<TableData> tableData = new TableData(aTable);
     mTableData.AppendElement(tableData);
     return tableData;
   }
 
+  nsCOMPtr<nsIURI> mURI;
   nsTArray<FeatureData> mFeatureData;
   nsTArray<RefPtr<TableData>> mTableData;
 };
 
 // Simple feature which wraps preferences and tables received by
 // AsyncClassifyLocalWithTables() as arguments.
 class DummyFeature final : public nsIUrlClassifierFeature {
  public:
@@ -295,16 +301,23 @@ class DummyFeature final : public nsIUrl
                  bool* aShouldContinue) override {
     NS_ENSURE_ARG_POINTER(aShouldContinue);
     *aShouldContinue = true;
 
     // Nothing to do here.
     return NS_OK;
   }
 
+  NS_IMETHODIMP
+  GetURIByListType(nsIChannel* aChannel,
+                   nsIUrlClassifierFeature::listType aListType,
+                   nsIURI** aURI) override {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
  private:
   ~DummyFeature() = default;
 
   Type mType;
   nsCString mName;
   nsTArray<nsCString> mHostsFromPreference;
 };
 
@@ -2834,17 +2847,18 @@ nsUrlClassifierDBService::AsyncClassifyL
     actor->SetFeaturesAndCallback(aFeatures, aCallback);
     return NS_OK;
   }
 
   using namespace mozilla::Telemetry;
   auto startTime = TimeStamp::Now();  // For telemetry.
 
   // Let's keep the features alive and release them on the correct thread.
-  RefPtr<FeatureHolder> holder = FeatureHolder::Create(aFeatures, aListType);
+  RefPtr<FeatureHolder> holder =
+      FeatureHolder::Create(aURI, aFeatures, aListType);
   if (NS_WARN_IF(!holder)) {
     return NS_ERROR_FAILURE;
   }
 
   auto worker = mWorker;
 
   // Since aCallback will be passed around threads...
   nsMainThreadPtrHandle<nsIUrlClassifierFeatureCallback> callback(
@@ -2900,17 +2914,18 @@ bool nsUrlClassifierDBService::AsyncClas
     rv = feature->HasHostInPreferences(host, aListType, tableName, &found);
     NS_ENSURE_SUCCESS(rv, false);
 
     if (found) {
       MOZ_ASSERT(!tableName.IsEmpty());
       LOG(("URI found in preferences. Table: %s", tableName.get()));
 
       RefPtr<mozilla::net::UrlClassifierFeatureResult> result =
-          new mozilla::net::UrlClassifierFeatureResult(feature, tableName);
+          new mozilla::net::UrlClassifierFeatureResult(aURI, feature,
+                                                       tableName);
       results.AppendElement(result);
     }
   }
 
   if (results.IsEmpty()) {
     return false;
   }
 
--- a/uriloader/exthandler/nsExternalProtocolHandler.cpp
+++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp
@@ -411,16 +411,22 @@ NS_IMETHODIMP nsExtProtocolChannel::SetC
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExtProtocolChannel::NotifyTrackingResource(bool aIsThirdParty) {
   // nothing to do
   return NS_OK;
 }
 
+NS_IMETHODIMP nsExtProtocolChannel::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  // nothing to do
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsExtProtocolChannel::Delete() {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExtProtocolChannel::OnStartRequest(nsIRequest *aRequest,
                                                    nsISupports *aContext) {
   // no data is expected