1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "ClearDataCallback.h"
6 #include "mozilla/glean/AntitrackingBouncetrackingprotectionMetrics.h"
7 #include "nsIBounceTrackingProtection.h"
8 #include "nsIURIClassifier.h"
9 #include "mozilla/net/UrlClassifierFeatureFactory.h"
11 #include "nsNetUtil.h"
12 #include "nsServiceManagerUtils.h"
13 #include "mozilla/ClearOnShutdown.h"
15 using namespace mozilla
;
17 // Used in automation. Dispatched when a site host has been purged, classified
18 // and telemetry has been collected for the given host.
19 #define TEST_OBSERVER_MSG_RECORDED_PURGE_TELEMETRY \
20 "bounce-tracking-protection-recorded-purge-telemetry"
22 // List of features classifying bounce trackers that have been purged.
23 static constexpr nsLiteralCString kUrlClassifierFeatures
[] = {
24 "emailtracking-protection"_ns
,
25 "fingerprinting-protection"_ns
,
26 "socialtracking-protection"_ns
,
27 "tracking-protection"_ns
,
29 static_assert(std::size(kUrlClassifierFeatures
) > 0,
30 "At least one URL classifier feature must be defined");
32 // List of features for classifying bounce trackers that have been purged.
33 // See kUrlClassifierFeatures for the list of features.
34 static StaticAutoPtr
<nsTArray
<RefPtr
<nsIUrlClassifierFeature
>>>
35 sUrlClassifierFeatures
;
37 NS_IMPL_ISUPPORTS(ClearDataCallback
, nsIClearDataCallback
,
38 nsIUrlClassifierFeatureCallback
);
40 ClearDataCallback::ClearDataCallback(ClearDataMozPromise::Private
* aPromise
,
41 const OriginAttributes
& aOriginAttributes
,
42 const nsACString
& aHost
,
44 : mPromise(aPromise
), mClearDurationTimer(0) {
45 MOZ_ASSERT(!aHost
.IsEmpty(), "Host must not be empty");
48 new BounceTrackingPurgeEntry(aOriginAttributes
, aHost
, aBounceTime
, 0);
50 if (StaticPrefs::privacy_bounceTrackingProtection_mode() ==
51 nsIBounceTrackingProtection::MODE_ENABLED
) {
52 // Only collect timing information when actually performing the deletion
54 glean::bounce_tracking_protection::purge_duration
.Start();
55 MOZ_ASSERT(mClearDurationTimer
);
58 // Populate feature list for URL classification as needed.
59 if (!sUrlClassifierFeatures
) {
60 sUrlClassifierFeatures
= new nsTArray
<RefPtr
<nsIUrlClassifierFeature
>>();
62 // Construct the list of classifier features used for purging telemetry.
63 for (const nsCString
& featureName
: kUrlClassifierFeatures
) {
64 nsCOMPtr
<nsIUrlClassifierFeature
> feature
=
65 net::UrlClassifierFeatureFactory::GetFeatureByName(featureName
);
66 if (NS_WARN_IF(!feature
)) {
69 sUrlClassifierFeatures
->AppendElement(feature
);
71 MOZ_ASSERT(!sUrlClassifierFeatures
->IsEmpty(),
72 "At least one URL classifier feature must be present");
74 sUrlClassifierFeatures
->Clear();
75 sUrlClassifierFeatures
= nullptr;
80 ClearDataCallback::~ClearDataCallback() {
81 mPromise
->Reject(0, __func__
);
82 if (mClearDurationTimer
) {
83 glean::bounce_tracking_protection::purge_duration
.Cancel(
84 std::move(mClearDurationTimer
));
88 // nsIClearDataCallback implementation
89 NS_IMETHODIMP
ClearDataCallback::OnDataDeleted(uint32_t aFailedFlags
) {
91 mPromise
->Reject(aFailedFlags
, __func__
);
93 MOZ_LOG(gBounceTrackingProtectionLog
, LogLevel::Debug
,
94 ("%s: Cleared host: %s, bounceTime: %" PRIu64
, __FUNCTION__
,
95 PromiseFlatCString(mEntry
->SiteHostRef()).get(),
96 mEntry
->TimeStampRef()));
98 mEntry
->PurgeTimeRef() = PR_Now();
99 mPromise
->Resolve(mEntry
, __func__
);
101 // Only record classifications on successful deletion.
102 RecordURLClassifierTelemetry();
104 // Always collect clear duration and purge count.
105 RecordClearDurationTelemetry();
106 RecordPurgeCountTelemetry(aFailedFlags
!= 0);
107 RecordPurgeEventTelemetry(aFailedFlags
== 0);
112 void ClearDataCallback::RecordClearDurationTelemetry() {
113 if (mClearDurationTimer
) {
114 glean::bounce_tracking_protection::purge_duration
.StopAndAccumulate(
115 std::move(mClearDurationTimer
));
116 mClearDurationTimer
= 0;
120 void ClearDataCallback::RecordPurgeCountTelemetry(bool aFailed
) {
121 if (StaticPrefs::privacy_bounceTrackingProtection_mode() ==
122 nsIBounceTrackingProtection::MODE_ENABLED_DRY_RUN
) {
123 MOZ_ASSERT(aFailed
== 0, "Dry-run purge can't fail");
124 glean::bounce_tracking_protection::purge_count
.Get("dry"_ns
).Add(1);
125 } else if (aFailed
) {
126 glean::bounce_tracking_protection::purge_count
.Get("failure"_ns
).Add(1);
128 glean::bounce_tracking_protection::purge_count
.Get("success"_ns
).Add(1);
132 void ClearDataCallback::RecordURLClassifierTelemetry() {
134 nsCOMPtr
<nsIURIClassifier
> uriClassifier
=
135 do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID
, &rv
);
136 NS_ENSURE_SUCCESS_VOID(rv
);
137 NS_ENSURE_TRUE_VOID(uriClassifier
);
139 // Create a copy of the site host because we might have to mutate it.
140 nsAutoCString
siteHost(mEntry
->SiteHostRef());
141 nsContentUtils::MaybeFixIPv6Host(siteHost
);
143 // Create URI from siteHost
144 nsAutoCString
uriStr("https://");
145 uriStr
.Append(siteHost
);
147 nsCOMPtr
<nsIURI
> uri
;
148 rv
= NS_NewURI(getter_AddRefs(uri
), uriStr
);
149 NS_ENSURE_SUCCESS_VOID(rv
);
151 MOZ_ASSERT(sUrlClassifierFeatures
);
152 rv
= uriClassifier
->AsyncClassifyLocalWithFeatures(
153 uri
, *sUrlClassifierFeatures
, nsIUrlClassifierFeature::blocklist
, this);
154 NS_ENSURE_SUCCESS_VOID(rv
);
157 // nsIUrlClassifierFeatureCallback
158 // Used for telemetry only.
160 ClearDataCallback::OnClassifyComplete(
161 const nsTArray
<RefPtr
<nsIUrlClassifierFeatureResult
>>& aResults
) {
162 if (!aResults
.IsEmpty()) {
163 // Classified as a tracker => Increase Glean counter. We don't have to count
164 // non-classified hosts because we already keep track of the total count of
165 // successful purges.
166 glean::bounce_tracking_protection::purge_count_classified_tracker
.Add(1);
169 // In test mode dispatch an observer message to indicate we've completed
170 // collecting telemetry for the purge for the given host. This is needed
171 // because classification happens async.
172 if (StaticPrefs::privacy_bounceTrackingProtection_enableTestMode()) {
173 nsCOMPtr
<nsIObserverService
> obsSvc
=
174 mozilla::services::GetObserverService();
175 NS_ENSURE_TRUE(obsSvc
, NS_ERROR_FAILURE
);
177 nsresult rv
= obsSvc
->NotifyObservers(
178 nullptr, TEST_OBSERVER_MSG_RECORDED_PURGE_TELEMETRY
,
179 NS_ConvertUTF8toUTF16(mEntry
->SiteHostRef()).get());
180 NS_ENSURE_SUCCESS(rv
, rv
);
186 void ClearDataCallback::RecordPurgeEventTelemetry(bool aSuccess
) {
187 // Record a glean event for the clear action.
188 glean::bounce_tracking_protection::PurgeActionExtra extra
= {
189 .bounceTime
= Some(mEntry
->TimeStampRef() / PR_USEC_PER_SEC
),
190 .isDryRun
= Some(StaticPrefs::privacy_bounceTrackingProtection_mode() ==
191 nsIBounceTrackingProtection::MODE_ENABLED_DRY_RUN
),
192 .siteHost
= Some(nsAutoCString(mEntry
->SiteHostRef())),
193 .success
= Some(aSuccess
),
195 glean::bounce_tracking_protection::purge_action
.Record(Some(extra
));