Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/important_sites_util.h" | 5 #include "chrome/browser/engagement/important_sites_util.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <map> | 8 #include <map> |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <set> | 10 #include <set> |
| 11 #include <utility> | 11 #include <utility> |
| 12 | 12 |
| 13 #include "base/containers/hash_tables.h" | 13 #include "base/containers/hash_tables.h" |
| 14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 15 #include "base/metrics/histogram_macros.h" | 15 #include "base/metrics/histogram_macros.h" |
| 16 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
| 17 #include "base/time/time.h" | 17 #include "base/time/time.h" |
| 18 #include "base/values.h" | 18 #include "base/values.h" |
| 19 #include "chrome/browser/banners/app_banner_settings_helper.h" | 19 #include "chrome/browser/banners/app_banner_settings_helper.h" |
| 20 #include "chrome/browser/bookmarks/bookmark_model_factory.h" | 20 #include "chrome/browser/bookmarks/bookmark_model_factory.h" |
| 21 #include "chrome/browser/content_settings/host_content_settings_map_factory.h" | 21 #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| 22 #include "chrome/browser/engagement/site_engagement_score.h" | 22 #include "chrome/browser/engagement/site_engagement_score.h" |
| 23 #include "chrome/browser/engagement/site_engagement_service.h" | 23 #include "chrome/browser/engagement/site_engagement_service.h" |
| 24 #include "chrome/browser/profiles/profile.h" | 24 #include "chrome/browser/profiles/profile.h" |
| 25 #include "chrome/common/pref_names.h" | |
| 25 #include "components/bookmarks/browser/bookmark_model.h" | 26 #include "components/bookmarks/browser/bookmark_model.h" |
| 26 #include "components/content_settings/core/browser/host_content_settings_map.h" | 27 #include "components/content_settings/core/browser/host_content_settings_map.h" |
| 27 #include "components/content_settings/core/common/content_settings.h" | 28 #include "components/content_settings/core/common/content_settings.h" |
| 29 #include "components/pref_registry/pref_registry_syncable.h" | |
| 30 #include "components/prefs/pref_service.h" | |
| 31 #include "components/prefs/scoped_user_pref_update.h" | |
| 28 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 32 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 29 #include "third_party/WebKit/public/platform/site_engagement.mojom.h" | 33 #include "third_party/WebKit/public/platform/site_engagement.mojom.h" |
| 30 #include "url/gurl.h" | 34 #include "url/gurl.h" |
| 31 | 35 |
| 32 namespace { | 36 namespace { |
| 33 using bookmarks::BookmarkModel; | 37 using bookmarks::BookmarkModel; |
| 34 using ImportantDomainInfo = ImportantSitesUtil::ImportantDomainInfo; | 38 using ImportantDomainInfo = ImportantSitesUtil::ImportantDomainInfo; |
| 35 | 39 |
| 40 static const char kTimeLastIgnored[] = "TimeBlacklisted"; | |
|
dominickn
2017/02/03 19:30:54
I'll mention that it took me the longest time to r
dmurph
2017/02/03 20:03:29
Done, and added a comment note.
| |
| 41 static const int kBlacklistExpirationTimeDays = 30 * 5; | |
| 42 | |
| 36 static const char kNumTimesIgnoredName[] = "NumTimesIgnored"; | 43 static const char kNumTimesIgnoredName[] = "NumTimesIgnored"; |
| 37 static const int kTimesIgnoredForBlacklist = 3; | 44 static const int kTimesIgnoredForBlacklist = 3; |
| 38 | 45 |
| 39 // These are the maximum # of bookmarks we can use as signals. If the user has | 46 // These are the maximum # of bookmarks we can use as signals. If the user has |
| 40 // <= kMaxBookmarks, then we just use those bookmarks. Otherwise we filter all | 47 // <= kMaxBookmarks, then we just use those bookmarks. Otherwise we filter all |
| 41 // bookmarks on site engagement > 0, sort, and trim to kMaxBookmarks. | 48 // bookmarks on site engagement > 0, sort, and trim to kMaxBookmarks. |
| 42 static const int kMaxBookmarks = 5; | 49 static const int kMaxBookmarks = 5; |
| 43 | 50 |
| 44 // Do not change the values here, as they are used for UMA histograms. | 51 // Do not change the values here, as they are used for UMA histograms. |
| 45 enum ImportantReason { | 52 enum ImportantReason { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 CROSSED_NOTIFICATIONS = 1, | 84 CROSSED_NOTIFICATIONS = 1, |
| 78 CROSSED_ENGAGEMENT = 2, | 85 CROSSED_ENGAGEMENT = 2, |
| 79 CROSSED_NOTIFICATIONS_AND_ENGAGEMENT = 3, | 86 CROSSED_NOTIFICATIONS_AND_ENGAGEMENT = 3, |
| 80 CROSSED_DURABLE_AND_ENGAGEMENT = 4, | 87 CROSSED_DURABLE_AND_ENGAGEMENT = 4, |
| 81 CROSSED_NOTIFICATIONS_AND_DURABLE = 5, | 88 CROSSED_NOTIFICATIONS_AND_DURABLE = 5, |
| 82 CROSSED_NOTIFICATIONS_AND_DURABLE_AND_ENGAGEMENT = 6, | 89 CROSSED_NOTIFICATIONS_AND_DURABLE_AND_ENGAGEMENT = 6, |
| 83 CROSSED_REASON_UNKNOWN = 7, | 90 CROSSED_REASON_UNKNOWN = 7, |
| 84 CROSSED_REASON_BOUNDARY | 91 CROSSED_REASON_BOUNDARY |
| 85 }; | 92 }; |
| 86 | 93 |
| 94 void IncrementTimesIgnoredAndSetDate(base::DictionaryValue* dict) { | |
|
dominickn
2017/02/03 19:30:54
Nit: perhaps just RecordIgnore()?
dmurph
2017/02/03 20:03:29
Done.
| |
| 95 int times_ignored = 0; | |
| 96 dict->GetInteger(kNumTimesIgnoredName, ×_ignored); | |
| 97 dict->SetInteger(kNumTimesIgnoredName, ++times_ignored); | |
| 98 dict->SetDouble(kTimeLastIgnored, base::Time::Now().ToDoubleT()); | |
| 99 } | |
| 100 | |
| 101 // Returns if we cleared the blacklist. | |
| 102 bool ClearBlacklistIfOld(base::DictionaryValue* dict) { | |
|
dominickn
2017/02/03 19:30:54
I'm not sure why the times_ignored < kTimesIgnored
dmurph
2017/02/03 20:03:29
Yeah, that sounds much better. Thanks!
| |
| 103 double last_ignored_time = 0; | |
| 104 if (dict->GetDouble(kTimeLastIgnored, &last_ignored_time)) { | |
| 105 base::TimeDelta diff = | |
| 106 base::Time::Now() - base::Time::FromDoubleT(last_ignored_time); | |
| 107 if (diff >= base::TimeDelta::FromDays(kBlacklistExpirationTimeDays)) { | |
| 108 dict->SetInteger(kNumTimesIgnoredName, 0); | |
| 109 return true; | |
| 110 } | |
| 111 } | |
| 112 return false; | |
| 113 } | |
| 114 | |
| 87 CrossedReason GetCrossedReasonFromBitfield(int32_t reason_bitfield) { | 115 CrossedReason GetCrossedReasonFromBitfield(int32_t reason_bitfield) { |
| 88 bool durable = (reason_bitfield & (1 << ImportantReason::DURABLE)) != 0; | 116 bool durable = (reason_bitfield & (1 << ImportantReason::DURABLE)) != 0; |
| 89 bool notifications = | 117 bool notifications = |
| 90 (reason_bitfield & (1 << ImportantReason::NOTIFICATIONS)) != 0; | 118 (reason_bitfield & (1 << ImportantReason::NOTIFICATIONS)) != 0; |
| 91 bool engagement = (reason_bitfield & (1 << ImportantReason::ENGAGEMENT)) != 0; | 119 bool engagement = (reason_bitfield & (1 << ImportantReason::ENGAGEMENT)) != 0; |
| 92 if (durable && notifications && engagement) | 120 if (durable && notifications && engagement) |
| 93 return CROSSED_NOTIFICATIONS_AND_DURABLE_AND_ENGAGEMENT; | 121 return CROSSED_NOTIFICATIONS_AND_DURABLE_AND_ENGAGEMENT; |
| 94 else if (notifications && durable) | 122 else if (notifications && durable) |
| 95 return CROSSED_NOTIFICATIONS_AND_DURABLE; | 123 return CROSSED_NOTIFICATIONS_AND_DURABLE; |
| 96 else if (notifications && engagement) | 124 else if (notifications && engagement) |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 191 } | 219 } |
| 192 | 220 |
| 193 std::unique_ptr<base::DictionaryValue> dict = | 221 std::unique_ptr<base::DictionaryValue> dict = |
| 194 base::DictionaryValue::From(map->GetWebsiteSetting( | 222 base::DictionaryValue::From(map->GetWebsiteSetting( |
| 195 origin, origin, CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, "", | 223 origin, origin, CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, "", |
| 196 nullptr)); | 224 nullptr)); |
| 197 | 225 |
| 198 if (!dict) | 226 if (!dict) |
| 199 continue; | 227 continue; |
| 200 | 228 |
| 229 bool cleared_blacklist = ClearBlacklistIfOld(dict.get()); | |
|
dominickn
2017/02/03 19:30:54
Inline this in the if statement? cleared_blacklist
dmurph
2017/02/03 20:03:29
Done.
| |
| 230 | |
| 201 int times_ignored = 0; | 231 int times_ignored = 0; |
| 202 if (!dict->GetInteger(kNumTimesIgnoredName, ×_ignored) || | 232 if (cleared_blacklist || |
|
dominickn
2017/02/03 19:30:54
Much nicer would be:
if (ShouldSuppress(dict.get(
dmurph
2017/02/03 20:03:29
Done.
| |
| 233 !dict->GetInteger(kNumTimesIgnoredName, ×_ignored) || | |
| 203 times_ignored < kTimesIgnoredForBlacklist) { | 234 times_ignored < kTimesIgnoredForBlacklist) { |
| 204 continue; | 235 continue; |
| 205 } | 236 } |
| 206 | 237 |
| 207 ignoring_domains.insert(origin.host()); | 238 ignoring_domains.insert(origin.host()); |
| 208 } | 239 } |
| 209 return ignoring_domains; | 240 return ignoring_domains; |
| 210 } | 241 } |
| 211 | 242 |
| 212 void PopulateInfoMapWithSiteEngagement( | 243 void PopulateInfoMapWithSiteEngagement( |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 313 GURL origin(site.primary_pattern.ToString()); | 344 GURL origin(site.primary_pattern.ToString()); |
| 314 if (!AppBannerSettingsHelper::WasLaunchedRecently(profile, origin, now)) | 345 if (!AppBannerSettingsHelper::WasLaunchedRecently(profile, origin, now)) |
| 315 continue; | 346 continue; |
| 316 MaybePopulateImportantInfoForReason(origin, &content_origins, | 347 MaybePopulateImportantInfoForReason(origin, &content_origins, |
| 317 ImportantReason::HOME_SCREEN, output); | 348 ImportantReason::HOME_SCREEN, output); |
| 318 } | 349 } |
| 319 } | 350 } |
| 320 | 351 |
| 321 } // namespace | 352 } // namespace |
| 322 | 353 |
| 354 bool ImportantSitesUtil::IsDialogDisabled(Profile* profile) { | |
| 355 PrefService* service = profile->GetPrefs(); | |
| 356 DictionaryPrefUpdate update(service, prefs::kImportantSitesDialogHistory); | |
| 357 | |
| 358 if (ClearBlacklistIfOld(update.Get())) | |
|
dominickn
2017/02/03 19:30:54
Just having a return ShouldSuppress(update.Get());
dmurph
2017/02/03 20:03:29
Done.
| |
| 359 return false; | |
| 360 | |
| 361 int times_ignored = 0; | |
| 362 update->GetInteger(kNumTimesIgnoredName, ×_ignored); | |
| 363 return times_ignored >= kTimesIgnoredForBlacklist; | |
| 364 } | |
| 365 | |
| 366 void ImportantSitesUtil::RegisterProfilePrefs( | |
|
dominickn
2017/02/03 19:30:54
Is it necessary to register the pref with a defaul
dmurph
2017/02/03 20:03:29
I need to register the pref at least. Yeah, I can
| |
| 367 user_prefs::PrefRegistrySyncable* registry) { | |
| 368 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 369 dict->SetInteger(kNumTimesIgnoredName, 0); | |
| 370 registry->RegisterDictionaryPref(prefs::kImportantSitesDialogHistory, | |
| 371 dict.release()); | |
| 372 } | |
| 373 | |
| 323 std::vector<ImportantDomainInfo> | 374 std::vector<ImportantDomainInfo> |
| 324 ImportantSitesUtil::GetImportantRegisterableDomains(Profile* profile, | 375 ImportantSitesUtil::GetImportantRegisterableDomains(Profile* profile, |
| 325 size_t max_results) { | 376 size_t max_results) { |
| 326 base::hash_map<std::string, ImportantDomainInfo> important_info; | 377 base::hash_map<std::string, ImportantDomainInfo> important_info; |
| 327 std::map<GURL, double> engagement_map; | 378 std::map<GURL, double> engagement_map; |
| 328 | 379 |
| 329 PopulateInfoMapWithSiteEngagement( | 380 PopulateInfoMapWithSiteEngagement( |
| 330 profile, blink::mojom::EngagementLevel::MEDIUM, &engagement_map, | 381 profile, blink::mojom::EngagementLevel::MEDIUM, &engagement_map, |
| 331 &important_info); | 382 &important_info); |
| 332 | 383 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 378 RECORD_UMA_FOR_IMPORTANT_REASON( | 429 RECORD_UMA_FOR_IMPORTANT_REASON( |
| 379 "Storage.ImportantSites.CBDChosenReason", | 430 "Storage.ImportantSites.CBDChosenReason", |
| 380 "Storage.ImportantSites.CBDChosenReasonCount", reason_bitfield); | 431 "Storage.ImportantSites.CBDChosenReasonCount", reason_bitfield); |
| 381 } | 432 } |
| 382 for (int32_t reason_bitfield : ignored_sites_reason_bitfield) { | 433 for (int32_t reason_bitfield : ignored_sites_reason_bitfield) { |
| 383 RECORD_UMA_FOR_IMPORTANT_REASON( | 434 RECORD_UMA_FOR_IMPORTANT_REASON( |
| 384 "Storage.ImportantSites.CBDIgnoredReason", | 435 "Storage.ImportantSites.CBDIgnoredReason", |
| 385 "Storage.ImportantSites.CBDIgnoredReasonCount", reason_bitfield); | 436 "Storage.ImportantSites.CBDIgnoredReasonCount", reason_bitfield); |
| 386 } | 437 } |
| 387 | 438 |
| 388 // We use the ignored sites to update our important sites blacklist. | |
| 389 HostContentSettingsMap* map = | 439 HostContentSettingsMap* map = |
| 390 HostContentSettingsMapFactory::GetForProfile(profile); | 440 HostContentSettingsMapFactory::GetForProfile(profile); |
| 391 for (const std::string& ignored_site : ignored_sites) { | |
| 392 GURL origin("http://" + ignored_site); | |
| 393 std::unique_ptr<base::Value> value = map->GetWebsiteSetting( | |
| 394 origin, origin, CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, "", nullptr); | |
| 395 | 441 |
| 396 std::unique_ptr<base::DictionaryValue> dict = | 442 // We use the ignored sites to update our important sites blacklist only if |
| 397 base::DictionaryValue::From(map->GetWebsiteSetting( | 443 // the user chose to blacklist a site. |
| 398 origin, origin, CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, "", | 444 if (ignored_sites.size() != blacklisted_sites.size()) { |
| 399 nullptr)); | 445 for (const std::string& ignored_site : ignored_sites) { |
| 446 GURL origin("http://" + ignored_site); | |
| 447 std::unique_ptr<base::DictionaryValue> dict = | |
| 448 base::DictionaryValue::From(map->GetWebsiteSetting( | |
| 449 origin, origin, CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, "", | |
| 450 nullptr)); | |
| 400 | 451 |
| 401 int times_ignored = 0; | 452 if (!dict) |
| 402 if (dict) | 453 dict = base::MakeUnique<base::DictionaryValue>(); |
| 403 dict->GetInteger(kNumTimesIgnoredName, ×_ignored); | |
| 404 else | |
| 405 dict = base::MakeUnique<base::DictionaryValue>(); | |
| 406 dict->SetInteger(kNumTimesIgnoredName, ++times_ignored); | |
| 407 | 454 |
| 408 map->SetWebsiteSettingDefaultScope( | 455 IncrementTimesIgnoredAndSetDate(dict.get()); |
| 409 origin, origin, CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, "", | 456 |
| 410 std::move(dict)); | 457 map->SetWebsiteSettingDefaultScope( |
| 458 origin, origin, CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, "", | |
| 459 std::move(dict)); | |
| 460 } | |
| 461 } else { | |
| 462 // Record that the user did not interact with the dialog. | |
| 463 PrefService* service = profile->GetPrefs(); | |
| 464 DictionaryPrefUpdate update(service, prefs::kImportantSitesDialogHistory); | |
| 465 IncrementTimesIgnoredAndSetDate(update.Get()); | |
| 411 } | 466 } |
| 412 | 467 |
| 413 // We clear our blacklist for sites that the user chose. | 468 // We clear our blacklist for sites that the user chose. |
| 414 for (const std::string& ignored_site : blacklisted_sites) { | 469 for (const std::string& ignored_site : blacklisted_sites) { |
| 415 GURL origin("http://" + ignored_site); | 470 GURL origin("http://" + ignored_site); |
| 416 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 471 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| 417 dict->SetInteger(kNumTimesIgnoredName, 0); | 472 dict->SetInteger(kNumTimesIgnoredName, 0); |
| 418 map->SetWebsiteSettingDefaultScope( | 473 map->SetWebsiteSettingDefaultScope( |
| 419 origin, origin, CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, "", | 474 origin, origin, CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, "", |
| 420 std::move(dict)); | 475 std::move(dict)); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 434 const GURL& origin) { | 489 const GURL& origin) { |
| 435 SiteEngagementScore::SetParamValuesForTesting(); | 490 SiteEngagementScore::SetParamValuesForTesting(); |
| 436 // First get data from site engagement. | 491 // First get data from site engagement. |
| 437 SiteEngagementService* site_engagement_service = | 492 SiteEngagementService* site_engagement_service = |
| 438 SiteEngagementService::Get(profile); | 493 SiteEngagementService::Get(profile); |
| 439 site_engagement_service->ResetScoreForURL( | 494 site_engagement_service->ResetScoreForURL( |
| 440 origin, SiteEngagementScore::GetMediumEngagementBoundary()); | 495 origin, SiteEngagementScore::GetMediumEngagementBoundary()); |
| 441 DCHECK(site_engagement_service->IsEngagementAtLeast( | 496 DCHECK(site_engagement_service->IsEngagementAtLeast( |
| 442 origin, blink::mojom::EngagementLevel::MEDIUM)); | 497 origin, blink::mojom::EngagementLevel::MEDIUM)); |
| 443 } | 498 } |
| OLD | NEW |