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

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

Issue 1986033002: Implement an observer interface for the site engagement service. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@site-engagement-refactor
Patch Set: Squash race condition in unit test which leads to a leak Created 4 years, 7 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>
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 SiteEngagementScore::UpdateFromVariations(kEngagementParams); 132 SiteEngagementScore::UpdateFromVariations(kEngagementParams);
133 g_updated_from_variations = true; 133 g_updated_from_variations = true;
134 } 134 }
135 } 135 }
136 136
137 SiteEngagementService::~SiteEngagementService() { 137 SiteEngagementService::~SiteEngagementService() {
138 history::HistoryService* history = HistoryServiceFactory::GetForProfile( 138 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
139 profile_, ServiceAccessType::IMPLICIT_ACCESS); 139 profile_, ServiceAccessType::IMPLICIT_ACCESS);
140 if (history) 140 if (history)
141 history->RemoveObserver(this); 141 history->RemoveObserver(this);
142
143 // Clean up any callbacks which haven't been called.
144 for (const auto& wrapper : engagement_callbacks_)
145 delete wrapper.callback;
142 } 146 }
143 147
144 SiteEngagementService::EngagementLevel 148 SiteEngagementService::EngagementLevel
145 SiteEngagementService::GetEngagementLevel(const GURL& url) const { 149 SiteEngagementService::GetEngagementLevel(const GURL& url) const {
146 DCHECK_LT(SiteEngagementScore::GetMediumEngagementBoundary(), 150 DCHECK_LT(SiteEngagementScore::GetMediumEngagementBoundary(),
147 SiteEngagementScore::GetHighEngagementBoundary()); 151 SiteEngagementScore::GetHighEngagementBoundary());
148 double score = GetScore(url); 152 double score = GetScore(url);
149 if (score == 0) 153 if (score == 0)
150 return ENGAGEMENT_LEVEL_NONE; 154 return ENGAGEMENT_LEVEL_NONE;
151 155
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
202 return score >= SiteEngagementScore::GetMediumEngagementBoundary(); 206 return score >= SiteEngagementScore::GetMediumEngagementBoundary();
203 case ENGAGEMENT_LEVEL_HIGH: 207 case ENGAGEMENT_LEVEL_HIGH:
204 return score >= SiteEngagementScore::GetHighEngagementBoundary(); 208 return score >= SiteEngagementScore::GetHighEngagementBoundary();
205 case ENGAGEMENT_LEVEL_MAX: 209 case ENGAGEMENT_LEVEL_MAX:
206 return score == SiteEngagementScore::kMaxPoints; 210 return score == SiteEngagementScore::kMaxPoints;
207 } 211 }
208 NOTREACHED(); 212 NOTREACHED();
209 return false; 213 return false;
210 } 214 }
211 215
216 void SiteEngagementService::RegisterCallback(
217 const EngagementCallback& callback,
218 const GURL& url,
219 EngagementLevel level) {
220 RegisterCallback(callback, url, GetScoreForEngagementLevel(level));
221 }
222
223 void SiteEngagementService::RegisterCallback(
224 const EngagementCallback& callback,
225 const GURL& url,
226 double score) {
227 // We copy the callback so that when it is invoked, we can immediately delete
228 // the wrapper item in engagement_callbacks_ and ensure it cannot be called
229 // twice. If the callback is run, the copy is deleted as soon as its execution
230 // finishes in RunAndDeleteCallback. Otherwise, the copy is cleaned up in the
231 // destructor.
232 EngagementCallback* callback_ptr = new EngagementCallback(callback);
233 double current_score = GetScore(url);
234 if (current_score >= score) {
235 // Dispatch the callback immediately if its score is already at or above the
236 // requested level.
237 PostCallback(callback_ptr, url.GetOrigin(), current_score);
238 } else {
calamity 2016/05/19 03:28:22 nit: replace with early return.
dominickn 2016/05/19 04:36:14 Done.
239 engagement_callbacks_.push_back({callback_ptr, url.GetOrigin(), score});
240 }
241 }
242
212 void SiteEngagementService::ResetScoreForURL(const GURL& url, double score) { 243 void SiteEngagementService::ResetScoreForURL(const GURL& url, double score) {
213 ResetScoreAndAccessTimesForURL(url, score, nullptr); 244 ResetScoreAndAccessTimesForURL(url, score, nullptr);
214 } 245 }
215 246
216 void SiteEngagementService::SetLastShortcutLaunchTime(const GURL& url) { 247 void SiteEngagementService::SetLastShortcutLaunchTime(const GURL& url) {
217 HostContentSettingsMap* settings_map = 248 HostContentSettingsMap* settings_map =
218 HostContentSettingsMapFactory::GetForProfile(profile_); 249 HostContentSettingsMapFactory::GetForProfile(profile_);
219 std::unique_ptr<base::DictionaryValue> score_dict = 250 std::unique_ptr<base::DictionaryValue> score_dict =
220 GetScoreDictForOrigin(settings_map, url); 251 GetScoreDictForOrigin(settings_map, url);
221 SiteEngagementScore score(clock_.get(), *score_dict); 252 SiteEngagementScore score(clock_.get(), *score_dict);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
254 285
255 double total_score = 0; 286 double total_score = 0;
256 for (const auto& value : score_map) 287 for (const auto& value : score_map)
257 total_score += value.second; 288 total_score += value.second;
258 289
259 return total_score; 290 return total_score;
260 } 291 }
261 292
262 SiteEngagementService::SiteEngagementService(Profile* profile, 293 SiteEngagementService::SiteEngagementService(Profile* profile,
263 std::unique_ptr<base::Clock> clock) 294 std::unique_ptr<base::Clock> clock)
264 : profile_(profile), clock_(std::move(clock)), weak_factory_(this) { 295 : profile_(profile),
296 clock_(std::move(clock)),
297 engagement_callbacks_(),
calamity 2016/05/19 03:28:22 vectors should initialize fine without being in th
dominickn 2016/05/19 04:36:14 Done.
298 weak_factory_(this) {
265 // May be null in tests. 299 // May be null in tests.
266 history::HistoryService* history = HistoryServiceFactory::GetForProfile( 300 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
267 profile, ServiceAccessType::IMPLICIT_ACCESS); 301 profile, ServiceAccessType::IMPLICIT_ACCESS);
268 if (history) 302 if (history)
269 history->AddObserver(this); 303 history->AddObserver(this);
270 } 304 }
271 305
272 void SiteEngagementService::AddPoints(const GURL& url, double points) { 306 void SiteEngagementService::AddPoints(const GURL& url, double points) {
273 HostContentSettingsMap* settings_map = 307 HostContentSettingsMap* settings_map =
274 HostContentSettingsMapFactory::GetForProfile(profile_); 308 HostContentSettingsMapFactory::GetForProfile(profile_);
275 std::unique_ptr<base::DictionaryValue> score_dict = 309 std::unique_ptr<base::DictionaryValue> score_dict =
276 GetScoreDictForOrigin(settings_map, url); 310 GetScoreDictForOrigin(settings_map, url);
277 SiteEngagementScore score(clock_.get(), *score_dict); 311 SiteEngagementScore score(clock_.get(), *score_dict);
278 312
279 score.AddPoints(points); 313 score.AddPoints(points);
280 if (score.UpdateScoreDict(score_dict.get())) { 314 if (score.UpdateScoreDict(score_dict.get())) {
281 settings_map->SetWebsiteSettingDefaultScope( 315 settings_map->SetWebsiteSettingDefaultScope(
282 url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), 316 url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
283 score_dict.release()); 317 score_dict.release());
284 } 318 }
319 ProcessCallbacks(url, score.GetScore());
calamity 2016/05/19 03:28:22 Does this also need to be run when scores are rese
dominickn 2016/05/19 04:36:14 I explicitly decided to not do that: this runs onl
285 } 320 }
286 321
287 void SiteEngagementService::AfterStartupTask() { 322 void SiteEngagementService::AfterStartupTask() {
288 CleanupEngagementScores(); 323 CleanupEngagementScores();
289 RecordMetrics(); 324 RecordMetrics();
290 } 325 }
291 326
292 void SiteEngagementService::CleanupEngagementScores() { 327 void SiteEngagementService::CleanupEngagementScores() {
293 HostContentSettingsMap* settings_map = 328 HostContentSettingsMap* settings_map =
294 HostContentSettingsMapFactory::GetForProfile(profile_); 329 HostContentSettingsMapFactory::GetForProfile(profile_);
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
358 // if there are an odd number of scores, or the average of the two middle 393 // if there are an odd number of scores, or the average of the two middle
359 // scores otherwise. 394 // scores otherwise.
360 std::sort(scores.begin(), scores.end()); 395 std::sort(scores.begin(), scores.end());
361 size_t mid = scores.size() / 2; 396 size_t mid = scores.size() / 2;
362 if (scores.size() % 2 == 1) 397 if (scores.size() % 2 == 1)
363 return scores[mid]; 398 return scores[mid];
364 else 399 else
365 return (scores[mid - 1] + scores[mid]) / 2; 400 return (scores[mid - 1] + scores[mid]) / 2;
366 } 401 }
367 402
403 double SiteEngagementService::GetScoreForEngagementLevel(
404 EngagementLevel level) const {
405 switch (level) {
406 case ENGAGEMENT_LEVEL_NONE:
407 return 0;
408 case ENGAGEMENT_LEVEL_LOW:
409 return SiteEngagementScore::GetMinimumEngagementIncrement();
410 case ENGAGEMENT_LEVEL_MEDIUM:
411 return SiteEngagementScore::GetMediumEngagementBoundary();
412 case ENGAGEMENT_LEVEL_HIGH:
413 return SiteEngagementScore::GetHighEngagementBoundary();
414 case ENGAGEMENT_LEVEL_MAX:
415 return SiteEngagementScore::kMaxPoints;
416 default:
417 NOTREACHED();
418 return 0;
419 }
420 }
421
368 void SiteEngagementService::HandleMediaPlaying(const GURL& url, 422 void SiteEngagementService::HandleMediaPlaying(const GURL& url,
369 bool is_hidden) { 423 bool is_hidden) {
370 SiteEngagementMetrics::RecordEngagement( 424 SiteEngagementMetrics::RecordEngagement(
371 is_hidden ? SiteEngagementMetrics::ENGAGEMENT_MEDIA_HIDDEN 425 is_hidden ? SiteEngagementMetrics::ENGAGEMENT_MEDIA_HIDDEN
372 : SiteEngagementMetrics::ENGAGEMENT_MEDIA_VISIBLE); 426 : SiteEngagementMetrics::ENGAGEMENT_MEDIA_VISIBLE);
373 AddPoints(url, is_hidden ? SiteEngagementScore::GetHiddenMediaPoints() 427 AddPoints(url, is_hidden ? SiteEngagementScore::GetHiddenMediaPoints()
374 : SiteEngagementScore::GetVisibleMediaPoints()); 428 : SiteEngagementScore::GetVisibleMediaPoints());
375 RecordMetrics(); 429 RecordMetrics();
376 } 430 }
377 431
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 undecay = periods * SiteEngagementScore::GetDecayPoints(); 550 undecay = periods * SiteEngagementScore::GetDecayPoints();
497 } 551 }
498 552
499 double score = 553 double score =
500 std::min(SiteEngagementScore::kMaxPoints, 554 std::min(SiteEngagementScore::kMaxPoints,
501 (proportion_remaining * GetScore(origin)) + undecay); 555 (proportion_remaining * GetScore(origin)) + undecay);
502 ResetScoreAndAccessTimesForURL(origin, score, &last_visit); 556 ResetScoreAndAccessTimesForURL(origin, score, &last_visit);
503 } 557 }
504 } 558 }
505 559
560 void SiteEngagementService::ProcessCallbacks(const GURL& url, double score) {
561 GURL origin = url.GetOrigin();
562 for (auto it = engagement_callbacks_.begin();
563 it != engagement_callbacks_.end();) {
564 if (origin == it->origin && score >= it->score) {
565 // Found a match. Post the callback to be run on the UI thread and
566 // immediately erase the wrapper object so we don't call it twice if the
567 // score increments again. The callback object will be deleted in
568 // RunAndDeleteCallback.
569 PostCallback(it->callback, url, score);
calamity 2016/05/19 03:28:22 Why do we post this rather than running immediatel
dominickn 2016/05/19 04:36:14 This is potentially run on user input (like scroll
570 it = engagement_callbacks_.erase(it);
571 } else {
572 ++it;
573 }
574 }
575 }
576
577 void SiteEngagementService::PostCallback(const EngagementCallback* callback,
578 const GURL& url,
579 double score) {
calamity 2016/05/19 03:28:22 nit: alignment
dominickn 2016/05/19 04:36:15 Done.
580 content::BrowserThread::PostTask(
581 content::BrowserThread::UI, FROM_HERE,
582 base::Bind(&SiteEngagementService::RunAndDeleteCallback,
583 weak_factory_.GetWeakPtr(), callback, url, score));
584 }
585
506 void SiteEngagementService::ResetScoreAndAccessTimesForURL( 586 void SiteEngagementService::ResetScoreAndAccessTimesForURL(
507 const GURL& url, double score, const base::Time* updated_time) { 587 const GURL& url, double score, const base::Time* updated_time) {
508 DCHECK(url.is_valid()); 588 DCHECK(url.is_valid());
509 DCHECK_GE(score, 0); 589 DCHECK_GE(score, 0);
510 DCHECK_LE(score, SiteEngagementScore::kMaxPoints); 590 DCHECK_LE(score, SiteEngagementScore::kMaxPoints);
511 591
512 HostContentSettingsMap* settings_map = 592 HostContentSettingsMap* settings_map =
513 HostContentSettingsMapFactory::GetForProfile(profile_); 593 HostContentSettingsMapFactory::GetForProfile(profile_);
514 std::unique_ptr<base::DictionaryValue> score_dict = 594 std::unique_ptr<base::DictionaryValue> score_dict =
515 GetScoreDictForOrigin(settings_map, url); 595 GetScoreDictForOrigin(settings_map, url);
516 SiteEngagementScore engagement_score(clock_.get(), *score_dict); 596 SiteEngagementScore engagement_score(clock_.get(), *score_dict);
517 597
518 engagement_score.Reset(score, updated_time); 598 engagement_score.Reset(score, updated_time);
519 if (score == 0) { 599 if (score == 0) {
520 settings_map->SetWebsiteSettingDefaultScope( 600 settings_map->SetWebsiteSettingDefaultScope(
521 url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), 601 url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
522 nullptr); 602 nullptr);
523 return; 603 return;
524 } 604 }
525 605
526 if (engagement_score.UpdateScoreDict(score_dict.get())) { 606 if (engagement_score.UpdateScoreDict(score_dict.get())) {
527 settings_map->SetWebsiteSettingDefaultScope( 607 settings_map->SetWebsiteSettingDefaultScope(
528 url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), 608 url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
529 score_dict.release()); 609 score_dict.release());
530 } 610 }
531 } 611 }
612
613 void SiteEngagementService::RunAndDeleteCallback(
calamity 2016/05/19 03:28:22 This can be in the anonymous namespace. Doesn't ne
dominickn 2016/05/19 04:36:15 Done.
614 const EngagementCallback* callback,
615 const GURL& origin,
616 double score) const {
617 callback->Run(origin, score);
618 delete callback;
619
620 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698