| 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 |