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> |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } | |
OLD | NEW |