Index: chrome/browser/engagement/site_engagement_service.cc |
diff --git a/chrome/browser/engagement/site_engagement_service.cc b/chrome/browser/engagement/site_engagement_service.cc |
index 7fd495f7e9a4b793f8121c8f3c6b653969e9a95f..46f78e55f1b3db92f6da51c614b737a96501614c 100644 |
--- a/chrome/browser/engagement/site_engagement_service.cc |
+++ b/chrome/browser/engagement/site_engagement_service.cc |
@@ -25,10 +25,13 @@ |
#include "chrome/browser/engagement/site_engagement_score.h" |
#include "chrome/browser/engagement/site_engagement_service_factory.h" |
#include "chrome/browser/history/history_service_factory.h" |
+#include "chrome/browser/profiles/profile.h" |
#include "chrome/common/chrome_switches.h" |
+#include "chrome/common/pref_names.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/prefs/pref_service.h" |
#include "content/public/browser/browser_thread.h" |
#include "content/public/browser/web_contents.h" |
#include "url/gurl.h" |
@@ -221,6 +224,11 @@ void SiteEngagementService::SetLastShortcutLaunchTime(const GURL& url) { |
} |
double SiteEngagementService::GetScore(const GURL& url) const { |
+ // Ensure that if engagement is stale, we clean things up before fetching the |
+ // score. |
+ if (IsLastEngagementStale()) |
+ CleanupEngagementScores(true); |
+ |
return CreateEngagementScore(url).GetScore(); |
} |
@@ -245,32 +253,81 @@ SiteEngagementService::SiteEngagementService(Profile* profile, |
} |
void SiteEngagementService::AddPoints(const GURL& url, double points) { |
- SiteEngagementScore score = CreateEngagementScore(url); |
+ // Trigger a cleanup and date adjustment if it has been a substantial length |
+ // of time since *any* engagement was recorded by the service. This will |
+ // ensure that we do not decay scores when the user did not use the browser. |
+ if (IsLastEngagementStale()) |
+ CleanupEngagementScores(true); |
+ SiteEngagementScore score = CreateEngagementScore(url); |
score.AddPoints(points); |
score.Commit(); |
+ |
+ SetLastEngagementTime(score.last_engagement_time()); |
} |
void SiteEngagementService::AfterStartupTask() { |
- CleanupEngagementScores(); |
+ // Check if we need to reset last engagement times on startup - we want to |
+ // avoid doing this in AddPoints() if possible. It is still necessary to check |
+ // in AddPoints for people who never restart Chrome, but leave it open and |
+ // their computer on standby. |
+ CleanupEngagementScores(IsLastEngagementStale()); |
RecordMetrics(); |
} |
-void SiteEngagementService::CleanupEngagementScores() { |
+void SiteEngagementService::CleanupEngagementScores( |
+ bool update_last_engagement_time) const { |
+ // This method should not be called with |update_last_engagement_time| = true |
+ // if the last engagement time isn't stale. |
+ DCHECK(!update_last_engagement_time || IsLastEngagementStale()); |
+ |
HostContentSettingsMap* settings_map = |
HostContentSettingsMapFactory::GetForProfile(profile_); |
std::unique_ptr<ContentSettingsForOneType> engagement_settings = |
GetEngagementContentSettings(settings_map); |
+ // We want to rebase last engagement times relative to MaxDecaysPerScore |
+ // periods of decay in the past. |
+ base::Time now = clock_->Now(); |
+ base::Time last_engagement_time = GetLastEngagementTime(); |
+ base::Time rebase_time = now - GetMaxDecayPeriod(); |
+ base::Time new_last_engagement_time; |
for (const auto& site : *engagement_settings) { |
GURL origin(site.primary_pattern.ToString()); |
- if (origin.is_valid() && GetScore(origin) != 0) |
- continue; |
+ if (origin.is_valid()) { |
+ SiteEngagementScore score = CreateEngagementScore(origin); |
+ if (update_last_engagement_time) { |
+ // Work out the offset between this score's last engagement time and the |
+ // last time the service recorded any engagement. Set the score's last |
+ // engagement time to rebase_time - offset to preserve its state, |
+ // relative to the rebase date. This ensures that the score will decay |
+ // the next time it is used, but will not decay too much. |
+ DCHECK_LE(score.last_engagement_time(), rebase_time); |
+ base::TimeDelta offset = |
+ last_engagement_time - score.last_engagement_time(); |
+ base::Time rebase_score_time = rebase_time - offset; |
+ score.set_last_engagement_time(rebase_score_time); |
+ if (rebase_score_time > new_last_engagement_time) |
+ new_last_engagement_time = rebase_score_time; |
+ |
+ score.Commit(); |
+ } |
+ |
+ if (score.GetScore() != 0) |
+ continue; |
+ } |
+ |
+ // This origin has a score of 0. Wipe it from content settings. |
settings_map->SetWebsiteSettingDefaultScope( |
origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), |
nullptr); |
} |
+ |
+ // Set the last engagement time to be consistent with the scores. This will |
+ // only occur if |update_last_engagement_time| is true. |
+ if (!new_last_engagement_time.is_null()) |
+ SetLastEngagementTime(new_last_engagement_time); |
} |
void SiteEngagementService::RecordMetrics() { |
@@ -307,6 +364,29 @@ void SiteEngagementService::RecordMetrics() { |
} |
} |
+base::Time SiteEngagementService::GetLastEngagementTime() const { |
+ return base::Time::FromInternalValue( |
+ profile_->GetPrefs()->GetInt64(prefs::kSiteEngagementLastUpdateTime)); |
+} |
+ |
+void SiteEngagementService::SetLastEngagementTime( |
+ base::Time last_engagement_time) const { |
+ profile_->GetPrefs()->SetInt64(prefs::kSiteEngagementLastUpdateTime, |
+ last_engagement_time.ToInternalValue()); |
+} |
+ |
+base::TimeDelta SiteEngagementService::GetMaxDecayPeriod() const { |
+ return base::TimeDelta::FromDays( |
+ SiteEngagementScore::GetDecayPeriodInDays()) * |
+ SiteEngagementScore::GetMaxDecaysPerScore(); |
+} |
+ |
+base::TimeDelta SiteEngagementService::GetStalePeriod() const { |
+ return GetMaxDecayPeriod() + |
+ base::TimeDelta::FromHours( |
+ SiteEngagementScore::GetLastEngagementGracePeriodInHours()); |
+} |
+ |
double SiteEngagementService::GetMedianEngagement( |
const std::map<GURL, double>& score_map) const { |
if (score_map.size() == 0) |
@@ -372,6 +452,16 @@ void SiteEngagementService::HandleUserInput( |
OnEngagementIncreased(web_contents, url, GetScore(url))); |
} |
+bool SiteEngagementService::IsLastEngagementStale() const { |
+ // This only happens when Chrome is first run and the user has never recorded |
+ // any engagement. |
+ base::Time last_engagement_time = GetLastEngagementTime(); |
+ if (last_engagement_time.is_null()) |
+ return false; |
+ |
+ return (clock_->Now() - last_engagement_time) >= GetStalePeriod(); |
+} |
+ |
void SiteEngagementService::OnURLsDeleted( |
history::HistoryService* history_service, |
bool all_history, |
@@ -391,15 +481,8 @@ void SiteEngagementService::OnURLsDeleted( |
weak_factory_.GetWeakPtr(), hs, origins, expired)); |
} |
-const SiteEngagementScore SiteEngagementService::CreateEngagementScore( |
- const GURL& origin) const { |
- return SiteEngagementScore( |
- clock_.get(), origin, |
- HostContentSettingsMapFactory::GetForProfile(profile_)); |
-} |
- |
SiteEngagementScore SiteEngagementService::CreateEngagementScore( |
- const GURL& origin) { |
+ const GURL& origin) const { |
return SiteEngagementScore( |
clock_.get(), origin, |
HostContentSettingsMapFactory::GetForProfile(profile_)); |
@@ -503,4 +586,6 @@ void SiteEngagementService::GetCountsAndLastVisitForOriginsComplete( |
engagement_score.Commit(); |
} |
+ |
+ SetLastEngagementTime(now); |
} |