blob: 6b4c0728b355a523aa5822e28797e14bad777e30 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/engagement/site_engagement_service.h"
#include <stddef.h>
#include <algorithm>
#include <cmath>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/banners/app_banner_settings_helper.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/engagement/site_engagement_eviction_policy.h"
#include "chrome/browser/engagement/site_engagement_helper.h"
#include "chrome/browser/engagement/site_engagement_service_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/common/chrome_switches.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/history/core/browser/history_service.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/browser_thread.h"
#include "url/gurl.h"
namespace {
// Global bool to ensure we only update the parameters from variations once.
bool g_updated_from_variations = false;
// Keys used in the variations params. Order matches
// SiteEngagementScore::Variation enum.
const char* kVariationNames[] = {
"max_points_per_day",
"decay_period_in_days",
"decay_points",
"navigation_points",
"user_input_points",
"visible_media_playing_points",
"hidden_media_playing_points",
"web_app_installed_points",
"first_daily_engagement_points",
};
// Length of time between metrics logging.
const int kMetricsIntervalInMinutes = 60;
// Delta within which to consider scores equal.
const double kScoreDelta = 0.001;
// Delta within which to consider internal time values equal. Internal time
// values are in microseconds, so this delta comes out at one second.
const double kTimeDelta = 1000000;
// Number of days after the last launch of an origin from an installed shortcut
// for which WEB_APP_INSTALLED_POINTS will be added to the engagement score.
const int kMaxDaysSinceShortcutLaunch = 10;
scoped_ptr<ContentSettingsForOneType> GetEngagementContentSettings(
HostContentSettingsMap* settings_map) {
scoped_ptr<ContentSettingsForOneType> engagement_settings(
new ContentSettingsForOneType);
settings_map->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
std::string(), engagement_settings.get());
return engagement_settings;
}
bool DoublesConsideredDifferent(double value1, double value2, double delta) {
double abs_difference = fabs(value1 - value2);
return abs_difference > delta;
}
// Only accept a navigation event for engagement if it is one of:
// a. direct typed navigation
// b. clicking on an omnibox suggestion brought up by typing a keyword
// c. clicking on a bookmark or opening a bookmark app
// d. a custom search engine keyword search (e.g. Wikipedia search box added as
// search engine).
bool IsEngagementNavigation(ui::PageTransition transition) {
return ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) ||
ui::PageTransitionCoreTypeIs(transition,
ui::PAGE_TRANSITION_GENERATED) ||
ui::PageTransitionCoreTypeIs(transition,
ui::PAGE_TRANSITION_AUTO_BOOKMARK) ||
ui::PageTransitionCoreTypeIs(transition,
ui::PAGE_TRANSITION_KEYWORD_GENERATED);
}
scoped_ptr<base::DictionaryValue> GetScoreDictForOrigin(
HostContentSettingsMap* settings,
const GURL& origin_url) {
if (!settings)
return scoped_ptr<base::DictionaryValue>();
scoped_ptr<base::Value> value = settings->GetWebsiteSetting(
origin_url, origin_url, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
std::string(), NULL);
if (!value.get())
return make_scoped_ptr(new base::DictionaryValue());
if (!value->IsType(base::Value::TYPE_DICTIONARY))
return make_scoped_ptr(new base::DictionaryValue());
return make_scoped_ptr(static_cast<base::DictionaryValue*>(value.release()));
}
} // namespace
const double SiteEngagementScore::kMaxPoints = 100;
double SiteEngagementScore::param_values[] = {
5, // MAX_POINTS_PER_DAY
7, // DECAY_PERIOD_IN_DAYS
5, // DECAY_POINTS
0.5, // NAVIGATION_POINTS
0.2, // USER_INPUT_POINTS
0.02, // VISIBLE_MEDIA_POINTS
0.01, // HIDDEN_MEDIA_POINTS
5, // WEB_APP_INSTALLED_POINTS
0.5, // FIRST_DAILY_ENGAGEMENT
8, // BOOTSTRAP_POINTS
};
const char* SiteEngagementScore::kRawScoreKey = "rawScore";
const char* SiteEngagementScore::kPointsAddedTodayKey = "pointsAddedToday";
const char* SiteEngagementScore::kLastEngagementTimeKey = "lastEngagementTime";
const char* SiteEngagementScore::kLastShortcutLaunchTimeKey =
"lastShortcutLaunchTime";
double SiteEngagementScore::GetMaxPointsPerDay() {
return param_values[MAX_POINTS_PER_DAY];
}
double SiteEngagementScore::GetDecayPeriodInDays() {
return param_values[DECAY_PERIOD_IN_DAYS];
}
double SiteEngagementScore::GetDecayPoints() {
return param_values[DECAY_POINTS];
}
double SiteEngagementScore::GetNavigationPoints() {
return param_values[NAVIGATION_POINTS];
}
double SiteEngagementScore::GetUserInputPoints() {
return param_values[USER_INPUT_POINTS];
}
double SiteEngagementScore::GetVisibleMediaPoints() {
return param_values[VISIBLE_MEDIA_POINTS];
}
double SiteEngagementScore::GetHiddenMediaPoints() {
return param_values[HIDDEN_MEDIA_POINTS];
}
double SiteEngagementScore::GetWebAppInstalledPoints() {
return param_values[WEB_APP_INSTALLED_POINTS];
}
double SiteEngagementScore::GetFirstDailyEngagementPoints() {
return param_values[FIRST_DAILY_ENGAGEMENT];
}
double SiteEngagementScore::GetBootstrapPoints() {
return param_values[BOOTSTRAP_POINTS];
}
void SiteEngagementScore::UpdateFromVariations() {
double param_vals[MAX_VARIATION];
for (int i = 0; i < MAX_VARIATION; ++i) {
std::string param_string = variations::GetVariationParamValue(
SiteEngagementService::kEngagementParams, kVariationNames[i]);
// Bail out if we didn't get a param string for the key, or if we couldn't
// convert the param string to a double, or if we get a negative value.
if (param_string.empty() ||
!base::StringToDouble(param_string, &param_vals[i]) ||
param_vals[i] < 0) {
return;
}
}
// Once we're sure everything is valid, assign the variation to the param
// values array.
for (int i = 0; i < MAX_VARIATION; ++i)
SiteEngagementScore::param_values[i] = param_vals[i];
}
SiteEngagementScore::SiteEngagementScore(
base::Clock* clock,
const base::DictionaryValue& score_dict)
: SiteEngagementScore(clock) {
score_dict.GetDouble(kRawScoreKey, &raw_score_);
score_dict.GetDouble(kPointsAddedTodayKey, &points_added_today_);
double internal_time;
if (score_dict.GetDouble(kLastEngagementTimeKey, &internal_time))
last_engagement_time_ = base::Time::FromInternalValue(internal_time);
if (score_dict.GetDouble(kLastShortcutLaunchTimeKey, &internal_time))
last_shortcut_launch_time_ = base::Time::FromInternalValue(internal_time);
}
SiteEngagementScore::~SiteEngagementScore() {
}
double SiteEngagementScore::Score() const {
return std::min(DecayedScore() + BonusScore(), kMaxPoints);
}
void SiteEngagementScore::AddPoints(double points) {
DCHECK_NE(0, points);
// As the score is about to be updated, commit any decay that has happened
// since the last update.
raw_score_ = DecayedScore();
base::Time now = clock_->Now();
if (!last_engagement_time_.is_null() &&
now.LocalMidnight() != last_engagement_time_.LocalMidnight()) {
points_added_today_ = 0;
}
if (points_added_today_ == 0) {
// Award bonus engagement for the first engagement of the day for a site.
points += GetFirstDailyEngagementPoints();
SiteEngagementMetrics::RecordEngagement(
SiteEngagementMetrics::ENGAGEMENT_FIRST_DAILY_ENGAGEMENT);
}
double to_add = std::min(kMaxPoints - raw_score_,
GetMaxPointsPerDay() - points_added_today_);
to_add = std::min(to_add, points);
points_added_today_ += to_add;
raw_score_ += to_add;
last_engagement_time_ = now;
}
void SiteEngagementScore::Reset(double points) {
raw_score_ = points;
points_added_today_ = 0;
// This must be set in order to prevent the score from decaying when read.
last_engagement_time_ = clock_->Now();
}
bool SiteEngagementScore::MaxPointsPerDayAdded() const {
if (!last_engagement_time_.is_null() &&
clock_->Now().LocalMidnight() != last_engagement_time_.LocalMidnight()) {
return false;
}
return points_added_today_ == GetMaxPointsPerDay();
}
bool SiteEngagementScore::UpdateScoreDict(base::DictionaryValue* score_dict) {
double raw_score_orig = 0;
double points_added_today_orig = 0;
double last_engagement_time_internal_orig = 0;
double last_shortcut_launch_time_internal_orig = 0;
score_dict->GetDouble(kRawScoreKey, &raw_score_orig);
score_dict->GetDouble(kPointsAddedTodayKey, &points_added_today_orig);
score_dict->GetDouble(kLastEngagementTimeKey,
&last_engagement_time_internal_orig);
score_dict->GetDouble(kLastShortcutLaunchTimeKey,
&last_shortcut_launch_time_internal_orig);
bool changed =
DoublesConsideredDifferent(raw_score_orig, raw_score_, kScoreDelta) ||
DoublesConsideredDifferent(points_added_today_orig, points_added_today_,
kScoreDelta) ||
DoublesConsideredDifferent(last_engagement_time_internal_orig,
last_engagement_time_.ToInternalValue(),
kTimeDelta) ||
DoublesConsideredDifferent(last_shortcut_launch_time_internal_orig,
last_shortcut_launch_time_.ToInternalValue(),
kTimeDelta);
if (!changed)
return false;
score_dict->SetDouble(kRawScoreKey, raw_score_);
score_dict->SetDouble(kPointsAddedTodayKey, points_added_today_);
score_dict->SetDouble(kLastEngagementTimeKey,
last_engagement_time_.ToInternalValue());
score_dict->SetDouble(kLastShortcutLaunchTimeKey,
last_shortcut_launch_time_.ToInternalValue());
return true;
}
SiteEngagementScore::SiteEngagementScore(base::Clock* clock)
: clock_(clock),
raw_score_(0),
points_added_today_(0),
last_engagement_time_(),
last_shortcut_launch_time_() {}
double SiteEngagementScore::DecayedScore() const {
// Note that users can change their clock, so from this system's perspective
// time can go backwards. If that does happen and the system detects that the
// current day is earlier than the last engagement, no decay (or growth) is
// applied.
int days_since_engagement = (clock_->Now() - last_engagement_time_).InDays();
if (days_since_engagement < 0)
return raw_score_;
int periods = days_since_engagement / GetDecayPeriodInDays();
double decayed_score = raw_score_ - periods * GetDecayPoints();
return std::max(0.0, decayed_score);
}
double SiteEngagementScore::BonusScore() const {
int days_since_shortcut_launch =
(clock_->Now() - last_shortcut_launch_time_).InDays();
if (days_since_shortcut_launch <= kMaxDaysSinceShortcutLaunch)
return GetWebAppInstalledPoints();
return 0;
}
void SiteEngagementScore::SetParamValuesForTesting() {
param_values[MAX_POINTS_PER_DAY] = 5;
param_values[DECAY_PERIOD_IN_DAYS] = 7;
param_values[DECAY_POINTS] = 5;
param_values[NAVIGATION_POINTS] = 0.5;
param_values[USER_INPUT_POINTS] = 0.05;
param_values[VISIBLE_MEDIA_POINTS] = 0.02;
param_values[HIDDEN_MEDIA_POINTS] = 0.01;
param_values[WEB_APP_INSTALLED_POINTS] = 5;
param_values[BOOTSTRAP_POINTS] = 8;
// This is set to zero to avoid interference with tests and is set when
// testing this functionality.
param_values[FIRST_DAILY_ENGAGEMENT] = 0;
}
const char SiteEngagementService::kEngagementParams[] = "SiteEngagement";
// static
SiteEngagementService* SiteEngagementService::Get(Profile* profile) {
return SiteEngagementServiceFactory::GetForProfile(profile);
}
// static
bool SiteEngagementService::IsEnabled() {
// If the engagement service or any of its dependencies are force-enabled,
// return true immediately.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableSiteEngagementService) ||
SiteEngagementEvictionPolicy::IsEnabled() ||
AppBannerSettingsHelper::ShouldUseSiteEngagementScore()) {
return true;
}
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteEngagementService)) {
return false;
}
const std::string group_name =
base::FieldTrialList::FindFullName(kEngagementParams);
return base::StartsWith(group_name, "Enabled", base::CompareCase::SENSITIVE);
}
SiteEngagementService::SiteEngagementService(Profile* profile)
: SiteEngagementService(profile, make_scoped_ptr(new base::DefaultClock)) {
content::BrowserThread::PostAfterStartupTask(
FROM_HERE, content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::UI),
base::Bind(&SiteEngagementService::AfterStartupTask,
weak_factory_.GetWeakPtr()));
if (!g_updated_from_variations) {
SiteEngagementScore::UpdateFromVariations();
g_updated_from_variations = true;
}
}
SiteEngagementService::~SiteEngagementService() {
history::HistoryService* history = HistoryServiceFactory::GetForProfile(
profile_, ServiceAccessType::IMPLICIT_ACCESS);
if (history)
history->RemoveObserver(this);
}
void SiteEngagementService::HandleNavigation(const GURL& url,
ui::PageTransition transition) {
if (IsEngagementNavigation(transition)) {
SiteEngagementMetrics::RecordEngagement(
SiteEngagementMetrics::ENGAGEMENT_NAVIGATION);
AddPoints(url, SiteEngagementScore::GetNavigationPoints());
RecordMetrics();
}
}
void SiteEngagementService::HandleUserInput(
const GURL& url,
SiteEngagementMetrics::EngagementType type) {
SiteEngagementMetrics::RecordEngagement(type);
AddPoints(url, SiteEngagementScore::GetUserInputPoints());
RecordMetrics();
}
void SiteEngagementService::HandleMediaPlaying(const GURL& url,
bool is_hidden) {
SiteEngagementMetrics::RecordEngagement(
is_hidden ? SiteEngagementMetrics::ENGAGEMENT_MEDIA_HIDDEN
: SiteEngagementMetrics::ENGAGEMENT_MEDIA_VISIBLE);
AddPoints(url, is_hidden ? SiteEngagementScore::GetHiddenMediaPoints()
: SiteEngagementScore::GetVisibleMediaPoints());
RecordMetrics();
}
void SiteEngagementService::ResetScoreForURL(const GURL& url, double score) {
DCHECK(url.is_valid());
DCHECK_GE(score, 0);
DCHECK_LE(score, SiteEngagementScore::kMaxPoints);
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(profile_);
scoped_ptr<base::DictionaryValue> score_dict =
GetScoreDictForOrigin(settings_map, url);
SiteEngagementScore engagement_score(clock_.get(), *score_dict);
engagement_score.Reset(score);
if (engagement_score.UpdateScoreDict(score_dict.get())) {
settings_map->SetWebsiteSettingDefaultScope(
url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
score_dict.release());
}
}
void SiteEngagementService::OnURLsDeleted(
history::HistoryService* history_service,
bool all_history,
bool expired,
const history::URLRows& deleted_rows,
const std::set<GURL>& favicon_urls) {
std::set<GURL> origins;
for (const history::URLRow& row : deleted_rows)
origins.insert(row.url().GetOrigin());
history::HistoryService* hs = HistoryServiceFactory::GetForProfile(
profile_, ServiceAccessType::EXPLICIT_ACCESS);
hs->GetCountsForOrigins(
origins, base::Bind(&SiteEngagementService::GetCountsForOriginsComplete,
weak_factory_.GetWeakPtr()));
}
void SiteEngagementService::SetLastShortcutLaunchTime(const GURL& url) {
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(profile_);
scoped_ptr<base::DictionaryValue> score_dict =
GetScoreDictForOrigin(settings_map, url);
SiteEngagementScore score(clock_.get(), *score_dict);
// Record the number of days since the last launch in UMA. If the user's clock
// has changed back in time, set this to 0.
base::Time now = clock_->Now();
base::Time last_launch = score.last_shortcut_launch_time();
if (!last_launch.is_null()) {
SiteEngagementMetrics::RecordDaysSinceLastShortcutLaunch(
std::max(0, (now - last_launch).InDays()));
}
SiteEngagementMetrics::RecordEngagement(
SiteEngagementMetrics::ENGAGEMENT_WEBAPP_SHORTCUT_LAUNCH);
score.set_last_shortcut_launch_time(now);
if (score.UpdateScoreDict(score_dict.get())) {
settings_map->SetWebsiteSettingDefaultScope(
url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
score_dict.release());
}
}
double SiteEngagementService::GetScore(const GURL& url) const {
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(profile_);
scoped_ptr<base::DictionaryValue> score_dict =
GetScoreDictForOrigin(settings_map, url);
SiteEngagementScore score(clock_.get(), *score_dict);
return score.Score();
}
double SiteEngagementService::GetTotalEngagementPoints() const {
std::map<GURL, double> score_map = GetScoreMap();
double total_score = 0;
for (const auto& value : score_map)
total_score += value.second;
return total_score;
}
std::map<GURL, double> SiteEngagementService::GetScoreMap() const {
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(profile_);
scoped_ptr<ContentSettingsForOneType> engagement_settings =
GetEngagementContentSettings(settings_map);
std::map<GURL, double> score_map;
for (const auto& site : *engagement_settings) {
GURL origin(site.primary_pattern.ToString());
if (!origin.is_valid())
continue;
scoped_ptr<base::DictionaryValue> score_dict =
GetScoreDictForOrigin(settings_map, origin);
SiteEngagementScore score(clock_.get(), *score_dict);
score_map[origin] = score.Score();
}
return score_map;
}
bool SiteEngagementService::IsBootstrapped() {
return GetTotalEngagementPoints() >=
SiteEngagementScore::GetBootstrapPoints();
}
SiteEngagementService::SiteEngagementService(Profile* profile,
scoped_ptr<base::Clock> clock)
: profile_(profile), clock_(std::move(clock)), weak_factory_(this) {
// May be null in tests.
history::HistoryService* history = HistoryServiceFactory::GetForProfile(
profile, ServiceAccessType::IMPLICIT_ACCESS);
if (history)
history->AddObserver(this);
}
void SiteEngagementService::AddPoints(const GURL& url, double points) {
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(profile_);
scoped_ptr<base::DictionaryValue> score_dict =
GetScoreDictForOrigin(settings_map, url);
SiteEngagementScore score(clock_.get(), *score_dict);
score.AddPoints(points);
if (score.UpdateScoreDict(score_dict.get())) {
settings_map->SetWebsiteSettingDefaultScope(
url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
score_dict.release());
}
}
void SiteEngagementService::AfterStartupTask() {
CleanupEngagementScores();
RecordMetrics();
}
void SiteEngagementService::CleanupEngagementScores() {
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(profile_);
scoped_ptr<ContentSettingsForOneType> engagement_settings =
GetEngagementContentSettings(settings_map);
for (const auto& site : *engagement_settings) {
GURL origin(site.primary_pattern.ToString());
if (origin.is_valid()) {
scoped_ptr<base::DictionaryValue> score_dict =
GetScoreDictForOrigin(settings_map, origin);
SiteEngagementScore score(clock_.get(), *score_dict);
if (score.Score() != 0)
continue;
}
settings_map->SetWebsiteSettingDefaultScope(
origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
nullptr);
}
}
void SiteEngagementService::RecordMetrics() {
base::Time now = clock_->Now();
if (last_metrics_time_.is_null() ||
(now - last_metrics_time_).InMinutes() >= kMetricsIntervalInMinutes) {
last_metrics_time_ = now;
std::map<GURL, double> score_map = GetScoreMap();
int origins_with_max_engagement = OriginsWithMaxEngagement(score_map);
int total_origins = score_map.size();
int percent_origins_with_max_engagement =
(total_origins == 0 ? 0 : (origins_with_max_engagement * 100) /
total_origins);
double total_engagement = GetTotalEngagementPoints();
double mean_engagement =
(total_origins == 0 ? 0 : total_engagement / total_origins);
SiteEngagementMetrics::RecordTotalOriginsEngaged(total_origins);
SiteEngagementMetrics::RecordTotalSiteEngagement(total_engagement);
SiteEngagementMetrics::RecordMeanEngagement(mean_engagement);
SiteEngagementMetrics::RecordMedianEngagement(
GetMedianEngagement(score_map));
SiteEngagementMetrics::RecordEngagementScores(score_map);
SiteEngagementMetrics::RecordOriginsWithMaxDailyEngagement(
OriginsWithMaxDailyEngagement());
SiteEngagementMetrics::RecordOriginsWithMaxEngagement(
origins_with_max_engagement);
SiteEngagementMetrics::RecordPercentOriginsWithMaxEngagement(
percent_origins_with_max_engagement);
}
}
double SiteEngagementService::GetMedianEngagement(
std::map<GURL, double>& score_map) const {
if (score_map.size() == 0)
return 0;
std::vector<double> scores;
scores.reserve(score_map.size());
for (const auto& value : score_map)
scores.push_back(value.second);
// Calculate the median as the middle value of the sorted engagement scores
// if there are an odd number of scores, or the average of the two middle
// scores otherwise.
std::sort(scores.begin(), scores.end());
size_t mid = scores.size() / 2;
if (scores.size() % 2 == 1)
return scores[mid];
else
return (scores[mid - 1] + scores[mid]) / 2;
}
int SiteEngagementService::OriginsWithMaxDailyEngagement() const {
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(profile_);
scoped_ptr<ContentSettingsForOneType> engagement_settings =
GetEngagementContentSettings(settings_map);
int total_origins = 0;
// We cannot call GetScoreMap as we need the score objects, not raw scores.
for (const auto& site : *engagement_settings) {
GURL origin(site.primary_pattern.ToString());
if (!origin.is_valid())
continue;
scoped_ptr<base::DictionaryValue> score_dict =
GetScoreDictForOrigin(settings_map, origin);
SiteEngagementScore score(clock_.get(), *score_dict);
if (score.MaxPointsPerDayAdded())
++total_origins;
}
return total_origins;
}
int SiteEngagementService::OriginsWithMaxEngagement(
std::map<GURL, double>& score_map) const {
int total_origins = 0;
for (const auto& value : score_map)
if (value.second == SiteEngagementScore::kMaxPoints)
++total_origins;
return total_origins;
}
void SiteEngagementService::GetCountsForOriginsComplete(
const history::OriginCountMap& origin_counts) {
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(profile_);
for (const auto& origin_to_count : origin_counts) {
if (origin_to_count.second != 0)
continue;
settings_map->SetWebsiteSettingDefaultScope(
origin_to_count.first, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
std::string(), nullptr);
}
}