OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/engagement/site_engagement_service.h" | 5 #include "chrome/browser/engagement/site_engagement_service.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <utility> | 10 #include <utility> |
11 #include <vector> | 11 #include <vector> |
12 | 12 |
13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
15 #include "base/metrics/field_trial.h" | 15 #include "base/metrics/field_trial.h" |
16 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
17 #include "base/time/clock.h" | 17 #include "base/time/clock.h" |
18 #include "base/time/default_clock.h" | 18 #include "base/time/default_clock.h" |
19 #include "base/time/time.h" | 19 #include "base/time/time.h" |
20 #include "base/values.h" | 20 #include "base/values.h" |
21 #include "chrome/browser/banners/app_banner_settings_helper.h" | 21 #include "chrome/browser/banners/app_banner_settings_helper.h" |
22 #include "chrome/browser/content_settings/host_content_settings_map_factory.h" | 22 #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
23 #include "chrome/browser/engagement/site_engagement_eviction_policy.h" | 23 #include "chrome/browser/engagement/site_engagement_eviction_policy.h" |
24 #include "chrome/browser/engagement/site_engagement_metrics.h" | 24 #include "chrome/browser/engagement/site_engagement_metrics.h" |
25 #include "chrome/browser/engagement/site_engagement_score.h" | 25 #include "chrome/browser/engagement/site_engagement_score.h" |
26 #include "chrome/browser/engagement/site_engagement_service_factory.h" | 26 #include "chrome/browser/engagement/site_engagement_service_factory.h" |
27 #include "chrome/browser/history/history_service_factory.h" | 27 #include "chrome/browser/history/history_service_factory.h" |
| 28 #include "chrome/browser/profiles/profile.h" |
28 #include "chrome/common/chrome_switches.h" | 29 #include "chrome/common/chrome_switches.h" |
| 30 #include "chrome/common/pref_names.h" |
29 #include "components/content_settings/core/browser/host_content_settings_map.h" | 31 #include "components/content_settings/core/browser/host_content_settings_map.h" |
30 #include "components/content_settings/core/common/content_settings_pattern.h" | 32 #include "components/content_settings/core/common/content_settings_pattern.h" |
31 #include "components/history/core/browser/history_service.h" | 33 #include "components/history/core/browser/history_service.h" |
| 34 #include "components/prefs/pref_service.h" |
32 #include "content/public/browser/browser_thread.h" | 35 #include "content/public/browser/browser_thread.h" |
33 #include "content/public/browser/web_contents.h" | 36 #include "content/public/browser/web_contents.h" |
34 #include "url/gurl.h" | 37 #include "url/gurl.h" |
35 | 38 |
36 namespace { | 39 namespace { |
37 | 40 |
38 const int FOUR_WEEKS_IN_DAYS = 28; | 41 const int FOUR_WEEKS_IN_DAYS = 28; |
39 | 42 |
40 // Global bool to ensure we only update the parameters from variations once. | 43 // Global bool to ensure we only update the parameters from variations once. |
41 bool g_updated_from_variations = false; | 44 bool g_updated_from_variations = false; |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
214 std::max(0, (now - last_launch).InDays())); | 217 std::max(0, (now - last_launch).InDays())); |
215 } | 218 } |
216 SiteEngagementMetrics::RecordEngagement( | 219 SiteEngagementMetrics::RecordEngagement( |
217 SiteEngagementMetrics::ENGAGEMENT_WEBAPP_SHORTCUT_LAUNCH); | 220 SiteEngagementMetrics::ENGAGEMENT_WEBAPP_SHORTCUT_LAUNCH); |
218 | 221 |
219 score.set_last_shortcut_launch_time(now); | 222 score.set_last_shortcut_launch_time(now); |
220 score.Commit(); | 223 score.Commit(); |
221 } | 224 } |
222 | 225 |
223 double SiteEngagementService::GetScore(const GURL& url) const { | 226 double SiteEngagementService::GetScore(const GURL& url) const { |
| 227 // Ensure that if engagement is stale, we clean things up before fetching the |
| 228 // score. |
| 229 if (IsLastEngagementStale()) |
| 230 CleanupEngagementScores(true); |
| 231 |
224 return CreateEngagementScore(url).GetScore(); | 232 return CreateEngagementScore(url).GetScore(); |
225 } | 233 } |
226 | 234 |
227 double SiteEngagementService::GetTotalEngagementPoints() const { | 235 double SiteEngagementService::GetTotalEngagementPoints() const { |
228 std::map<GURL, double> score_map = GetScoreMap(); | 236 std::map<GURL, double> score_map = GetScoreMap(); |
229 | 237 |
230 double total_score = 0; | 238 double total_score = 0; |
231 for (const auto& value : score_map) | 239 for (const auto& value : score_map) |
232 total_score += value.second; | 240 total_score += value.second; |
233 | 241 |
234 return total_score; | 242 return total_score; |
235 } | 243 } |
236 | 244 |
237 SiteEngagementService::SiteEngagementService(Profile* profile, | 245 SiteEngagementService::SiteEngagementService(Profile* profile, |
238 std::unique_ptr<base::Clock> clock) | 246 std::unique_ptr<base::Clock> clock) |
239 : profile_(profile), clock_(std::move(clock)), weak_factory_(this) { | 247 : profile_(profile), clock_(std::move(clock)), weak_factory_(this) { |
240 // May be null in tests. | 248 // May be null in tests. |
241 history::HistoryService* history = HistoryServiceFactory::GetForProfile( | 249 history::HistoryService* history = HistoryServiceFactory::GetForProfile( |
242 profile, ServiceAccessType::IMPLICIT_ACCESS); | 250 profile, ServiceAccessType::IMPLICIT_ACCESS); |
243 if (history) | 251 if (history) |
244 history->AddObserver(this); | 252 history->AddObserver(this); |
245 } | 253 } |
246 | 254 |
247 void SiteEngagementService::AddPoints(const GURL& url, double points) { | 255 void SiteEngagementService::AddPoints(const GURL& url, double points) { |
| 256 // Trigger a cleanup and date adjustment if it has been a substantial length |
| 257 // of time since *any* engagement was recorded by the service. This will |
| 258 // ensure that we do not decay scores when the user did not use the browser. |
| 259 if (IsLastEngagementStale()) |
| 260 CleanupEngagementScores(true); |
| 261 |
248 SiteEngagementScore score = CreateEngagementScore(url); | 262 SiteEngagementScore score = CreateEngagementScore(url); |
249 | |
250 score.AddPoints(points); | 263 score.AddPoints(points); |
251 score.Commit(); | 264 score.Commit(); |
| 265 |
| 266 SetLastEngagementTime(score.last_engagement_time()); |
252 } | 267 } |
253 | 268 |
254 void SiteEngagementService::AfterStartupTask() { | 269 void SiteEngagementService::AfterStartupTask() { |
255 CleanupEngagementScores(); | 270 // Check if we need to reset last engagement times on startup - we want to |
| 271 // avoid doing this in AddPoints() if possible. It is still necessary to check |
| 272 // in AddPoints for people who never restart Chrome, but leave it open and |
| 273 // their computer on standby. |
| 274 CleanupEngagementScores(IsLastEngagementStale()); |
256 RecordMetrics(); | 275 RecordMetrics(); |
257 } | 276 } |
258 | 277 |
259 void SiteEngagementService::CleanupEngagementScores() { | 278 void SiteEngagementService::CleanupEngagementScores( |
| 279 bool update_last_engagement_time) const { |
| 280 // This method should not be called with |update_last_engagement_time| = true |
| 281 // if the last engagement time isn't stale. |
| 282 DCHECK(!update_last_engagement_time || IsLastEngagementStale()); |
| 283 |
260 HostContentSettingsMap* settings_map = | 284 HostContentSettingsMap* settings_map = |
261 HostContentSettingsMapFactory::GetForProfile(profile_); | 285 HostContentSettingsMapFactory::GetForProfile(profile_); |
262 std::unique_ptr<ContentSettingsForOneType> engagement_settings = | 286 std::unique_ptr<ContentSettingsForOneType> engagement_settings = |
263 GetEngagementContentSettings(settings_map); | 287 GetEngagementContentSettings(settings_map); |
264 | 288 |
| 289 // We want to rebase last engagement times relative to MaxDecaysPerScore |
| 290 // periods of decay in the past. |
| 291 base::Time now = clock_->Now(); |
| 292 base::Time last_engagement_time = GetLastEngagementTime(); |
| 293 base::Time rebase_time = now - GetMaxDecayPeriod(); |
| 294 base::Time new_last_engagement_time; |
265 for (const auto& site : *engagement_settings) { | 295 for (const auto& site : *engagement_settings) { |
266 GURL origin(site.primary_pattern.ToString()); | 296 GURL origin(site.primary_pattern.ToString()); |
267 if (origin.is_valid() && GetScore(origin) != 0) | |
268 continue; | |
269 | 297 |
| 298 if (origin.is_valid()) { |
| 299 SiteEngagementScore score = CreateEngagementScore(origin); |
| 300 if (update_last_engagement_time) { |
| 301 // Work out the offset between this score's last engagement time and the |
| 302 // last time the service recorded any engagement. Set the score's last |
| 303 // engagement time to rebase_time - offset to preserve its state, |
| 304 // relative to the rebase date. This ensures that the score will decay |
| 305 // the next time it is used, but will not decay too much. |
| 306 DCHECK_LE(score.last_engagement_time(), rebase_time); |
| 307 base::TimeDelta offset = |
| 308 last_engagement_time - score.last_engagement_time(); |
| 309 base::Time rebase_score_time = rebase_time - offset; |
| 310 score.set_last_engagement_time(rebase_score_time); |
| 311 if (rebase_score_time > new_last_engagement_time) |
| 312 new_last_engagement_time = rebase_score_time; |
| 313 |
| 314 score.Commit(); |
| 315 } |
| 316 |
| 317 if (score.GetScore() != 0) |
| 318 continue; |
| 319 } |
| 320 |
| 321 // This origin has a score of 0. Wipe it from content settings. |
270 settings_map->SetWebsiteSettingDefaultScope( | 322 settings_map->SetWebsiteSettingDefaultScope( |
271 origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), | 323 origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), |
272 nullptr); | 324 nullptr); |
273 } | 325 } |
| 326 |
| 327 // Set the last engagement time to be consistent with the scores. This will |
| 328 // only occur if |update_last_engagement_time| is true. |
| 329 if (!new_last_engagement_time.is_null()) |
| 330 SetLastEngagementTime(new_last_engagement_time); |
274 } | 331 } |
275 | 332 |
276 void SiteEngagementService::RecordMetrics() { | 333 void SiteEngagementService::RecordMetrics() { |
277 base::Time now = clock_->Now(); | 334 base::Time now = clock_->Now(); |
278 if (last_metrics_time_.is_null() || | 335 if (last_metrics_time_.is_null() || |
279 (now - last_metrics_time_).InMinutes() >= kMetricsIntervalInMinutes) { | 336 (now - last_metrics_time_).InMinutes() >= kMetricsIntervalInMinutes) { |
280 last_metrics_time_ = now; | 337 last_metrics_time_ = now; |
281 std::map<GURL, double> score_map = GetScoreMap(); | 338 std::map<GURL, double> score_map = GetScoreMap(); |
282 | 339 |
283 int origins_with_max_engagement = OriginsWithMaxEngagement(score_map); | 340 int origins_with_max_engagement = OriginsWithMaxEngagement(score_map); |
(...skipping 16 matching lines...) Expand all Loading... |
300 | 357 |
301 SiteEngagementMetrics::RecordOriginsWithMaxDailyEngagement( | 358 SiteEngagementMetrics::RecordOriginsWithMaxDailyEngagement( |
302 OriginsWithMaxDailyEngagement()); | 359 OriginsWithMaxDailyEngagement()); |
303 SiteEngagementMetrics::RecordOriginsWithMaxEngagement( | 360 SiteEngagementMetrics::RecordOriginsWithMaxEngagement( |
304 origins_with_max_engagement); | 361 origins_with_max_engagement); |
305 SiteEngagementMetrics::RecordPercentOriginsWithMaxEngagement( | 362 SiteEngagementMetrics::RecordPercentOriginsWithMaxEngagement( |
306 percent_origins_with_max_engagement); | 363 percent_origins_with_max_engagement); |
307 } | 364 } |
308 } | 365 } |
309 | 366 |
| 367 base::Time SiteEngagementService::GetLastEngagementTime() const { |
| 368 return base::Time::FromInternalValue( |
| 369 profile_->GetPrefs()->GetInt64(prefs::kSiteEngagementLastUpdateTime)); |
| 370 } |
| 371 |
| 372 void SiteEngagementService::SetLastEngagementTime( |
| 373 base::Time last_engagement_time) const { |
| 374 profile_->GetPrefs()->SetInt64(prefs::kSiteEngagementLastUpdateTime, |
| 375 last_engagement_time.ToInternalValue()); |
| 376 } |
| 377 |
| 378 base::TimeDelta SiteEngagementService::GetMaxDecayPeriod() const { |
| 379 return base::TimeDelta::FromDays( |
| 380 SiteEngagementScore::GetDecayPeriodInDays()) * |
| 381 SiteEngagementScore::GetMaxDecaysPerScore(); |
| 382 } |
| 383 |
| 384 base::TimeDelta SiteEngagementService::GetStalePeriod() const { |
| 385 return GetMaxDecayPeriod() + |
| 386 base::TimeDelta::FromHours( |
| 387 SiteEngagementScore::GetLastEngagementGracePeriodInHours()); |
| 388 } |
| 389 |
310 double SiteEngagementService::GetMedianEngagement( | 390 double SiteEngagementService::GetMedianEngagement( |
311 const std::map<GURL, double>& score_map) const { | 391 const std::map<GURL, double>& score_map) const { |
312 if (score_map.size() == 0) | 392 if (score_map.size() == 0) |
313 return 0; | 393 return 0; |
314 | 394 |
315 std::vector<double> scores; | 395 std::vector<double> scores; |
316 scores.reserve(score_map.size()); | 396 scores.reserve(score_map.size()); |
317 for (const auto& value : score_map) | 397 for (const auto& value : score_map) |
318 scores.push_back(value.second); | 398 scores.push_back(value.second); |
319 | 399 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
365 const GURL& url = web_contents->GetVisibleURL(); | 445 const GURL& url = web_contents->GetVisibleURL(); |
366 SiteEngagementMetrics::RecordEngagement(type); | 446 SiteEngagementMetrics::RecordEngagement(type); |
367 AddPoints(url, SiteEngagementScore::GetUserInputPoints()); | 447 AddPoints(url, SiteEngagementScore::GetUserInputPoints()); |
368 | 448 |
369 RecordMetrics(); | 449 RecordMetrics(); |
370 FOR_EACH_OBSERVER( | 450 FOR_EACH_OBSERVER( |
371 SiteEngagementObserver, observer_list_, | 451 SiteEngagementObserver, observer_list_, |
372 OnEngagementIncreased(web_contents, url, GetScore(url))); | 452 OnEngagementIncreased(web_contents, url, GetScore(url))); |
373 } | 453 } |
374 | 454 |
| 455 bool SiteEngagementService::IsLastEngagementStale() const { |
| 456 // This only happens when Chrome is first run and the user has never recorded |
| 457 // any engagement. |
| 458 base::Time last_engagement_time = GetLastEngagementTime(); |
| 459 if (last_engagement_time.is_null()) |
| 460 return false; |
| 461 |
| 462 return (clock_->Now() - last_engagement_time) >= GetStalePeriod(); |
| 463 } |
| 464 |
375 void SiteEngagementService::OnURLsDeleted( | 465 void SiteEngagementService::OnURLsDeleted( |
376 history::HistoryService* history_service, | 466 history::HistoryService* history_service, |
377 bool all_history, | 467 bool all_history, |
378 bool expired, | 468 bool expired, |
379 const history::URLRows& deleted_rows, | 469 const history::URLRows& deleted_rows, |
380 const std::set<GURL>& favicon_urls) { | 470 const std::set<GURL>& favicon_urls) { |
381 std::multiset<GURL> origins; | 471 std::multiset<GURL> origins; |
382 for (const history::URLRow& row : deleted_rows) | 472 for (const history::URLRow& row : deleted_rows) |
383 origins.insert(row.url().GetOrigin()); | 473 origins.insert(row.url().GetOrigin()); |
384 | 474 |
385 history::HistoryService* hs = HistoryServiceFactory::GetForProfile( | 475 history::HistoryService* hs = HistoryServiceFactory::GetForProfile( |
386 profile_, ServiceAccessType::EXPLICIT_ACCESS); | 476 profile_, ServiceAccessType::EXPLICIT_ACCESS); |
387 hs->GetCountsAndLastVisitForOrigins( | 477 hs->GetCountsAndLastVisitForOrigins( |
388 std::set<GURL>(origins.begin(), origins.end()), | 478 std::set<GURL>(origins.begin(), origins.end()), |
389 base::Bind( | 479 base::Bind( |
390 &SiteEngagementService::GetCountsAndLastVisitForOriginsComplete, | 480 &SiteEngagementService::GetCountsAndLastVisitForOriginsComplete, |
391 weak_factory_.GetWeakPtr(), hs, origins, expired)); | 481 weak_factory_.GetWeakPtr(), hs, origins, expired)); |
392 } | 482 } |
393 | 483 |
394 const SiteEngagementScore SiteEngagementService::CreateEngagementScore( | 484 SiteEngagementScore SiteEngagementService::CreateEngagementScore( |
395 const GURL& origin) const { | 485 const GURL& origin) const { |
396 return SiteEngagementScore( | 486 return SiteEngagementScore( |
397 clock_.get(), origin, | 487 clock_.get(), origin, |
398 HostContentSettingsMapFactory::GetForProfile(profile_)); | 488 HostContentSettingsMapFactory::GetForProfile(profile_)); |
399 } | |
400 | |
401 SiteEngagementScore SiteEngagementService::CreateEngagementScore( | |
402 const GURL& origin) { | |
403 return SiteEngagementScore( | |
404 clock_.get(), origin, | |
405 HostContentSettingsMapFactory::GetForProfile(profile_)); | |
406 } | 489 } |
407 | 490 |
408 int SiteEngagementService::OriginsWithMaxDailyEngagement() const { | 491 int SiteEngagementService::OriginsWithMaxDailyEngagement() const { |
409 HostContentSettingsMap* settings_map = | 492 HostContentSettingsMap* settings_map = |
410 HostContentSettingsMapFactory::GetForProfile(profile_); | 493 HostContentSettingsMapFactory::GetForProfile(profile_); |
411 std::unique_ptr<ContentSettingsForOneType> engagement_settings = | 494 std::unique_ptr<ContentSettingsForOneType> engagement_settings = |
412 GetEngagementContentSettings(settings_map); | 495 GetEngagementContentSettings(settings_map); |
413 | 496 |
414 int total_origins = 0; | 497 int total_origins = 0; |
415 | 498 |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
496 SiteEngagementScore::kMaxPoints, | 579 SiteEngagementScore::kMaxPoints, |
497 (proportion_remaining * engagement_score.GetScore()) + undecay); | 580 (proportion_remaining * engagement_score.GetScore()) + undecay); |
498 engagement_score.Reset(score, last_visit); | 581 engagement_score.Reset(score, last_visit); |
499 if (!engagement_score.last_shortcut_launch_time().is_null() && | 582 if (!engagement_score.last_shortcut_launch_time().is_null() && |
500 engagement_score.last_shortcut_launch_time() > last_visit) { | 583 engagement_score.last_shortcut_launch_time() > last_visit) { |
501 engagement_score.set_last_shortcut_launch_time(last_visit); | 584 engagement_score.set_last_shortcut_launch_time(last_visit); |
502 } | 585 } |
503 | 586 |
504 engagement_score.Commit(); | 587 engagement_score.Commit(); |
505 } | 588 } |
| 589 |
| 590 SetLastEngagementTime(now); |
506 } | 591 } |
OLD | NEW |