Bug 1489252 - Part 2: Add a telemetry probe for measuring the rate at which popular analytics providers get blocked by fastblock for top-level documents; r=baku,mayhemer,chutten data-r=liuche
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 07 Sep 2018 14:43:11 -0400
changeset 492529 b48481b32497dd8ddd91981a9f998ba3e6a23bb2
parent 492528 d8201405128b5c4bd5ad8ecfe95435d81058915f
child 492530 682340882841e33cd1af699341d0247086d010a2
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, mayhemer, chutten
bugs1489252
milestone63.0
Bug 1489252 - Part 2: Add a telemetry probe for measuring the rate at which popular analytics providers get blocked by fastblock for top-level documents; r=baku,mayhemer,chutten data-r=liuche Differential Revision: https://phabricator.services.mozilla.com/D5296
dom/base/nsDocument.cpp
dom/base/nsIDocument.h
ipc/glue/BackgroundUtils.cpp
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/http/DocumentAnalyticsTrackerFastBlocked.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/moz.build
netwerk/protocol/http/nsHttpChannel.cpp
toolkit/components/telemetry/Histograms.json
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1666,16 +1666,30 @@ nsDocument::~nsDocument()
       ScalarAdd(Telemetry::ScalarID::MEDIA_PAGE_COUNT, 1);
       if (mDocTreeHadAudibleMedia) {
         ScalarAdd(Telemetry::ScalarID::MEDIA_PAGE_HAD_MEDIA_COUNT, 1);
       }
       if (mDocTreeHadPlayRevoked) {
         ScalarAdd(Telemetry::ScalarID::MEDIA_PAGE_HAD_PLAY_REVOKED_COUNT, 1);
       }
     }
+
+    // Report the fastblock telemetry probes when the document is dying if
+    // fastblock is enabled and we're not a private document.  We always report
+    // the all probe, and for the rest, report each category's probe depending
+    // on whether the respective bit has been set in our enum set.
+    if (StaticPrefs::browser_contentblocking_enabled() &&
+        StaticPrefs::browser_fastblock_enabled() &&
+        !nsContentUtils::IsInPrivateBrowsing(this)) {
+      for (auto label : mTrackerBlockedReasons) {
+        AccumulateCategorical(label);
+      }
+      // Always accumulate the "all" probe since we will use it as a baseline counter.
+      AccumulateCategorical(Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all);
+    }
   }
 
   ReportUseCounters();
 
   mInDestructor = true;
   mInUnlinkOrDeletion = true;
 
   mozilla::DropJSObjects(this);
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -40,16 +40,17 @@
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsContentListDeclarations.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/dom/DispatcherTrait.h"
 #include "mozilla/dom/DocumentOrShadowRoot.h"
+#include "mozilla/EnumSet.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/SegmentedVector.h"
 #include "mozilla/ServoBindingTypes.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include <bitset>                        // for member
@@ -3626,16 +3627,23 @@ public:
   }
 
   void IncrementTrackerBlockedCount()
   {
     MOZ_ASSERT(!GetSameTypeParentDocument());
     ++mNumTrackersBlocked;
   }
 
+  void NoteTrackerBlockedReason(
+    mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED aLabel)
+  {
+    MOZ_ASSERT(!GetSameTypeParentDocument());
+    mTrackerBlockedReasons += aLabel;
+  }
+
   uint32_t NumTrackersFound()
   {
     MOZ_ASSERT(!GetSameTypeParentDocument() || mNumTrackersFound == 0);
 
     return mNumTrackersFound;
   }
 
   uint32_t NumTrackersBlocked()
@@ -4614,16 +4622,19 @@ protected:
   nsCOMPtr<nsIDOMXULCommandDispatcher> mCommandDispatcher; // [OWNER] of the focus tracker
 
   // At the moment, trackers might be blocked by Tracking Protection or FastBlock.
   // In order to know the numbers of trackers detected and blocked, we add
   // these two values here and those are shared by TP and FB.
   uint32_t mNumTrackersFound;
   uint32_t mNumTrackersBlocked;
 
+  mozilla::EnumSet<mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED>
+    mTrackerBlockedReasons;
+
   // document lightweight theme for use with :-moz-lwtheme, :-moz-lwtheme-brighttext
   // and :-moz-lwtheme-darktext
   DocumentTheme                         mDocLWTheme;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -630,37 +630,43 @@ LoadInfoToParentLoadInfoForwarder(nsILoa
                                   ParentLoadInfoForwarderArgs* aForwarderArgsOut)
 {
   if (!aLoadInfo) {
     *aForwarderArgsOut = ParentLoadInfoForwarderArgs(false, void_t(),
                                                      nsILoadInfo::TAINTING_BASIC,
                                                      false, // serviceWorkerTaintingSynthesized
                                                      false, // isTracker
                                                      false, // isTrackerBlocked
+                                                     mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all, // trackerBlockedReason
                                                      false  // documentHasUserInteracted
                                                     );
     return;
   }
 
   OptionalIPCServiceWorkerDescriptor ipcController = void_t();
   Maybe<ServiceWorkerDescriptor> controller(aLoadInfo->GetController());
   if (controller.isSome()) {
     ipcController = controller.ref().ToIPC();
   }
 
   uint32_t tainting = nsILoadInfo::TAINTING_BASIC;
   Unused << aLoadInfo->GetTainting(&tainting);
 
+  mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED label =
+    mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all;
+  Unused << aLoadInfo->GetTrackerBlockedReason(&label);
+
   *aForwarderArgsOut = ParentLoadInfoForwarderArgs(
     aLoadInfo->GetAllowInsecureRedirectToDataURI(),
     ipcController,
     tainting,
     aLoadInfo->GetServiceWorkerTaintingSynthesized(),
     aLoadInfo->GetIsTracker(),
     aLoadInfo->GetIsTrackerBlocked(),
+    label,
     aLoadInfo->GetDocumentHasUserInteracted()
   );
 }
 
 nsresult
 MergeParentLoadInfoForwarder(ParentLoadInfoForwarderArgs const& aForwarderArgs,
                              nsILoadInfo* aLoadInfo)
 {
@@ -685,16 +691,17 @@ MergeParentLoadInfoForwarder(ParentLoadI
     aLoadInfo->SynthesizeServiceWorkerTainting(
       static_cast<LoadTainting>(aForwarderArgs.tainting()));
   } else {
     aLoadInfo->MaybeIncreaseTainting(aForwarderArgs.tainting());
   }
 
   MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetIsTracker(aForwarderArgs.isTracker()));
   MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetIsTrackerBlocked(aForwarderArgs.isTrackerBlocked()));
+  MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetTrackerBlockedReason(aForwarderArgs.trackerBlockedReason()));
   MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetDocumentHasUserInteracted(aForwarderArgs.documentHasUserInteracted()));
 
   return NS_OK;
 }
 
 void
 LoadInfoToChildLoadInfoForwarder(nsILoadInfo* aLoadInfo,
                                  ChildLoadInfoForwarderArgs* aForwarderArgsOut)
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -78,16 +78,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mParentOuterWindowID(0)
   , mTopOuterWindowID(0)
   , mFrameOuterWindowID(0)
   , mEnforceSecurity(false)
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(false)
   , mIsDocshellReload(false)
   , mSendCSPViolationEvents(true)
+  , mTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all)
   , mForcePreflight(false)
   , mIsPreflight(false)
   , mLoadTriggeredFromExternal(false)
   , mServiceWorkerTaintingSynthesized(false)
   , mIsTracker(false)
   , mIsTrackerBlocked(false)
   , mDocumentHasUserInteracted(false)
 {
@@ -317,16 +318,17 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
   , mParentOuterWindowID(0)
   , mTopOuterWindowID(0)
   , mFrameOuterWindowID(0)
   , mEnforceSecurity(false)
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(false) // NB: TYPE_DOCUMENT implies not third-party.
   , mIsDocshellReload(false)
   , mSendCSPViolationEvents(true)
+  , mTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all)
   , mForcePreflight(false)
   , mIsPreflight(false)
   , mLoadTriggeredFromExternal(false)
   , mServiceWorkerTaintingSynthesized(false)
   , mIsTracker(false)
   , mIsTrackerBlocked(false)
   , mDocumentHasUserInteracted(false)
 {
@@ -416,16 +418,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mSendCSPViolationEvents(rhs.mSendCSPViolationEvents)
   , mOriginAttributes(rhs.mOriginAttributes)
   , mRedirectChainIncludingInternalRedirects(
       rhs.mRedirectChainIncludingInternalRedirects)
   , mRedirectChain(rhs.mRedirectChain)
   , mAncestorPrincipals(rhs.mAncestorPrincipals)
   , mAncestorOuterWindowIDs(rhs.mAncestorOuterWindowIDs)
   , mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders)
+  , mTrackerBlockedReason(rhs.mTrackerBlockedReason)
   , mForcePreflight(rhs.mForcePreflight)
   , mIsPreflight(rhs.mIsPreflight)
   , mLoadTriggeredFromExternal(rhs.mLoadTriggeredFromExternal)
   // mServiceWorkerTaintingSynthesized must be handled specially during redirect
   , mServiceWorkerTaintingSynthesized(false)
   , mIsTracker(rhs.mIsTracker)
   , mIsTrackerBlocked(rhs.mIsTrackerBlocked)
   , mDocumentHasUserInteracted(rhs.mDocumentHasUserInteracted)
@@ -508,16 +511,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mInitialSecurityCheckDone(aInitialSecurityCheckDone)
   , mIsThirdPartyContext(aIsThirdPartyContext)
   , mIsDocshellReload(aIsDocshellReload)
   , mSendCSPViolationEvents(aSendCSPViolationEvents)
   , mOriginAttributes(aOriginAttributes)
   , mAncestorPrincipals(std::move(aAncestorPrincipals))
   , mAncestorOuterWindowIDs(aAncestorOuterWindowIDs)
   , mCorsUnsafeHeaders(aCorsUnsafeHeaders)
+  , mTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all)
   , mForcePreflight(aForcePreflight)
   , mIsPreflight(aIsPreflight)
   , mLoadTriggeredFromExternal(aLoadTriggeredFromExternal)
   , mServiceWorkerTaintingSynthesized(aServiceWorkerTaintingSynthesized)
   , mIsTracker(false)
   , mIsTrackerBlocked(false)
   , mDocumentHasUserInteracted(aDocumentHasUserInteracted)
 {
@@ -1368,16 +1372,31 @@ LoadInfo::GetIsTrackerBlocked(bool *aIsT
 NS_IMETHODIMP
 LoadInfo::SetIsTrackerBlocked(bool aIsTrackerBlocked)
 {
   mIsTrackerBlocked = aIsTrackerBlocked;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::GetTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED *aLabel)
+{
+  MOZ_ASSERT(aLabel);
+  *aLabel = mTrackerBlockedReason;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::SetTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED aLabel)
+{
+  mTrackerBlockedReason = aLabel;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 LoadInfo::GetDocumentHasUserInteracted(bool *aDocumentHasUserInteracted)
 {
   MOZ_ASSERT(aDocumentHasUserInteracted);
   *aDocumentHasUserInteracted = mDocumentHasUserInteracted;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -200,16 +200,19 @@ private:
   bool                             mIsDocshellReload;
   bool                             mSendCSPViolationEvents;
   OriginAttributes                 mOriginAttributes;
   RedirectHistoryArray             mRedirectChainIncludingInternalRedirects;
   RedirectHistoryArray             mRedirectChain;
   nsTArray<nsCOMPtr<nsIPrincipal>> mAncestorPrincipals;
   nsTArray<uint64_t>               mAncestorOuterWindowIDs;
   nsTArray<nsCString>              mCorsUnsafeHeaders;
+
+  mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED mTrackerBlockedReason;
+
   bool                             mForcePreflight;
   bool                             mIsPreflight;
   bool                             mLoadTriggeredFromExternal;
   bool                             mServiceWorkerTaintingSynthesized;
 
   bool                             mIsTracker;
   bool                             mIsTrackerBlocked;
   bool                             mDocumentHasUserInteracted;
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -14,28 +14,30 @@ interface nsIRedirectHistoryEntry;
 interface nsIURI;
 webidl Document;
 native LoadContextRef(already_AddRefed<nsISupports>);
 %{C++
 #include "nsTArray.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/LoadTainting.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/TelemetryHistogramEnums.h"
 #include "nsStringFwd.h"
 
 namespace mozilla {
 namespace dom {
 class ClientInfo;
 class ClientSource;
 class PerformanceStorage;
 class ServiceWorkerDescriptor;
 } // namespace dom
 } // namespace mozilla
 %}
 
+native AnalyticsProvider(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED);
 [ref] native nsIRedirectHistoryEntryArray(const nsTArray<nsCOMPtr<nsIRedirectHistoryEntry>>);
 native OriginAttributes(mozilla::OriginAttributes);
 [ref] native const_OriginAttributesRef(const mozilla::OriginAttributes);
 [ref] native CStringArrayRef(const nsTArray<nsCString>);
 [ref] native StringArrayRef(const nsTArray<nsString>);
 [ref] native Uint64ArrayRef(const nsTArray<uint64_t>);
 [ref] native PrincipalArrayRef(const nsTArray<nsCOMPtr<nsIPrincipal>>);
 [ref] native const_ClientInfoRef(const mozilla::dom::ClientInfo);
@@ -1053,14 +1055,15 @@ interface nsILoadInfo : nsISupports
   void SynthesizeServiceWorkerTainting(in LoadTainting aTainting);
 
   /**
     * These flags are used for FastBlock statistics to see if a resource is a
     * tracker and whether it was blocked by the FastBlock mechanism or not.
     */
   [infallible] attribute boolean isTracker;
   [infallible] attribute boolean isTrackerBlocked;
+  attribute AnalyticsProvider trackerBlockedReason;
 
   /**
     * The top-level document has been user-interacted.
     */
   [infallible] attribute boolean documentHasUserInteracted;
 };
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -16,16 +16,17 @@ include IPCStream;
 include PBackgroundSharedTypes;
 
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
 using struct nsHttpAtom from "nsHttp.h";
 using class mozilla::net::nsHttpResponseHead from "nsHttpResponseHead.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED from "DocumentAnalyticsTrackerFastBlocked.h";
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
 // LoadInfo IPDL structs
 //-----------------------------------------------------------------------------
 
@@ -142,16 +143,17 @@ struct ParentLoadInfoForwarderArgs
 
   // We must also note that the tainting value was explicitly set
   // by the service worker.
   bool serviceWorkerTaintingSynthesized;
 
   // Tracker information, currently used by FastBlock
   bool isTracker;
   bool isTrackerBlocked;
+  LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED trackerBlockedReason;
   bool documentHasUserInteracted;
 
   // IMPORTANT: when you add new properites here you must also update
   // LoadInfoToParentLoadInfoForwarder and MergeParentLoadInfoForwarder
   // in BackgroundUtils.cpp/.h!
 };
 
 /**
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/DocumentAnalyticsTrackerFastBlocked.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et cin: */
+/* 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 DocumentAnalyticsTrackerFastBlocked_h__
+#define DocumentAnalyticsTrackerFastBlocked_h__
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/TelemetryHistogramEnums.h"
+
+namespace IPC {
+
+  template <>
+  struct ParamTraits<mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED> :
+    public ContiguousEnumSerializer<mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED,
+                                    mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::other,
+                                    (mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED)
+                                      (uint32_t(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all) + 1)>
+  {};
+
+}
+
+#endif
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -754,16 +754,21 @@ HttpChannelChild::DoOnStartRequest(nsIRe
          this));
 
     nsCOMPtr<nsIDocument> doc;
     if (!NS_WARN_IF(NS_FAILED(GetTopDocument(this,
                                              getter_AddRefs(doc))))) {
       doc->IncrementTrackerCount();
       if (isTrackerBlocked) {
         doc->IncrementTrackerBlockedCount();
+
+        Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED label =
+          Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::other;
+        MOZ_ALWAYS_SUCCEEDS(mLoadInfo->GetTrackerBlockedReason(&label));
+        doc->NoteTrackerBlockedReason(label);
       }
     }
   }
 
   nsresult rv = mListener->OnStartRequest(aRequest, aContext);
   if (NS_FAILED(rv)) {
     Cancel(rv);
     return;
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -21,16 +21,17 @@ XPIDL_SOURCES += [
     'nsIHttpProtocolHandler.idl',
     'nsIRaceCacheWithNetwork.idl',
     'nsIWellKnownOpportunisticUtils.idl',
 ]
 
 XPIDL_MODULE = 'necko_http'
 
 EXPORTS += [
+    'DocumentAnalyticsTrackerFastBlocked.h',
     'nsCORSListenerProxy.h',
     'nsHttp.h',
     'nsHttpAtomList.h',
     'nsHttpHeaderArray.h',
     'nsHttpRequestHead.h',
     'nsHttpResponseHead.h',
 ]
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -155,16 +155,34 @@ static NS_DEFINE_CID(kStreamListenerTeeC
 
 enum CacheDisposition {
     kCacheHit = 1,
     kCacheHitViaReval = 2,
     kCacheMissedViaReval = 3,
     kCacheMissed = 4
 };
 
+using mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED;
+
+static const struct {
+  LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED mTelemetryLabel;
+  const char* mHostName;
+} gFastBlockAnalyticsProviders[] = {
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::googleanalytics, "google-analytics.com" },
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::scorecardresearch, "scorecardresearch.com" },
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::hotjar, "hotjar.com" },
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::newrelic, "newrelic.com" },
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::nrdata, "nr-data.net" },
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::crwdcntrl, "crwdcntrl.net" },
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::eyeota, "eyeota.net" },
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::yahooanalytics, "analytics.yahoo.com" },
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::statcounter, "statcounter.com" },
+  { LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::v12group, "v12group.com" }
+};
+
 void
 AccumulateCacheHitTelemetry(CacheDisposition hitOrMiss)
 {
     Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2_V2, hitOrMiss);
 }
 
 // Computes and returns a SHA1 hash of the input buffer. The input buffer
 // must be a null-terminated string.
@@ -701,52 +719,105 @@ nsHttpChannel::CheckFastBlocked()
     static uint32_t sFastBlockLimit = 0;
 
     if (!sFastBlockInited) {
         sFastBlockInited = true;
         Preferences::AddUintVarCache(&sFastBlockTimeout, "browser.fastblock.timeout");
         Preferences::AddUintVarCache(&sFastBlockLimit, "browser.fastblock.limit");
     }
 
+    if (!StaticPrefs::browser_contentblocking_enabled() ||
+        !StaticPrefs::browser_fastblock_enabled()) {
+        LOG(("FastBlock disabled by pref [this=%p]\n", this));
+
+        return false;
+    }
+
     TimeStamp timestamp;
     if (NS_FAILED(GetNavigationStartTimeStamp(&timestamp)) || !timestamp) {
         LOG(("FastBlock passed (no timestamp) [this=%p]\n", this));
 
         return false;
     }
 
-    if (!StaticPrefs::browser_contentblocking_enabled() ||
-        !StaticPrefs::browser_fastblock_enabled() ||
-        IsContentPolicyTypeWhitelistedForFastBlock(mLoadInfo) ||
+    bool engageFastBlock = false;
+
+    if (IsContentPolicyTypeWhitelistedForFastBlock(mLoadInfo) ||
         // If the user has interacted with the document, we disable fastblock.
         (mLoadInfo && mLoadInfo->GetDocumentHasUserInteracted())) {
 
         LOG(("FastBlock passed (invalid) [this=%p]\n", this));
-        
-        return false;
-    }
-
-    TimeDuration duration = TimeStamp::NowLoRes() - timestamp;
-    bool hasFastBlockStarted = duration.ToMilliseconds() >= sFastBlockTimeout;
-    bool hasFastBlockStopped = false;
-    if ((sFastBlockLimit != 0) && (sFastBlockLimit > sFastBlockTimeout)) {
-        hasFastBlockStopped = duration.ToMilliseconds() > sFastBlockLimit;
-    }
-    const bool isFastBlocking = hasFastBlockStarted && !hasFastBlockStopped;
-
-    if (isFastBlocking && mLoadInfo) {
-        MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTrackerBlocked(true));
-    }
-
-    LOG(("FastBlock started=%d stopped=%d (%lf) [this=%p]\n",
-         static_cast<int>(hasFastBlockStarted),
-         static_cast<int>(hasFastBlockStopped),
-         duration.ToMilliseconds(),
-         this));
-    return isFastBlocking;
+    } else {
+        TimeDuration duration = TimeStamp::NowLoRes() - timestamp;
+        bool hasFastBlockStarted = duration.ToMilliseconds() >= sFastBlockTimeout;
+        bool hasFastBlockStopped = false;
+        if ((sFastBlockLimit != 0) && (sFastBlockLimit > sFastBlockTimeout)) {
+            hasFastBlockStopped = duration.ToMilliseconds() > sFastBlockLimit;
+        }
+        LOG(("FastBlock started=%d stopped=%d (%lf) [this=%p]\n",
+             static_cast<int>(hasFastBlockStarted),
+             static_cast<int>(hasFastBlockStopped),
+             duration.ToMilliseconds(),
+             this));
+        engageFastBlock = hasFastBlockStarted && !hasFastBlockStopped;
+    }
+
+    // Remember the data needed for fastblock telemetry in case fastblock is
+    // enabled, we have decided to block the channel, and the channel isn't
+    // marked as private.
+    if (engageFastBlock && !NS_UsePrivateBrowsing(this)) {
+        nsCOMPtr<nsIURI> uri;
+        nsresult rv = GetURI(getter_AddRefs(uri));
+        NS_ENSURE_SUCCESS(rv, false);
+
+        nsAutoCString host;
+        rv = uri->GetHost(host);
+        NS_ENSURE_SUCCESS(rv, false);
+
+        nsCOMPtr<nsIEffectiveTLDService> tldService =
+            do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+        NS_ENSURE_TRUE(tldService, false);
+
+        LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED label =
+            LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::other;
+        for (const auto& entry : gFastBlockAnalyticsProviders) {
+          // For each entry in the list of our analytics providers, use the
+          // effective TLD service to look up subdomains to make sure we find a
+          // potential match if one is available.
+          while (true) {
+            if (host == entry.mHostName) {
+              label = entry.mTelemetryLabel;
+              break;
+            }
+
+            nsAutoCString newHost;
+            rv = tldService->GetNextSubDomain(host, newHost);
+            if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+              // we're done searching this entry.
+              break;
+            }
+            NS_ENSURE_SUCCESS(rv, false);
+
+            host = newHost;
+          }
+
+          if (label != LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::other) {
+            // We have found a label in the previous loop, bail out now!
+            break;
+          }
+        }
+
+        if (mLoadInfo) {
+          MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTrackerBlocked(true));
+          MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetTrackerBlockedReason(label));
+        }
+    }
+
+    return engageFastBlock;
+>>>>>>> graft
 }
 
 nsresult
 nsHttpChannel::ConnectOnTailUnblock()
 {
     nsresult rv;
 
     LOG(("nsHttpChannel::ConnectOnTailUnblock [this=%p]\n", this));
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -498,16 +498,25 @@
     "record_in_processes": ["main", "content", "gpu"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "rhunt@mozilla.com"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 10,
     "bug_numbers": [1135408],
     "description": "GPU Device Reset Reason (ok, hung, removed, reset, internal error, invalid call, out of memory)"
   },
+  "DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED": {
+    "record_in_processes": ["content"],
+    "expires_in_version": "67",
+    "alert_emails": ["seceng-telemetry@mozilla.com", "fxprivacyandsecurity@mozilla.com"],
+    "kind": "categorical",
+    "labels": ["other", "googleanalytics", "scorecardresearch", "hotjar", "newrelic", "nrdata", "crwdcntrl", "eyeota", "yahooanalytics", "statcounter", "v12group", "all"],
+    "bug_numbers": [1489252],
+    "description": "Number of top-level documents which have resources blocked by fastblock, categorized by analytics provider."
+  },
   "FETCH_IS_MAINTHREAD": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "50",
     "kind": "boolean",
     "description": "Was Fetch request initiated from the main thread?"
   },
   "FORCED_DEVICE_RESET_REASON": {
     "record_in_processes": ["main", "content", "gpu"],