Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(135)

Side by Side Diff: chrome/browser/engagement/site_engagement_service.cc

Issue 2082953002: Prevent site engagement scores from decaying when Chrome isn't in use. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « chrome/browser/engagement/site_engagement_service.h ('k') | chrome/browser/engagement/site_engagement_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698