1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/engagement/site_engagement_service.h"
10 #include "base/command_line.h"
11 #include "base/time/clock.h"
12 #include "base/values.h"
13 #include "chrome/browser/engagement/site_engagement_helper.h"
14 #include "chrome/browser/engagement/site_engagement_service_factory.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "components/content_settings/core/browser/host_content_settings_map.h"
18 #include "components/content_settings/core/common/content_settings_pattern.h"
23 // Delta within which to consider scores equal.
24 const double kScoreDelta
= 0.001;
26 // Delta within which to consider internal time values equal. Internal time
27 // values are in microseconds, so this delta comes out at one second.
28 const double kTimeDelta
= 1000000;
30 bool DoublesConsideredDifferent(double value1
, double value2
, double delta
) {
31 double abs_difference
= fabs(value1
- value2
);
32 return abs_difference
> delta
;
35 scoped_ptr
<base::DictionaryValue
> GetScoreDictForOrigin(
36 HostContentSettingsMap
* settings
,
37 const GURL
& origin_url
) {
39 return scoped_ptr
<base::DictionaryValue
>();
41 scoped_ptr
<base::Value
> value
= settings
->GetWebsiteSetting(
42 origin_url
, origin_url
, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT
,
45 return make_scoped_ptr(new base::DictionaryValue());
47 if (!value
->IsType(base::Value::TYPE_DICTIONARY
))
48 return make_scoped_ptr(new base::DictionaryValue());
50 return make_scoped_ptr(static_cast<base::DictionaryValue
*>(value
.release()));
55 const char* SiteEngagementScore::kRawScoreKey
= "rawScore";
56 const char* SiteEngagementScore::kPointsAddedTodayKey
= "pointsAddedToday";
57 const char* SiteEngagementScore::kLastEngagementTimeKey
= "lastEngagementTime";
59 const double SiteEngagementScore::kMaxPoints
= 100;
60 const double SiteEngagementScore::kMaxPointsPerDay
= 5;
61 const double SiteEngagementScore::kNavigationPoints
= 1;
62 const int SiteEngagementScore::kDecayPeriodInDays
= 7;
63 const double SiteEngagementScore::kDecayPoints
= 5;
65 SiteEngagementScore::SiteEngagementScore(
67 const base::DictionaryValue
& score_dict
)
68 : SiteEngagementScore(clock
) {
69 score_dict
.GetDouble(kRawScoreKey
, &raw_score_
);
70 score_dict
.GetDouble(kPointsAddedTodayKey
, &points_added_today_
);
72 if (score_dict
.GetDouble(kLastEngagementTimeKey
, &internal_time
))
73 last_engagement_time_
= base::Time::FromInternalValue(internal_time
);
76 SiteEngagementScore::~SiteEngagementScore() {
79 double SiteEngagementScore::Score() const {
80 return DecayedScore();
83 void SiteEngagementScore::AddPoints(double points
) {
84 // As the score is about to be updated, commit any decay that has happened
85 // since the last update.
86 raw_score_
= DecayedScore();
88 base::Time now
= clock_
->Now();
89 if (!last_engagement_time_
.is_null() &&
90 now
.LocalMidnight() != last_engagement_time_
.LocalMidnight()) {
91 points_added_today_
= 0;
95 std::min(kMaxPoints
- raw_score_
, kMaxPointsPerDay
- points_added_today_
);
96 to_add
= std::min(to_add
, points
);
98 points_added_today_
+= to_add
;
101 last_engagement_time_
= now
;
104 bool SiteEngagementScore::UpdateScoreDict(base::DictionaryValue
* score_dict
) {
105 double raw_score_orig
= 0;
106 double points_added_today_orig
= 0;
107 double last_engagement_time_internal_orig
= 0;
109 score_dict
->GetDouble(kRawScoreKey
, &raw_score_orig
);
110 score_dict
->GetDouble(kPointsAddedTodayKey
, &points_added_today_orig
);
111 score_dict
->GetDouble(kLastEngagementTimeKey
,
112 &last_engagement_time_internal_orig
);
114 DoublesConsideredDifferent(raw_score_orig
, raw_score_
, kScoreDelta
) ||
115 DoublesConsideredDifferent(points_added_today_orig
, points_added_today_
,
117 DoublesConsideredDifferent(last_engagement_time_internal_orig
,
118 last_engagement_time_
.ToInternalValue(),
124 score_dict
->SetDouble(kRawScoreKey
, raw_score_
);
125 score_dict
->SetDouble(kPointsAddedTodayKey
, points_added_today_
);
126 score_dict
->SetDouble(kLastEngagementTimeKey
,
127 last_engagement_time_
.ToInternalValue());
132 SiteEngagementScore::SiteEngagementScore(base::Clock
* clock
)
135 points_added_today_(0),
136 last_engagement_time_() {}
138 double SiteEngagementScore::DecayedScore() const {
139 // Note that users can change their clock, so from this system's perspective
140 // time can go backwards. If that does happen and the system detects that the
141 // current day is earlier than the last engagement, no decay (or growth) is
143 int days_since_engagement
= (clock_
->Now() - last_engagement_time_
).InDays();
144 if (days_since_engagement
< 0)
147 int periods
= days_since_engagement
/ kDecayPeriodInDays
;
148 double decayed_score
= raw_score_
- periods
* kDecayPoints
;
149 return std::max(0.0, decayed_score
);
153 SiteEngagementService
* SiteEngagementService::Get(Profile
* profile
) {
154 return SiteEngagementServiceFactory::GetForProfile(profile
);
158 bool SiteEngagementService::IsEnabled() {
159 return base::CommandLine::ForCurrentProcess()->HasSwitch(
160 switches::kEnableSiteEngagementService
);
163 SiteEngagementService::SiteEngagementService(Profile
* profile
)
164 : profile_(profile
) {
167 SiteEngagementService::~SiteEngagementService() {
170 void SiteEngagementService::HandleNavigation(const GURL
& url
) {
171 HostContentSettingsMap
* settings_map
= profile_
->GetHostContentSettingsMap();
172 scoped_ptr
<base::DictionaryValue
> score_dict
=
173 GetScoreDictForOrigin(settings_map
, url
);
174 SiteEngagementScore
score(&clock_
, *score_dict
);
176 score
.AddPoints(SiteEngagementScore::kNavigationPoints
);
177 if (score
.UpdateScoreDict(score_dict
.get())) {
178 ContentSettingsPattern
pattern(
179 ContentSettingsPattern::FromURLNoWildcard(url
));
180 if (!pattern
.IsValid())
183 settings_map
->SetWebsiteSetting(pattern
, ContentSettingsPattern::Wildcard(),
184 CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT
,
185 std::string(), score_dict
.release());
189 int SiteEngagementService::GetScore(const GURL
& url
) {
190 HostContentSettingsMap
* settings_map
= profile_
->GetHostContentSettingsMap();
191 scoped_ptr
<base::DictionaryValue
> score_dict
=
192 GetScoreDictForOrigin(settings_map
, url
);
193 SiteEngagementScore
score(&clock_
, *score_dict
);
195 return score
.Score();
198 int SiteEngagementService::GetTotalEngagementPoints() {
199 HostContentSettingsMap
* settings_map
= profile_
->GetHostContentSettingsMap();
200 ContentSettingsForOneType engagement_settings
;
201 settings_map
->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT
,
202 std::string(), &engagement_settings
);
204 for (const auto& site
: engagement_settings
) {
205 GURL
origin(site
.primary_pattern
.ToString());
206 if (!origin
.is_valid())
209 scoped_ptr
<base::DictionaryValue
> score_dict
=
210 GetScoreDictForOrigin(settings_map
, origin
);
211 SiteEngagementScore
score(&clock_
, *score_dict
);
212 total_score
+= score
.Score();