blob: 7e1543425cfa0f95f5f3efbaf953ea95aed12dab [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 "chrome/browser/engagement/history_aware_site_engagement_service.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/site_engagement/content/site_engagement_score.h"
#include "content/public/browser/browser_context.h"
namespace site_engagement {
HistoryAwareSiteEngagementService::HistoryAwareSiteEngagementService(
content::BrowserContext* browser_context,
history::HistoryService* history_service)
: SiteEngagementService(browser_context) {
// May be null in tests.
if (history_service)
history_service_observation_.Observe(history_service);
}
HistoryAwareSiteEngagementService::~HistoryAwareSiteEngagementService() =
default;
void HistoryAwareSiteEngagementService::Shutdown() {
history_service_observation_.Reset();
}
void HistoryAwareSiteEngagementService::OnHistoryDeletions(
history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) {
std::multiset<GURL> origins;
for (const history::URLRow& row : deletion_info.deleted_rows())
origins.insert(row.url().DeprecatedGetOriginAsURL());
UpdateEngagementScores(origins, deletion_info.is_from_expiration(),
deletion_info.deleted_urls_origin_map());
}
void HistoryAwareSiteEngagementService::UpdateEngagementScores(
const std::multiset<GURL>& deleted_origins,
bool expired,
const history::OriginCountAndLastVisitMap& remaining_origins) {
// The most in-the-past option in the Clear Browsing Dialog aside from "all
// time" is 4 weeks ago. Set the last updated date to 4 weeks ago for origins
// where we can't find a valid last visit date.
base::Time now = clock().Now();
base::Time four_weeks_ago = now - base::Days(28);
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(browser_context());
for (const auto& origin_to_count : remaining_origins) {
GURL origin = origin_to_count.first;
// It appears that the history service occasionally sends bad URLs to us.
// See crbug.com/612881.
if (!origin.is_valid())
continue;
int remaining = origin_to_count.second.first;
base::Time last_visit = origin_to_count.second.second;
int deleted = deleted_origins.count(origin);
// Do not update engagement scores if the deletion was an expiry, but the
// URL still has entries in history.
if ((expired && remaining != 0) || deleted == 0)
continue;
// Remove origins that have no urls left.
if (remaining == 0) {
settings_map->SetWebsiteSettingDefaultScope(
origin, GURL(), ContentSettingsType::SITE_ENGAGEMENT, base::Value());
continue;
}
// Remove engagement proportional to the urls expired from the origin's
// entire history.
double proportion_remaining =
static_cast<double>(remaining) / (remaining + deleted);
if (last_visit.is_null() || last_visit > now)
last_visit = four_weeks_ago;
// At this point, we are going to proportionally decay the origin's
// engagement, and reset its last visit date to the last visit to a URL
// under the origin in history. If this new last visit date is long enough
// in the past, the next time the origin's engagement is accessed the
// automatic decay will kick in - i.e. a double decay will have occurred.
// To prevent this, compute the decay that would have taken place since the
// new last visit and add it to the engagement at this point. When the
// engagement is next accessed, it will decay back to the proportionally
// reduced value rather than being decayed once here, and then once again
// when it is next accessed.
// TODO(crbug.com/41308686): Move the proportional decay logic into
// SiteEngagementScore, so it can decay raw_score_ directly, without the
// double-decay issue.
SiteEngagementScore engagement_score = CreateEngagementScore(origin);
double new_score = proportion_remaining * engagement_score.GetTotalScore();
int hours_since_engagement = (now - last_visit).InHours();
int periods =
hours_since_engagement / SiteEngagementScore::GetDecayPeriodInHours();
new_score += periods * SiteEngagementScore::GetDecayPoints();
new_score *= pow(1.0 / SiteEngagementScore::GetDecayProportion(), periods);
double score = std::min(SiteEngagementScore::kMaxPoints, new_score);
engagement_score.Reset(score, last_visit);
if (!engagement_score.last_shortcut_launch_time().is_null() &&
engagement_score.last_shortcut_launch_time() > last_visit) {
engagement_score.set_last_shortcut_launch_time(last_visit);
}
engagement_score.Commit();
}
SetLastEngagementTime(now);
}
} // namespace site_engagement