Bug 1620322 - Part 2: Refactor the code for content blocking events and notifications out of AntiTrackingCommon.cpp; r=baku
☠☠ backed out by 5b47cfcab155 ☠ ☠
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 09 Mar 2020 18:12:26 +0000
changeset 517650 31b24d79db3ddbadb0034b8f317b677f615593cd
parent 517649 3004bc7597a088e181fbcb9a9044b8d952e9d45f
child 517651 0e5e5d41597d83dc79d898e9d2bdfb0e9095db71
push id37199
push useropoprus@mozilla.com
push dateTue, 10 Mar 2020 03:43:44 +0000
treeherdermozilla-central@6f21f98dcfcd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1620322
milestone75.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 1620322 - Part 2: Refactor the code for content blocking events and notifications out of AntiTrackingCommon.cpp; r=baku Differential Revision: https://phabricator.services.mozilla.com/D65743
dom/base/ContentBlockingLog.cpp
dom/base/ContentBlockingLog.h
dom/base/Document.cpp
dom/base/Navigator.cpp
dom/base/ThirdPartyUtil.cpp
dom/base/nsGlobalWindowOuter.cpp
dom/ipc/BrowserChild.cpp
dom/ipc/BrowserChild.h
dom/ipc/BrowserParent.cpp
dom/ipc/BrowserParent.h
dom/ipc/PBrowser.ipdl
dom/ipc/WindowGlobalParent.cpp
dom/ipc/WindowGlobalParent.h
netwerk/cookie/nsCookieService.cpp
netwerk/url-classifier/UrlClassifierCommon.cpp
toolkit/components/antitracking/AntiTrackingCommon.cpp
toolkit/components/antitracking/AntiTrackingCommon.h
toolkit/components/antitracking/AntiTrackingIPCUtils.h
toolkit/components/antitracking/AntiTrackingUtils.cpp
toolkit/components/antitracking/AntiTrackingUtils.h
toolkit/components/antitracking/ContentBlockingNotifier.cpp
toolkit/components/antitracking/ContentBlockingNotifier.h
toolkit/components/antitracking/StorageAccess.cpp
toolkit/components/antitracking/moz.build
--- a/dom/base/ContentBlockingLog.cpp
+++ b/dom/base/ContentBlockingLog.cpp
@@ -105,17 +105,17 @@ static void ReportOriginSingleHash(Origi
       Telemetry::MetricIDToString[static_cast<uint32_t>(aId)]);
   LOG("ReportOriginSingleHash origin=%s", PromiseFlatCString(aOrigin).get());
 
   Telemetry::RecordOrigin(aId, aOrigin);
 }
 
 Maybe<uint32_t> ContentBlockingLog::RecordLogParent(
     const nsACString& aOrigin, uint32_t aType, bool aBlocked,
-    const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason,
+    const Maybe<ContentBlockingNotifier::StorageAccessGrantedReason>& aReason,
     const nsTArray<nsCString>& aTrackingFullHashes) {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   uint32_t events = GetContentBlockingEventsInLog();
 
   bool blockedValue = aBlocked;
   bool unblocked = false;
 
--- a/dom/base/ContentBlockingLog.h
+++ b/dom/base/ContentBlockingLog.h
@@ -2,39 +2,41 @@
 /* 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_dom_ContentBlockingLog_h
 #define mozilla_dom_ContentBlockingLog_h
 
-#include "mozilla/AntiTrackingCommon.h"
+#include "mozilla/ContentBlockingNotifier.h"
 #include "mozilla/JSONWriter.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/StaticPrefs_browser.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/UniquePtr.h"
 #include "nsIWebProgressListener.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsWindowSizes.h"
 
+class nsIPrincipal;
+
 namespace mozilla {
 namespace dom {
 
 class ContentBlockingLog final {
-  typedef AntiTrackingCommon::StorageAccessGrantedReason
+  typedef ContentBlockingNotifier::StorageAccessGrantedReason
       StorageAccessGrantedReason;
 
   struct LogEntry {
     uint32_t mType;
     uint32_t mRepeatCount;
     bool mBlocked;
-    Maybe<AntiTrackingCommon::StorageAccessGrantedReason> mReason;
+    Maybe<ContentBlockingNotifier::StorageAccessGrantedReason> mReason;
     nsTArray<nsCString> mTrackingFullHashes;
   };
 
   struct OriginDataEntry {
     OriginDataEntry()
         : mHasLevel1TrackingContentLoaded(false),
           mHasLevel2TrackingContentLoaded(false) {}
 
@@ -82,22 +84,22 @@ class ContentBlockingLog final {
   ContentBlockingLog() = default;
   ~ContentBlockingLog() = default;
 
   // Record the log in the parent process. This should be called only in the
   // parent process and will replace the RecordLog below after we remove the
   // ContentBlockingLog from content processes.
   Maybe<uint32_t> RecordLogParent(
       const nsACString& aOrigin, uint32_t aType, bool aBlocked,
-      const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason,
+      const Maybe<ContentBlockingNotifier::StorageAccessGrantedReason>& aReason,
       const nsTArray<nsCString>& aTrackingFullHashes);
 
   void RecordLog(
       const nsACString& aOrigin, uint32_t aType, bool aBlocked,
-      const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason,
+      const Maybe<ContentBlockingNotifier::StorageAccessGrantedReason>& aReason,
       const nsTArray<nsCString>& aTrackingFullHashes) {
     RecordLogInternal(aOrigin, aType, aBlocked, aReason, aTrackingFullHashes);
   }
 
   void ReportOrigins();
   void ReportLog(nsIPrincipal* aFirstPartyPrincipal);
 
   nsAutoCString Stringify() {
@@ -236,18 +238,18 @@ class ContentBlockingLog final {
     }
 
     return events;
   }
 
  private:
   void RecordLogInternal(
       const nsACString& aOrigin, uint32_t aType, bool aBlocked,
-      const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason =
-          Nothing(),
+      const Maybe<ContentBlockingNotifier::StorageAccessGrantedReason>&
+          aReason = Nothing(),
       const nsTArray<nsCString>& aTrackingFullHashes = nsTArray<nsCString>()) {
     DebugOnly<bool> isCookiesBlockedTracker =
         aType == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
         aType == nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER;
     MOZ_ASSERT_IF(aBlocked, aReason.isNothing());
     MOZ_ASSERT_IF(!isCookiesBlockedTracker, aReason.isNothing());
     MOZ_ASSERT_IF(isCookiesBlockedTracker && !aBlocked, aReason.isSome());
 
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -15061,17 +15061,17 @@ void Document::MaybeAllowStorageForOpene
       !nsContentUtils::IsThirdPartyWindowOrChannel(openerInner, nullptr,
                                                    nullptr)) {
     return;
   }
 
   // We don't care when the asynchronous work finishes here.
   Unused << AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(
       NodePrincipal(), openerInner,
-      AntiTrackingCommon::eOpenerAfterUserInteraction);
+      ContentBlockingNotifier::eOpenerAfterUserInteraction);
 }
 
 namespace {
 
 // Documents can stay alive for days. We don't want to update the permission
 // value at any user-interaction, and, using a timer triggered any X seconds
 // should be good enough. 'X' is taken from
 // privacy.userInteraction.document.interval pref.
@@ -15716,17 +15716,17 @@ already_AddRefed<mozilla::dom::Promise> 
               sapr->RequestDelayedTask(
                   inner->EventTargetFor(TaskCategory::Other),
                   ContentPermissionRequestBase::DelayedTaskType::Request);
             });
 
         return std::move(p);
       };
       AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(
-          NodePrincipal(), inner, AntiTrackingCommon::eStorageAccessAPI,
+          NodePrincipal(), inner, ContentBlockingNotifier::eStorageAccessAPI,
           performFinalChecks)
           ->Then(
               GetCurrentThreadSerialEventTarget(), __func__,
               [outer, promise] {
                 // Step 10. Grant the document access to cookies and store
                 // that fact for
                 //          the purposes of future calls to
                 //          hasStorageAccess() and requestStorageAccess().
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -7,16 +7,17 @@
 // Needs to be first.
 #include "base/basictypes.h"
 
 #include "Navigator.h"
 #include "nsIXULAppInfo.h"
 #include "nsPluginArray.h"
 #include "nsMimeTypeArray.h"
 #include "mozilla/AntiTrackingCommon.h"
+#include "mozilla/ContentBlockingNotifier.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/BodyExtractor.h"
 #include "mozilla/dom/FetchBinding.h"
 #include "mozilla/dom/File.h"
 #include "Geolocation.h"
 #include "nsIClassOfService.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsIContentPolicy.h"
@@ -525,20 +526,20 @@ bool Navigator::CookieEnabled() {
     // just return the default value.
     return cookieEnabled;
   }
 
   uint32_t rejectedReason = 0;
   bool granted = AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
       mWindow, contentURI, &rejectedReason);
 
-  AntiTrackingCommon::NotifyBlockingDecision(
+  ContentBlockingNotifier::OnDecision(
       mWindow,
-      granted ? AntiTrackingCommon::BlockingDecision::eAllow
-              : AntiTrackingCommon::BlockingDecision::eBlock,
+      granted ? ContentBlockingNotifier::BlockingDecision::eAllow
+              : ContentBlockingNotifier::BlockingDecision::eBlock,
       rejectedReason);
   return granted;
 }
 
 bool Navigator::OnLine() { return !NS_IsOffline(); }
 
 void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
                            ErrorResult& aRv) const {
--- a/dom/base/ThirdPartyUtil.cpp
+++ b/dom/base/ThirdPartyUtil.cpp
@@ -495,16 +495,16 @@ ThirdPartyUtil::AnalyzeChannel(nsIChanne
     if (performStorageChecks &&
         AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
             aChannel, aURI ? aURI : uri.get(), aRejectedReason)) {
       result += ThirdPartyAnalysis::IsFirstPartyStorageAccessGranted;
     }
 
     if (aNotify && !result.contains(
                        ThirdPartyAnalysis::IsFirstPartyStorageAccessGranted)) {
-      AntiTrackingCommon::NotifyBlockingDecision(
-          aChannel, AntiTrackingCommon::BlockingDecision::eBlock,
+      ContentBlockingNotifier::OnDecision(
+          aChannel, ContentBlockingNotifier::BlockingDecision::eBlock,
           *aRejectedReason);
     }
   }
 
   return result;
 }
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -7179,17 +7179,17 @@ void nsGlobalWindowOuter::MaybeAllowStor
   if (!doc) {
     return;
   }
   nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
       aURI, doc->NodePrincipal()->OriginAttributesRef());
 
   // We don't care when the asynchronous work finishes here.
   Unused << AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(
-      principal, inner, AntiTrackingCommon::eOpener);
+      principal, inner, ContentBlockingNotifier::eOpener);
 }
 
 //*****************************************************************************
 // nsGlobalWindowOuter: Helper Functions
 //*****************************************************************************
 
 already_AddRefed<nsIDocShellTreeOwner> nsPIDOMWindowOuter::GetTreeOwner() {
   // If there's no docShellAsItem, this window must have been closed,
--- a/dom/ipc/BrowserChild.cpp
+++ b/dom/ipc/BrowserChild.cpp
@@ -4043,17 +4043,17 @@ BrowserChild::DoesWindowSupportProtected
           });
 }
 #endif
 
 void BrowserChild::NotifyContentBlockingEvent(
     uint32_t aEvent, nsIChannel* aChannel, bool aBlocked,
     const nsACString& aTrackingOrigin,
     const nsTArray<nsCString>& aTrackingFullHashes,
-    const Maybe<mozilla::AntiTrackingCommon::StorageAccessGrantedReason>&
+    const Maybe<mozilla::ContentBlockingNotifier::StorageAccessGrantedReason>&
         aReason) {
   if (!IPCOpen()) {
     return;
   }
 
   Maybe<WebProgressData> webProgressData;
   RequestData requestData;
   nsresult rv = PrepareProgressListenerData(nullptr, aChannel, webProgressData,
--- a/dom/ipc/BrowserChild.h
+++ b/dom/ipc/BrowserChild.h
@@ -694,17 +694,18 @@ class BrowserChild final : public nsMess
 
   // Notify the content blocking event in the parent process. This sends an IPC
   // message to the BrowserParent in the parent. The BrowserParent will find the
   // top-level WindowGlobalParent and notify the event from it.
   void NotifyContentBlockingEvent(
       uint32_t aEvent, nsIChannel* aChannel, bool aBlocked,
       const nsACString& aTrackingOrigin,
       const nsTArray<nsCString>& aTrackingFullHashes,
-      const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason);
+      const Maybe<ContentBlockingNotifier::StorageAccessGrantedReason>&
+          aReason);
 
  protected:
   virtual ~BrowserChild();
 
   mozilla::ipc::IPCResult RecvDestroy();
 
   mozilla::ipc::IPCResult RecvSetDocShellIsActive(const bool& aIsActive);
 
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -2690,17 +2690,17 @@ mozilla::ipc::IPCResult BrowserParent::R
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult BrowserParent::RecvNotifyContentBlockingEvent(
     const uint32_t& aEvent, const RequestData& aRequestData,
     const bool aBlocked, const nsACString& aTrackingOrigin,
     nsTArray<nsCString>&& aTrackingFullHashes,
-    const Maybe<mozilla::AntiTrackingCommon::StorageAccessGrantedReason>&
+    const Maybe<mozilla::ContentBlockingNotifier::StorageAccessGrantedReason>&
         aReason) {
   MOZ_ASSERT(aRequestData.elapsedLoadTimeMS().isNothing());
 
   RefPtr<BrowsingContext> bc = GetBrowsingContext();
 
   if (!bc || bc->IsDiscarded()) {
     return IPC_OK();
   }
--- a/dom/ipc/BrowserParent.h
+++ b/dom/ipc/BrowserParent.h
@@ -312,17 +312,17 @@ class BrowserParent final : public PBrow
       const Maybe<WebProgressData>& aWebProgressData,
       const RequestData& aRequestData, const uint32_t aState,
       const Maybe<WebProgressSecurityChangeData>& aSecurityChangeData);
 
   mozilla::ipc::IPCResult RecvNotifyContentBlockingEvent(
       const uint32_t& aEvent, const RequestData& aRequestData,
       const bool aBlocked, const nsACString& aTrackingOrigin,
       nsTArray<nsCString>&& aTrackingFullHashes,
-      const Maybe<mozilla::AntiTrackingCommon::StorageAccessGrantedReason>&
+      const Maybe<mozilla::ContentBlockingNotifier::StorageAccessGrantedReason>&
           aReason);
 
   mozilla::ipc::IPCResult RecvNavigationFinished();
 
   bool GetWebProgressListener(nsIBrowser** aOutBrowser,
                               nsIWebProgress** aOutManager,
                               nsIWebProgressListener** aOutListener);
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -90,17 +90,17 @@ using mozilla::a11y::IAccessibleHolder f
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 using mozilla::dom::EffectsInfo from "mozilla/dom/TabMessageUtils.h";
 using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
 using mozilla::ScrollAxis from "mozilla/PresShellForwards.h";
 using mozilla::ScrollFlags from "mozilla/PresShellForwards.h";
 using struct InputFormData from "mozilla/dom/SessionStoreMessageUtils.h";
 using struct CollectedInputDataValue from "mozilla/dom/SessionStoreMessageUtils.h";
 using refcounted class nsITransportSecurityInfo from "nsITransportSecurityInfo.h";
-using mozilla::AntiTrackingCommon::StorageAccessGrantedReason from "mozilla/AntiTrackingCommon.h";
+using mozilla::ContentBlockingNotifier::StorageAccessGrantedReason from "mozilla/ContentBlockingNotifier.h";
 using CallerType from "mozilla/dom/BindingDeclarations.h";
 
 namespace mozilla {
 namespace dom {
 
 struct WebProgressData
 {
   bool isTopLevel;
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -306,17 +306,17 @@ const nsAString& WindowGlobalParent::Get
 
   return VoidString();
 }
 
 void WindowGlobalParent::NotifyContentBlockingEvent(
     uint32_t aEvent, nsIRequest* aRequest, bool aBlocked,
     const nsACString& aTrackingOrigin,
     const nsTArray<nsCString>& aTrackingFullHashes,
-    const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason) {
+    const Maybe<ContentBlockingNotifier::StorageAccessGrantedReason>& aReason) {
   MOZ_ASSERT(NS_IsMainThread());
   DebugOnly<bool> isCookiesBlockedTracker =
       aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
       aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER;
   MOZ_ASSERT_IF(aBlocked, aReason.isNothing());
   MOZ_ASSERT_IF(!isCookiesBlockedTracker, aReason.isNothing());
   MOZ_ASSERT_IF(isCookiesBlockedTracker && !aBlocked, aReason.isSome());
   MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -2,17 +2,17 @@
 /* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
 /* 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_dom_WindowGlobalParent_h
 #define mozilla_dom_WindowGlobalParent_h
 
-#include "mozilla/AntiTrackingCommon.h"
+#include "mozilla/ContentBlockingNotifier.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/PWindowGlobalParent.h"
 #include "mozilla/dom/BrowserParent.h"
 #include "mozilla/dom/WindowContext.h"
 #include "nsRefPtrHashtable.h"
 #include "nsWrapperCache.h"
 #include "nsISupports.h"
@@ -141,18 +141,18 @@ class WindowGlobalParent final : public 
   nsIGlobalObject* GetParentObject();
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   void NotifyContentBlockingEvent(
       uint32_t aEvent, nsIRequest* aRequest, bool aBlocked,
       const nsACString& aTrackingOrigin,
       const nsTArray<nsCString>& aTrackingFullHashes,
-      const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason =
-          Nothing());
+      const Maybe<ContentBlockingNotifier::StorageAccessGrantedReason>&
+          aReason = Nothing());
 
   ContentBlockingLog* GetContentBlockingLog() { return &mContentBlockingLog; }
 
  protected:
   const nsAString& GetRemoteType() override;
   JSWindowActor::Type GetSide() override { return JSWindowActor::Type::Parent; }
 
   // IPC messages
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et 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/AntiTrackingCommon.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ContentBlockingNotifier.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Printf.h"
 #include "mozilla/StorageAccess.h"
 #include "mozilla/Unused.h"
 
 #include "mozilla/net/CookieJarSettings.h"
 #include "mozilla/net/CookieServiceChild.h"
@@ -2260,35 +2260,36 @@ void nsCookieService::SetCookieStringInt
   while (SetCookieInternal(aHostURI, key, requireHostMatch, cookieStatus,
                            aCookieHeader, serverTime, aFromHttp, aChannel)) {
     // document.cookie can only set one cookie at a time
     if (!aFromHttp) break;
   }
 }
 
 void nsCookieService::NotifyAccepted(nsIChannel* aChannel) {
-  AntiTrackingCommon::NotifyBlockingDecision(
-      aChannel, AntiTrackingCommon::BlockingDecision::eAllow, 0);
+  ContentBlockingNotifier::OnDecision(
+      aChannel, ContentBlockingNotifier::BlockingDecision::eAllow, 0);
 }
 
 // notify observers that a cookie was rejected due to the users' prefs.
 void nsCookieService::NotifyRejected(nsIURI* aHostURI, nsIChannel* aChannel,
                                      uint32_t aRejectedReason,
                                      CookieOperation aOperation) {
   if (aOperation == OPERATION_WRITE) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       os->NotifyObservers(aHostURI, "cookie-rejected", nullptr);
     }
   } else {
     MOZ_ASSERT(aOperation == OPERATION_READ);
   }
 
-  AntiTrackingCommon::NotifyBlockingDecision(
-      aChannel, AntiTrackingCommon::BlockingDecision::eBlock, aRejectedReason);
+  ContentBlockingNotifier::OnDecision(
+      aChannel, ContentBlockingNotifier::BlockingDecision::eBlock,
+      aRejectedReason);
 }
 
 // notify observers that the cookie list changed. there are five possible
 // values for aData:
 // "deleted" means a cookie was deleted. aSubject is the deleted cookie.
 // "added"   means a cookie was added. aSubject is the added cookie.
 // "changed" means a cookie was altered. aSubject is the new cookie.
 // "cleared" means the entire cookie list was cleared. aSubject is null.
--- a/netwerk/url-classifier/UrlClassifierCommon.cpp
+++ b/netwerk/url-classifier/UrlClassifierCommon.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/net/UrlClassifierCommon.h"
 
 #include "ClassifierDummyChannel.h"
 #include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/ContentBlockingAllowList.h"
+#include "mozilla/ContentBlockingNotifier.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/net/HttpBaseChannel.h"
 #include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/StaticPrefs_network.h"
 #include "mozilla/StaticPrefs_privacy.h"
 #include "mozilla/StaticPrefs_channelclassifier.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsContentUtils.h"
@@ -206,17 +207,17 @@ nsresult UrlClassifierCommon::SetBlocked
       parentChannel->SetClassifierMatchedInfo(aList, aProvider, aFullHash);
     }
 
     unsigned state =
         UrlClassifierFeatureFactory::GetClassifierBlockingEventCode(aErrorCode);
     if (!state) {
       state = nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
     }
-    AntiTrackingCommon::NotifyContentBlockingEvent(channel, state);
+    ContentBlockingNotifier::OnEvent(channel, state);
 
     return NS_OK;
   }
 
   // TODO: ReportToConsole is called in the child process,
   // If nsContentUtils::ReportToConsole is not fission compatiable(cannot report
   // to correct top-level window), we need to do this in the parent process
   // instead (find the top-level window in the parent and send an IPC to child
@@ -438,17 +439,17 @@ void UrlClassifierCommon::AnnotateChanne
 
   // We consider valid tracking flags (based on the current strict vs basic list
   // prefs) and cryptomining (which is not considered as tracking).
   bool validClassificationFlags =
       IsTrackingClassificationFlag(aClassificationFlags) ||
       IsCryptominingClassificationFlag(aClassificationFlags);
 
   if (validClassificationFlags && isThirdPartyWithTopLevelWinURI) {
-    AntiTrackingCommon::NotifyContentBlockingEvent(aChannel, aLoadingState);
+    ContentBlockingNotifier::OnEvent(aChannel, aLoadingState);
   }
 
   if (isThirdPartyWithTopLevelWinURI &&
       StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
     LowerPriorityHelper(aChannel);
   }
 }
 
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "AntiTrackingLog.h"
 #include "AntiTrackingCommon.h"
+#include "AntiTrackingUtils.h"
 
 #include "mozilla/ContentBlockingAllowList.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/net/UrlClassifierCommon.h"
 #include "mozilla/ipc/MessageChannel.h"
@@ -43,31 +44,28 @@
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintfCString.h"
 #include "nsScriptSecurityManager.h"
 #include "nsSandboxFlags.h"
 #include "prtime.h"
 
 #define ANTITRACKING_PERM_KEY "3rdPartyStorage"
-#define ANTITRACKING_CONSOLE_CATEGORY NS_LITERAL_CSTRING("Content Blocking")
 
 namespace mozilla {
 
 LazyLogModule gAntiTrackingLog("AntiTracking");
 
 }
 
 using namespace mozilla;
 using mozilla::dom::BrowsingContext;
 using mozilla::dom::ContentChild;
 using mozilla::dom::Document;
 
-static const uint32_t kMaxConsoleOutputDelayMs = 100;
-
 namespace {
 
 bool GetParentPrincipalAndTrackingOrigin(
     nsGlobalWindowInner* a3rdPartyTrackingWindow, uint32_t aBehavior,
     nsIPrincipal** aTopLevelStoragePrincipal, nsACString& aTrackingOrigin,
     nsIPrincipal** aTrackingPrincipal) {
   // Now we need the principal and the origin of the parent window.
   nsCOMPtr<nsIPrincipal> topLevelStoragePrincipal =
@@ -175,239 +173,16 @@ int32_t CookiesBehavior(nsIPrincipal* aP
   // (See Bug 1406675 for rationale).
   if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
     return nsICookieService::BEHAVIOR_ACCEPT;
   }
 
   return aCookieJarSettings->GetCookieBehavior();
 }
 
-void RunConsoleReportingRunnable(already_AddRefed<nsIRunnable>&& aRunnable) {
-  if (StaticPrefs::privacy_restrict3rdpartystorage_console_lazy()) {
-    nsresult rv = NS_DispatchToCurrentThreadQueue(std::move(aRunnable),
-                                                  kMaxConsoleOutputDelayMs,
-                                                  EventQueuePriority::Idle);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-  } else {
-    nsCOMPtr<nsIRunnable> runnable(std::move(aRunnable));
-    nsresult rv = runnable->Run();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-  }
-}
-
-void ReportBlockingToConsole(uint64_t aWindowID, nsIURI* aURI,
-                             uint32_t aRejectedReason) {
-  MOZ_ASSERT(aWindowID);
-  MOZ_ASSERT(aURI);
-  MOZ_ASSERT(
-      aRejectedReason == 0 ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN ||
-      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL ||
-      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN);
-
-  nsAutoString sourceLine;
-  uint32_t lineNumber = 0, columnNumber = 0;
-  JSContext* cx = nsContentUtils::GetCurrentJSContext();
-  if (cx) {
-    nsJSUtils::GetCallingLocation(cx, sourceLine, &lineNumber, &columnNumber);
-  }
-
-  nsCOMPtr<nsIURI> uri(aURI);
-
-  RefPtr<Runnable> runnable = NS_NewRunnableFunction(
-      "ReportBlockingToConsoleDelayed", [aWindowID, sourceLine, lineNumber,
-                                         columnNumber, uri, aRejectedReason]() {
-        const char* message = nullptr;
-        nsAutoCString category;
-        // When changing this list, please make sure to update the corresponding
-        // code in antitracking_head.js (inside _createTask).
-        switch (aRejectedReason) {
-          case nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION:
-            message = "CookieBlockedByPermission";
-            category = NS_LITERAL_CSTRING("cookieBlockedPermission");
-            break;
-
-          case nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER:
-            message = "CookieBlockedTracker";
-            category = NS_LITERAL_CSTRING("cookieBlockedTracker");
-            break;
-
-          case nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL:
-            message = "CookieBlockedAll";
-            category = NS_LITERAL_CSTRING("cookieBlockedAll");
-            break;
-
-          case nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN:
-            message = "CookieBlockedForeign";
-            category = NS_LITERAL_CSTRING("cookieBlockedForeign");
-            break;
-
-          default:
-            return;
-        }
-
-        MOZ_ASSERT(message);
-
-        // Strip the URL of any possible username/password and make it ready
-        // to be presented in the UI.
-        nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
-        NS_ENSURE_TRUE_VOID(urifixup);
-        nsCOMPtr<nsIURI> exposableURI;
-        nsresult rv =
-            urifixup->CreateExposableURI(uri, getter_AddRefs(exposableURI));
-        NS_ENSURE_SUCCESS_VOID(rv);
-
-        AutoTArray<nsString, 1> params;
-        CopyUTF8toUTF16(exposableURI->GetSpecOrDefault(),
-                        *params.AppendElement());
-
-        nsAutoString errorText;
-        rv = nsContentUtils::FormatLocalizedString(
-            nsContentUtils::eNECKO_PROPERTIES, message, params, errorText);
-        NS_ENSURE_SUCCESS_VOID(rv);
-
-        nsContentUtils::ReportToConsoleByWindowID(
-            errorText, nsIScriptError::warningFlag, category, aWindowID,
-            nullptr, sourceLine, lineNumber, columnNumber);
-      });
-
-  RunConsoleReportingRunnable(runnable.forget());
-}
-
-void ReportBlockingToConsole(nsIChannel* aChannel, nsIURI* aURI,
-                             uint32_t aRejectedReason) {
-  MOZ_ASSERT(aChannel && aURI);
-
-  uint64_t windowID;
-
-  if (XRE_IsParentProcess()) {
-    // Get the top-level window ID from the top-level BrowsingContext
-    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
-    RefPtr<dom::BrowsingContext> bc;
-    loadInfo->GetBrowsingContext(getter_AddRefs(bc));
-
-    if (!bc || bc->IsDiscarded()) {
-      return;
-    }
-
-    bc = bc->Top();
-    RefPtr<dom::WindowGlobalParent> wgp =
-        bc->Canonical()->GetCurrentWindowGlobal();
-    if (!wgp) {
-      return;
-    }
-
-    windowID = wgp->InnerWindowId();
-  } else {
-    nsresult rv;
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel, &rv);
-
-    if (!httpChannel) {
-      return;
-    }
-
-    rv = httpChannel->GetTopLevelContentWindowId(&windowID);
-    if (NS_FAILED(rv) || !windowID) {
-      windowID = nsContentUtils::GetInnerWindowID(httpChannel);
-    }
-  }
-
-  ReportBlockingToConsole(windowID, aURI, aRejectedReason);
-}
-
-void ReportUnblockingToConsole(
-    nsPIDOMWindowInner* aWindow, const nsAString& aTrackingOrigin,
-    AntiTrackingCommon::StorageAccessGrantedReason aReason) {
-  nsCOMPtr<nsIPrincipal> principal =
-      nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
-  if (NS_WARN_IF(!principal)) {
-    return;
-  }
-
-  RefPtr<Document> doc = aWindow->GetExtantDoc();
-  if (NS_WARN_IF(!doc)) {
-    return;
-  }
-
-  nsAutoString trackingOrigin(aTrackingOrigin);
-
-  nsAutoString sourceLine;
-  uint32_t lineNumber = 0, columnNumber = 0;
-  JSContext* cx = nsContentUtils::GetCurrentJSContext();
-  if (cx) {
-    nsJSUtils::GetCallingLocation(cx, sourceLine, &lineNumber, &columnNumber);
-  }
-
-  RefPtr<Runnable> runnable = NS_NewRunnableFunction(
-      "ReportUnblockingToConsoleDelayed",
-      [doc, principal, trackingOrigin, sourceLine, lineNumber, columnNumber,
-       aReason]() {
-        nsAutoString origin;
-        nsresult rv = nsContentUtils::GetUTFOrigin(principal, origin);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return;
-        }
-
-        // Not adding grantedOrigin yet because we may not want it later.
-        AutoTArray<nsString, 3> params = {origin, trackingOrigin};
-        const char* messageWithSameOrigin = nullptr;
-
-        switch (aReason) {
-          case AntiTrackingCommon::eStorageAccessAPI:
-            messageWithSameOrigin = "CookieAllowedForTrackerByStorageAccessAPI";
-            break;
-
-          case AntiTrackingCommon::eOpenerAfterUserInteraction:
-            [[fallthrough]];
-          case AntiTrackingCommon::eOpener:
-            messageWithSameOrigin = "CookieAllowedForTrackerByHeuristic";
-            break;
-        }
-
-        nsContentUtils::ReportToConsole(
-            nsIScriptError::warningFlag, ANTITRACKING_CONSOLE_CATEGORY, doc,
-            nsContentUtils::eNECKO_PROPERTIES, messageWithSameOrigin, params,
-            nullptr, sourceLine, lineNumber, columnNumber);
-      });
-
-  RunConsoleReportingRunnable(runnable.forget());
-}
-
-already_AddRefed<nsPIDOMWindowOuter> GetTopWindow(nsPIDOMWindowInner* aWindow) {
-  Document* document = aWindow->GetExtantDoc();
-  if (!document) {
-    return nullptr;
-  }
-
-  nsIChannel* channel = document->GetChannel();
-  if (!channel) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsPIDOMWindowOuter> pwin =
-      aWindow->GetBrowsingContext()->Top()->GetDOMWindow();
-
-  if (!pwin) {
-    return nullptr;
-  }
-
-  return pwin.forget();
-}
-
 class TemporaryAccessGrantCacheKey : public PLDHashEntryHdr {
  public:
   typedef Pair<nsCOMPtr<nsIPrincipal>, nsCString> KeyType;
   typedef const KeyType* KeyTypePointer;
 
   explicit TemporaryAccessGrantCacheKey(KeyTypePointer aKey)
       : mPrincipal(aKey->first()), mType(aKey->second()) {}
   TemporaryAccessGrantCacheKey(TemporaryAccessGrantCacheKey&& aOther) = default;
@@ -631,195 +406,37 @@ bool CheckAntiTrackingPermission(nsIPrin
       }
       return false;
     }
   }
 
   return true;
 }
 
-// This API finishes the remaining work left in NotifyBlockingDecisionInternal.
-void NotifyAllowDecisionInternal(nsIChannel* aReportingChannel,
-                                 nsIChannel* aTrackingChannel, nsIURI* aURI,
-                                 nsPIDOMWindowOuter* aWindow) {
-  nsAutoCString trackingOrigin;
-  if (aURI) {
-    Unused << nsContentUtils::GetASCIIOrigin(aURI, trackingOrigin);
-  }
-
-  // This can be called in either the parent process or the child processes.
-
-  // Now send the generic "cookies loaded" notifications, from the most generic
-  // to the most specific.
-  AntiTrackingCommon::NotifyContentBlockingEvent(
-      aWindow, aReportingChannel, aTrackingChannel, false,
-      nsIWebProgressListener::STATE_COOKIES_LOADED, trackingOrigin);
-
-  nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
-      do_QueryInterface(aTrackingChannel);
-  if (!classifiedChannel) {
-    return;
-  }
-
-  uint32_t classificationFlags =
-      classifiedChannel->GetThirdPartyClassificationFlags();
-  if (classificationFlags &
-      nsIClassifiedChannel::ClassificationFlags::CLASSIFIED_TRACKING) {
-    AntiTrackingCommon::NotifyContentBlockingEvent(
-        aWindow, aReportingChannel, aTrackingChannel, false,
-        nsIWebProgressListener::STATE_COOKIES_LOADED_TRACKER, trackingOrigin);
-  }
-
-  if (classificationFlags &
-      nsIClassifiedChannel::ClassificationFlags::CLASSIFIED_SOCIALTRACKING) {
-    AntiTrackingCommon::NotifyContentBlockingEvent(
-        aWindow, aReportingChannel, aTrackingChannel, false,
-        nsIWebProgressListener::STATE_COOKIES_LOADED_SOCIALTRACKER,
-        trackingOrigin);
-  }
-}
-
-void NotifyBlockingDecisionInternal(
-    nsIChannel* aReportingChannel, nsIChannel* aTrackingChannel,
-    AntiTrackingCommon::BlockingDecision aDecision, uint32_t aRejectedReason,
-    nsIURI* aURI, nsPIDOMWindowOuter* aWindow) {
-  MOZ_ASSERT(aWindow);
-
-  // When this is called with system priviledged, the decision should always be
-  // ALLOW, and we can also stop processing this event.
-  if (nsGlobalWindowOuter::Cast(aWindow)->GetPrincipal() ==
-      nsContentUtils::GetSystemPrincipal()) {
-    MOZ_DIAGNOSTIC_ASSERT(aDecision ==
-                          AntiTrackingCommon::BlockingDecision::eAllow);
-    return;
-  }
-
-  nsAutoCString trackingOrigin;
-  if (aURI) {
-    Unused << nsContentUtils::GetASCIIOrigin(aURI, trackingOrigin);
-  }
-
-  if (aDecision == AntiTrackingCommon::BlockingDecision::eBlock) {
-    AntiTrackingCommon::NotifyContentBlockingEvent(
-        aWindow, aReportingChannel, aTrackingChannel, true, aRejectedReason,
-        trackingOrigin);
-
-    ReportBlockingToConsole(aReportingChannel, aURI, aRejectedReason);
-  }
-
-  NotifyAllowDecisionInternal(aReportingChannel, aTrackingChannel, aURI,
-                              aWindow);
-}
-
-void NotifyBlockingDecisionInternal(
-    nsIChannel* aReportingChannel, nsIChannel* aTrackingChannel,
-    AntiTrackingCommon::BlockingDecision aDecision, uint32_t aRejectedReason,
-    nsIURI* aURI) {
-  // Can be called only in the parent process when there is no window.
-  MOZ_ASSERT(XRE_IsParentProcess());
-
-  nsAutoCString trackingOrigin;
-  if (aURI) {
-    Unused << nsContentUtils::GetASCIIOrigin(aURI, trackingOrigin);
-  }
-
-  if (aDecision == AntiTrackingCommon::BlockingDecision::eBlock) {
-    AntiTrackingCommon::NotifyContentBlockingEvent(
-        nullptr, aReportingChannel, aTrackingChannel, true, aRejectedReason,
-        trackingOrigin);
-
-    ReportBlockingToConsole(aReportingChannel, aURI, aRejectedReason);
-  }
-
-  NotifyAllowDecisionInternal(aReportingChannel, aTrackingChannel, aURI,
-                              nullptr);
-}
-
-// Send a message to notify OnContentBlockingEvent in the parent, which will
-// update the ContentBlockingLog in the parent.
-void NotifyContentBlockingEventInChild(
-    nsPIDOMWindowOuter* aWindow, nsIChannel* aReportingChannel,
-    nsIChannel* aTrackingChannel, bool aBlocked, uint32_t aRejectedReason,
-    const nsACString& aTrackingOrigin,
-    const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason) {
-  MOZ_ASSERT(XRE_IsContentProcess());
-  MOZ_ASSERT(aWindow);
-
-  RefPtr<dom::BrowserChild> browserChild = dom::BrowserChild::GetFrom(aWindow);
-  NS_ENSURE_TRUE_VOID(browserChild);
-
-  nsTArray<nsCString> trackingFullHashes;
-  nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
-      do_QueryInterface(aTrackingChannel);
-
-  if (classifiedChannel) {
-    Unused << classifiedChannel->GetMatchedTrackingFullHashes(
-        trackingFullHashes);
-  }
-
-  browserChild->NotifyContentBlockingEvent(aRejectedReason, aReportingChannel,
-                                           aBlocked, aTrackingOrigin,
-                                           trackingFullHashes, aReason);
-}
-
-// Update the ContentBlockingLog of the top-level WindowGlobalParent of
-// the reporting channel.
-void NotifyContentBlockingEventInParent(
-    nsIChannel* aReportingChannel, nsIChannel* aTrackingChannel, bool aBlocked,
-    uint32_t aRejectedReason, const nsACString& aTrackingOrigin,
-    const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason) {
-  MOZ_ASSERT(XRE_IsParentProcess());
-
-  nsCOMPtr<nsILoadInfo> loadInfo = aReportingChannel->LoadInfo();
-  RefPtr<dom::BrowsingContext> bc;
-  loadInfo->GetBrowsingContext(getter_AddRefs(bc));
-
-  if (!bc || bc->IsDiscarded()) {
-    return;
-  }
-
-  bc = bc->Top();
-  RefPtr<dom::WindowGlobalParent> wgp =
-      bc->Canonical()->GetCurrentWindowGlobal();
-  NS_ENSURE_TRUE_VOID(wgp);
-
-  nsTArray<nsCString> trackingFullHashes;
-  nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
-      do_QueryInterface(aTrackingChannel);
-
-  if (classifiedChannel) {
-    Unused << classifiedChannel->GetMatchedTrackingFullHashes(
-        trackingFullHashes);
-  }
-
-  wgp->NotifyContentBlockingEvent(aRejectedReason, aReportingChannel, aBlocked,
-                                  aTrackingOrigin, trackingFullHashes, aReason);
-}
-
 }  // namespace
 
 /* static */ RefPtr<AntiTrackingCommon::StorageAccessGrantPromise>
 AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(
     nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aParentWindow,
-    StorageAccessGrantedReason aReason,
+    ContentBlockingNotifier::StorageAccessGrantedReason aReason,
     const AntiTrackingCommon::PerformFinalChecks& aPerformFinalChecks) {
   MOZ_ASSERT(aParentWindow);
 
   switch (aReason) {
-    case eOpener:
+    case ContentBlockingNotifier::eOpener:
       if (!StaticPrefs::
               privacy_restrict3rdpartystorage_heuristic_window_open()) {
         LOG(
             ("Bailing out early because the "
              "privacy.restrict3rdpartystorage.heuristic.window_open preference "
              "has been disabled"));
         return StorageAccessGrantPromise::CreateAndReject(false, __func__);
       }
       break;
-    case eOpenerAfterUserInteraction:
+    case ContentBlockingNotifier::eOpenerAfterUserInteraction:
       if (!StaticPrefs::
               privacy_restrict3rdpartystorage_heuristic_opened_window_after_interaction()) {
         LOG(
             ("Bailing out early because the "
              "privacy.restrict3rdpartystorage.heuristic.opened_window_after_"
              "interaction preference has been disabled"));
         return StorageAccessGrantPromise::CreateAndReject(false, __func__);
       }
@@ -971,22 +588,24 @@ AntiTrackingCommon::AddFirstPartyStorage
       "privacy.restrict3rdpartystorage."
       "userInteractionRequiredForHosts",
       &isInPrefList);
   if (isInPrefList && !HasUserInteraction(trackingPrincipal)) {
     LOG_PRIN(("Tracking principal (%s) hasn't been interacted with before, "
               "refusing to add a first-party storage permission to access it",
               _spec),
              trackingPrincipal);
-    NotifyBlockingDecision(aParentWindow, BlockingDecision::eBlock,
-                           blockReason);
+    ContentBlockingNotifier::OnDecision(
+        aParentWindow, ContentBlockingNotifier::BlockingDecision::eBlock,
+        blockReason);
     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
   }
 
-  nsCOMPtr<nsPIDOMWindowOuter> pwin = GetTopWindow(parentWindow);
+  nsCOMPtr<nsPIDOMWindowOuter> pwin =
+      AntiTrackingUtils::GetTopWindow(parentWindow);
   if (!pwin) {
     LOG(("Couldn't get the top window"));
     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
   }
 
   auto storePermission =
       [pwin, parentWindow, trackingOrigin, trackingPrincipal, topInnerWindow,
        topLevelStoragePrincipal,
@@ -998,22 +617,22 @@ AntiTrackingCommon::AddFirstPartyStorage
     topInnerWindow->SaveStorageAccessGranted(permissionKey);
 
     // Let's inform the parent window.
     parentWindow->StorageAccessGranted();
 
     nsIChannel* channel =
         pwin->GetCurrentInnerWindow()->GetExtantDoc()->GetChannel();
 
-    NotifyContentBlockingEvent(
+    ContentBlockingNotifier::OnEvent(
         pwin, channel, parentWindow->GetExtantDoc()->GetChannel(), false,
         blockReason, trackingOrigin, Some(aReason));
 
-    ReportUnblockingToConsole(parentWindow,
-                              NS_ConvertUTF8toUTF16(trackingOrigin), aReason);
+    ContentBlockingNotifier::ReportUnblockingToConsole(
+        parentWindow, NS_ConvertUTF8toUTF16(trackingOrigin), aReason);
 
     if (XRE_IsParentProcess()) {
       LOG(("Saving the permission: trackingOrigin=%s", trackingOrigin.get()));
       return SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(
                  topLevelStoragePrincipal, trackingPrincipal, trackingOrigin,
                  aAllowMode)
           ->Then(GetCurrentThreadSerialEventTarget(), __func__,
                  [](FirstPartyStorageAccessGrantPromise::ResolveOrRejectValue&&
@@ -1736,121 +1355,16 @@ bool AntiTrackingCommon::MaybeIsFirstPar
   CreatePermissionKey(origin, type);
 
   return CheckAntiTrackingPermission(
       parentPrincipal, type,
       nsContentUtils::IsInPrivateBrowsing(parentDocument), nullptr, 0);
 }
 
 /* static */
-void AntiTrackingCommon::NotifyBlockingDecision(nsIChannel* aChannel,
-                                                BlockingDecision aDecision,
-                                                uint32_t aRejectedReason) {
-  MOZ_ASSERT(
-      aRejectedReason == 0 ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN ||
-      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL ||
-      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN);
-  MOZ_ASSERT(aDecision == BlockingDecision::eBlock ||
-             aDecision == BlockingDecision::eAllow);
-
-  if (!aChannel) {
-    return;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  aChannel->GetURI(getter_AddRefs(uri));
-
-  // Can be called in EITHER the parent or child process.
-  // Window is only needed while in child processes.
-  if (XRE_IsParentProcess()) {
-    NotifyBlockingDecisionInternal(aChannel, aChannel, aDecision,
-                                   aRejectedReason, uri);
-    return;
-  }
-
-  MOZ_ASSERT(XRE_IsContentProcess());
-
-  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
-  if (!thirdPartyUtil) {
-    return;
-  }
-
-  nsCOMPtr<nsIURI> uriBeingLoaded = MaybeGetDocumentURIBeingLoaded(aChannel);
-  nsCOMPtr<mozIDOMWindowProxy> win;
-  nsresult rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, uriBeingLoaded,
-                                                       getter_AddRefs(win));
-  NS_ENSURE_SUCCESS_VOID(rv);
-
-  nsCOMPtr<nsPIDOMWindowOuter> pwin = nsPIDOMWindowOuter::From(win);
-  if (!pwin) {
-    return;
-  }
-
-  NotifyBlockingDecisionInternal(aChannel, aChannel, aDecision, aRejectedReason,
-                                 uri, pwin);
-}
-
-/* static */
-void AntiTrackingCommon::NotifyBlockingDecision(nsPIDOMWindowInner* aWindow,
-                                                BlockingDecision aDecision,
-                                                uint32_t aRejectedReason) {
-  MOZ_ASSERT(aWindow);
-  MOZ_ASSERT(
-      aRejectedReason == 0 ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER ||
-      aRejectedReason ==
-          nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN ||
-      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL ||
-      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN);
-  MOZ_ASSERT(aDecision == BlockingDecision::eBlock ||
-             aDecision == BlockingDecision::eAllow);
-
-  nsCOMPtr<nsPIDOMWindowOuter> pwin = GetTopWindow(aWindow);
-  if (!pwin) {
-    return;
-  }
-
-  nsPIDOMWindowInner* inner = pwin->GetCurrentInnerWindow();
-  if (!inner) {
-    return;
-  }
-  Document* pwinDoc = inner->GetExtantDoc();
-  if (!pwinDoc) {
-    return;
-  }
-  nsIChannel* channel = pwinDoc->GetChannel();
-  if (!channel) {
-    return;
-  }
-
-  Document* document = aWindow->GetExtantDoc();
-  if (!document) {
-    return;
-  }
-  nsIURI* uri = document->GetDocumentURI();
-  nsIChannel* trackingChannel = document->GetChannel();
-
-  NotifyBlockingDecisionInternal(channel, trackingChannel, aDecision,
-                                 aRejectedReason, uri, pwin);
-}
-
-/* static */
 void AntiTrackingCommon::StoreUserInteractionFor(nsIPrincipal* aPrincipal) {
   if (!aPrincipal) {
     // The content process may have sent us garbage data.
     return;
   }
 
   if (XRE_IsParentProcess()) {
     LOG_PRIN(("Saving the userInteraction for %s", _spec), aPrincipal);
@@ -1927,50 +1441,16 @@ already_AddRefed<nsIURI> AntiTrackingCom
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
   }
   return uriBeingLoaded.forget();
 }
 
 /* static */
-void AntiTrackingCommon::NotifyContentBlockingEvent(nsIChannel* aChannel,
-                                                    uint32_t aRejectedReason) {
-  MOZ_ASSERT(XRE_IsParentProcess() && aChannel);
-
-  nsCOMPtr<nsIURI> uri;
-  aChannel->GetURI(getter_AddRefs(uri));
-
-  nsAutoCString trackingOrigin;
-  if (uri) {
-    Unused << nsContentUtils::GetASCIIOrigin(uri, trackingOrigin);
-  }
-
-  return AntiTrackingCommon::NotifyContentBlockingEvent(
-      nullptr, aChannel, aChannel, true, aRejectedReason, trackingOrigin);
-}
-
-/* static */
-void AntiTrackingCommon::NotifyContentBlockingEvent(
-    nsPIDOMWindowOuter* aWindow, nsIChannel* aReportingChannel,
-    nsIChannel* aTrackingChannel, bool aBlocked, uint32_t aRejectedReason,
-    const nsACString& aTrackingOrigin,
-    const Maybe<StorageAccessGrantedReason>& aReason) {
-  if (XRE_IsParentProcess()) {
-    NotifyContentBlockingEventInParent(aReportingChannel, aTrackingChannel,
-                                       aBlocked, aRejectedReason,
-                                       aTrackingOrigin, aReason);
-  } else {
-    NotifyContentBlockingEventInChild(
-        aWindow, aReportingChannel, aTrackingChannel, aBlocked, aRejectedReason,
-        aTrackingOrigin, aReason);
-  }
-}
-
-/* static */
 void AntiTrackingCommon::RedirectHeuristic(nsIChannel* aOldChannel,
                                            nsIURI* aOldURI,
                                            nsIChannel* aNewChannel,
                                            nsIURI* aNewURI) {
   MOZ_ASSERT(aOldChannel);
   MOZ_ASSERT(aOldURI);
   MOZ_ASSERT(aNewChannel);
   MOZ_ASSERT(aNewURI);
--- a/toolkit/components/antitracking/AntiTrackingCommon.h
+++ b/toolkit/components/antitracking/AntiTrackingCommon.h
@@ -3,16 +3,17 @@
 /* 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_antitrackingservice_h
 #define mozilla_antitrackingservice_h
 
 #include "nsString.h"
+#include "mozilla/ContentBlockingNotifier.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPrefs_privacy.h"
 
 #define USER_INTERACTION_PERM NS_LITERAL_CSTRING("storageAccessAPI")
 
 class nsIChannel;
 class nsICookieJarSettings;
@@ -68,21 +69,16 @@ class AntiTrackingCommon final {
                                                   nsIURI* aURI,
                                                   uint32_t* aRejectedReason);
 
   // This method checks if the principal has the permission to access to the
   // first party storage.
   static bool IsFirstPartyStorageAccessGrantedFor(
       nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings);
 
-  enum StorageAccessGrantedReason {
-    eStorageAccessAPI,
-    eOpenerAfterUserInteraction,
-    eOpener
-  };
   enum StorageAccessPromptChoices { eAllow, eAllowAutoGrant };
 
   // Grant the permission for aOrigin to have access to the first party storage.
   // This method can handle 2 different scenarios:
   // - aParentWindow is a 3rd party context, it opens an aOrigin window and the
   //   user interacts with it. We want to grant the permission at the
   //   combination: top-level + aParentWindow + aOrigin.
   //   Ex: example.net loads an iframe tracker.com, which opens a popup
@@ -97,17 +93,17 @@ class AntiTrackingCommon final {
   //   example.net.
   typedef MozPromise<int, bool, true> StorageAccessFinalCheckPromise;
   typedef std::function<RefPtr<StorageAccessFinalCheckPromise>()>
       PerformFinalChecks;
   typedef MozPromise<int, bool, true> StorageAccessGrantPromise;
   static MOZ_MUST_USE RefPtr<StorageAccessGrantPromise>
   AddFirstPartyStorageAccessGrantedFor(
       nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aParentWindow,
-      StorageAccessGrantedReason aReason,
+      ContentBlockingNotifier::StorageAccessGrantedReason aReason,
       const PerformFinalChecks& aPerformFinalChecks = nullptr);
 
   // Given a principal, returns the storage permission key that will be used for
   // the principal.  Returns true on success.
   static bool CreateStoragePermissionKey(nsIPrincipal* aPrincipal,
                                          nsACString& aKey);
 
   // Returns true if the permission passed in is a storage access permission
@@ -123,54 +119,19 @@ class AntiTrackingCommon final {
   typedef MozPromise<nsresult, bool, true> FirstPartyStorageAccessGrantPromise;
   static RefPtr<FirstPartyStorageAccessGrantPromise>
   SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(
       nsIPrincipal* aPrincipal, nsIPrincipal* aTrackingPrinciapl,
       const nsCString& aTrackingOrigin, int aAllowMode,
       uint64_t aExpirationTime =
           StaticPrefs::privacy_restrict3rdpartystorage_expiration());
 
-  enum class BlockingDecision {
-    eBlock,
-    eAllow,
-  };
-
-  // This method can be called on the parent process or on the content process.
-  // The notification is propagated to the child channel if aChannel is a parent
-  // channel proxy.
-  //
-  // aDecision can be eBlock if we have decided to block some content, or eAllow
-  // if we have decided to allow the content through.
-  //
-  // aRejectedReason must be one of these values:
-  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION
-  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER
-  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER
-  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL
-  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN
-  static void NotifyBlockingDecision(nsIChannel* aChannel,
-                                     BlockingDecision aDecision,
-                                     uint32_t aRejectedReason);
-
-  static void NotifyBlockingDecision(nsPIDOMWindowInner* aWindow,
-                                     BlockingDecision aDecision,
-                                     uint32_t aRejectedReason);
-
   // Get the current document URI from a document channel as it is being loaded.
   static already_AddRefed<nsIURI> MaybeGetDocumentURIBeingLoaded(
       nsIChannel* aChannel);
 
-  static void NotifyContentBlockingEvent(nsIChannel* aChannel,
-                                         uint32_t aRejectedReason);
-
-  static void NotifyContentBlockingEvent(
-      nsPIDOMWindowOuter* aWindow, nsIChannel* aReportingChannel,
-      nsIChannel* aTrackingChannel, bool aBlocked, uint32_t aRejectedReason,
-      const nsACString& aTrackingOrigin,
-      const Maybe<StorageAccessGrantedReason>& aReason = Nothing());
-
   static void RedirectHeuristic(nsIChannel* aOldChannel, nsIURI* aOldURI,
                                 nsIChannel* aNewChannel, nsIURI* aNewURI);
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_antitrackingservice_h
--- a/toolkit/components/antitracking/AntiTrackingIPCUtils.h
+++ b/toolkit/components/antitracking/AntiTrackingIPCUtils.h
@@ -4,25 +4,26 @@
  * 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_antitrackingipcutils_h
 #define mozilla_antitrackingipcutils_h
 
 #include "ipc/IPCMessageUtils.h"
 
-#include "mozilla/AntiTrackingCommon.h"
+#include "mozilla/ContentBlockingNotifier.h"
 
 namespace IPC {
 
-// For allowing passing the enum AntiTrackingCommon::StorageAccessGrantedReason
-// over IPC.
+// For allowing passing the enum
+// ContentBlockingNotifier::StorageAccessGrantedReason over IPC.
 template <>
-struct ParamTraits<mozilla::AntiTrackingCommon::StorageAccessGrantedReason>
+struct ParamTraits<mozilla::ContentBlockingNotifier::StorageAccessGrantedReason>
     : public ContiguousEnumSerializerInclusive<
-          mozilla::AntiTrackingCommon::StorageAccessGrantedReason,
-          mozilla::AntiTrackingCommon::StorageAccessGrantedReason::
+          mozilla::ContentBlockingNotifier::StorageAccessGrantedReason,
+          mozilla::ContentBlockingNotifier::StorageAccessGrantedReason::
               eStorageAccessAPI,
-          mozilla::AntiTrackingCommon::StorageAccessGrantedReason::eOpener> {};
+          mozilla::ContentBlockingNotifier::StorageAccessGrantedReason::
+              eOpener> {};
 
 }  // namespace IPC
 
 #endif  // mozilla_antitrackingipcutils_h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/AntiTrackingUtils.cpp
@@ -0,0 +1,38 @@
+/* -*- 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 "AntiTrackingUtils.h"
+
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/Document.h"
+#include "nsIChannel.h"
+#include "nsPIDOMWindow.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+/* static */ already_AddRefed<nsPIDOMWindowOuter>
+AntiTrackingUtils::GetTopWindow(nsPIDOMWindowInner* aWindow) {
+  Document* document = aWindow->GetExtantDoc();
+  if (!document) {
+    return nullptr;
+  }
+
+  nsIChannel* channel = document->GetChannel();
+  if (!channel) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsPIDOMWindowOuter> pwin =
+      aWindow->GetBrowsingContext()->Top()->GetDOMWindow();
+
+  if (!pwin) {
+    return nullptr;
+  }
+
+  return pwin.forget();
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/AntiTrackingUtils.h
@@ -0,0 +1,25 @@
+/* -*- 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_antitrackingutils_h
+#define mozilla_antitrackingutils_h
+
+#include "mozilla/AlreadyAddRefed.h"
+
+class nsPIDOMWindowInner;
+class nsPIDOMWindowOuter;
+
+namespace mozilla {
+
+class AntiTrackingUtils final {
+ public:
+  static already_AddRefed<nsPIDOMWindowOuter> GetTopWindow(
+      nsPIDOMWindowInner* aWindow);
+};
+
+}  // namespace mozilla
+
+#endif  // mozilla_antitrackingutils_h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/ContentBlockingNotifier.cpp
@@ -0,0 +1,531 @@
+/* -*- 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 "AntiTrackingLog.h"
+#include "ContentBlockingNotifier.h"
+#include "AntiTrackingCommon.h"
+#include "AntiTrackingUtils.h"
+
+#include "mozilla/AbstractEventQueue.h"
+#include "mozilla/StaticPrefs_privacy.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "nsIClassifiedChannel.h"
+#include "nsIRunnable.h"
+#include "nsIScriptError.h"
+#include "nsIURI.h"
+#include "nsIURIFixup.h"
+#include "nsGlobalWindowInner.h"
+#include "nsJSUtils.h"
+#include "mozIThirdPartyUtil.h"
+
+using namespace mozilla;
+using mozilla::dom::BrowsingContext;
+using mozilla::dom::ContentChild;
+using mozilla::dom::Document;
+
+static const uint32_t kMaxConsoleOutputDelayMs = 100;
+
+namespace {
+
+void RunConsoleReportingRunnable(already_AddRefed<nsIRunnable>&& aRunnable) {
+  if (StaticPrefs::privacy_restrict3rdpartystorage_console_lazy()) {
+    nsresult rv = NS_DispatchToCurrentThreadQueue(std::move(aRunnable),
+                                                  kMaxConsoleOutputDelayMs,
+                                                  EventQueuePriority::Idle);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  } else {
+    nsCOMPtr<nsIRunnable> runnable(std::move(aRunnable));
+    nsresult rv = runnable->Run();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  }
+}
+
+void ReportBlockingToConsole(uint64_t aWindowID, nsIURI* aURI,
+                             uint32_t aRejectedReason) {
+  MOZ_ASSERT(aWindowID);
+  MOZ_ASSERT(aURI);
+  MOZ_ASSERT(
+      aRejectedReason == 0 ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN ||
+      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL ||
+      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN);
+
+  nsAutoString sourceLine;
+  uint32_t lineNumber = 0, columnNumber = 0;
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  if (cx) {
+    nsJSUtils::GetCallingLocation(cx, sourceLine, &lineNumber, &columnNumber);
+  }
+
+  nsCOMPtr<nsIURI> uri(aURI);
+
+  RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+      "ReportBlockingToConsoleDelayed", [aWindowID, sourceLine, lineNumber,
+                                         columnNumber, uri, aRejectedReason]() {
+        const char* message = nullptr;
+        nsAutoCString category;
+        // When changing this list, please make sure to update the corresponding
+        // code in antitracking_head.js (inside _createTask).
+        switch (aRejectedReason) {
+          case nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION:
+            message = "CookieBlockedByPermission";
+            category = NS_LITERAL_CSTRING("cookieBlockedPermission");
+            break;
+
+          case nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER:
+            message = "CookieBlockedTracker";
+            category = NS_LITERAL_CSTRING("cookieBlockedTracker");
+            break;
+
+          case nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL:
+            message = "CookieBlockedAll";
+            category = NS_LITERAL_CSTRING("cookieBlockedAll");
+            break;
+
+          case nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN:
+            message = "CookieBlockedForeign";
+            category = NS_LITERAL_CSTRING("cookieBlockedForeign");
+            break;
+
+          default:
+            return;
+        }
+
+        MOZ_ASSERT(message);
+
+        // Strip the URL of any possible username/password and make it ready
+        // to be presented in the UI.
+        nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
+        NS_ENSURE_TRUE_VOID(urifixup);
+        nsCOMPtr<nsIURI> exposableURI;
+        nsresult rv =
+            urifixup->CreateExposableURI(uri, getter_AddRefs(exposableURI));
+        NS_ENSURE_SUCCESS_VOID(rv);
+
+        AutoTArray<nsString, 1> params;
+        CopyUTF8toUTF16(exposableURI->GetSpecOrDefault(),
+                        *params.AppendElement());
+
+        nsAutoString errorText;
+        rv = nsContentUtils::FormatLocalizedString(
+            nsContentUtils::eNECKO_PROPERTIES, message, params, errorText);
+        NS_ENSURE_SUCCESS_VOID(rv);
+
+        nsContentUtils::ReportToConsoleByWindowID(
+            errorText, nsIScriptError::warningFlag, category, aWindowID,
+            nullptr, sourceLine, lineNumber, columnNumber);
+      });
+
+  RunConsoleReportingRunnable(runnable.forget());
+}
+
+void ReportBlockingToConsole(nsIChannel* aChannel, nsIURI* aURI,
+                             uint32_t aRejectedReason) {
+  MOZ_ASSERT(aChannel && aURI);
+
+  uint64_t windowID;
+
+  if (XRE_IsParentProcess()) {
+    // Get the top-level window ID from the top-level BrowsingContext
+    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+    RefPtr<dom::BrowsingContext> bc;
+    loadInfo->GetBrowsingContext(getter_AddRefs(bc));
+
+    if (!bc || bc->IsDiscarded()) {
+      return;
+    }
+
+    bc = bc->Top();
+    RefPtr<dom::WindowGlobalParent> wgp =
+        bc->Canonical()->GetCurrentWindowGlobal();
+    if (!wgp) {
+      return;
+    }
+
+    windowID = wgp->InnerWindowId();
+  } else {
+    nsresult rv;
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel, &rv);
+
+    if (!httpChannel) {
+      return;
+    }
+
+    rv = httpChannel->GetTopLevelContentWindowId(&windowID);
+    if (NS_FAILED(rv) || !windowID) {
+      windowID = nsContentUtils::GetInnerWindowID(httpChannel);
+    }
+  }
+
+  ReportBlockingToConsole(windowID, aURI, aRejectedReason);
+}
+
+// This API finishes the remaining work left in NotifyBlockingDecision.
+void NotifyAllowDecision(nsIChannel* aReportingChannel,
+                         nsIChannel* aTrackingChannel, nsIURI* aURI,
+                         nsPIDOMWindowOuter* aWindow) {
+  nsAutoCString trackingOrigin;
+  if (aURI) {
+    Unused << nsContentUtils::GetASCIIOrigin(aURI, trackingOrigin);
+  }
+
+  // This can be called in either the parent process or the child processes.
+
+  // Now send the generic "cookies loaded" notifications, from the most generic
+  // to the most specific.
+  ContentBlockingNotifier::OnEvent(
+      aWindow, aReportingChannel, aTrackingChannel, false,
+      nsIWebProgressListener::STATE_COOKIES_LOADED, trackingOrigin);
+
+  nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
+      do_QueryInterface(aTrackingChannel);
+  if (!classifiedChannel) {
+    return;
+  }
+
+  uint32_t classificationFlags =
+      classifiedChannel->GetThirdPartyClassificationFlags();
+  if (classificationFlags &
+      nsIClassifiedChannel::ClassificationFlags::CLASSIFIED_TRACKING) {
+    ContentBlockingNotifier::OnEvent(
+        aWindow, aReportingChannel, aTrackingChannel, false,
+        nsIWebProgressListener::STATE_COOKIES_LOADED_TRACKER, trackingOrigin);
+  }
+
+  if (classificationFlags &
+      nsIClassifiedChannel::ClassificationFlags::CLASSIFIED_SOCIALTRACKING) {
+    ContentBlockingNotifier::OnEvent(
+        aWindow, aReportingChannel, aTrackingChannel, false,
+        nsIWebProgressListener::STATE_COOKIES_LOADED_SOCIALTRACKER,
+        trackingOrigin);
+  }
+}
+
+void NotifyBlockingDecision(nsIChannel* aReportingChannel,
+                            nsIChannel* aTrackingChannel,
+                            ContentBlockingNotifier::BlockingDecision aDecision,
+                            uint32_t aRejectedReason, nsIURI* aURI,
+                            nsPIDOMWindowOuter* aWindow) {
+  MOZ_ASSERT(aWindow);
+
+  // When this is called with system priviledged, the decision should always be
+  // ALLOW, and we can also stop processing this event.
+  if (nsGlobalWindowOuter::Cast(aWindow)->GetPrincipal() ==
+      nsContentUtils::GetSystemPrincipal()) {
+    MOZ_DIAGNOSTIC_ASSERT(aDecision ==
+                          ContentBlockingNotifier::BlockingDecision::eAllow);
+    return;
+  }
+
+  nsAutoCString trackingOrigin;
+  if (aURI) {
+    Unused << nsContentUtils::GetASCIIOrigin(aURI, trackingOrigin);
+  }
+
+  if (aDecision == ContentBlockingNotifier::BlockingDecision::eBlock) {
+    ContentBlockingNotifier::OnEvent(aWindow, aReportingChannel,
+                                     aTrackingChannel, true, aRejectedReason,
+                                     trackingOrigin);
+
+    ReportBlockingToConsole(aReportingChannel, aURI, aRejectedReason);
+  }
+
+  NotifyAllowDecision(aReportingChannel, aTrackingChannel, aURI, aWindow);
+}
+
+void NotifyBlockingDecision(nsIChannel* aReportingChannel,
+                            nsIChannel* aTrackingChannel,
+                            ContentBlockingNotifier::BlockingDecision aDecision,
+                            uint32_t aRejectedReason, nsIURI* aURI) {
+  // Can be called only in the parent process when there is no window.
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  nsAutoCString trackingOrigin;
+  if (aURI) {
+    Unused << nsContentUtils::GetASCIIOrigin(aURI, trackingOrigin);
+  }
+
+  if (aDecision == ContentBlockingNotifier::BlockingDecision::eBlock) {
+    ContentBlockingNotifier::OnEvent(nullptr, aReportingChannel,
+                                     aTrackingChannel, true, aRejectedReason,
+                                     trackingOrigin);
+
+    ReportBlockingToConsole(aReportingChannel, aURI, aRejectedReason);
+  }
+
+  NotifyAllowDecision(aReportingChannel, aTrackingChannel, aURI, nullptr);
+}
+
+// Send a message to notify OnContentBlockingEvent in the parent, which will
+// update the ContentBlockingLog in the parent.
+void NotifyEventInChild(
+    nsPIDOMWindowOuter* aWindow, nsIChannel* aReportingChannel,
+    nsIChannel* aTrackingChannel, bool aBlocked, uint32_t aRejectedReason,
+    const nsACString& aTrackingOrigin,
+    const Maybe<ContentBlockingNotifier::StorageAccessGrantedReason>& aReason) {
+  MOZ_ASSERT(XRE_IsContentProcess());
+  MOZ_ASSERT(aWindow);
+
+  RefPtr<dom::BrowserChild> browserChild = dom::BrowserChild::GetFrom(aWindow);
+  NS_ENSURE_TRUE_VOID(browserChild);
+
+  nsTArray<nsCString> trackingFullHashes;
+  nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
+      do_QueryInterface(aTrackingChannel);
+
+  if (classifiedChannel) {
+    Unused << classifiedChannel->GetMatchedTrackingFullHashes(
+        trackingFullHashes);
+  }
+
+  browserChild->NotifyContentBlockingEvent(aRejectedReason, aReportingChannel,
+                                           aBlocked, aTrackingOrigin,
+                                           trackingFullHashes, aReason);
+}
+
+// Update the ContentBlockingLog of the top-level WindowGlobalParent of
+// the reporting channel.
+void NotifyEventInParent(
+    nsIChannel* aReportingChannel, nsIChannel* aTrackingChannel, bool aBlocked,
+    uint32_t aRejectedReason, const nsACString& aTrackingOrigin,
+    const Maybe<ContentBlockingNotifier::StorageAccessGrantedReason>& aReason) {
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  nsCOMPtr<nsILoadInfo> loadInfo = aReportingChannel->LoadInfo();
+  RefPtr<dom::BrowsingContext> bc;
+  loadInfo->GetBrowsingContext(getter_AddRefs(bc));
+
+  if (!bc || bc->IsDiscarded()) {
+    return;
+  }
+
+  bc = bc->Top();
+  RefPtr<dom::WindowGlobalParent> wgp =
+      bc->Canonical()->GetCurrentWindowGlobal();
+  NS_ENSURE_TRUE_VOID(wgp);
+
+  nsTArray<nsCString> trackingFullHashes;
+  nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
+      do_QueryInterface(aTrackingChannel);
+
+  if (classifiedChannel) {
+    Unused << classifiedChannel->GetMatchedTrackingFullHashes(
+        trackingFullHashes);
+  }
+
+  wgp->NotifyContentBlockingEvent(aRejectedReason, aReportingChannel, aBlocked,
+                                  aTrackingOrigin, trackingFullHashes, aReason);
+}
+
+}  // namespace
+
+/* static */ void ContentBlockingNotifier::ReportUnblockingToConsole(
+    nsPIDOMWindowInner* aWindow, const nsAString& aTrackingOrigin,
+    ContentBlockingNotifier::StorageAccessGrantedReason aReason) {
+  nsCOMPtr<nsIPrincipal> principal =
+      nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
+  if (NS_WARN_IF(!principal)) {
+    return;
+  }
+
+  RefPtr<Document> doc = aWindow->GetExtantDoc();
+  if (NS_WARN_IF(!doc)) {
+    return;
+  }
+
+  nsAutoString trackingOrigin(aTrackingOrigin);
+
+  nsAutoString sourceLine;
+  uint32_t lineNumber = 0, columnNumber = 0;
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  if (cx) {
+    nsJSUtils::GetCallingLocation(cx, sourceLine, &lineNumber, &columnNumber);
+  }
+
+  RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+      "ReportUnblockingToConsoleDelayed",
+      [doc, principal, trackingOrigin, sourceLine, lineNumber, columnNumber,
+       aReason]() {
+        nsAutoString origin;
+        nsresult rv = nsContentUtils::GetUTFOrigin(principal, origin);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return;
+        }
+
+        // Not adding grantedOrigin yet because we may not want it later.
+        AutoTArray<nsString, 3> params = {origin, trackingOrigin};
+        const char* messageWithSameOrigin = nullptr;
+
+        switch (aReason) {
+          case ContentBlockingNotifier::eStorageAccessAPI:
+            messageWithSameOrigin = "CookieAllowedForTrackerByStorageAccessAPI";
+            break;
+
+          case ContentBlockingNotifier::eOpenerAfterUserInteraction:
+            [[fallthrough]];
+          case ContentBlockingNotifier::eOpener:
+            messageWithSameOrigin = "CookieAllowedForTrackerByHeuristic";
+            break;
+        }
+
+        nsContentUtils::ReportToConsole(
+            nsIScriptError::warningFlag, ANTITRACKING_CONSOLE_CATEGORY, doc,
+            nsContentUtils::eNECKO_PROPERTIES, messageWithSameOrigin, params,
+            nullptr, sourceLine, lineNumber, columnNumber);
+      });
+
+  RunConsoleReportingRunnable(runnable.forget());
+}
+
+/* static */
+void ContentBlockingNotifier::OnDecision(nsIChannel* aChannel,
+                                         BlockingDecision aDecision,
+                                         uint32_t aRejectedReason) {
+  MOZ_ASSERT(
+      aRejectedReason == 0 ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN ||
+      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL ||
+      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN);
+  MOZ_ASSERT(aDecision == BlockingDecision::eBlock ||
+             aDecision == BlockingDecision::eAllow);
+
+  if (!aChannel) {
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  aChannel->GetURI(getter_AddRefs(uri));
+
+  // Can be called in EITHER the parent or child process.
+  // Window is only needed while in child processes.
+  if (XRE_IsParentProcess()) {
+    NotifyBlockingDecision(aChannel, aChannel, aDecision, aRejectedReason, uri);
+    return;
+  }
+
+  MOZ_ASSERT(XRE_IsContentProcess());
+
+  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
+  if (!thirdPartyUtil) {
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uriBeingLoaded =
+      AntiTrackingCommon::MaybeGetDocumentURIBeingLoaded(aChannel);
+  nsCOMPtr<mozIDOMWindowProxy> win;
+  nsresult rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, uriBeingLoaded,
+                                                       getter_AddRefs(win));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsCOMPtr<nsPIDOMWindowOuter> pwin = nsPIDOMWindowOuter::From(win);
+  if (!pwin) {
+    return;
+  }
+
+  NotifyBlockingDecision(aChannel, aChannel, aDecision, aRejectedReason, uri,
+                         pwin);
+}
+
+/* static */
+void ContentBlockingNotifier::OnDecision(nsPIDOMWindowInner* aWindow,
+                                         BlockingDecision aDecision,
+                                         uint32_t aRejectedReason) {
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(
+      aRejectedReason == 0 ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER ||
+      aRejectedReason ==
+          nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN ||
+      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL ||
+      aRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN);
+  MOZ_ASSERT(aDecision == BlockingDecision::eBlock ||
+             aDecision == BlockingDecision::eAllow);
+
+  nsCOMPtr<nsPIDOMWindowOuter> pwin = AntiTrackingUtils::GetTopWindow(aWindow);
+  if (!pwin) {
+    return;
+  }
+
+  nsPIDOMWindowInner* inner = pwin->GetCurrentInnerWindow();
+  if (!inner) {
+    return;
+  }
+  Document* pwinDoc = inner->GetExtantDoc();
+  if (!pwinDoc) {
+    return;
+  }
+  nsIChannel* channel = pwinDoc->GetChannel();
+  if (!channel) {
+    return;
+  }
+
+  Document* document = aWindow->GetExtantDoc();
+  if (!document) {
+    return;
+  }
+  nsIURI* uri = document->GetDocumentURI();
+  nsIChannel* trackingChannel = document->GetChannel();
+
+  NotifyBlockingDecision(channel, trackingChannel, aDecision, aRejectedReason,
+                         uri, pwin);
+}
+
+/* static */
+void ContentBlockingNotifier::OnEvent(nsIChannel* aChannel,
+                                      uint32_t aRejectedReason) {
+  MOZ_ASSERT(XRE_IsParentProcess() && aChannel);
+
+  nsCOMPtr<nsIURI> uri;
+  aChannel->GetURI(getter_AddRefs(uri));
+
+  nsAutoCString trackingOrigin;
+  if (uri) {
+    Unused << nsContentUtils::GetASCIIOrigin(uri, trackingOrigin);
+  }
+
+  return ContentBlockingNotifier::OnEvent(nullptr, aChannel, aChannel, true,
+                                          aRejectedReason, trackingOrigin);
+}
+
+/* static */
+void ContentBlockingNotifier::OnEvent(
+    nsPIDOMWindowOuter* aWindow, nsIChannel* aReportingChannel,
+    nsIChannel* aTrackingChannel, bool aBlocked, uint32_t aRejectedReason,
+    const nsACString& aTrackingOrigin,
+    const Maybe<StorageAccessGrantedReason>& aReason) {
+  if (XRE_IsParentProcess()) {
+    NotifyEventInParent(aReportingChannel, aTrackingChannel, aBlocked,
+                        aRejectedReason, aTrackingOrigin, aReason);
+  } else {
+    NotifyEventInChild(aWindow, aReportingChannel, aTrackingChannel, aBlocked,
+                       aRejectedReason, aTrackingOrigin, aReason);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/ContentBlockingNotifier.h
@@ -0,0 +1,67 @@
+/* -*- 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_contentblockingnotifier_h
+#define mozilla_contentblockingnotifier_h
+
+#include "nsStringFwd.h"
+#include "mozilla/Maybe.h"
+
+#define ANTITRACKING_CONSOLE_CATEGORY NS_LITERAL_CSTRING("Content Blocking")
+
+class nsIChannel;
+class nsPIDOMWindowInner;
+class nsPIDOMWindowOuter;
+
+namespace mozilla {
+
+class ContentBlockingNotifier final {
+ public:
+  enum class BlockingDecision {
+    eBlock,
+    eAllow,
+  };
+  enum StorageAccessGrantedReason {
+    eStorageAccessAPI,
+    eOpenerAfterUserInteraction,
+    eOpener
+  };
+
+  // This method can be called on the parent process or on the content process.
+  // The notification is propagated to the child channel if aChannel is a parent
+  // channel proxy.
+  //
+  // aDecision can be eBlock if we have decided to block some content, or eAllow
+  // if we have decided to allow the content through.
+  //
+  // aRejectedReason must be one of these values:
+  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION
+  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER
+  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER
+  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL
+  //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN
+  static void OnDecision(nsIChannel* aChannel, BlockingDecision aDecision,
+                         uint32_t aRejectedReason);
+
+  static void OnDecision(nsPIDOMWindowInner* aWindow,
+                         BlockingDecision aDecision, uint32_t aRejectedReason);
+
+  static void OnEvent(nsIChannel* aChannel, uint32_t aRejectedReason);
+
+  static void OnEvent(
+      nsPIDOMWindowOuter* aWindow, nsIChannel* aReportingChannel,
+      nsIChannel* aTrackingChannel, bool aBlocked, uint32_t aRejectedReason,
+      const nsACString& aTrackingOrigin,
+      const Maybe<StorageAccessGrantedReason>& aReason = Nothing());
+
+  static void ReportUnblockingToConsole(nsPIDOMWindowInner* aWindow,
+                                        const nsAString& aTrackingOrigin,
+                                        StorageAccessGrantedReason aReason);
+};
+
+}  // namespace mozilla
+
+#endif  // mozilla_contentblockingnotifier_h
--- a/toolkit/components/antitracking/StorageAccess.cpp
+++ b/toolkit/components/antitracking/StorageAccess.cpp
@@ -295,26 +295,26 @@ bool StorageDisabledByAntiTracking(nsPID
     Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
   }
   if (!cookieJarSettings) {
     cookieJarSettings = net::CookieJarSettings::Create();
   }
   bool disabled = StorageDisabledByAntiTrackingInternal(
       aWindow, aChannel, aPrincipal, aURI, cookieJarSettings, aRejectedReason);
   if (aWindow) {
-    AntiTrackingCommon::NotifyBlockingDecision(
+    ContentBlockingNotifier::OnDecision(
         aWindow,
-        disabled ? AntiTrackingCommon::BlockingDecision::eBlock
-                 : AntiTrackingCommon::BlockingDecision::eAllow,
+        disabled ? ContentBlockingNotifier::BlockingDecision::eBlock
+                 : ContentBlockingNotifier::BlockingDecision::eAllow,
         aRejectedReason);
   } else if (aChannel) {
-    AntiTrackingCommon::NotifyBlockingDecision(
+    ContentBlockingNotifier::OnDecision(
         aChannel,
-        disabled ? AntiTrackingCommon::BlockingDecision::eBlock
-                 : AntiTrackingCommon::BlockingDecision::eAllow,
+        disabled ? ContentBlockingNotifier::BlockingDecision::eBlock
+                 : ContentBlockingNotifier::BlockingDecision::eAllow,
         aRejectedReason);
   }
   return disabled;
 }
 
 bool ShouldPartitionStorage(StorageAccess aAccess) {
   return aAccess == StorageAccess::ePartitionTrackersOrDeny ||
          aAccess == StorageAccess::ePartitionForeignOrDeny;
--- a/toolkit/components/antitracking/moz.build
+++ b/toolkit/components/antitracking/moz.build
@@ -29,24 +29,27 @@ EXTRA_JS_MODULES += [
 XPCOM_MANIFESTS += [
     'components.conf',
 ]
 
 EXPORTS.mozilla = [
     'AntiTrackingCommon.h',
     'AntiTrackingIPCUtils.h',
     'ContentBlockingAllowList.h',
+    'ContentBlockingNotifier.h',
     'StorageAccess.h',
     'StoragePrincipalHelper.h',
     'URLDecorationStripper.h',
 ]
 
 UNIFIED_SOURCES += [
     'AntiTrackingCommon.cpp',
+    'AntiTrackingUtils.cpp',
     'ContentBlockingAllowList.cpp',
+    'ContentBlockingNotifier.cpp',
     'SettingsChangeObserver.cpp',
     'StorageAccess.cpp',
     'StoragePrincipalHelper.cpp',
     'URLDecorationStripper.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/extensions/permissions',