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> |
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 Loading... | |
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 // Returns the combined list of origins which either have site engagement |
56 HostContentSettingsMap* settings_map) { | 55 // data stored, or have other settings that would provide a score bonus. |
57 std::unique_ptr<ContentSettingsForOneType> engagement_settings( | 56 std::set<GURL> GetEngagementOriginsFromContentSettings(Profile* profile) { |
58 new ContentSettingsForOneType); | 57 HostContentSettingsMap* settings_map = |
58 HostContentSettingsMapFactory::GetForProfile(profile); | |
59 | |
60 ContentSettingsForOneType content_settings_list; | |
61 std::set<GURL> urls; | |
62 | |
63 // Fetch site engagement content settings. | |
59 settings_map->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, | 64 settings_map->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, |
60 std::string(), engagement_settings.get()); | 65 content_settings::ResourceIdentifier(), |
61 return engagement_settings; | 66 &content_settings_list); |
67 for (const auto& site : content_settings_list) { | |
dominickn
2017/04/10 04:41:26
Nit: no braces
Wez
2017/04/10 21:18:09
Done, though note that other areas of Chromium ten
| |
68 urls.insert(GURL(site.primary_pattern.ToString())); | |
69 } | |
70 | |
71 // Fetch notifications allowed content settings. | |
72 settings_map->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, | |
73 content_settings::ResourceIdentifier(), | |
74 &content_settings_list); | |
75 for (const auto& site : content_settings_list) { | |
76 if (site.setting == CONTENT_SETTING_ALLOW) | |
dominickn
2017/04/10 04:41:26
Should this be != ?
Wez
2017/04/10 21:18:09
Yup; and of course my simple test wouldn't catch t
| |
77 continue; | |
78 urls.insert(GURL(site.primary_pattern.ToString())); | |
79 } | |
80 | |
81 return urls; | |
62 } | 82 } |
63 | 83 |
64 // Only accept a navigation event for engagement if it is one of: | 84 // Only accept a navigation event for engagement if it is one of: |
65 // a. direct typed navigation | 85 // a. direct typed navigation |
66 // b. clicking on an omnibox suggestion brought up by typing a keyword | 86 // b. clicking on an omnibox suggestion brought up by typing a keyword |
67 // c. clicking on a bookmark or opening a bookmark app | 87 // 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 | 88 // d. a custom search engine keyword search (e.g. Wikipedia search box added as |
69 // search engine). | 89 // search engine). |
70 bool IsEngagementNavigation(ui::PageTransition transition) { | 90 bool IsEngagementNavigation(ui::PageTransition transition) { |
71 return ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) || | 91 return ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) || |
(...skipping 25 matching lines...) Expand all Loading... | |
97 base::FieldTrialList::FindFullName(kEngagementParams); | 117 base::FieldTrialList::FindFullName(kEngagementParams); |
98 return !base::StartsWith(group_name, "Disabled", | 118 return !base::StartsWith(group_name, "Disabled", |
99 base::CompareCase::SENSITIVE); | 119 base::CompareCase::SENSITIVE); |
100 } | 120 } |
101 | 121 |
102 // static | 122 // static |
103 double SiteEngagementService::GetScoreFromSettings( | 123 double SiteEngagementService::GetScoreFromSettings( |
104 HostContentSettingsMap* settings, | 124 HostContentSettingsMap* settings, |
105 const GURL& origin) { | 125 const GURL& origin) { |
106 auto clock = base::MakeUnique<base::DefaultClock>(); | 126 auto clock = base::MakeUnique<base::DefaultClock>(); |
107 return SiteEngagementScore(clock.get(), origin, settings) | 127 return SiteEngagementScore(clock.get(), origin, settings).GetTotalScore(); |
108 .GetScore(); | |
109 } | 128 } |
110 | 129 |
111 SiteEngagementService::SiteEngagementService(Profile* profile) | 130 SiteEngagementService::SiteEngagementService(Profile* profile) |
112 : SiteEngagementService(profile, base::MakeUnique<base::DefaultClock>()) { | 131 : SiteEngagementService(profile, base::MakeUnique<base::DefaultClock>()) { |
113 content::BrowserThread::PostAfterStartupTask( | 132 content::BrowserThread::PostAfterStartupTask( |
114 FROM_HERE, content::BrowserThread::GetTaskRunnerForThread( | 133 FROM_HERE, content::BrowserThread::GetTaskRunnerForThread( |
115 content::BrowserThread::UI), | 134 content::BrowserThread::UI), |
116 base::Bind(&SiteEngagementService::AfterStartupTask, | 135 base::Bind(&SiteEngagementService::AfterStartupTask, |
117 weak_factory_.GetWeakPtr())); | 136 weak_factory_.GetWeakPtr())); |
118 | 137 |
(...skipping 13 matching lines...) Expand all Loading... | |
132 } | 151 } |
133 | 152 |
134 blink::mojom::EngagementLevel | 153 blink::mojom::EngagementLevel |
135 SiteEngagementService::GetEngagementLevel(const GURL& url) const { | 154 SiteEngagementService::GetEngagementLevel(const GURL& url) const { |
136 if (IsLastEngagementStale()) | 155 if (IsLastEngagementStale()) |
137 CleanupEngagementScores(true); | 156 CleanupEngagementScores(true); |
138 | 157 |
139 return CreateEngagementScore(url).GetEngagementLevel(); | 158 return CreateEngagementScore(url).GetEngagementLevel(); |
140 } | 159 } |
141 | 160 |
142 std::map<GURL, double> SiteEngagementService::GetScoreMap() const { | 161 std::vector<mojom::SiteEngagementDetails> SiteEngagementService::GetAllDetails() |
143 HostContentSettingsMap* settings_map = | 162 const { |
144 HostContentSettingsMapFactory::GetForProfile(profile_); | 163 std::set<GURL> origins = GetEngagementOriginsFromContentSettings(profile_); |
145 std::unique_ptr<ContentSettingsForOneType> engagement_settings = | |
146 GetEngagementContentSettings(settings_map); | |
147 | 164 |
148 std::map<GURL, double> score_map; | 165 std::vector<mojom::SiteEngagementDetails> details; |
149 for (const auto& site : *engagement_settings) { | 166 details.reserve(origins.size()); |
150 GURL origin(site.primary_pattern.ToString()); | 167 for (const GURL& origin : origins) { |
151 if (!origin.is_valid()) | 168 if (!origin.is_valid()) |
152 continue; | 169 continue; |
170 details.push_back(GetDetails(origin)); | |
171 } | |
153 | 172 |
173 return details; | |
174 } | |
175 | |
176 std::map<GURL, double> SiteEngagementService::GetScoreMap() const { | |
177 std::map<GURL, double> score_map; | |
178 for (const GURL& origin : GetEngagementOriginsFromContentSettings(profile_)) { | |
179 if (!origin.is_valid()) | |
180 continue; | |
154 score_map[origin] = GetScore(origin); | 181 score_map[origin] = GetScore(origin); |
155 } | 182 } |
156 | |
157 return score_map; | 183 return score_map; |
158 } | 184 } |
159 | 185 |
160 void SiteEngagementService::HandleNotificationInteraction(const GURL& url) { | 186 void SiteEngagementService::HandleNotificationInteraction(const GURL& url) { |
161 if (!ShouldRecordEngagement(url)) | 187 if (!ShouldRecordEngagement(url)) |
162 return; | 188 return; |
163 | 189 |
164 SiteEngagementMetrics::RecordEngagement( | 190 SiteEngagementMetrics::RecordEngagement( |
165 SiteEngagementMetrics::ENGAGEMENT_NOTIFICATION_INTERACTION); | 191 SiteEngagementMetrics::ENGAGEMENT_NOTIFICATION_INTERACTION); |
166 AddPoints(url, SiteEngagementScore::GetNotificationInteractionPoints()); | 192 AddPoints(url, SiteEngagementScore::GetNotificationInteractionPoints()); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
237 SiteEngagementService::Helper* helper) { | 263 SiteEngagementService::Helper* helper) { |
238 helpers_.insert(helper); | 264 helpers_.insert(helper); |
239 } | 265 } |
240 | 266 |
241 void SiteEngagementService::HelperDeleted( | 267 void SiteEngagementService::HelperDeleted( |
242 SiteEngagementService::Helper* helper) { | 268 SiteEngagementService::Helper* helper) { |
243 helpers_.erase(helper); | 269 helpers_.erase(helper); |
244 } | 270 } |
245 | 271 |
246 double SiteEngagementService::GetScore(const GURL& url) const { | 272 double SiteEngagementService::GetScore(const GURL& url) const { |
273 return GetDetails(url).total_score; | |
274 } | |
275 | |
276 mojom::SiteEngagementDetails SiteEngagementService::GetDetails( | |
277 const GURL& url) const { | |
247 // Ensure that if engagement is stale, we clean things up before fetching the | 278 // Ensure that if engagement is stale, we clean things up before fetching the |
248 // score. | 279 // score. |
249 if (IsLastEngagementStale()) | 280 if (IsLastEngagementStale()) |
250 CleanupEngagementScores(true); | 281 CleanupEngagementScores(true); |
251 | 282 |
252 return CreateEngagementScore(url).GetScore(); | 283 return CreateEngagementScore(url).GetDetails(); |
253 } | 284 } |
254 | 285 |
255 double SiteEngagementService::GetTotalEngagementPoints() const { | 286 double SiteEngagementService::GetTotalEngagementPoints() const { |
256 std::map<GURL, double> score_map = GetScoreMap(); | 287 std::map<GURL, double> score_map = GetScoreMap(); |
257 | 288 |
258 double total_score = 0; | 289 double total_score = 0; |
259 for (const auto& value : score_map) | 290 for (const auto& value : score_map) |
260 total_score += value.second; | 291 total_score += value.second; |
261 | 292 |
262 return total_score; | 293 return total_score; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
308 | 339 |
309 void SiteEngagementService::AfterStartupTask() { | 340 void SiteEngagementService::AfterStartupTask() { |
310 // Check if we need to reset last engagement times on startup - we want to | 341 // 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 | 342 // 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 | 343 // in AddPoints for people who never restart Chrome, but leave it open and |
313 // their computer on standby. | 344 // their computer on standby. |
314 CleanupEngagementScores(IsLastEngagementStale()); | 345 CleanupEngagementScores(IsLastEngagementStale()); |
315 RecordMetrics(); | 346 RecordMetrics(); |
316 } | 347 } |
317 | 348 |
318 void SiteEngagementService::CleanupEngagementScores( | 349 void SiteEngagementService::CleanupEngagementScores( |
dominickn
2017/04/10 04:41:26
This cleanup method doesn't need to iterate over o
Wez
2017/04/10 21:18:09
Done.
| |
319 bool update_last_engagement_time) const { | 350 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 | 351 // We want to rebase last engagement times relative to MaxDecaysPerScore |
326 // periods of decay in the past. | 352 // periods of decay in the past. |
327 base::Time now = clock_->Now(); | 353 base::Time now = clock_->Now(); |
328 base::Time last_engagement_time = GetLastEngagementTime(); | 354 base::Time last_engagement_time = GetLastEngagementTime(); |
329 base::Time rebase_time = now - GetMaxDecayPeriod(); | 355 base::Time rebase_time = now - GetMaxDecayPeriod(); |
330 base::Time new_last_engagement_time; | 356 base::Time new_last_engagement_time; |
331 | 357 |
332 // If |update_last_engagement_time| is true, we must have either: | 358 // If |update_last_engagement_time| is true, we must have either: |
333 // a) last_engagement_time is in the future; OR | 359 // a) last_engagement_time is in the future; OR |
334 // b) last_engagement_time < rebase_time < now | 360 // b) last_engagement_time < rebase_time < now |
335 DCHECK(!update_last_engagement_time || last_engagement_time >= now || | 361 DCHECK(!update_last_engagement_time || last_engagement_time >= now || |
336 (last_engagement_time < rebase_time && rebase_time < now)); | 362 (last_engagement_time < rebase_time && rebase_time < now)); |
337 | 363 |
338 // Cap |last_engagement_time| at |now| if it is in the future. This ensures | 364 // 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 | 365 // 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|. | 366 // have a mix of scores prior to and after |now|. |
341 if (last_engagement_time > now) | 367 if (last_engagement_time > now) |
342 last_engagement_time = now; | 368 last_engagement_time = now; |
343 | 369 |
344 for (const auto& site : *engagement_settings) { | 370 HostContentSettingsMap* settings_map = |
345 GURL origin(site.primary_pattern.ToString()); | 371 HostContentSettingsMapFactory::GetForProfile(profile_); |
346 | 372 for (const GURL& origin : GetEngagementOriginsFromContentSettings(profile_)) { |
347 if (origin.is_valid()) { | 373 if (origin.is_valid()) { |
348 SiteEngagementScore score = CreateEngagementScore(origin); | 374 SiteEngagementScore score = CreateEngagementScore(origin); |
349 if (update_last_engagement_time) { | 375 if (update_last_engagement_time) { |
350 // Catch cases of users moving their clocks, or a potential race where | 376 // 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 | 377 // a score content setting is written out to prefs, but the updated |
352 // |last_engagement_time| was not written, as both are lossy | 378 // |last_engagement_time| was not written, as both are lossy |
353 // preferences. |rebase_time| is strictly in the past, so any score with | 379 // 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. | 380 // a last updated time in the future is caught by this branch. |
355 if (score.last_engagement_time() > rebase_time) { | 381 if (score.last_engagement_time() > rebase_time) { |
356 score.set_last_engagement_time(now); | 382 score.set_last_engagement_time(now); |
(...skipping 12 matching lines...) Expand all Loading... | |
369 last_engagement_time - score.last_engagement_time(); | 395 last_engagement_time - score.last_engagement_time(); |
370 base::Time rebase_score_time = rebase_time - offset; | 396 base::Time rebase_score_time = rebase_time - offset; |
371 score.set_last_engagement_time(rebase_score_time); | 397 score.set_last_engagement_time(rebase_score_time); |
372 } | 398 } |
373 | 399 |
374 if (score.last_engagement_time() > new_last_engagement_time) | 400 if (score.last_engagement_time() > new_last_engagement_time) |
375 new_last_engagement_time = score.last_engagement_time(); | 401 new_last_engagement_time = score.last_engagement_time(); |
376 score.Commit(); | 402 score.Commit(); |
377 } | 403 } |
378 | 404 |
379 if (score.GetScore() > SiteEngagementScore::GetScoreCleanupThreshold()) | 405 if (score.GetTotalScore() > |
406 SiteEngagementScore::GetScoreCleanupThreshold()) | |
380 continue; | 407 continue; |
381 } | 408 } |
382 | 409 |
383 // This origin has a score of 0. Wipe it from content settings. | 410 // This origin has a score of 0. Wipe it from content settings. |
384 settings_map->SetWebsiteSettingDefaultScope( | 411 settings_map->SetWebsiteSettingDefaultScope( |
385 origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), | 412 origin, GURL(), CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, std::string(), |
386 nullptr); | 413 nullptr); |
387 } | 414 } |
388 | 415 |
389 // Set the last engagement time to be consistent with the scores. This will | 416 // Set the last engagement time to be consistent with the scores. This will |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
570 const GURL& origin) const { | 597 const GURL& origin) const { |
571 // If we are in incognito, |settings| will automatically have the data from | 598 // If we are in incognito, |settings| will automatically have the data from |
572 // the original profile migrated in, so all engagement scores in incognito | 599 // the original profile migrated in, so all engagement scores in incognito |
573 // will be initialised to the values from the original profile. | 600 // will be initialised to the values from the original profile. |
574 return SiteEngagementScore( | 601 return SiteEngagementScore( |
575 clock_.get(), origin, | 602 clock_.get(), origin, |
576 HostContentSettingsMapFactory::GetForProfile(profile_)); | 603 HostContentSettingsMapFactory::GetForProfile(profile_)); |
577 } | 604 } |
578 | 605 |
579 int SiteEngagementService::OriginsWithMaxDailyEngagement() const { | 606 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; | 607 int total_origins = 0; |
586 | 608 |
587 // We cannot call GetScoreMap as we need the score objects, not raw scores. | 609 // We cannot call GetScoreMap as we need the score objects, not raw scores. |
588 for (const auto& site : *engagement_settings) { | 610 for (const GURL& origin : GetEngagementOriginsFromContentSettings(profile_)) { |
dominickn
2017/04/10 04:41:26
An origin can't possibly get maximum daily engagem
Wez
2017/04/10 21:18:09
Done.
| |
589 GURL origin(site.primary_pattern.ToString()); | |
590 if (!origin.is_valid()) | 611 if (!origin.is_valid()) |
591 continue; | 612 continue; |
592 | 613 |
593 if (CreateEngagementScore(origin).MaxPointsPerDayAdded()) | 614 if (CreateEngagementScore(origin).MaxPointsPerDayAdded()) |
594 ++total_origins; | 615 ++total_origins; |
595 } | 616 } |
596 | 617 |
597 return total_origins; | 618 return total_origins; |
598 } | 619 } |
599 | 620 |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
648 // under the origin in history. If this new last visit date is long enough | 669 // 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 | 670 // 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. | 671 // 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 | 672 // 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 | 673 // 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 | 674 // engagement is next accessed, it will decay back to the proportionally |
654 // reduced value rather than being decayed once here, and then once again | 675 // reduced value rather than being decayed once here, and then once again |
655 // when it is next accessed. | 676 // when it is next accessed. |
656 SiteEngagementScore engagement_score = CreateEngagementScore(origin); | 677 SiteEngagementScore engagement_score = CreateEngagementScore(origin); |
657 | 678 |
658 double new_score = proportion_remaining * engagement_score.GetScore(); | 679 double new_score = proportion_remaining * engagement_score.raw_score(); |
659 int hours_since_engagement = (now - last_visit).InHours(); | 680 int hours_since_engagement = (now - last_visit).InHours(); |
660 int periods = | 681 int periods = |
661 hours_since_engagement / SiteEngagementScore::GetDecayPeriodInHours(); | 682 hours_since_engagement / SiteEngagementScore::GetDecayPeriodInHours(); |
662 new_score += periods * SiteEngagementScore::GetDecayPoints(); | 683 new_score += periods * SiteEngagementScore::GetDecayPoints(); |
663 new_score *= pow(1.0 / SiteEngagementScore::GetDecayProportion(), periods); | 684 new_score *= pow(1.0 / SiteEngagementScore::GetDecayProportion(), periods); |
664 | 685 |
665 double score = std::min(SiteEngagementScore::kMaxPoints, new_score); | 686 double score = std::min(SiteEngagementScore::kMaxPoints, new_score); |
666 engagement_score.Reset(score, last_visit); | 687 engagement_score.Reset(score, last_visit); |
667 if (!engagement_score.last_shortcut_launch_time().is_null() && | 688 if (!engagement_score.last_shortcut_launch_time().is_null() && |
668 engagement_score.last_shortcut_launch_time() > last_visit) { | 689 engagement_score.last_shortcut_launch_time() > last_visit) { |
669 engagement_score.set_last_shortcut_launch_time(last_visit); | 690 engagement_score.set_last_shortcut_launch_time(last_visit); |
670 } | 691 } |
671 | 692 |
672 engagement_score.Commit(); | 693 engagement_score.Commit(); |
673 } | 694 } |
674 | 695 |
675 SetLastEngagementTime(now); | 696 SetLastEngagementTime(now); |
676 } | 697 } |
OLD | NEW |