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
|
/* 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/GleanMetrics.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(ArrayLength(kUrlClassifierFeatures) > 0,
"At least one URL classifier feature must be defined");
NS_IMPL_ISUPPORTS(ClearDataCallback, nsIClearDataCallback,
nsIUrlClassifierFeatureCallback);
// static
nsTArray<RefPtr<nsIUrlClassifierFeature>>
ClearDataCallback::sUrlClassifierFeatures;
ClearDataCallback::ClearDataCallback(ClearDataMozPromise::Private* aPromise,
const nsACString& aHost)
: mHost(aHost), mPromise(aPromise), mClearDurationTimer(0) {
MOZ_ASSERT(!aHost.IsEmpty(), "Host must not be empty");
if (!StaticPrefs::privacy_bounceTrackingProtection_enableDryRunMode()) {
// 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.IsEmpty()) {
// 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(); });
}
};
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 %s", __FUNCTION__, mHost.get()));
mPromise->Resolve(mHost, __func__);
// Only record classifications on successful deletion.
RecordURLClassifierTelemetry();
}
// Always collect clear duration and purge count.
RecordClearDurationTelemetry();
RecordPurgeCountTelemetry(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_enableDryRunMode()) {
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(mHost);
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);
rv = uriClassifier->AsyncClassifyLocalWithFeatures(
uri, sUrlClassifierFeatures, nsIUrlClassifierFeature::blocklist, this);
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(mHost).get());
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
|