Chromium Code Reviews| 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 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 111 base::Bind(&SiteEngagementService::AfterStartupTask, | 114 base::Bind(&SiteEngagementService::AfterStartupTask, |
| 112 weak_factory_.GetWeakPtr())); | 115 weak_factory_.GetWeakPtr())); |
| 113 | 116 |
| 114 if (!g_updated_from_variations) { | 117 if (!g_updated_from_variations) { |
| 115 SiteEngagementScore::UpdateFromVariations(kEngagementParams); | 118 SiteEngagementScore::UpdateFromVariations(kEngagementParams); |
| 116 g_updated_from_variations = true; | 119 g_updated_from_variations = true; |
| 117 } | 120 } |
| 118 } | 121 } |
| 119 | 122 |
| 120 SiteEngagementService::~SiteEngagementService() { | 123 SiteEngagementService::~SiteEngagementService() { |
| 124 // Persist the last engagement time to disk. | |
| 125 profile_->GetPrefs()->SetInt64(prefs::kSiteEngagementLastUpdateTime, | |
| 126 last_engagement_time_.ToInternalValue()); | |
| 127 | |
| 121 history::HistoryService* history = HistoryServiceFactory::GetForProfile( | 128 history::HistoryService* history = HistoryServiceFactory::GetForProfile( |
| 122 profile_, ServiceAccessType::IMPLICIT_ACCESS); | 129 profile_, ServiceAccessType::IMPLICIT_ACCESS); |
| 123 if (history) | 130 if (history) |
| 124 history->RemoveObserver(this); | 131 history->RemoveObserver(this); |
| 125 } | 132 } |
| 126 | 133 |
| 127 SiteEngagementService::EngagementLevel | 134 SiteEngagementService::EngagementLevel |
| 128 SiteEngagementService::GetEngagementLevel(const GURL& url) const { | 135 SiteEngagementService::GetEngagementLevel(const GURL& url) const { |
| 129 DCHECK_LT(SiteEngagementScore::GetMediumEngagementBoundary(), | 136 DCHECK_LT(SiteEngagementScore::GetMediumEngagementBoundary(), |
| 130 SiteEngagementScore::GetHighEngagementBoundary()); | 137 SiteEngagementScore::GetHighEngagementBoundary()); |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 223 double total_score = 0; | 230 double total_score = 0; |
| 224 for (const auto& value : score_map) | 231 for (const auto& value : score_map) |
| 225 total_score += value.second; | 232 total_score += value.second; |
| 226 | 233 |
| 227 return total_score; | 234 return total_score; |
| 228 } | 235 } |
| 229 | 236 |
| 230 SiteEngagementService::SiteEngagementService(Profile* profile, | 237 SiteEngagementService::SiteEngagementService(Profile* profile, |
| 231 std::unique_ptr<base::Clock> clock) | 238 std::unique_ptr<base::Clock> clock) |
| 232 : profile_(profile), clock_(std::move(clock)), weak_factory_(this) { | 239 : profile_(profile), clock_(std::move(clock)), weak_factory_(this) { |
| 240 // Read in the last engagement time (or a null time on first run) from disk. | |
| 241 last_engagement_time_ = base::Time::FromInternalValue( | |
| 242 profile->GetPrefs()->GetInt64(prefs::kSiteEngagementLastUpdateTime)); | |
| 243 | |
| 233 // May be null in tests. | 244 // May be null in tests. |
| 234 history::HistoryService* history = HistoryServiceFactory::GetForProfile( | 245 history::HistoryService* history = HistoryServiceFactory::GetForProfile( |
| 235 profile, ServiceAccessType::IMPLICIT_ACCESS); | 246 profile, ServiceAccessType::IMPLICIT_ACCESS); |
| 236 if (history) | 247 if (history) |
| 237 history->AddObserver(this); | 248 history->AddObserver(this); |
| 238 } | 249 } |
| 239 | 250 |
| 240 void SiteEngagementService::AddPoints(const GURL& url, double points) { | 251 void SiteEngagementService::AddPoints(const GURL& url, double points) { |
| 252 // Trigger a cleanup and date adjustment if it has been a substantial length | |
| 253 // of time since *any* engagement was recorded by the service. This will | |
| 254 // ensure that we do not decay scores when the user did not use the browser. | |
| 255 // IsLastEngagementObsolete is tested separately so that we do not run cleanup | |
| 256 // in this method unless absolutely necessary. | |
| 257 if (IsLastEngagementObsolete()) | |
| 258 CleanupEngagementScores(true); | |
| 259 | |
| 241 SiteEngagementScore score = CreateEngagementScore(url); | 260 SiteEngagementScore score = CreateEngagementScore(url); |
| 242 | |
| 243 score.AddPoints(points); | 261 score.AddPoints(points); |
| 244 score.Commit(); | 262 score.Commit(); |
| 263 | |
| 264 last_engagement_time_ = score.last_engagement_time(); | |
| 245 } | 265 } |
| 246 | 266 |
| 247 void SiteEngagementService::AfterStartupTask() { | 267 void SiteEngagementService::AfterStartupTask() { |
| 248 CleanupEngagementScores(); | 268 // Check if we need to reset last engagement times on startup - we want to |
| 269 // avoid doing this in AddPoints() if possible. It is still necessary to check | |
| 270 // in AddPoints for people who never restart Chrome, but leave it open and | |
| 271 // their computer on standby. | |
| 272 CleanupEngagementScores(IsLastEngagementObsolete()); | |
| 249 RecordMetrics(); | 273 RecordMetrics(); |
| 250 } | 274 } |
| 251 | 275 |
| 252 void SiteEngagementService::CleanupEngagementScores() { | 276 void SiteEngagementService::CleanupEngagementScores( |
|
calamity
2016/06/23 05:18:40
It might be worth renaming this method now since i
dominickn
2016/06/27 02:53:35
I think Cleanup is still the right name for the me
| |
| 277 bool update_last_engagement_time) { | |
| 278 // This method should not be called with |update_last_engagement_time| = true | |
| 279 // if the last engagement time isn't obsolete. | |
| 280 DCHECK(!update_last_engagement_time || IsLastEngagementObsolete()); | |
| 281 | |
| 253 HostContentSettingsMap* settings_map = | 282 HostContentSettingsMap* settings_map = |
| 254 HostContentSettingsMapFactory::GetForProfile(profile_); | 283 HostContentSettingsMapFactory::GetForProfile(profile_); |
| 255 std::unique_ptr<ContentSettingsForOneType> engagement_settings = | 284 std::unique_ptr<ContentSettingsForOneType> engagement_settings = |
| 256 GetEngagementContentSettings(settings_map); | 285 GetEngagementContentSettings(settings_map); |
| 257 | 286 |
| 287 base::Time now = clock_->Now(); | |
| 258 for (const auto& site : *engagement_settings) { | 288 for (const auto& site : *engagement_settings) { |
| 259 GURL origin(site.primary_pattern.ToString()); | 289 GURL origin(site.primary_pattern.ToString()); |
| 260 if (origin.is_valid() && GetScore(origin) != 0) | 290 SiteEngagementScore score = CreateEngagementScore(origin); |
| 291 | |
| 292 if (origin.is_valid()) { | |
| 293 if (update_last_engagement_time) { | |
| 294 // Work out the offset between this score's last engagement time and the | |
| 295 // last time the service recorded any engagement. Set the score's last | |
| 296 // engagement time to now - offset to preserve its state, but relative | |
| 297 // to now. | |
| 298 base::TimeDelta offset = | |
| 299 last_engagement_time_ - score.last_engagement_time(); | |
| 300 score.set_last_engagement_time(now - offset); | |
|
calamity
2016/06/23 05:18:40
I think that all scores should be rebased to the o
dominickn
2016/06/27 02:53:35
Done.
| |
| 301 } | |
| 302 | |
| 303 if (score.GetScore() != 0) { | |
| 304 score.Commit(); | |
| 261 continue; | 305 continue; |
| 306 } | |
| 307 } | |
| 262 | 308 |
| 309 // This origin has a score of 0. Wipe it from content settings. | |
| 263 settings_map->SetWebsiteSettingDefaultScope( | 310 settings_map->SetWebsiteSettingDefaultScope( |
| 264 origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), | 311 origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), |
| 265 nullptr); | 312 nullptr); |
| 266 } | 313 } |
| 314 | |
| 315 // Set the last engagement time to now for consistency and to ensure we do not | |
| 316 // double-reset. Persist the time to disk. | |
| 317 last_engagement_time_ = now; | |
| 318 profile_->GetPrefs()->SetInt64(prefs::kSiteEngagementLastUpdateTime, | |
| 319 last_engagement_time_.ToInternalValue()); | |
| 267 } | 320 } |
| 268 | 321 |
| 269 void SiteEngagementService::RecordMetrics() { | 322 void SiteEngagementService::RecordMetrics() { |
| 270 base::Time now = clock_->Now(); | 323 base::Time now = clock_->Now(); |
| 271 if (last_metrics_time_.is_null() || | 324 if (last_metrics_time_.is_null() || |
| 272 (now - last_metrics_time_).InMinutes() >= kMetricsIntervalInMinutes) { | 325 (now - last_metrics_time_).InMinutes() >= kMetricsIntervalInMinutes) { |
| 273 last_metrics_time_ = now; | 326 last_metrics_time_ = now; |
| 274 std::map<GURL, double> score_map = GetScoreMap(); | 327 std::map<GURL, double> score_map = GetScoreMap(); |
| 275 | 328 |
| 276 int origins_with_max_engagement = OriginsWithMaxEngagement(score_map); | 329 int origins_with_max_engagement = OriginsWithMaxEngagement(score_map); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 292 | 345 |
| 293 SiteEngagementMetrics::RecordOriginsWithMaxDailyEngagement( | 346 SiteEngagementMetrics::RecordOriginsWithMaxDailyEngagement( |
| 294 OriginsWithMaxDailyEngagement()); | 347 OriginsWithMaxDailyEngagement()); |
| 295 SiteEngagementMetrics::RecordOriginsWithMaxEngagement( | 348 SiteEngagementMetrics::RecordOriginsWithMaxEngagement( |
| 296 origins_with_max_engagement); | 349 origins_with_max_engagement); |
| 297 SiteEngagementMetrics::RecordPercentOriginsWithMaxEngagement( | 350 SiteEngagementMetrics::RecordPercentOriginsWithMaxEngagement( |
| 298 percent_origins_with_max_engagement); | 351 percent_origins_with_max_engagement); |
| 299 } | 352 } |
| 300 } | 353 } |
| 301 | 354 |
| 355 bool SiteEngagementService::IsLastEngagementObsolete() { | |
| 356 // This only happens when Chrome is first run and the user has never recorded | |
| 357 // any engagement. | |
| 358 if (last_engagement_time_.is_null()) | |
| 359 return false; | |
| 360 | |
| 361 // Last engagement is obsolete when more than | |
| 362 // OBSOLETE_LAST_ENGAGEMENT_PERIOD_IN_HOURS have passed. | |
| 363 return (clock_->Now() - last_engagement_time_) >= | |
| 364 base::TimeDelta::FromHours( | |
| 365 SiteEngagementScore::GetObsoleteLastEngagementPeriodInHours()); | |
| 366 } | |
| 367 | |
| 302 double SiteEngagementService::GetMedianEngagement( | 368 double SiteEngagementService::GetMedianEngagement( |
| 303 const std::map<GURL, double>& score_map) const { | 369 const std::map<GURL, double>& score_map) const { |
| 304 if (score_map.size() == 0) | 370 if (score_map.size() == 0) |
| 305 return 0; | 371 return 0; |
| 306 | 372 |
| 307 std::vector<double> scores; | 373 std::vector<double> scores; |
| 308 scores.reserve(score_map.size()); | 374 scores.reserve(score_map.size()); |
| 309 for (const auto& value : score_map) | 375 for (const auto& value : score_map) |
| 310 scores.push_back(value.second); | 376 scores.push_back(value.second); |
| 311 | 377 |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 477 (proportion_remaining * engagement_score.GetScore()) + undecay); | 543 (proportion_remaining * engagement_score.GetScore()) + undecay); |
| 478 engagement_score.Reset(score, last_visit); | 544 engagement_score.Reset(score, last_visit); |
| 479 if (!engagement_score.last_shortcut_launch_time().is_null() | 545 if (!engagement_score.last_shortcut_launch_time().is_null() |
| 480 && engagement_score.last_shortcut_launch_time() > last_visit) { | 546 && engagement_score.last_shortcut_launch_time() > last_visit) { |
| 481 engagement_score.set_last_shortcut_launch_time(last_visit); | 547 engagement_score.set_last_shortcut_launch_time(last_visit); |
| 482 } | 548 } |
| 483 | 549 |
| 484 engagement_score.Commit(); | 550 engagement_score.Commit(); |
| 485 } | 551 } |
| 486 } | 552 } |
| OLD | NEW |