Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/ntp_snippets/content_suggestions_service.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <iterator> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "ui/gfx/image/image.h" | |
| 12 | |
| 13 namespace ntp_snippets { | |
| 14 | |
| 15 namespace { | |
| 16 | |
| 17 // Helper method get the provider_type from a combined ID. | |
| 18 ContentSuggestionsProviderType GetProviderTypeFromCombinedID(std::string id) { | |
|
Marc Treib
2016/06/30 10:36:54
const std::string&
tschumann
2016/06/30 10:55:19
do we already have move semantics in chrome?
I'd g
Marc Treib
2016/06/30 11:34:23
We do, but there's no reason to pass in ownership
Philipp Keck
2016/06/30 17:14:08
Done.
| |
| 19 auto colon_index = id.find(":"); | |
|
Marc Treib
2016/06/30 10:36:54
size_t please - if you say "auto", then it kinda l
Philipp Keck
2016/06/30 17:14:08
Done.
| |
| 20 DCHECK_NE(std::string::npos, colon_index); | |
| 21 int provider_type = std::stoi(id.substr(0, colon_index)); | |
|
Marc Treib
2016/06/30 10:36:54
What happens if this fails to parse as an int?
std
Philipp Keck
2016/06/30 17:14:08
Done.
| |
| 22 return ContentSuggestionsProviderType(provider_type); | |
|
Marc Treib
2016/06/30 10:36:54
What if the int isn't a valid provider type? (Shou
tschumann
2016/06/30 10:55:19
use the most restrictive cast, i.e. static_cast<Co
Marc Treib
2016/06/30 11:34:23
AFAIK that will actually do the exact same thing.
tschumann
2016/06/30 11:58:11
in this particular case, c-style cast does the sam
Marc Treib
2016/06/30 13:36:12
IMO, the out-of-range case is one that should *nev
Philipp Keck
2016/06/30 17:14:08
I agree with Marc, and equally the case when the s
Marc Treib
2016/07/01 09:15:32
I find "create a ContentSuggestionsProviderType ou
| |
| 23 } | |
| 24 | |
| 25 // Helper method get the original ID from a combined ID. | |
| 26 std::string GetIDFromCombinedID(std::string id) { | |
| 27 auto colon_index = id.find(":"); | |
| 28 DCHECK_NE(std::string::npos, colon_index); | |
| 29 return id.substr(colon_index + 1); | |
| 30 } | |
|
Marc Treib
2016/06/30 10:36:54
I think it'd be nice if the "combine" and "extract
tschumann
2016/06/30 11:58:11
+1
They should live together but not on the Conten
Philipp Keck
2016/06/30 17:14:08
I had the same dilemma. ContentSuggestion is defin
Marc Treib
2016/07/01 09:15:32
Naaah.
Philipp Keck
2016/07/01 13:00:03
Ok. So should I do that? Btw this is also one of t
Marc Treib
2016/07/01 13:29:17
How exactly would that solve it? We'll still have
Philipp Keck
2016/07/01 13:45:11
Yes, but it could be public logic because categori
| |
| 31 | |
| 32 // Helper method to remove all suggestions of the given |provider_type| from | |
| 33 // the given |suggestions|. Returns true if anything changed. | |
| 34 bool RemoveSuggestionsOfProvider(std::vector<ContentSuggestion>* suggestions, | |
| 35 ContentSuggestionsProviderType provider_type) { | |
| 36 auto remove_from = std::remove_if( | |
| 37 suggestions->begin(), suggestions->end(), | |
| 38 [provider_type](const ContentSuggestion& s) { | |
| 39 return GetProviderTypeFromCombinedID(s.id()) == provider_type; | |
| 40 }); | |
| 41 if (remove_from == suggestions->end()) { | |
| 42 return false; | |
| 43 } else { | |
|
Marc Treib
2016/06/30 10:36:54
No "else" after "return"
Philipp Keck
2016/06/30 17:14:08
I changed it. Though I personally like the shape o
Marc Treib
2016/07/01 09:15:32
I agree, in the second example, the benefit is cle
Philipp Keck
2016/07/01 13:00:03
Done.
| |
| 44 suggestions->erase(remove_from, suggestions->end()); | |
| 45 return true; | |
| 46 } | |
| 47 } | |
| 48 | |
| 49 } // namespace | |
| 50 | |
| 51 ContentSuggestionsService::ContentSuggestionsService(bool enabled) | |
| 52 : enabled_(enabled) {} | |
| 53 | |
| 54 ContentSuggestionsService::~ContentSuggestionsService() {} | |
| 55 | |
| 56 void ContentSuggestionsService::Shutdown() { | |
| 57 FOR_EACH_OBSERVER(Observer, observers_, ContentSuggestionsServiceShutdown()); | |
| 58 observers_.Clear(); | |
|
Marc Treib
2016/06/30 10:36:54
Hm. Since the observers register themselves, they
tschumann
2016/06/30 11:58:11
+1
Philipp Keck
2016/06/30 17:14:08
Marc's precondition is not perfectly true: Observe
Marc Treib
2016/07/01 09:15:32
But the observers need a reference to the service
Philipp Keck
2016/07/01 13:00:04
The Unregistering would need to be triggered from
Marc Treib
2016/07/01 13:29:17
Hm, I think the categories should be registered "a
Philipp Keck
2016/07/01 13:45:11
I agree about the static categories, when conflict
Marc Treib
2016/07/01 15:08:14
DCHECK should be fine; I don't actually expect any
| |
| 59 for (auto& provider : providers_) { | |
| 60 provider.second->SetObserver(nullptr); | |
| 61 } | |
| 62 providers_.clear(); | |
| 63 enabled_ = false; | |
|
Marc Treib
2016/06/30 10:36:54
Also clear the cached suggestions, just to be sure
Philipp Keck
2016/06/30 17:14:08
Yes, see previous comment.
Marc Treib
2016/07/01 09:15:32
Hmm?
Philipp Keck
2016/07/01 13:00:04
Depending on how we change the shutdown, I would a
Marc Treib
2016/07/01 15:08:14
Acknowledged.
| |
| 64 } | |
| 65 | |
| 66 const std::vector<ContentSuggestion>& | |
| 67 ContentSuggestionsService::GetSuggestionsForCategory( | |
| 68 ContentSuggestionCategory category) const { | |
| 69 return suggestions_by_category_.at(category); | |
|
Marc Treib
2016/06/30 10:36:54
Hm, so this assumes that it'll never be called for
tschumann
2016/06/30 10:55:19
hmmm... is there anything guaranteeing that we hav
Philipp Keck
2016/06/30 17:14:08
In the obvious, simple Android implementation, thi
Marc Treib
2016/07/01 09:15:32
"no list" isn't an option if you return a referenc
Philipp Keck
2016/07/01 13:00:04
Changed to empty list.
| |
| 70 } | |
| 71 | |
| 72 void ContentSuggestionsService::FetchSuggestionImage( | |
| 73 const std::string& suggestion_id, | |
| 74 const ImageFetchedCallback& callback) { | |
| 75 ContentSuggestionsProviderType provider_type = | |
| 76 GetProviderTypeFromCombinedID(suggestion_id); | |
| 77 std::string original_id = GetIDFromCombinedID(suggestion_id); | |
|
Marc Treib
2016/06/30 10:36:54
nit: move this into the "if"
Philipp Keck
2016/06/30 17:14:08
Done.
| |
| 78 if (providers_.count(provider_type)) { | |
| 79 providers_[provider_type]->FetchSuggestionImage( | |
| 80 original_id, base::Bind(callback, suggestion_id)); | |
| 81 } else { | |
| 82 LOG(WARNING) << "Requested image for suggestion " << suggestion_id | |
| 83 << " for unavailable provider type " << int(provider_type); | |
| 84 callback.Run(suggestion_id, gfx::Image()); | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 void ContentSuggestionsService::ClearCachedSuggestions() { | |
| 89 categories_.clear(); | |
| 90 suggestions_by_category_.clear(); | |
| 91 for (auto& provider : providers_) { | |
| 92 provider.second->ClearCachedSuggestions(); | |
| 93 } | |
| 94 FOR_EACH_OBSERVER(Observer, observers_, OnSuggestionsChanged()); | |
| 95 } | |
| 96 | |
| 97 void ContentSuggestionsService::ClearDiscardedSuggestions() { | |
| 98 for (auto& provider : providers_) { | |
| 99 provider.second->ClearDiscardedSuggestions(); | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 void ContentSuggestionsService::DiscardSuggestion( | |
| 104 const std::string& suggestion_id) { | |
| 105 ContentSuggestionsProviderType provider_type = | |
| 106 GetProviderTypeFromCombinedID(suggestion_id); | |
| 107 std::string original_id = GetIDFromCombinedID(suggestion_id); | |
| 108 | |
| 109 if (providers_.count(provider_type)) { | |
| 110 providers_[provider_type]->DiscardSuggestion(original_id); | |
| 111 | |
| 112 // Remove the suggestion locally. | |
| 113 for (auto& pair : suggestions_by_category_) { | |
|
Marc Treib
2016/06/30 10:36:54
Can't you get the category from the suggestions, r
Philipp Keck
2016/06/30 17:14:08
Then I would need to include the category in the D
Marc Treib
2016/07/01 09:15:32
Ah, right, sorry. Carry on :)
Philipp Keck
2016/07/01 13:00:04
It might be part of the ID, if we enforce that the
Marc Treib
2016/07/01 13:29:17
Yup, SGTM.
| |
| 114 auto position = | |
| 115 std::find_if(pair.second.begin(), pair.second.end(), | |
| 116 [suggestion_id](const ContentSuggestion& suggestion) { | |
|
Marc Treib
2016/06/30 10:36:54
You could capture this by reference, to save a str
Philipp Keck
2016/06/30 17:14:08
Done.
| |
| 117 return suggestion_id == suggestion.id(); | |
| 118 }); | |
| 119 if (position != pair.second.end()) { | |
| 120 pair.second.erase(position); | |
|
Marc Treib
2016/07/01 09:15:32
Add a break? Once we're removed the suggestion, th
Philipp Keck
2016/07/01 13:00:03
Done.
| |
| 121 } | |
| 122 } | |
| 123 } else { | |
|
Marc Treib
2016/07/01 09:15:32
nit: Turning the if around might make things a bit
Philipp Keck
2016/07/01 13:00:04
Done.
| |
| 124 LOG(WARNING) << "Discarded suggestion " << suggestion_id | |
| 125 << " for unavailable provider type " | |
| 126 << static_cast<base::underlying_type< | |
| 127 ContentSuggestionsProviderType>::type>(provider_type); | |
|
Marc Treib
2016/06/30 10:36:54
int(provider_type)
tschumann
2016/06/30 11:58:11
i'd vote for implicit_cast<int> as it's the least
Philipp Keck
2016/06/30 17:14:08
Wasn't sure where to import the implicit_cast from
Marc Treib
2016/07/01 09:15:32
implicit_cast isn't a standard C++ feature, and do
tschumann
2016/07/01 09:55:59
=) argh... spent too much time in google3 it seems
Philipp Keck
2016/07/01 13:00:04
It's "int(provider_type)" now? See discussion abov
| |
| 128 } | |
| 129 } | |
| 130 | |
| 131 void ContentSuggestionsService::AddObserver(Observer* observer) { | |
| 132 observers_.AddObserver(observer); | |
| 133 } | |
| 134 | |
| 135 void ContentSuggestionsService::RemoveObserver(Observer* observer) { | |
| 136 observers_.RemoveObserver(observer); | |
| 137 } | |
| 138 | |
| 139 void ContentSuggestionsService::RegisterProvider( | |
| 140 ContentSuggestionsProvider* provider) { | |
| 141 DCHECK_EQ(0ul, providers_.count(provider->GetProviderType())); | |
| 142 providers_[provider->GetProviderType()] = provider; | |
| 143 provider->SetObserver(this); | |
| 144 } | |
| 145 | |
| 146 //////////////////////////////////////////////////////////////////////////////// | |
| 147 // Private methods | |
| 148 | |
| 149 void ContentSuggestionsService::OnSuggestionsChanged( | |
| 150 ContentSuggestionsProviderType provider_type, | |
| 151 ContentSuggestionCategory changed_category, | |
| 152 std::vector<ContentSuggestion> new_suggestions) { | |
| 153 RemoveSuggestionsOfProvider(&suggestions_by_category_[changed_category], | |
| 154 provider_type); | |
| 155 | |
| 156 std::move(new_suggestions.begin(), new_suggestions.end(), | |
| 157 std::back_inserter(suggestions_by_category_[changed_category])); | |
| 158 | |
| 159 // Ensure the category exists exactly if its list of suggestions is nonempty. | |
| 160 auto position = | |
| 161 std::find(categories_.begin(), categories_.end(), changed_category); | |
| 162 if (suggestions_by_category_[changed_category].empty()) { | |
| 163 suggestions_by_category_.erase(changed_category); | |
| 164 if (position != categories_.end()) { | |
| 165 categories_.erase(position); | |
| 166 } | |
| 167 } else { | |
| 168 if (position == categories_.end()) { | |
| 169 // TODO(pke) Useful ordering of categories. | |
|
tschumann
2016/06/30 10:55:19
can you explain this TODO() in a bit more detail?
Philipp Keck
2016/06/30 17:14:08
Done.
| |
| 170 categories_.emplace_back(changed_category); | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 FOR_EACH_OBSERVER(Observer, observers_, OnSuggestionsChanged()); | |
| 175 } | |
| 176 | |
| 177 void ContentSuggestionsService::OnProviderShutdown( | |
| 178 ContentSuggestionsProviderType provider_type) { | |
| 179 auto iterator = suggestions_by_category_.begin(); | |
| 180 while (iterator != suggestions_by_category_.end()) { | |
| 181 if (RemoveSuggestionsOfProvider(&iterator->second, provider_type) && | |
| 182 iterator->second.empty()) { | |
| 183 categories_.erase( | |
| 184 std::find(categories_.begin(), categories_.end(), iterator->first)); | |
| 185 iterator = suggestions_by_category_.erase(iterator); | |
| 186 } else { | |
| 187 ++iterator; | |
| 188 } | |
| 189 } | |
| 190 FOR_EACH_OBSERVER(Observer, observers_, OnSuggestionsChanged()); | |
| 191 providers_.erase(provider_type); | |
| 192 } | |
| 193 | |
| 194 } // namespace ntp_snippets | |
| OLD | NEW |