blob: 9995091123d04fa078741f86e1f018276f579991 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/subresource_filter/content/browser/ads_intervention_manager.h"
#include <optional>
#include "base/metrics/histogram_macros.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/subresource_filter/content/browser/subresource_filter_content_settings_manager.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "content/public/browser/navigation_handle.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "url/gurl.h"
namespace subresource_filter {
namespace {
// Key into the website settings dict for last active ads violation.
const char kLastAdsViolationTimeKey[] = "LastAdsViolationTime";
const char kLastAdsViolationKey[] = "LastAdsViolation";
// Histograms
const char kAdsInterventionRecordedHistogramName[] =
"SubresourceFilter.PageLoad.AdsInterventionTriggered";
AdsInterventionStatus GetAdsInterventionStatus(bool activation_status,
bool intervention_active) {
if (!intervention_active)
return AdsInterventionStatus::kExpired;
return activation_status ? AdsInterventionStatus::kBlocking
: AdsInterventionStatus::kWouldBlock;
}
} // namespace
// static
base::TimeDelta AdsInterventionManager::GetInterventionDuration(
mojom::AdsViolation violation) {
switch (violation) {
case mojom::AdsViolation::kHeavyAdsInterventionAtHostLimit:
return base::Days(1);
default:
return kAdsInterventionDuration.Get();
}
}
AdsInterventionManager::AdsInterventionManager(
SubresourceFilterContentSettingsManager* settings_manager)
: settings_manager_(settings_manager),
clock_(base::DefaultClock::GetInstance()) {}
AdsInterventionManager::~AdsInterventionManager() = default;
void AdsInterventionManager::TriggerAdsInterventionForUrlOnSubsequentLoads(
const GURL& url,
mojom::AdsViolation ads_violation) {
base::Value::Dict additional_metadata;
double now = clock_->Now().InSecondsFSinceUnixEpoch();
additional_metadata.Set(kLastAdsViolationTimeKey, now);
additional_metadata.Set(kLastAdsViolationKey,
static_cast<int>(ads_violation));
bool activated = base::FeatureList::IsEnabled(kAdsInterventionsEnforced);
// This is a no-op if the metadata already exists for an active
// ads intervention.
settings_manager_->SetSiteMetadataBasedOnActivation(
url, activated,
SubresourceFilterContentSettingsManager::ActivationSource::
kAdsIntervention,
std::move(additional_metadata));
UMA_HISTOGRAM_ENUMERATION(kAdsInterventionRecordedHistogramName,
ads_violation);
}
std::optional<AdsInterventionManager::LastAdsIntervention>
AdsInterventionManager::GetLastAdsIntervention(const GURL& url) const {
// The last active ads intervention is stored in the site metadata.
std::optional<base::Value::Dict> dict =
settings_manager_->GetSiteMetadata(url);
if (!dict)
return std::nullopt;
std::optional<int> ads_violation = dict->FindInt(kLastAdsViolationKey);
std::optional<double> last_violation_time =
dict->FindDouble(kLastAdsViolationTimeKey);
if (ads_violation && last_violation_time) {
base::TimeDelta diff =
clock_->Now() -
base::Time::FromSecondsSinceUnixEpoch(*last_violation_time);
return LastAdsIntervention(
{diff, static_cast<mojom::AdsViolation>(*ads_violation)});
}
return std::nullopt;
}
bool AdsInterventionManager::ShouldActivate(
content::NavigationHandle* navigation_handle) const {
const GURL& url(navigation_handle->GetURL());
// TODO(crbug.com/40724530): Add new ads intervention
// manager function to return struct with all ads intervention
// metadata to reduce metadata accesses.
std::optional<AdsInterventionManager::LastAdsIntervention> last_intervention =
GetLastAdsIntervention(url);
// Only activate the subresource filter if we are intervening on
// ads.
bool current_activation_status =
settings_manager_->GetSiteActivationFromMetadata(url);
bool has_active_ads_intervention = false;
// TODO(crbug.com/1131971): If a host triggers multiple times on a single
// navigate and the durations don't match, we'll use the last duration rather
// than the longest. The metadata should probably store the activation with
// the longest duration.
if (last_intervention) {
has_active_ads_intervention =
last_intervention->duration_since <
AdsInterventionManager::GetInterventionDuration(
last_intervention->ads_violation);
auto* ukm_recorder = ukm::UkmRecorder::Get();
ukm::builders::AdsIntervention_LastIntervention builder(
ukm::ConvertToSourceId(navigation_handle->GetNavigationId(),
ukm::SourceIdType::NAVIGATION_ID));
builder
.SetInterventionType(static_cast<int>(last_intervention->ads_violation))
.SetInterventionStatus(static_cast<int>(GetAdsInterventionStatus(
current_activation_status, has_active_ads_intervention)));
builder.Record(ukm_recorder->Get());
}
return current_activation_status && has_active_ads_intervention;
}
} // namespace subresource_filter