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

Side by Side Diff: components/ntp_snippets/content_suggestions_service.cc

Issue 2194203002: Make ContentSuggestionsService recognize new/removed categories (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@renaming
Patch Set: Marc's comments Created 4 years, 4 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 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 "components/ntp_snippets/content_suggestions_service.h" 5 #include "components/ntp_snippets/content_suggestions_service.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/string_number_conversions.h"
12 #include "ui/gfx/image/image.h" 12 #include "ui/gfx/image/image.h"
13 13
14 namespace ntp_snippets { 14 namespace ntp_snippets {
15 15
16 bool ContentSuggestionsService::CompareCategoriesByID::operator()( 16 bool ContentSuggestionsService::CompareCategoriesByID::operator()(
17 const Category& left, 17 const Category& left,
18 const Category& right) const { 18 const Category& right) const {
19 return left.id() < right.id(); 19 return left.id() < right.id();
20 } 20 }
21 21
22 ContentSuggestionsService::ContentSuggestionsService(State state) 22 ContentSuggestionsService::ContentSuggestionsService(State state)
23 : state_(state) {} 23 : state_(state) {}
24 24
25 ContentSuggestionsService::~ContentSuggestionsService() {} 25 ContentSuggestionsService::~ContentSuggestionsService() {}
26 26
27 void ContentSuggestionsService::Shutdown() { 27 void ContentSuggestionsService::Shutdown() {
28 DCHECK(providers_.empty()); 28 DCHECK(providers_by_category_.empty());
29 DCHECK(categories_.empty()); 29 DCHECK(categories_.empty());
30 DCHECK(suggestions_by_category_.empty()); 30 DCHECK(suggestions_by_category_.empty());
31 DCHECK(id_category_map_.empty()); 31 DCHECK(id_category_map_.empty());
32 state_ = State::DISABLED; 32 state_ = State::DISABLED;
33 FOR_EACH_OBSERVER(Observer, observers_, ContentSuggestionsServiceShutdown()); 33 FOR_EACH_OBSERVER(Observer, observers_, ContentSuggestionsServiceShutdown());
34 } 34 }
35 35
36 CategoryStatus ContentSuggestionsService::GetCategoryStatus( 36 CategoryStatus ContentSuggestionsService::GetCategoryStatus(
37 Category category) const { 37 Category category) const {
38 if (state_ == State::DISABLED) { 38 if (state_ == State::DISABLED) {
39 return CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED; 39 return CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED;
40 } 40 }
41 41
42 auto iterator = providers_.find(category); 42 auto iterator = providers_by_category_.find(category);
43 if (iterator == providers_.end()) 43 if (iterator == providers_by_category_.end())
44 return CategoryStatus::NOT_PROVIDED; 44 return CategoryStatus::NOT_PROVIDED;
45 45
46 return iterator->second->GetCategoryStatus(category); 46 return iterator->second->GetCategoryStatus(category);
47 } 47 }
48 48
49 const std::vector<ContentSuggestion>& 49 const std::vector<ContentSuggestion>&
50 ContentSuggestionsService::GetSuggestionsForCategory(Category category) const { 50 ContentSuggestionsService::GetSuggestionsForCategory(Category category) const {
51 auto iterator = suggestions_by_category_.find(category); 51 auto iterator = suggestions_by_category_.find(category);
52 if (iterator == suggestions_by_category_.end()) 52 if (iterator == suggestions_by_category_.end())
53 return no_suggestions_; 53 return no_suggestions_;
54 return iterator->second; 54 return iterator->second;
55 } 55 }
56 56
57 void ContentSuggestionsService::FetchSuggestionImage( 57 void ContentSuggestionsService::FetchSuggestionImage(
58 const std::string& suggestion_id, 58 const std::string& suggestion_id,
59 const ImageFetchedCallback& callback) { 59 const ImageFetchedCallback& callback) {
60 if (!id_category_map_.count(suggestion_id)) { 60 if (!id_category_map_.count(suggestion_id)) {
61 LOG(WARNING) << "Requested image for unknown suggestion " << suggestion_id; 61 LOG(WARNING) << "Requested image for unknown suggestion " << suggestion_id;
62 callback.Run(suggestion_id, gfx::Image()); 62 callback.Run(suggestion_id, gfx::Image());
63 return; 63 return;
64 } 64 }
65 Category category = id_category_map_.at(suggestion_id); 65 Category category = id_category_map_.at(suggestion_id);
66 if (!providers_.count(category)) { 66 if (!providers_by_category_.count(category)) {
67 LOG(WARNING) << "Requested image for suggestion " << suggestion_id 67 LOG(WARNING) << "Requested image for suggestion " << suggestion_id
68 << " for unavailable category " << category; 68 << " for unavailable category " << category;
69 callback.Run(suggestion_id, gfx::Image()); 69 callback.Run(suggestion_id, gfx::Image());
70 return; 70 return;
71 } 71 }
72 providers_[category]->FetchSuggestionImage(suggestion_id, callback); 72 providers_by_category_[category]->FetchSuggestionImage(suggestion_id,
73 callback);
73 } 74 }
74 75
75 void ContentSuggestionsService::ClearCachedSuggestionsForDebugging() { 76 void ContentSuggestionsService::ClearCachedSuggestionsForDebugging() {
76 suggestions_by_category_.clear(); 77 suggestions_by_category_.clear();
77 id_category_map_.clear(); 78 id_category_map_.clear();
78 for (auto& category_provider_pair : providers_) { 79 for (auto& category_provider_pair : providers_by_category_) {
79 category_provider_pair.second->ClearCachedSuggestionsForDebugging(); 80 category_provider_pair.second->ClearCachedSuggestionsForDebugging();
80 } 81 }
81 FOR_EACH_OBSERVER(Observer, observers_, OnNewSuggestions()); 82 FOR_EACH_OBSERVER(Observer, observers_, OnNewSuggestions());
82 } 83 }
83 84
84 void ContentSuggestionsService::ClearDismissedSuggestionsForDebugging() { 85 void ContentSuggestionsService::ClearDismissedSuggestionsForDebugging() {
85 for (auto& category_provider_pair : providers_) { 86 for (auto& category_provider_pair : providers_by_category_) {
86 category_provider_pair.second->ClearDismissedSuggestionsForDebugging(); 87 category_provider_pair.second->ClearDismissedSuggestionsForDebugging();
87 } 88 }
88 } 89 }
89 90
90 void ContentSuggestionsService::DismissSuggestion( 91 void ContentSuggestionsService::DismissSuggestion(
91 const std::string& suggestion_id) { 92 const std::string& suggestion_id) {
92 if (!id_category_map_.count(suggestion_id)) { 93 if (!id_category_map_.count(suggestion_id)) {
93 LOG(WARNING) << "Dismissed unknown suggestion " << suggestion_id; 94 LOG(WARNING) << "Dismissed unknown suggestion " << suggestion_id;
94 return; 95 return;
95 } 96 }
96 Category category = id_category_map_.at(suggestion_id); 97 Category category = id_category_map_.at(suggestion_id);
97 if (!providers_.count(category)) { 98 if (!providers_by_category_.count(category)) {
98 LOG(WARNING) << "Dismissed suggestion " << suggestion_id 99 LOG(WARNING) << "Dismissed suggestion " << suggestion_id
99 << " for unavailable category " << category; 100 << " for unavailable category " << category;
100 return; 101 return;
101 } 102 }
102 providers_[category]->DismissSuggestion(suggestion_id); 103 providers_by_category_[category]->DismissSuggestion(suggestion_id);
103 104
104 // Remove the suggestion locally. 105 // Remove the suggestion locally.
105 id_category_map_.erase(suggestion_id); 106 id_category_map_.erase(suggestion_id);
106 std::vector<ContentSuggestion>* suggestions = 107 std::vector<ContentSuggestion>* suggestions =
107 &suggestions_by_category_[category]; 108 &suggestions_by_category_[category];
108 auto position = 109 auto position =
109 std::find_if(suggestions->begin(), suggestions->end(), 110 std::find_if(suggestions->begin(), suggestions->end(),
110 [&suggestion_id](const ContentSuggestion& suggestion) { 111 [&suggestion_id](const ContentSuggestion& suggestion) {
111 return suggestion_id == suggestion.id(); 112 return suggestion_id == suggestion.id();
112 }); 113 });
(...skipping 12 matching lines...) Expand all
125 observers_.RemoveObserver(observer); 126 observers_.RemoveObserver(observer);
126 } 127 }
127 128
128 void ContentSuggestionsService::RegisterProvider( 129 void ContentSuggestionsService::RegisterProvider(
129 ContentSuggestionsProvider* provider) { 130 ContentSuggestionsProvider* provider) {
130 // TODO(pke): When NTPSnippetsService is purely a provider, think about 131 // TODO(pke): When NTPSnippetsService is purely a provider, think about
131 // removing this state check. 132 // removing this state check.
132 if (state_ == State::DISABLED) 133 if (state_ == State::DISABLED)
133 return; 134 return;
134 135
136 provider->SetObserver(this);
135 for (Category category : provider->GetProvidedCategories()) { 137 for (Category category : provider->GetProvidedCategories()) {
136 DCHECK_EQ(0ul, providers_.count(category)); 138 DCHECK_NE(CategoryStatus::NOT_PROVIDED,
137 providers_[category] = provider; 139 provider->GetCategoryStatus(category));
138 categories_.push_back(category); 140 RegisterCategoryIfRequired(provider, category);
139 if (IsCategoryStatusAvailable(provider->GetCategoryStatus(category))) {
140 suggestions_by_category_[category] = std::vector<ContentSuggestion>();
141 }
142 NotifyCategoryStatusChanged(category); 141 NotifyCategoryStatusChanged(category);
143 } 142 }
144 std::sort(categories_.begin(), categories_.end(),
145 [this](const Category& left, const Category& right) {
146 return category_factory_.CompareCategories(left, right);
147 });
148 provider->SetObserver(this);
149 } 143 }
150 144
151 //////////////////////////////////////////////////////////////////////////////// 145 ////////////////////////////////////////////////////////////////////////////////
152 // Private methods 146 // Private methods
153 147
154 void ContentSuggestionsService::OnNewSuggestions( 148 void ContentSuggestionsService::OnNewSuggestions(
155 Category changed_category, 149 ContentSuggestionsProvider* provider,
150 Category category,
156 std::vector<ContentSuggestion> new_suggestions) { 151 std::vector<ContentSuggestion> new_suggestions) {
157 DCHECK(IsCategoryRegistered(changed_category)); 152 if (RegisterCategoryIfRequired(provider, category)) {
153 NotifyCategoryStatusChanged(category);
154 }
158 155
159 for (const ContentSuggestion& suggestion : 156 for (const ContentSuggestion& suggestion :
160 suggestions_by_category_[changed_category]) { 157 suggestions_by_category_[category]) {
161 id_category_map_.erase(suggestion.id()); 158 id_category_map_.erase(suggestion.id());
162 } 159 }
163 160
164 for (const ContentSuggestion& suggestion : new_suggestions) { 161 for (const ContentSuggestion& suggestion : new_suggestions) {
165 id_category_map_.insert(std::make_pair(suggestion.id(), changed_category)); 162 id_category_map_.insert(std::make_pair(suggestion.id(), category));
166 } 163 }
167 164
168 suggestions_by_category_[changed_category] = std::move(new_suggestions); 165 suggestions_by_category_[category] = std::move(new_suggestions);
169 166
170 FOR_EACH_OBSERVER(Observer, observers_, OnNewSuggestions()); 167 FOR_EACH_OBSERVER(Observer, observers_, OnNewSuggestions());
171 } 168 }
172 169
173 void ContentSuggestionsService::OnCategoryStatusChanged( 170 void ContentSuggestionsService::OnCategoryStatusChanged(
174 Category changed_category, 171 ContentSuggestionsProvider* provider,
172 Category category,
175 CategoryStatus new_status) { 173 CategoryStatus new_status) {
176 if (!IsCategoryStatusAvailable(new_status)) { 174 if (!IsCategoryStatusAvailable(new_status)) {
177 for (const ContentSuggestion& suggestion : 175 for (const ContentSuggestion& suggestion :
178 suggestions_by_category_[changed_category]) { 176 suggestions_by_category_[category]) {
179 id_category_map_.erase(suggestion.id()); 177 id_category_map_.erase(suggestion.id());
180 } 178 }
181 suggestions_by_category_.erase(changed_category); 179 suggestions_by_category_.erase(category);
182 } 180 }
183 NotifyCategoryStatusChanged(changed_category); 181 if (new_status == CategoryStatus::NOT_PROVIDED) {
182 auto providers_it = providers_by_category_.find(category);
183 DCHECK(providers_it != providers_by_category_.end());
184 DCHECK_EQ(provider, providers_it->second);
185 providers_by_category_.erase(providers_it);
186 categories_.erase(
187 std::find(categories_.begin(), categories_.end(), category));
188 } else {
189 RegisterCategoryIfRequired(provider, category);
190 DCHECK_EQ(new_status, provider->GetCategoryStatus(category));
191 }
192 NotifyCategoryStatusChanged(category);
184 } 193 }
185 194
186 void ContentSuggestionsService::OnProviderShutdown( 195 void ContentSuggestionsService::OnProviderShutdown(
187 ContentSuggestionsProvider* provider) { 196 ContentSuggestionsProvider* provider) {
188 for (Category category : provider->GetProvidedCategories()) { 197 for (Category category : provider->GetProvidedCategories()) {
189 auto iterator = std::find(categories_.begin(), categories_.end(), category); 198 auto iterator = std::find(categories_.begin(), categories_.end(), category);
190 DCHECK(iterator != categories_.end()); 199 DCHECK(iterator != categories_.end());
191 categories_.erase(iterator); 200 categories_.erase(iterator);
192 for (const ContentSuggestion& suggestion : 201 for (const ContentSuggestion& suggestion :
193 suggestions_by_category_[category]) { 202 suggestions_by_category_[category]) {
194 id_category_map_.erase(suggestion.id()); 203 id_category_map_.erase(suggestion.id());
195 } 204 }
196 suggestions_by_category_.erase(category); 205 suggestions_by_category_.erase(category);
197 providers_.erase(category); 206 providers_by_category_.erase(category);
198 NotifyCategoryStatusChanged(category); 207 NotifyCategoryStatusChanged(category);
199 } 208 }
200 } 209 }
201 210
202 bool ContentSuggestionsService::IsCategoryRegistered(Category category) const { 211 bool ContentSuggestionsService::RegisterCategoryIfRequired(
203 return std::find(categories_.begin(), categories_.end(), category) != 212 ContentSuggestionsProvider* provider,
204 categories_.end(); 213 Category category) {
214 auto it = providers_by_category_.find(category);
215 if (it != providers_by_category_.end()) {
216 DCHECK_EQ(it->second, provider);
217 return false;
218 }
219
220 providers_by_category_[category] = provider;
221 categories_.push_back(category);
222 std::sort(categories_.begin(), categories_.end(),
223 [this](const Category& left, const Category& right) {
224 return category_factory_.CompareCategories(left, right);
225 });
226 if (IsCategoryStatusAvailable(provider->GetCategoryStatus(category))) {
227 suggestions_by_category_.insert(
228 std::make_pair(category, std::vector<ContentSuggestion>()));
229 }
230 return true;
205 } 231 }
206 232
207 void ContentSuggestionsService::NotifyCategoryStatusChanged(Category category) { 233 void ContentSuggestionsService::NotifyCategoryStatusChanged(Category category) {
208 FOR_EACH_OBSERVER( 234 FOR_EACH_OBSERVER(
209 Observer, observers_, 235 Observer, observers_,
210 OnCategoryStatusChanged(category, GetCategoryStatus(category))); 236 OnCategoryStatusChanged(category, GetCategoryStatus(category)));
211 } 237 }
212 238
213 } // namespace ntp_snippets 239 } // namespace ntp_snippets
OLDNEW
« no previous file with comments | « components/ntp_snippets/content_suggestions_service.h ('k') | components/ntp_snippets/content_suggestions_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698