Add GetTotalEngagementPoints to SiteEngagementService.
[chromium-blink-merge.git] / chrome / browser / engagement / site_engagement_service.cc
blob5f36f19ba525753cb54e04c913119451d858a8fa
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"
7 #include <algorithm>
8 #include <cmath>
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"
19 #include "url/gurl.h"
21 namespace {
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) {
38 if (!settings)
39 return scoped_ptr<base::DictionaryValue>();
41 scoped_ptr<base::Value> value = settings->GetWebsiteSetting(
42 origin_url, origin_url, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
43 std::string(), NULL);
44 if (!value.get())
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()));
53 } // namespace
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(
66 base::Clock* clock,
67 const base::DictionaryValue& score_dict)
68 : SiteEngagementScore(clock) {
69 score_dict.GetDouble(kRawScoreKey, &raw_score_);
70 score_dict.GetDouble(kPointsAddedTodayKey, &points_added_today_);
71 double internal_time;
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;
94 double to_add =
95 std::min(kMaxPoints - raw_score_, kMaxPointsPerDay - points_added_today_);
96 to_add = std::min(to_add, points);
98 points_added_today_ += to_add;
99 raw_score_ += 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);
113 bool changed =
114 DoublesConsideredDifferent(raw_score_orig, raw_score_, kScoreDelta) ||
115 DoublesConsideredDifferent(points_added_today_orig, points_added_today_,
116 kScoreDelta) ||
117 DoublesConsideredDifferent(last_engagement_time_internal_orig,
118 last_engagement_time_.ToInternalValue(),
119 kTimeDelta);
121 if (!changed)
122 return false;
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());
129 return true;
132 SiteEngagementScore::SiteEngagementScore(base::Clock* clock)
133 : clock_(clock),
134 raw_score_(0),
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
142 // applied.
143 int days_since_engagement = (clock_->Now() - last_engagement_time_).InDays();
144 if (days_since_engagement < 0)
145 return raw_score_;
147 int periods = days_since_engagement / kDecayPeriodInDays;
148 double decayed_score = raw_score_ - periods * kDecayPoints;
149 return std::max(0.0, decayed_score);
152 // static
153 SiteEngagementService* SiteEngagementService::Get(Profile* profile) {
154 return SiteEngagementServiceFactory::GetForProfile(profile);
157 // static
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())
181 return;
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);
203 int total_score = 0;
204 for (const auto& site : engagement_settings) {
205 GURL origin(site.primary_pattern.ToString());
206 if (!origin.is_valid())
207 continue;
209 scoped_ptr<base::DictionaryValue> score_dict =
210 GetScoreDictForOrigin(settings_map, origin);
211 SiteEngagementScore score(&clock_, *score_dict);
212 total_score += score.Score();
214 return total_score;