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