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

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

Issue 2788413003: Add SiteEngagementService::GetAllDetails(), to return detailed scores. (Closed)
Patch Set: Fix notifications permission logic & test Created 3 years, 8 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>
12 11
13 #include "base/command_line.h" 12 #include "base/command_line.h"
14 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
15 #include "base/metrics/field_trial.h" 14 #include "base/metrics/field_trial.h"
16 #include "base/strings/string_util.h" 15 #include "base/strings/string_util.h"
17 #include "base/time/clock.h" 16 #include "base/time/clock.h"
18 #include "base/time/default_clock.h" 17 #include "base/time/default_clock.h"
19 #include "base/time/time.h" 18 #include "base/time/time.h"
20 #include "base/values.h" 19 #include "base/values.h"
21 #include "chrome/browser/banners/app_banner_settings_helper.h" 20 #include "chrome/browser/banners/app_banner_settings_helper.h"
(...skipping 23 matching lines...) Expand all
45 namespace { 44 namespace {
46 45
47 const int FOUR_WEEKS_IN_DAYS = 28; 46 const int FOUR_WEEKS_IN_DAYS = 28;
48 47
49 // Global bool to ensure we only update the parameters from variations once. 48 // Global bool to ensure we only update the parameters from variations once.
50 bool g_updated_from_variations = false; 49 bool g_updated_from_variations = false;
51 50
52 // Length of time between metrics logging. 51 // Length of time between metrics logging.
53 const int kMetricsIntervalInMinutes = 60; 52 const int kMetricsIntervalInMinutes = 60;
54 53
55 std::unique_ptr<ContentSettingsForOneType> GetEngagementContentSettings( 54 // Helper for fetching content settings for one type.
56 HostContentSettingsMap* settings_map) { 55 ContentSettingsForOneType GetContentSettingsFromProfile(
57 std::unique_ptr<ContentSettingsForOneType> engagement_settings( 56 Profile* profile,
58 new ContentSettingsForOneType); 57 ContentSettingsType type) {
59 settings_map->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, 58 ContentSettingsForOneType content_settings;
60 std::string(), engagement_settings.get()); 59 HostContentSettingsMapFactory::GetForProfile(profile)->GetSettingsForOneType(
61 return engagement_settings; 60 type, content_settings::ResourceIdentifier(), &content_settings);
61 return content_settings;
62 }
63
64 // Returns the combined list of origins which either have site engagement
65 // data stored, or have other settings that would provide a score bonus.
66 std::set<GURL> GetEngagementOriginsFromContentSettings(Profile* profile) {
67 std::set<GURL> urls;
68
69 // Fetch URLs of sites with engagement details stored.
70 for (const auto& site : GetContentSettingsFromProfile(
71 profile, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT)) {
72 urls.insert(GURL(site.primary_pattern.ToString()));
73 }
74
75 // Fetch URLs of sites for which notifications are allowed.
76 for (const auto& site : GetContentSettingsFromProfile(
77 profile, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)) {
78 if (site.setting != CONTENT_SETTING_ALLOW)
79 continue;
80 urls.insert(GURL(site.primary_pattern.ToString()));
81 }
82
83 return urls;
62 } 84 }
63 85
64 // Only accept a navigation event for engagement if it is one of: 86 // Only accept a navigation event for engagement if it is one of:
65 // a. direct typed navigation 87 // a. direct typed navigation
66 // b. clicking on an omnibox suggestion brought up by typing a keyword 88 // b. clicking on an omnibox suggestion brought up by typing a keyword
67 // c. clicking on a bookmark or opening a bookmark app 89 // c. clicking on a bookmark or opening a bookmark app
68 // d. a custom search engine keyword search (e.g. Wikipedia search box added as 90 // d. a custom search engine keyword search (e.g. Wikipedia search box added as
69 // search engine). 91 // search engine).
70 bool IsEngagementNavigation(ui::PageTransition transition) { 92 bool IsEngagementNavigation(ui::PageTransition transition) {
71 return ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) || 93 return ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) ||
(...skipping 25 matching lines...) Expand all
97 base::FieldTrialList::FindFullName(kEngagementParams); 119 base::FieldTrialList::FindFullName(kEngagementParams);
98 return !base::StartsWith(group_name, "Disabled", 120 return !base::StartsWith(group_name, "Disabled",
99 base::CompareCase::SENSITIVE); 121 base::CompareCase::SENSITIVE);
100 } 122 }
101 123
102 // static 124 // static
103 double SiteEngagementService::GetScoreFromSettings( 125 double SiteEngagementService::GetScoreFromSettings(
104 HostContentSettingsMap* settings, 126 HostContentSettingsMap* settings,
105 const GURL& origin) { 127 const GURL& origin) {
106 auto clock = base::MakeUnique<base::DefaultClock>(); 128 auto clock = base::MakeUnique<base::DefaultClock>();
107 return SiteEngagementScore(clock.get(), origin, settings) 129 return SiteEngagementScore(clock.get(), origin, settings).GetTotalScore();
108 .GetScore();
109 } 130 }
110 131
111 SiteEngagementService::SiteEngagementService(Profile* profile) 132 SiteEngagementService::SiteEngagementService(Profile* profile)
112 : SiteEngagementService(profile, base::MakeUnique<base::DefaultClock>()) { 133 : SiteEngagementService(profile, base::MakeUnique<base::DefaultClock>()) {
113 content::BrowserThread::PostAfterStartupTask( 134 content::BrowserThread::PostAfterStartupTask(
114 FROM_HERE, content::BrowserThread::GetTaskRunnerForThread( 135 FROM_HERE, content::BrowserThread::GetTaskRunnerForThread(
115 content::BrowserThread::UI), 136 content::BrowserThread::UI),
116 base::Bind(&SiteEngagementService::AfterStartupTask, 137 base::Bind(&SiteEngagementService::AfterStartupTask,
117 weak_factory_.GetWeakPtr())); 138 weak_factory_.GetWeakPtr()));
118 139
(...skipping 13 matching lines...) Expand all
132 } 153 }
133 154
134 blink::mojom::EngagementLevel 155 blink::mojom::EngagementLevel
135 SiteEngagementService::GetEngagementLevel(const GURL& url) const { 156 SiteEngagementService::GetEngagementLevel(const GURL& url) const {
136 if (IsLastEngagementStale()) 157 if (IsLastEngagementStale())
137 CleanupEngagementScores(true); 158 CleanupEngagementScores(true);
138 159
139 return CreateEngagementScore(url).GetEngagementLevel(); 160 return CreateEngagementScore(url).GetEngagementLevel();
140 } 161 }
141 162
142 std::map<GURL, double> SiteEngagementService::GetScoreMap() const { 163 std::vector<mojom::SiteEngagementDetails> SiteEngagementService::GetAllDetails()
143 HostContentSettingsMap* settings_map = 164 const {
144 HostContentSettingsMapFactory::GetForProfile(profile_); 165 std::set<GURL> origins = GetEngagementOriginsFromContentSettings(profile_);
145 std::unique_ptr<ContentSettingsForOneType> engagement_settings =
146 GetEngagementContentSettings(settings_map);
147 166
148 std::map<GURL, double> score_map; 167 std::vector<mojom::SiteEngagementDetails> details;
149 for (const auto& site : *engagement_settings) { 168 details.reserve(origins.size());
150 GURL origin(site.primary_pattern.ToString()); 169 for (const GURL& origin : origins) {
151 if (!origin.is_valid()) 170 if (!origin.is_valid())
152 continue; 171 continue;
172 details.push_back(GetDetails(origin));
173 }
153 174
175 return details;
176 }
177
178 std::map<GURL, double> SiteEngagementService::GetScoreMap() const {
179 std::map<GURL, double> score_map;
180 for (const GURL& origin : GetEngagementOriginsFromContentSettings(profile_)) {
181 if (!origin.is_valid())
182 continue;
154 score_map[origin] = GetScore(origin); 183 score_map[origin] = GetScore(origin);
155 } 184 }
156
157 return score_map; 185 return score_map;
158 } 186 }
159 187
160 void SiteEngagementService::HandleNotificationInteraction(const GURL& url) { 188 void SiteEngagementService::HandleNotificationInteraction(const GURL& url) {
161 if (!ShouldRecordEngagement(url)) 189 if (!ShouldRecordEngagement(url))
162 return; 190 return;
163 191
164 SiteEngagementMetrics::RecordEngagement( 192 SiteEngagementMetrics::RecordEngagement(
165 SiteEngagementMetrics::ENGAGEMENT_NOTIFICATION_INTERACTION); 193 SiteEngagementMetrics::ENGAGEMENT_NOTIFICATION_INTERACTION);
166 AddPoints(url, SiteEngagementScore::GetNotificationInteractionPoints()); 194 AddPoints(url, SiteEngagementScore::GetNotificationInteractionPoints());
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 SiteEngagementService::Helper* helper) { 265 SiteEngagementService::Helper* helper) {
238 helpers_.insert(helper); 266 helpers_.insert(helper);
239 } 267 }
240 268
241 void SiteEngagementService::HelperDeleted( 269 void SiteEngagementService::HelperDeleted(
242 SiteEngagementService::Helper* helper) { 270 SiteEngagementService::Helper* helper) {
243 helpers_.erase(helper); 271 helpers_.erase(helper);
244 } 272 }
245 273
246 double SiteEngagementService::GetScore(const GURL& url) const { 274 double SiteEngagementService::GetScore(const GURL& url) const {
275 return GetDetails(url).total_score;
276 }
277
278 mojom::SiteEngagementDetails SiteEngagementService::GetDetails(
279 const GURL& url) const {
247 // Ensure that if engagement is stale, we clean things up before fetching the 280 // Ensure that if engagement is stale, we clean things up before fetching the
248 // score. 281 // score.
249 if (IsLastEngagementStale()) 282 if (IsLastEngagementStale())
250 CleanupEngagementScores(true); 283 CleanupEngagementScores(true);
251 284
252 return CreateEngagementScore(url).GetScore(); 285 return CreateEngagementScore(url).GetDetails();
253 } 286 }
254 287
255 double SiteEngagementService::GetTotalEngagementPoints() const { 288 double SiteEngagementService::GetTotalEngagementPoints() const {
256 std::map<GURL, double> score_map = GetScoreMap(); 289 std::map<GURL, double> score_map = GetScoreMap();
257 290
258 double total_score = 0; 291 double total_score = 0;
259 for (const auto& value : score_map) 292 for (const auto& value : score_map)
260 total_score += value.second; 293 total_score += value.second;
261 294
262 return total_score; 295 return total_score;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 // Check if we need to reset last engagement times on startup - we want to 343 // Check if we need to reset last engagement times on startup - we want to
311 // avoid doing this in AddPoints() if possible. It is still necessary to check 344 // avoid doing this in AddPoints() if possible. It is still necessary to check
312 // in AddPoints for people who never restart Chrome, but leave it open and 345 // in AddPoints for people who never restart Chrome, but leave it open and
313 // their computer on standby. 346 // their computer on standby.
314 CleanupEngagementScores(IsLastEngagementStale()); 347 CleanupEngagementScores(IsLastEngagementStale());
315 RecordMetrics(); 348 RecordMetrics();
316 } 349 }
317 350
318 void SiteEngagementService::CleanupEngagementScores( 351 void SiteEngagementService::CleanupEngagementScores(
319 bool update_last_engagement_time) const { 352 bool update_last_engagement_time) const {
320 HostContentSettingsMap* settings_map =
321 HostContentSettingsMapFactory::GetForProfile(profile_);
322 std::unique_ptr<ContentSettingsForOneType> engagement_settings =
323 GetEngagementContentSettings(settings_map);
324
325 // We want to rebase last engagement times relative to MaxDecaysPerScore 353 // We want to rebase last engagement times relative to MaxDecaysPerScore
326 // periods of decay in the past. 354 // periods of decay in the past.
327 base::Time now = clock_->Now(); 355 base::Time now = clock_->Now();
328 base::Time last_engagement_time = GetLastEngagementTime(); 356 base::Time last_engagement_time = GetLastEngagementTime();
329 base::Time rebase_time = now - GetMaxDecayPeriod(); 357 base::Time rebase_time = now - GetMaxDecayPeriod();
330 base::Time new_last_engagement_time; 358 base::Time new_last_engagement_time;
331 359
332 // If |update_last_engagement_time| is true, we must have either: 360 // If |update_last_engagement_time| is true, we must have either:
333 // a) last_engagement_time is in the future; OR 361 // a) last_engagement_time is in the future; OR
334 // b) last_engagement_time < rebase_time < now 362 // b) last_engagement_time < rebase_time < now
335 DCHECK(!update_last_engagement_time || last_engagement_time >= now || 363 DCHECK(!update_last_engagement_time || last_engagement_time >= now ||
336 (last_engagement_time < rebase_time && rebase_time < now)); 364 (last_engagement_time < rebase_time && rebase_time < now));
337 365
338 // Cap |last_engagement_time| at |now| if it is in the future. This ensures 366 // Cap |last_engagement_time| at |now| if it is in the future. This ensures
339 // that we use sane offsets when a user has adjusted their clock backwards and 367 // that we use sane offsets when a user has adjusted their clock backwards and
340 // have a mix of scores prior to and after |now|. 368 // have a mix of scores prior to and after |now|.
341 if (last_engagement_time > now) 369 if (last_engagement_time > now)
342 last_engagement_time = now; 370 last_engagement_time = now;
343 371
344 for (const auto& site : *engagement_settings) { 372 HostContentSettingsMap* settings_map =
373 HostContentSettingsMapFactory::GetForProfile(profile_);
374 for (const auto& site : GetContentSettingsFromProfile(
375 profile_, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT)) {
345 GURL origin(site.primary_pattern.ToString()); 376 GURL origin(site.primary_pattern.ToString());
346 377
347 if (origin.is_valid()) { 378 if (origin.is_valid()) {
348 SiteEngagementScore score = CreateEngagementScore(origin); 379 SiteEngagementScore score = CreateEngagementScore(origin);
349 if (update_last_engagement_time) { 380 if (update_last_engagement_time) {
350 // Catch cases of users moving their clocks, or a potential race where 381 // Catch cases of users moving their clocks, or a potential race where
351 // a score content setting is written out to prefs, but the updated 382 // a score content setting is written out to prefs, but the updated
352 // |last_engagement_time| was not written, as both are lossy 383 // |last_engagement_time| was not written, as both are lossy
353 // preferences. |rebase_time| is strictly in the past, so any score with 384 // preferences. |rebase_time| is strictly in the past, so any score with
354 // a last updated time in the future is caught by this branch. 385 // a last updated time in the future is caught by this branch.
(...skipping 14 matching lines...) Expand all
369 last_engagement_time - score.last_engagement_time(); 400 last_engagement_time - score.last_engagement_time();
370 base::Time rebase_score_time = rebase_time - offset; 401 base::Time rebase_score_time = rebase_time - offset;
371 score.set_last_engagement_time(rebase_score_time); 402 score.set_last_engagement_time(rebase_score_time);
372 } 403 }
373 404
374 if (score.last_engagement_time() > new_last_engagement_time) 405 if (score.last_engagement_time() > new_last_engagement_time)
375 new_last_engagement_time = score.last_engagement_time(); 406 new_last_engagement_time = score.last_engagement_time();
376 score.Commit(); 407 score.Commit();
377 } 408 }
378 409
379 if (score.GetScore() > SiteEngagementScore::GetScoreCleanupThreshold()) 410 if (score.GetTotalScore() >
411 SiteEngagementScore::GetScoreCleanupThreshold())
380 continue; 412 continue;
381 } 413 }
382 414
383 // This origin has a score of 0. Wipe it from content settings. 415 // This origin has a score of 0. Wipe it from content settings.
384 settings_map->SetWebsiteSettingDefaultScope( 416 settings_map->SetWebsiteSettingDefaultScope(
385 origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), 417 origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
386 nullptr); 418 content_settings::ResourceIdentifier(), nullptr);
387 } 419 }
388 420
389 // Set the last engagement time to be consistent with the scores. This will 421 // Set the last engagement time to be consistent with the scores. This will
390 // only occur if |update_last_engagement_time| is true. 422 // only occur if |update_last_engagement_time| is true.
391 if (!new_last_engagement_time.is_null()) 423 if (!new_last_engagement_time.is_null())
392 SetLastEngagementTime(new_last_engagement_time); 424 SetLastEngagementTime(new_last_engagement_time);
393 } 425 }
394 426
395 void SiteEngagementService::RecordMetrics() { 427 void SiteEngagementService::RecordMetrics() {
396 base::Time now = clock_->Now(); 428 base::Time now = clock_->Now();
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
570 const GURL& origin) const { 602 const GURL& origin) const {
571 // If we are in incognito, |settings| will automatically have the data from 603 // If we are in incognito, |settings| will automatically have the data from
572 // the original profile migrated in, so all engagement scores in incognito 604 // the original profile migrated in, so all engagement scores in incognito
573 // will be initialised to the values from the original profile. 605 // will be initialised to the values from the original profile.
574 return SiteEngagementScore( 606 return SiteEngagementScore(
575 clock_.get(), origin, 607 clock_.get(), origin,
576 HostContentSettingsMapFactory::GetForProfile(profile_)); 608 HostContentSettingsMapFactory::GetForProfile(profile_));
577 } 609 }
578 610
579 int SiteEngagementService::OriginsWithMaxDailyEngagement() const { 611 int SiteEngagementService::OriginsWithMaxDailyEngagement() const {
580 HostContentSettingsMap* settings_map =
581 HostContentSettingsMapFactory::GetForProfile(profile_);
582 std::unique_ptr<ContentSettingsForOneType> engagement_settings =
583 GetEngagementContentSettings(settings_map);
584
585 int total_origins = 0; 612 int total_origins = 0;
586 613
587 // We cannot call GetScoreMap as we need the score objects, not raw scores. 614 // We cannot call GetScoreMap as we need the score objects, not raw scores.
588 for (const auto& site : *engagement_settings) { 615 for (const auto& site : GetContentSettingsFromProfile(
616 profile_, CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT)) {
589 GURL origin(site.primary_pattern.ToString()); 617 GURL origin(site.primary_pattern.ToString());
618
590 if (!origin.is_valid()) 619 if (!origin.is_valid())
591 continue; 620 continue;
592 621
593 if (CreateEngagementScore(origin).MaxPointsPerDayAdded()) 622 if (CreateEngagementScore(origin).MaxPointsPerDayAdded())
594 ++total_origins; 623 ++total_origins;
595 } 624 }
596 625
597 return total_origins; 626 return total_origins;
598 } 627 }
599 628
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
646 // At this point, we are going to proportionally decay the origin's 675 // At this point, we are going to proportionally decay the origin's
647 // engagement, and reset its last visit date to the last visit to a URL 676 // engagement, and reset its last visit date to the last visit to a URL
648 // under the origin in history. If this new last visit date is long enough 677 // under the origin in history. If this new last visit date is long enough
649 // in the past, the next time the origin's engagement is accessed the 678 // in the past, the next time the origin's engagement is accessed the
650 // automatic decay will kick in - i.e. a double decay will have occurred. 679 // automatic decay will kick in - i.e. a double decay will have occurred.
651 // To prevent this, compute the decay that would have taken place since the 680 // To prevent this, compute the decay that would have taken place since the
652 // new last visit and add it to the engagement at this point. When the 681 // new last visit and add it to the engagement at this point. When the
653 // engagement is next accessed, it will decay back to the proportionally 682 // engagement is next accessed, it will decay back to the proportionally
654 // reduced value rather than being decayed once here, and then once again 683 // reduced value rather than being decayed once here, and then once again
655 // when it is next accessed. 684 // when it is next accessed.
685 // TODO(703848): Move the proportional decay logic into SiteEngagementScore,
686 // so it can decay raw_score_ directly, without the double-decay issue.
656 SiteEngagementScore engagement_score = CreateEngagementScore(origin); 687 SiteEngagementScore engagement_score = CreateEngagementScore(origin);
657 688
658 double new_score = proportion_remaining * engagement_score.GetScore(); 689 double new_score = proportion_remaining * engagement_score.GetTotalScore();
659 int hours_since_engagement = (now - last_visit).InHours(); 690 int hours_since_engagement = (now - last_visit).InHours();
660 int periods = 691 int periods =
661 hours_since_engagement / SiteEngagementScore::GetDecayPeriodInHours(); 692 hours_since_engagement / SiteEngagementScore::GetDecayPeriodInHours();
662 new_score += periods * SiteEngagementScore::GetDecayPoints(); 693 new_score += periods * SiteEngagementScore::GetDecayPoints();
663 new_score *= pow(1.0 / SiteEngagementScore::GetDecayProportion(), periods); 694 new_score *= pow(1.0 / SiteEngagementScore::GetDecayProportion(), periods);
664 695
665 double score = std::min(SiteEngagementScore::kMaxPoints, new_score); 696 double score = std::min(SiteEngagementScore::kMaxPoints, new_score);
666 engagement_score.Reset(score, last_visit); 697 engagement_score.Reset(score, last_visit);
667 if (!engagement_score.last_shortcut_launch_time().is_null() && 698 if (!engagement_score.last_shortcut_launch_time().is_null() &&
668 engagement_score.last_shortcut_launch_time() > last_visit) { 699 engagement_score.last_shortcut_launch_time() > last_visit) {
669 engagement_score.set_last_shortcut_launch_time(last_visit); 700 engagement_score.set_last_shortcut_launch_time(last_visit);
670 } 701 }
671 702
672 engagement_score.Commit(); 703 engagement_score.Commit();
673 } 704 }
674 705
675 SetLastEngagementTime(now); 706 SetLastEngagementTime(now);
676 } 707 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698