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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/engagement/site_engagement_service.cc
diff --git a/chrome/browser/engagement/site_engagement_service.cc b/chrome/browser/engagement/site_engagement_service.cc
index fa8f699fa73d44699dceb25ff8fd40be2f399503..80671f721c91d8679450f2583f87d8744738e2fd 100644
--- a/chrome/browser/engagement/site_engagement_service.cc
+++ b/chrome/browser/engagement/site_engagement_service.cc
@@ -139,6 +139,10 @@ SiteEngagementService::~SiteEngagementService() {
profile_, ServiceAccessType::IMPLICIT_ACCESS);
if (history)
history->RemoveObserver(this);
+
+ // Clean up any callbacks which haven't been called.
+ for (const auto& wrapper : engagement_callbacks_)
+ delete wrapper.callback;
}
SiteEngagementService::EngagementLevel
@@ -209,6 +213,33 @@ bool SiteEngagementService::IsEngagementAtLeast(
return false;
}
+void SiteEngagementService::RegisterCallback(
+ const EngagementCallback& callback,
+ const GURL& url,
+ EngagementLevel level) {
+ RegisterCallback(callback, url, GetScoreForEngagementLevel(level));
+}
+
+void SiteEngagementService::RegisterCallback(
+ const EngagementCallback& callback,
+ const GURL& url,
+ double score) {
+ // We copy the callback so that when it is invoked, we can immediately delete
+ // the wrapper item in engagement_callbacks_ and ensure it cannot be called
+ // twice. If the callback is run, the copy is deleted as soon as its execution
+ // finishes in RunAndDeleteCallback. Otherwise, the copy is cleaned up in the
+ // destructor.
+ EngagementCallback* callback_ptr = new EngagementCallback(callback);
+ double current_score = GetScore(url);
+ if (current_score >= score) {
+ // Dispatch the callback immediately if its score is already at or above the
+ // requested level.
+ PostCallback(callback_ptr, url.GetOrigin(), current_score);
+ } else {
calamity 2016/05/19 03:28:22 nit: replace with early return.
dominickn 2016/05/19 04:36:14 Done.
+ engagement_callbacks_.push_back({callback_ptr, url.GetOrigin(), score});
+ }
+}
+
void SiteEngagementService::ResetScoreForURL(const GURL& url, double score) {
ResetScoreAndAccessTimesForURL(url, score, nullptr);
}
@@ -261,7 +292,10 @@ double SiteEngagementService::GetTotalEngagementPoints() const {
SiteEngagementService::SiteEngagementService(Profile* profile,
std::unique_ptr<base::Clock> clock)
- : profile_(profile), clock_(std::move(clock)), weak_factory_(this) {
+ : profile_(profile),
+ clock_(std::move(clock)),
+ 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.
+ weak_factory_(this) {
// May be null in tests.
history::HistoryService* history = HistoryServiceFactory::GetForProfile(
profile, ServiceAccessType::IMPLICIT_ACCESS);
@@ -282,6 +316,7 @@ void SiteEngagementService::AddPoints(const GURL& url, double points) {
url, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(),
score_dict.release());
}
+ 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
}
void SiteEngagementService::AfterStartupTask() {
@@ -365,6 +400,25 @@ double SiteEngagementService::GetMedianEngagement(
return (scores[mid - 1] + scores[mid]) / 2;
}
+double SiteEngagementService::GetScoreForEngagementLevel(
+ EngagementLevel level) const {
+ switch (level) {
+ case ENGAGEMENT_LEVEL_NONE:
+ return 0;
+ case ENGAGEMENT_LEVEL_LOW:
+ return SiteEngagementScore::GetMinimumEngagementIncrement();
+ case ENGAGEMENT_LEVEL_MEDIUM:
+ return SiteEngagementScore::GetMediumEngagementBoundary();
+ case ENGAGEMENT_LEVEL_HIGH:
+ return SiteEngagementScore::GetHighEngagementBoundary();
+ case ENGAGEMENT_LEVEL_MAX:
+ return SiteEngagementScore::kMaxPoints;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
void SiteEngagementService::HandleMediaPlaying(const GURL& url,
bool is_hidden) {
SiteEngagementMetrics::RecordEngagement(
@@ -503,6 +557,32 @@ void SiteEngagementService::GetCountsAndLastVisitForOriginsComplete(
}
}
+void SiteEngagementService::ProcessCallbacks(const GURL& url, double score) {
+ GURL origin = url.GetOrigin();
+ for (auto it = engagement_callbacks_.begin();
+ it != engagement_callbacks_.end();) {
+ if (origin == it->origin && score >= it->score) {
+ // Found a match. Post the callback to be run on the UI thread and
+ // immediately erase the wrapper object so we don't call it twice if the
+ // score increments again. The callback object will be deleted in
+ // RunAndDeleteCallback.
+ 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
+ it = engagement_callbacks_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+void SiteEngagementService::PostCallback(const EngagementCallback* callback,
+ const GURL& url,
+ double score) {
calamity 2016/05/19 03:28:22 nit: alignment
dominickn 2016/05/19 04:36:15 Done.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&SiteEngagementService::RunAndDeleteCallback,
+ weak_factory_.GetWeakPtr(), callback, url, score));
+}
+
void SiteEngagementService::ResetScoreAndAccessTimesForURL(
const GURL& url, double score, const base::Time* updated_time) {
DCHECK(url.is_valid());
@@ -529,3 +609,12 @@ void SiteEngagementService::ResetScoreAndAccessTimesForURL(
score_dict.release());
}
}
+
+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.
+ const EngagementCallback* callback,
+ const GURL& origin,
+ double score) const {
+ callback->Run(origin, score);
+ delete callback;
+
+}

Powered by Google App Engine
This is Rietveld 408576698