Chromium Code Reviews| Index: components/ntp_snippets/content_suggestions_service.cc |
| diff --git a/components/ntp_snippets/content_suggestions_service.cc b/components/ntp_snippets/content_suggestions_service.cc |
| index 8b1bdc15eae83a3180fece814448f8805d01484f..61c9368f8eca66b3d469b2c22339e255e14620cb 100644 |
| --- a/components/ntp_snippets/content_suggestions_service.cc |
| +++ b/components/ntp_snippets/content_suggestions_service.cc |
| @@ -13,6 +13,9 @@ |
| #include "base/location.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| +#include "base/values.h" |
| +#include "components/ntp_snippets/pref_names.h" |
| +#include "components/prefs/pref_registry_simple.h" |
| #include "ui/gfx/image/image.h" |
| namespace ntp_snippets { |
| @@ -24,10 +27,13 @@ ContentSuggestionsService::ContentSuggestionsService( |
| : state_(state), |
| history_service_observer_(this), |
| ntp_snippets_service_(nullptr), |
| + pref_service_(pref_service), |
| user_classifier_(pref_service) { |
| // Can be null in tests. |
| if (history_service) |
| history_service_observer_.Add(history_service); |
| + |
| + RestoreDismissedCategoriesFromPrefs(); |
| } |
| ContentSuggestionsService::~ContentSuggestionsService() = default; |
| @@ -42,6 +48,12 @@ void ContentSuggestionsService::Shutdown() { |
| FOR_EACH_OBSERVER(Observer, observers_, ContentSuggestionsServiceShutdown()); |
| } |
| +// static |
| +void ContentSuggestionsService::RegisterProfilePrefs( |
| + PrefRegistrySimple* registry) { |
| + registry->RegisterListPref(prefs::kDismissedCategories); |
| +} |
| + |
| CategoryStatus ContentSuggestionsService::GetCategoryStatus( |
| Category category) const { |
| if (state_ == State::DISABLED) { |
| @@ -150,11 +162,13 @@ void ContentSuggestionsService::DismissCategory(Category category) { |
| if (providers_it == providers_by_category_.end()) |
| return; |
| - suggestions_by_category_[category].clear(); |
| - dismissed_providers_by_category_[providers_it->first] = providers_it->second; |
| - providers_by_category_.erase(providers_it); |
| - categories_.erase( |
| - std::find(categories_.begin(), categories_.end(), category)); |
| + suggestions_by_category_.erase(category); |
|
dgn
2016/10/13 14:46:51
I'm not sure why we just cleared the entry before,
Marc Treib
2016/10/13 15:59:00
No, I think I missed this in a previous CL - erase
dgn
2016/10/14 21:20:28
It's not done currently, but it would make sense t
|
| + ContentSuggestionsProvider* provider = providers_it->second; |
| + |
| + UnregisterCategory(category, provider); |
| + |
| + dismissed_providers_by_category_[category] = provider; |
| + StoreDismissedCategoriesToPrefs(); |
| } |
| void ContentSuggestionsService::RestoreDismissedCategories() { |
| @@ -162,9 +176,14 @@ void ContentSuggestionsService::RestoreDismissedCategories() { |
| auto dismissed_providers_by_category_copy = dismissed_providers_by_category_; |
| for (const auto& category_provider_pair : |
| dismissed_providers_by_category_copy) { |
| - RegisterCategoryIfRequired(category_provider_pair.second, |
| - category_provider_pair.first); |
| + if (category_provider_pair.second) { |
| + RestoreDismissedCategory(category_provider_pair.first); |
| + } else { |
| + // There is no provider for this category, we just remove the entry. |
| + dismissed_providers_by_category_.erase(category_provider_pair.first); |
| + } |
| } |
| + StoreDismissedCategoriesToPrefs(); |
| DCHECK(dismissed_providers_by_category_.empty()); |
| } |
| @@ -192,6 +211,18 @@ void ContentSuggestionsService::OnNewSuggestions( |
| if (RegisterCategoryIfRequired(provider, category)) |
| NotifyCategoryStatusChanged(category); |
| + if (IsCategoryDismissed(category)) { |
|
Marc Treib
2016/10/13 15:59:00
else if?
If the category was just successfully reg
dgn
2016/10/14 21:20:28
Done.
|
| + // The category has been registered as a dismissed one. We need to |
| + // check if the dismissal can be cleared now that we received new data. |
| + if (suggestions.empty()) |
| + return; |
| + |
| + RestoreDismissedCategory(category); |
| + StoreDismissedCategoriesToPrefs(); |
| + |
| + NotifyCategoryStatusChanged(category); |
| + } |
| + |
| if (!IsCategoryStatusAvailable(provider->GetCategoryStatus(category))) |
| return; |
| @@ -213,15 +244,14 @@ void ContentSuggestionsService::OnCategoryStatusChanged( |
| suggestions_by_category_.erase(category); |
| } |
| if (new_status == CategoryStatus::NOT_PROVIDED) { |
| - DCHECK(providers_by_category_.find(category) != |
| - providers_by_category_.end()); |
| - DCHECK_EQ(provider, providers_by_category_.find(category)->second); |
| - DismissCategory(category); |
| + UnregisterCategory(category, provider); |
| } else { |
| RegisterCategoryIfRequired(provider, category); |
| DCHECK_EQ(new_status, provider->GetCategoryStatus(category)); |
| } |
| - NotifyCategoryStatusChanged(category); |
| + |
| + if (!IsCategoryDismissed(category)) |
| + NotifyCategoryStatusChanged(category); |
| } |
| void ContentSuggestionsService::OnSuggestionInvalidated( |
| @@ -281,18 +311,36 @@ void ContentSuggestionsService::HistoryServiceBeingDeleted( |
| bool ContentSuggestionsService::RegisterCategoryIfRequired( |
| ContentSuggestionsProvider* provider, |
| Category category) { |
| + // Search for the category in both the regular and dismissed lists. |
| auto it = providers_by_category_.find(category); |
|
Marc Treib
2016/10/13 15:59:00
Can we early-out here if |it != providers_by_categ
dgn
2016/10/14 21:20:28
Done, also rewrote the function since it removes b
|
| - if (it != providers_by_category_.end()) { |
| - DCHECK_EQ(it->second, provider); |
| - return false; |
| - } |
| - |
| auto dismissed_it = dismissed_providers_by_category_.find(category); |
| - if (dismissed_it != dismissed_providers_by_category_.end()) { |
| - DCHECK_EQ(dismissed_it->second, provider); |
| - dismissed_providers_by_category_.erase(dismissed_it); |
| + |
| + // We will need to register the provider for the category if it's absent from |
| + // both. Note that the initialisation of dismissed categories registers them |
| + // with |nullptr| for providers, we need to check for that too. |
| + bool is_dismissed = dismissed_it != dismissed_providers_by_category_.end(); |
| + bool is_new = it == providers_by_category_.end() && |
| + (!is_dismissed || !dismissed_it->second); |
|
Marc Treib
2016/10/13 15:59:00
Maybe |category_is_dismissed| and |provider_is_new
dgn
2016/10/14 21:20:28
Done.
|
| + |
| + if (is_new) { |
| + if (is_dismissed) { |
| + dismissed_it->second = provider; |
| + return false; |
| + } |
| + RegisterCategoryInternal(category, provider); |
| + } else { |
| + DCHECK_EQ(is_dismissed ? dismissed_it->second : it->second, provider); |
| } |
| + return is_new; |
| +} |
| + |
| +void ContentSuggestionsService::RegisterCategoryInternal( |
| + Category category, |
| + ContentSuggestionsProvider* provider) { |
| + DCHECK(providers_by_category_.find(category) == providers_by_category_.end()); |
|
Marc Treib
2016/10/13 15:59:00
optional nit: base::ContainsKey
dgn
2016/10/14 21:20:28
Thanks! I was annoyed the STL doesn't provide anyt
|
| + DCHECK(!IsCategoryDismissed(category)); |
| + |
| providers_by_category_[category] = provider; |
| categories_.push_back(category); |
| SortCategories(); |
| @@ -300,7 +348,20 @@ bool ContentSuggestionsService::RegisterCategoryIfRequired( |
| suggestions_by_category_.insert( |
| std::make_pair(category, std::vector<ContentSuggestion>())); |
| } |
| - return true; |
| +} |
| + |
| +void ContentSuggestionsService::UnregisterCategory( |
| + Category category, |
| + ContentSuggestionsProvider* provider) { |
| + if (IsCategoryDismissed(category)) |
|
Marc Treib
2016/10/13 15:59:00
Why?
dgn
2016/10/14 21:20:28
Because when OnCategoryStatusChanged() is called w
|
| + return; |
| + |
| + auto providers_it = providers_by_category_.find(category); |
| + DCHECK(providers_it != providers_by_category_.end()); |
| + DCHECK_EQ(provider, providers_it->second); |
| + providers_by_category_.erase(providers_it); |
| + categories_.erase( |
| + std::find(categories_.begin(), categories_.end(), category)); |
| } |
| bool ContentSuggestionsService::RemoveSuggestionByID( |
| @@ -350,4 +411,50 @@ void ContentSuggestionsService::SortCategories() { |
| }); |
| } |
| +bool ContentSuggestionsService::IsCategoryDismissed(Category category) const { |
| + return dismissed_providers_by_category_.find(category) != |
| + dismissed_providers_by_category_.end(); |
|
Marc Treib
2016/10/13 15:59:00
optional nit: base::ContainsKey
dgn
2016/10/14 21:20:28
Done.
|
| +} |
| + |
| +void ContentSuggestionsService::RestoreDismissedCategory(Category category) { |
| + auto dismissed_it = dismissed_providers_by_category_.find(category); |
| + DCHECK(dismissed_it != dismissed_providers_by_category_.end()); |
| + |
| + // Keep the reference to the provider and remove it from the dismissed ones, |
| + // because the category registration enforces that it's not dismissed. |
| + ContentSuggestionsProvider* provider = dismissed_it->second; |
|
Marc Treib
2016/10/13 15:59:01
Can provider be null here? If not, DCHECK that?
dgn
2016/10/14 21:20:28
Good catch, it's possible, yes.
|
| + dismissed_providers_by_category_.erase(dismissed_it); |
| + |
| + RegisterCategoryInternal(category, provider); |
| +} |
| + |
| +void ContentSuggestionsService::RestoreDismissedCategoriesFromPrefs() { |
| + // This must only be called at startup. |
| + DCHECK(dismissed_providers_by_category_.empty()); |
| + DCHECK(providers_by_category_.empty()); |
| + |
| + const base::ListValue* list = |
| + pref_service_->GetList(prefs::kDismissedCategories); |
| + for (const std::unique_ptr<base::Value>& entry : *list) { |
| + int id = 0; |
| + if (!entry->GetAsInteger(&id)) { |
| + DLOG(WARNING) << "Invalid category pref value: " << *entry; |
| + continue; |
| + } |
| + |
| + // When the provider is registered, it will be moved to this map. |
|
Marc Treib
2016/10/13 15:59:00
nit: s/moved to/stored in/ ? "moved to" sounds lik
dgn
2016/10/14 21:20:28
Done.
|
| + dismissed_providers_by_category_[category_factory()->FromIDValue(id)] = |
| + nullptr; |
| + } |
| +} |
| + |
| +void ContentSuggestionsService::StoreDismissedCategoriesToPrefs() { |
| + base::ListValue list; |
| + for (const auto& category_provider_pair : dismissed_providers_by_category_) { |
| + list.AppendInteger(category_provider_pair.first.id()); |
| + } |
| + |
| + pref_service_->Set(prefs::kDismissedCategories, list); |
| +} |
| + |
| } // namespace ntp_snippets |