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