mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	Requires some changes elsewhere because C++ headers are the bad kind of magic. Differential Revision: https://phabricator.services.mozilla.com/D245004
		
			
				
	
	
		
			202 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* 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));
 | 
						|
}
 |