diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index dc9dd0514b76..25764d83b0d2 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1673,6 +1673,20 @@ nsDocument::~nsDocument() 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(); diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index a5b779b15a0b..09665a5b658b 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -45,6 +45,7 @@ #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" @@ -3696,6 +3697,13 @@ public: ++mNumTrackersBlocked; } + void NoteTrackerBlockedReason( + mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED aLabel) + { + MOZ_ASSERT(!GetSameTypeParentDocument()); + mTrackerBlockedReasons += aLabel; + } + uint32_t NumTrackersFound() { MOZ_ASSERT(!GetSameTypeParentDocument() || mNumTrackersFound == 0); @@ -4684,6 +4692,9 @@ protected: uint32_t mNumTrackersFound; uint32_t mNumTrackersBlocked; + mozilla::EnumSet + mTrackerBlockedReasons; + // document lightweight theme for use with :-moz-lwtheme, :-moz-lwtheme-brighttext // and :-moz-lwtheme-darktext DocumentTheme mDocLWTheme; diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp index 828b258ef3fd..d4be7eba8d1c 100644 --- a/ipc/glue/BackgroundUtils.cpp +++ b/ipc/glue/BackgroundUtils.cpp @@ -635,6 +635,7 @@ LoadInfoToParentLoadInfoForwarder(nsILoadInfo* aLoadInfo, false, // serviceWorkerTaintingSynthesized false, // isTracker false, // isTrackerBlocked + mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all, // trackerBlockedReason false // documentHasUserInteracted ); return; @@ -649,6 +650,10 @@ LoadInfoToParentLoadInfoForwarder(nsILoadInfo* aLoadInfo, 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, @@ -656,6 +661,7 @@ LoadInfoToParentLoadInfoForwarder(nsILoadInfo* aLoadInfo, aLoadInfo->GetServiceWorkerTaintingSynthesized(), aLoadInfo->GetIsTracker(), aLoadInfo->GetIsTrackerBlocked(), + label, aLoadInfo->GetDocumentHasUserInteracted() ); } @@ -690,6 +696,7 @@ MergeParentLoadInfoForwarder(ParentLoadInfoForwarderArgs const& aForwarderArgs, 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; diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp index 6626453e495d..ce0e98a57e7e 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -84,6 +84,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, , mIsThirdPartyContext(false) , mIsDocshellReload(false) , mSendCSPViolationEvents(true) + , mTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all) , mForcePreflight(false) , mIsPreflight(false) , mLoadTriggeredFromExternal(false) @@ -323,6 +324,7 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow, , 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) @@ -422,6 +424,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) , mAncestorPrincipals(rhs.mAncestorPrincipals) , mAncestorOuterWindowIDs(rhs.mAncestorOuterWindowIDs) , mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders) + , mTrackerBlockedReason(rhs.mTrackerBlockedReason) , mForcePreflight(rhs.mForcePreflight) , mIsPreflight(rhs.mIsPreflight) , mLoadTriggeredFromExternal(rhs.mLoadTriggeredFromExternal) @@ -514,6 +517,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, , mAncestorPrincipals(std::move(aAncestorPrincipals)) , mAncestorOuterWindowIDs(aAncestorOuterWindowIDs) , mCorsUnsafeHeaders(aCorsUnsafeHeaders) + , mTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all) , mForcePreflight(aForcePreflight) , mIsPreflight(aIsPreflight) , mLoadTriggeredFromExternal(aLoadTriggeredFromExternal) @@ -1373,6 +1377,21 @@ LoadInfo::SetIsTrackerBlocked(bool 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) { diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h index 80c028e644d0..cee8761c4b98 100644 --- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h @@ -205,6 +205,9 @@ private: nsTArray> mAncestorPrincipals; nsTArray mAncestorOuterWindowIDs; nsTArray mCorsUnsafeHeaders; + + mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED mTrackerBlockedReason; + bool mForcePreflight; bool mIsPreflight; bool mLoadTriggeredFromExternal; diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl index 02775b40f453..714583285da0 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -19,6 +19,7 @@ native LoadContextRef(already_AddRefed); #include "mozilla/BasePrincipal.h" #include "mozilla/LoadTainting.h" #include "mozilla/UniquePtr.h" +#include "mozilla/TelemetryHistogramEnums.h" #include "nsStringFwd.h" namespace mozilla { @@ -31,6 +32,7 @@ class ServiceWorkerDescriptor; } // namespace mozilla %} +native AnalyticsProvider(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED); [ref] native nsIRedirectHistoryEntryArray(const nsTArray>); native OriginAttributes(mozilla::OriginAttributes); [ref] native const_OriginAttributesRef(const mozilla::OriginAttributes); @@ -1058,6 +1060,7 @@ interface nsILoadInfo : nsISupports */ [infallible] attribute boolean isTracker; [infallible] attribute boolean isTrackerBlocked; + attribute AnalyticsProvider trackerBlockedReason; /** * The top-level document has been user-interacted. diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh index 582f7c4f7d2e..4cc47c627fbd 100644 --- a/netwerk/ipc/NeckoChannelParams.ipdlh +++ b/netwerk/ipc/NeckoChannelParams.ipdlh @@ -21,6 +21,7 @@ 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 { @@ -147,6 +148,7 @@ struct ParentLoadInfoForwarderArgs // 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 diff --git a/netwerk/protocol/http/DocumentAnalyticsTrackerFastBlocked.h b/netwerk/protocol/http/DocumentAnalyticsTrackerFastBlocked.h new file mode 100644 index 000000000000..dab8c256ec9b --- /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 : + public ContiguousEnumSerializer + {}; + +} + +#endif diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 17111f577b88..9a7f5d643dbb 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -759,6 +759,11 @@ HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext) 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); } } } diff --git a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build index 20dffb5967ea..9905dab132bb 100644 --- a/netwerk/protocol/http/moz.build +++ b/netwerk/protocol/http/moz.build @@ -27,6 +27,7 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'necko_http' EXPORTS += [ + 'DocumentAnalyticsTrackerFastBlocked.h', 'nsCORSListenerProxy.h', 'nsHttp.h', 'nsHttpAtomList.h', diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 2c0598b0a8d3..0cdc31cb22d6 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -162,6 +162,24 @@ enum CacheDisposition { 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) { @@ -708,6 +726,13 @@ nsHttpChannel::CheckFastBlocked() 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(×tamp)) || !timestamp) { LOG(("FastBlock passed (no timestamp) [this=%p]\n", this)); @@ -715,35 +740,80 @@ nsHttpChannel::CheckFastBlocked() 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; + } 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(hasFastBlockStarted), + static_cast(hasFastBlockStopped), + duration.ToMilliseconds(), + this)); + engageFastBlock = hasFastBlockStarted && !hasFastBlockStopped; } - 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; + // 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 uri; + nsresult rv = GetURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, false); - if (isFastBlocking && mLoadInfo) { - MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTrackerBlocked(true)); + nsAutoCString host; + rv = uri->GetHost(host); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr 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)); + } } - LOG(("FastBlock started=%d stopped=%d (%lf) [this=%p]\n", - static_cast(hasFastBlockStarted), - static_cast(hasFastBlockStopped), - duration.ToMilliseconds(), - this)); - return isFastBlocking; + return engageFastBlock; } nsresult diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index a33e92c67479..805444c8c505 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -503,6 +503,15 @@ "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",