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

Side by Side Diff: chrome/browser/ntp_snippets/downloads/download_suggestions_provider.cc

Issue 2360263002: [NTPSnippets] Show all downloads on the NTP (3/3): Downloads provider. (Closed)
Patch Set: Marc's comments. Created 4 years, 2 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
(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 "chrome/browser/ntp_snippets/downloads/download_suggestions_provider.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/guid.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "base/time/time.h"
16 #include "components/ntp_snippets/pref_names.h"
17 #include "components/ntp_snippets/pref_util.h"
18 #include "components/offline_pages/client_namespace_constants.h"
19 #include "components/prefs/pref_registry_simple.h"
20 #include "components/prefs/pref_service.h"
21 #include "grit/components_strings.h"
22 #include "net/base/filename_util.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/gfx/image/image.h"
25
26 using content::DownloadItem;
27 using content::DownloadManager;
28 using offline_pages::OfflinePageItem;
29
30 namespace ntp_snippets {
31
32 namespace {
33
34 const int kMaxSuggestionsCount = 5;
Marc Treib 2016/10/11 12:07:40 This should probably be configurable via a variati
vitaliii 2016/10/13 08:43:06 Done.
35 const char kAssetDownloadsPrefix[] = "D";
36 const char kOfflinePageDownloadsPrefix[] = "O";
Marc Treib 2016/10/11 12:07:40 Just make these two chars (not array)
vitaliii 2016/10/13 08:43:06 Done.
37
38 std::string GetOfflinePagePerCategoryID(int64_t raw_offline_page_id) {
39 // Raw ID is prefixed in order to avoid conflicts with asset downloads.
40 return base::JoinString(
41 {kOfflinePageDownloadsPrefix, base::IntToString(raw_offline_page_id)},
42 "");
Marc Treib 2016/10/11 12:07:41 s/""/std::string()/ But if you're just appending
vitaliii 2016/10/13 08:43:05 Done.
43 }
44
45 std::string GetAssetDownloadPerCategoryID(uint32_t raw_download_id) {
46 // Raw ID is prefixed in order to avoid conflicts with offline page downloads.
47 return base::JoinString(
48 {kAssetDownloadsPrefix, base::UintToString(raw_download_id)}, "");
Marc Treib 2016/10/11 12:07:41 Same here
vitaliii 2016/10/13 08:43:05 Done.
49 }
50
51 // Determines whether |suggestion_id| corresponds to offline page suggestion or
52 // asset download based on |id_within_category| prefix.
53 bool IsOfflinePageID(const ContentSuggestion::ID& suggestion_id) {
Marc Treib 2016/10/11 12:07:40 nit: "OfflinePageID" is somewhat overloaded. Maybe
vitaliii 2016/10/13 08:43:05 Done.
54 const std::string& id_within_category = suggestion_id.id_within_category();
55 if (!id_within_category.empty()) {
56 if (id_within_category[0] == kOfflinePageDownloadsPrefix[0])
57 return true;
58 if (id_within_category[0] == kAssetDownloadsPrefix[0])
59 return false;
60 }
61 NOTREACHED() << "Unknown id_within_category " << id_within_category;
62 return false;
63 }
64
65 struct OrderOfflinePagesByMostRecentlyVisitedFirst {
66 bool operator()(const OfflinePageItem* left,
67 const OfflinePageItem* right) const {
68 return left->last_access_time > right->last_access_time;
69 }
70 };
71
72 bool IsOfflinePageDownload(const offline_pages::ClientId& client_id) {
73 return (client_id.name_space == offline_pages::kAsyncNamespace ||
74 client_id.name_space == offline_pages::kDownloadNamespace ||
75 client_id.name_space == offline_pages::kNTPSuggestionsNamespace) &&
Marc Treib 2016/10/11 12:07:41 What's kNTPSuggestionsNamespace?
vitaliii 2016/10/13 08:43:05 This is for offlined NTP items, e.g. right click o
76 base::IsValidGUID(client_id.id);
Marc Treib 2016/10/11 12:07:41 Is this needed? Would we ever expect to get an inv
vitaliii 2016/10/13 08:43:06 It was done like that previously (and the logic wa
77 }
78
79 bool IsDownloadCompleted(const DownloadItem* item) {
Marc Treib 2016/10/11 12:07:40 nit: "const DownloadItem&" if it can't be null
vitaliii 2016/10/13 08:43:06 Wow, I did not know that this is possible with poi
80 return item->GetState() == DownloadItem::DownloadState::COMPLETE &&
81 !item->GetFileExternallyRemoved();
82 }
83
84 struct OrderDownloadsMostRecentlyDownloadedCompletedFirst {
85 bool operator()(const DownloadItem* left, const DownloadItem* right) const {
86 if (IsDownloadCompleted(left) != IsDownloadCompleted(right))
87 return IsDownloadCompleted(left);
88 return left->GetEndTime() > right->GetEndTime();
89 }
90 };
91
92 struct SuggestionItemWrapper {
93 base::Time time;
94 bool is_offline_page;
95 int index;
96 bool operator<(const SuggestionItemWrapper& other) const {
97 return time > other.time;
98 }
99 };
100
101 } // namespace
102
103 DownloadSuggestionsProvider::DownloadSuggestionsProvider(
104 ContentSuggestionsProvider::Observer* observer,
105 CategoryFactory* category_factory,
106 const scoped_refptr<OfflinePageProxy>& offline_page_proxy,
107 content::DownloadManager* download_manager,
108 PrefService* pref_service,
109 bool download_manager_ui_enabled)
110 : ContentSuggestionsProvider(observer, category_factory),
111 category_status_(CategoryStatus::AVAILABLE_LOADING),
112 provided_category_(
113 category_factory->FromKnownCategory(KnownCategories::DOWNLOADS)),
114 offline_page_proxy_(offline_page_proxy),
115 download_manager_notifier_(download_manager, this),
116 pref_service_(pref_service),
117 download_manager_ui_enabled_(download_manager_ui_enabled),
118 weak_ptr_factory_(this) {
119 observer->OnCategoryStatusChanged(this, provided_category_, category_status_);
120 offline_page_proxy_->AddObserver(this);
121 FetchAllDownloadsAndSubmitSuggestions();
122 }
123
124 DownloadSuggestionsProvider::~DownloadSuggestionsProvider() {
125 offline_page_proxy_->RemoveObserver(this);
126 }
127
128 CategoryStatus DownloadSuggestionsProvider::GetCategoryStatus(
129 Category category) {
130 if (category == provided_category_)
131 return category_status_;
132 NOTREACHED() << "Unknown category " << category.id();
133 return CategoryStatus::NOT_PROVIDED;
Marc Treib 2016/10/11 12:07:40 Just do DCHECK_EQ(provided_category_, category); r
vitaliii 2016/10/13 08:43:05 Done.
134 }
135
136 CategoryInfo DownloadSuggestionsProvider::GetCategoryInfo(Category category) {
137 if (category == provided_category_) {
138 return CategoryInfo(
139 l10n_util::GetStringUTF16(IDS_NTP_DOWNLOAD_SUGGESTIONS_SECTION_HEADER),
140 ContentSuggestionsCardLayout::MINIMAL_CARD,
141 /*has_more_button=*/download_manager_ui_enabled_,
142 /*show_if_empty=*/false);
143 }
144 NOTREACHED() << "Unknown category " << category.id();
Marc Treib 2016/10/11 12:07:40 Same here
vitaliii 2016/10/13 08:43:06 Done.
145 return CategoryInfo(base::string16(),
146 ContentSuggestionsCardLayout::MINIMAL_CARD,
147 /*has_more_button=*/false,
148 /*show_if_empty=*/false);
149 }
150
151 void DownloadSuggestionsProvider::DismissSuggestion(
152 const ContentSuggestion::ID& suggestion_id) {
153 DCHECK_EQ(provided_category_, suggestion_id.category());
154 std::set<std::string> dismissed_ids =
155 ReadDismissedIDsFromPrefs(suggestion_id);
156 dismissed_ids.insert(suggestion_id.id_within_category());
157 StoreDismissedIDsToPrefs(suggestion_id, dismissed_ids);
Marc Treib 2016/10/11 12:07:41 nit: I find it a bit confusing to pass in the sugg
vitaliii 2016/10/13 08:43:05 I did the former. The latter is less attractive,
158
159 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id);
160 }
161
162 void DownloadSuggestionsProvider::FetchSuggestionImage(
163 const ContentSuggestion::ID& suggestion_id,
164 const ImageFetchedCallback& callback) {
165 // TODO(vitaliii): Fetch proper thumbnail from OfflinePageModel once it is
166 // available there.
167 // TODO(vitaliii): Provide site's favicon for assets downloads.
168 base::ThreadTaskRunnerHandle::Get()->PostTask(
169 FROM_HERE, base::Bind(callback, gfx::Image()));
170 }
171
172 void DownloadSuggestionsProvider::ClearHistory(
173 base::Time begin,
174 base::Time end,
175 const base::Callback<bool(const GURL& url)>& filter) {
176 ClearDismissedSuggestionsForDebugging(provided_category_);
Marc Treib 2016/10/11 12:07:40 This will call FetchAllDownloadsAndSubmitSuggestio
vitaliii 2016/10/13 08:43:05 Good point! Actually current cache is completely i
Marc Treib 2016/10/13 12:11:25 Hm, but that is async, right? So maybe it's best t
vitaliii 2016/10/15 18:36:30 Done.
177 cached_offline_page_downloads_.clear();
178 cached_asset_downloads_.clear();
179 FetchAllDownloadsAndSubmitSuggestions();
180 }
181
182 void DownloadSuggestionsProvider::ClearCachedSuggestions(Category category) {
183 DCHECK_EQ(provided_category_, category);
184 cached_offline_page_downloads_.clear();
185 cached_asset_downloads_.clear();
186 FetchAllDownloadsAndSubmitSuggestions();
Marc Treib 2016/10/11 12:07:41 Would we expect this to change anything? The cache
vitaliii 2016/10/13 08:43:05 Good point.
187 }
188
189 void DownloadSuggestionsProvider::GetDismissedSuggestionsForDebugging(
190 Category category,
191 const DismissedSuggestionsCallback& callback) {
192 DCHECK_EQ(provided_category_, category);
193 // TODO(vitaliii): Implement.
194 callback.Run(std::vector<ContentSuggestion>());
195 }
196
197 void DownloadSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
198 Category category) {
199 DCHECK_EQ(provided_category_, category);
200 StoreAssetDismissedIDsToPrefs(std::set<std::string>());
201 StoreOfflinePageDismissedIDsToPrefs(std::set<std::string>());
202 FetchAllDownloadsAndSubmitSuggestions();
203 }
204
205 // static
206 void DownloadSuggestionsProvider::RegisterProfilePrefs(
207 PrefRegistrySimple* registry) {
208 registry->RegisterListPref(prefs::kDismissedAssetDownloadSuggestions);
209 registry->RegisterListPref(prefs::kDismissedOfflinePageDownloadSuggestions);
210 }
211
212 ////////////////////////////////////////////////////////////////////////////////
213 // Private methods
214
215 void DownloadSuggestionsProvider::OfflinePageModelChanged(
216 const std::vector<offline_pages::OfflinePageItem>& offline_pages) {
217 NotifyStatusChanged(CategoryStatus::AVAILABLE);
Marc Treib 2016/10/11 12:07:41 SubmitContentSuggestions will change the status, s
vitaliii 2016/10/13 08:43:07 Yes, but it will happen just before the new sugges
Marc Treib 2016/10/13 12:11:25 But all this happens synchronously, so the UI does
vitaliii 2016/10/15 18:36:30 Done.
218 ProcessAllOfflinePages(offline_pages);
219 SubmitContentSuggestions();
220 }
221
222 void DownloadSuggestionsProvider::OfflinePageDeleted(
223 int64_t offline_id,
224 const offline_pages::ClientId& client_id) {
225 // Because we never switch to NOT_PROVIDED dynamically, there can be no open
226 // UI containing an invalidated suggestion unless the status is something
227 // other than NOT_PROVIDED, so only notify invalidation in that case.
Marc Treib 2016/10/11 12:07:41 Actually, can the status *ever* be NOT_PROVIDED no
vitaliii 2016/10/13 08:43:05 Looks like not, but I am not sure. However, I do
228 if (category_status_ != CategoryStatus::NOT_PROVIDED &&
229 IsOfflinePageDownload(client_id)) {
230 InvalidateSuggestion(GetOfflinePagePerCategoryID(offline_id));
231 }
232 }
233
234 void DownloadSuggestionsProvider::OnDownloadCreated(DownloadManager* manager,
235 DownloadItem* item) {
236 // This is called when new downloads are started and on startup for existing
237 // ones.
238 if (CacheAssetDownloadIfNeeded(item))
239 SubmitContentSuggestions();
240 }
241
242 void DownloadSuggestionsProvider::OnDownloadUpdated(DownloadManager* manager,
243 DownloadItem* item) {
244 // Unfinished downloads may become completed, therefore, this call cannot be
245 // ignored.
246 if (CacheAssetDownloadIfNeeded(item))
247 SubmitContentSuggestions();
248 }
249 void DownloadSuggestionsProvider::OnDownloadOpened(DownloadManager* manager,
Marc Treib 2016/10/11 12:07:40 nit: empty line before
vitaliii 2016/10/13 08:43:06 Done.
250 DownloadItem* item) {
251 // Ignored.
252 }
253 void DownloadSuggestionsProvider::OnDownloadRemoved(DownloadManager* manager,
Marc Treib 2016/10/11 12:07:40 Also here
vitaliii 2016/10/13 08:43:06 Done.
254 DownloadItem* item) {
255 if (!IsDownloadCompleted(item))
256 return;
257 // TODO(vitaliii): Implement a better way to clean up dismissed IDs (in case
258 // some calls are missed).
259 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId()));
260 }
261
262 void DownloadSuggestionsProvider::NotifyStatusChanged(
263 CategoryStatus new_status) {
264 DCHECK_NE(CategoryStatus::NOT_PROVIDED, category_status_);
Marc Treib 2016/10/11 12:07:40 Should this check new_status instead of category_s
vitaliii 2016/10/13 08:43:07 Done.
265 if (category_status_ == new_status)
266 return;
267 category_status_ = new_status;
268 observer()->OnCategoryStatusChanged(this, provided_category_, new_status);
Marc Treib 2016/10/11 12:07:40 nit: s/new_status/category_status_/
vitaliii 2016/10/13 08:43:06 Done.
269 }
270
271 void DownloadSuggestionsProvider::FetchOfflinePagesDownloads() {
272 offline_page_proxy_->GetAllPages(
273 base::Bind(&DownloadSuggestionsProvider::ProcessAllOfflinePages,
274 weak_ptr_factory_.GetWeakPtr()));
275 }
276
277 void DownloadSuggestionsProvider::
278 FetchOfflinePagesDownloadsAndSubmitSuggestions() {
279 offline_page_proxy_->GetAllPages(
280 base::Bind(&DownloadSuggestionsProvider::OfflinePageModelChanged,
281 weak_ptr_factory_.GetWeakPtr()));
282 }
283
284 void DownloadSuggestionsProvider::FetchAssetsDownloads() {
285 DownloadManager* manager = download_manager_notifier_.GetManager();
286 if (!manager) {
287 // The manager has gone down.
288 return;
289 }
290
291 NotifyStatusChanged(CategoryStatus::AVAILABLE);
Marc Treib 2016/10/11 12:07:41 Why is this here?
vitaliii 2016/10/13 08:43:07 Done.
292
293 std::vector<DownloadItem*> downloads;
294 manager->GetAllDownloads(&downloads);
295
296 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs();
297 std::set<std::string> new_dismissed_ids;
Marc Treib 2016/10/11 12:07:40 Maybe old_dismissed_ids and retained_dismissed_ids
vitaliii 2016/10/13 08:43:06 Done.
298 std::vector<DownloadItem*> not_dismissed_downloads;
299 for (DownloadItem* item : downloads) {
Marc Treib 2016/10/11 12:07:41 const?
vitaliii 2016/10/13 08:43:05 Done.
300 std::string within_category_id =
301 GetAssetDownloadPerCategoryID(item->GetId());
302 if (!dismissed_ids.count(within_category_id)) {
303 not_dismissed_downloads.push_back(item);
304 } else {
305 new_dismissed_ids.insert(within_category_id);
306 }
307 }
308
309 if (dismissed_ids.size() != new_dismissed_ids.size())
310 StoreAssetDismissedIDsToPrefs(new_dismissed_ids);
311
312 downloads = not_dismissed_downloads;
Marc Treib 2016/10/11 12:07:40 Maybe rename "downloads" to "all_downloads", "not_
vitaliii 2016/10/13 08:43:06 Done.
313 if (static_cast<int>(downloads.size()) > kMaxSuggestionsCount) {
314 std::nth_element(downloads.begin(),
Marc Treib 2016/10/11 12:07:41 Nice! But since nth_element isn't very common, and
vitaliii 2016/10/13 08:43:06 Done.
315 downloads.begin() + kMaxSuggestionsCount, downloads.end(),
316 OrderDownloadsMostRecentlyDownloadedCompletedFirst());
317 downloads.resize(kMaxSuggestionsCount);
318 }
319
320 cached_asset_downloads_.clear();
321 for (DownloadItem* item : downloads) {
322 if (IsDownloadCompleted(item))
Marc Treib 2016/10/11 12:07:41 Wouldn't it be easier if the filtering happened be
vitaliii 2016/10/13 08:43:06 Done.
323 cached_asset_downloads_.push_back(item);
324 }
325 }
326
327 void DownloadSuggestionsProvider::FetchAllDownloadsAndSubmitSuggestions() {
328 FetchAssetsDownloads();
329 FetchOfflinePagesDownloadsAndSubmitSuggestions();
Marc Treib 2016/10/11 12:07:41 I was gonna say that this isn't safe since the off
vitaliii 2016/10/13 08:43:05 Done.
330 }
331
332 void DownloadSuggestionsProvider::SubmitContentSuggestions() {
333 NotifyStatusChanged(CategoryStatus::AVAILABLE);
334
335 std::vector<SuggestionItemWrapper> suggestion_items;
336 for (int i = 0; i < static_cast<int>(cached_offline_page_downloads_.size());
337 ++i) {
338 SuggestionItemWrapper wrapped_item;
339 wrapped_item.is_offline_page = true;
340 wrapped_item.index = i;
Marc Treib 2016/10/11 12:07:41 Mh, this is a bit yucky. Maybe instead of bool+ind
vitaliii 2016/10/13 08:43:05 Acknowledged.
Marc Treib 2016/10/13 12:11:25 ...this reply confused me ;P "Obsolete, Suggestion
vitaliii 2016/10/15 18:36:29 I've got it. Sorry about that.
341 wrapped_item.time = cached_offline_page_downloads_[i].last_access_time;
342 suggestion_items.push_back(wrapped_item);
343 }
344
345 for (int i = 0; i < static_cast<int>(cached_asset_downloads_.size()); ++i) {
346 SuggestionItemWrapper wrapped_item;
347 wrapped_item.is_offline_page = false;
348 wrapped_item.index = i;
349 wrapped_item.time = cached_asset_downloads_[i]->GetEndTime();
350 suggestion_items.push_back(wrapped_item);
351 }
352
353 std::sort(suggestion_items.begin(), suggestion_items.end());
Marc Treib 2016/10/11 12:07:40 Or in fact: Can the sorting just happen after conv
vitaliii 2016/10/13 08:43:06 Done.
354
355 std::vector<ContentSuggestion> suggestions;
356 for (const SuggestionItemWrapper& wrapped_item : suggestion_items) {
357 if (suggestions.size() >= kMaxSuggestionsCount)
358 break;
359
360 if (wrapped_item.is_offline_page) {
361 suggestions.push_back(ConvertOfflinePage(
362 cached_offline_page_downloads_[wrapped_item.index]));
363 } else {
364 suggestions.push_back(
365 ConvertDownloadItem(cached_asset_downloads_[wrapped_item.index]));
366 }
367 }
368
369 observer()->OnNewSuggestions(this, provided_category_,
370 std::move(suggestions));
371 }
372
373 ContentSuggestion DownloadSuggestionsProvider::ConvertOfflinePage(
374 const OfflinePageItem& offline_page) const {
375 // TODO(vitaliii): Make sure the URL is actually opened as an offline URL even
376 // when the user is online.
Marc Treib 2016/10/11 12:07:40 Do we have a bug for this? If not, could you file
vitaliii 2016/10/13 08:43:06 Done. Why do not you like https://cs.chromium.org
Marc Treib 2016/10/13 12:11:25 TODOs in the code aren't a good issue tracking sys
vitaliii 2016/10/15 18:36:29 I see, good point.
377 ContentSuggestion suggestion(
378 ContentSuggestion::ID(provided_category_, GetOfflinePagePerCategoryID(
379 offline_page.offline_id)),
380 offline_page.url);
381
382 if (offline_page.title.empty()) {
383 // TODO(vitaliii): Remove this fallback once the OfflinePageModel provides
384 // titles for all (relevant) OfflinePageItems.
385 suggestion.set_title(base::UTF8ToUTF16(offline_page.url.spec()));
386 } else {
387 suggestion.set_title(offline_page.title);
388 }
389 suggestion.set_publish_date(offline_page.creation_time);
390 suggestion.set_publisher_name(base::UTF8ToUTF16(offline_page.url.host()));
391 return suggestion;
392 }
393
394 ContentSuggestion DownloadSuggestionsProvider::ConvertDownloadItem(
395 const DownloadItem* download_item) const {
396 ContentSuggestion::ID per_category_id(
Marc Treib 2016/10/11 12:07:40 This is not a per-category-ID. But I'd just inline
vitaliii 2016/10/13 08:43:05 Done.
397 provided_category_,
398 GetAssetDownloadPerCategoryID(download_item->GetId()));
399 // TODO(vitaliii): Ensure that files are opened in browser, but not downloaded
400 // again.
401 ContentSuggestion suggestion(
402 per_category_id,
403 net::FilePathToFileURL(download_item->GetTargetFilePath()));
404 // TODO(vitaliii): Set proper title.
405 suggestion.set_title(
406 base::UTF8ToUTF16(download_item->GetTargetFilePath().BaseName().value()));
Marc Treib 2016/10/11 12:07:40 I think this won't work on all platforms, since pa
vitaliii 2016/10/13 08:43:06 This is Anroid only for now, added DCHECK for futu
407 suggestion.set_publish_date(download_item->GetEndTime());
408 suggestion.set_publisher_name(
409 base::UTF8ToUTF16(download_item->GetURL().host()));
410 // TODO(vitaliii): Set suggestion icon.
411 return suggestion;
412 }
413
414 bool DownloadSuggestionsProvider::CacheAssetDownloadIfNeeded(
415 const content::DownloadItem* item) {
416 if (!IsDownloadCompleted(item))
417 return false;
418
419 std::vector<const content::DownloadItem*>& cache = cached_asset_downloads_;
Marc Treib 2016/10/11 12:07:41 Please avoid non-const references in general. Here
vitaliii 2016/10/13 08:43:06 Done.
420 if (std::find(cache.begin(), cache.end(), item) != cache.end())
Marc Treib 2016/10/11 12:07:41 optional: base::ContainsValue might be a bit easie
vitaliii 2016/10/13 08:43:07 Done.
421 return false;
422
423 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs();
424 if (dismissed_ids.count(GetAssetDownloadPerCategoryID(item->GetId())))
425 return false;
426
427 DCHECK(cache.size() <= kMaxSuggestionsCount);
Marc Treib 2016/10/11 12:07:40 DCHECK_LE?
vitaliii 2016/10/13 08:43:06 Done.
428 if (cache.size() == kMaxSuggestionsCount) {
429 std::vector<const content::DownloadItem*>::iterator oldest =
Marc Treib 2016/10/11 12:07:40 optional: auto oldest_it = ...?
vitaliii 2016/10/13 08:43:05 Done.
430 std::max_element(cache.begin(), cache.end(),
431 OrderDownloadsMostRecentlyDownloadedCompletedFirst());
432 if (item->GetEndTime() <= (*oldest)->GetEndTime())
vitaliii 2016/10/11 08:15:56 Would using an index here be better?
Marc Treib 2016/10/11 12:07:41 I don't follow - an index for what?
vitaliii 2016/10/13 08:43:06 i.e. int i = oldest - cached_asset_downloads_.beg
Marc Treib 2016/10/13 12:11:25 Ah - no, what you have is fine.
vitaliii 2016/10/15 18:36:29 Acknowledged.
433 return false;
434
435 (*oldest) = item;
Marc Treib 2016/10/11 12:07:41 nit: parens not required
vitaliii 2016/10/13 08:43:05 Done.
436 } else {
437 cache.push_back(item);
438 }
439
440 return true;
441 }
442
443 bool DownloadSuggestionsProvider::RemoveSuggestionFromCacheIfPresent(
444 const ContentSuggestion::ID& suggestion_id) {
445 DCHECK_EQ(provided_category_, suggestion_id.category());
446 if (IsOfflinePageID(suggestion_id)) {
447 std::vector<offline_pages::OfflinePageItem>& cache =
Marc Treib 2016/10/11 12:07:41 As above: Please spell out cached_offline_page_dow
vitaliii 2016/10/13 08:43:06 Done.
448 cached_offline_page_downloads_;
449 for (int i = 0; i < static_cast<int>(cache.size()); ++i) {
Marc Treib 2016/10/11 12:07:40 optional: This might be slightly nicer as an std::
vitaliii 2016/10/13 08:43:06 Done.
450 if (GetOfflinePagePerCategoryID(cache[i].offline_id) ==
451 suggestion_id.id_within_category()) {
452 cache.erase(cache.begin() + i);
453 return true;
454 }
455 }
456 return false;
457 } else {
Marc Treib 2016/10/11 12:07:40 No "else" after "return"
vitaliii 2016/10/13 08:43:06 Done.
458 std::vector<const content::DownloadItem*>& cache = cached_asset_downloads_;
Marc Treib 2016/10/11 12:07:40 Also here: Please spell out
vitaliii 2016/10/13 08:43:05 Done.
459 for (int i = 0; i < static_cast<int>(cache.size()); ++i) {
460 if (GetAssetDownloadPerCategoryID(cache[i]->GetId()) ==
461 suggestion_id.id_within_category()) {
462 cache.erase(cache.begin() + i);
463 return true;
464 }
465 }
466 return false;
467 }
468 }
469
470 void DownloadSuggestionsProvider::
471 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(
472 const ContentSuggestion::ID& suggestion_id) {
473 DCHECK_EQ(provided_category_, suggestion_id.category());
474 if (RemoveSuggestionFromCacheIfPresent(suggestion_id)) {
Marc Treib 2016/10/11 12:07:41 if (!Remove...) return; and save a level of inde
vitaliii 2016/10/13 08:43:07 Done.
475 if (IsOfflinePageID(suggestion_id)) {
476 if (cached_offline_page_downloads_.size() == kMaxSuggestionsCount - 1) {
477 // Previously there were |kMaxSuggestionsCount| cached suggestion,
478 // therefore, overall there may be more than |kMaxSuggestionsCount|
479 // suggestions in the model and now one of them may be cached instead of
480 // the removed one. Even though, the suggestions are not immediately
481 // used the cache has to be kept up to date, because it may be used when
482 // other data source is updated.
483 FetchOfflinePagesDownloads();
484 }
485 } else {
486 if (cached_asset_downloads_.size() == kMaxSuggestionsCount - 1) {
487 // The same as the case above.
488 FetchAssetsDownloads();
489 }
490 }
491 }
492 }
493
494 void DownloadSuggestionsProvider::ProcessAllOfflinePages(
495 const std::vector<offline_pages::OfflinePageItem>& all_offline_pages) {
496 std::set<std::string> old_dismissed_ids =
497 ReadOfflinePageDismissedIDsFromPrefs();
498 std::set<std::string> new_dismissed_ids;
499 std::vector<const OfflinePageItem*> items;
500 // Filtering out dismissed items and pruning dismissed IDs.
501 for (const OfflinePageItem& item : all_offline_pages) {
502 if (!IsOfflinePageDownload(item.client_id))
503 continue;
504
505 std::string per_category_id = GetOfflinePagePerCategoryID(item.offline_id);
506 if (!old_dismissed_ids.count(per_category_id))
Marc Treib 2016/10/11 12:07:41 I think the dismissed IDs sometimes have the "O"/"
vitaliii 2016/10/13 08:43:05 For the former - I do not think so, I added DCHECK
Marc Treib 2016/10/13 12:11:25 I looked over all the code, and it looks like you
vitaliii 2016/10/15 18:36:29 Let's stick with this approach for now and then I
507 items.push_back(&item);
508 else
509 new_dismissed_ids.insert(per_category_id);
510 }
511
512 if (static_cast<int>(items.size()) > kMaxSuggestionsCount) {
513 std::nth_element(items.begin(), items.begin() + kMaxSuggestionsCount,
514 items.end(),
515 OrderOfflinePagesByMostRecentlyVisitedFirst());
Marc Treib 2016/10/11 12:07:41 You could use a lambda here
vitaliii 2016/10/13 08:43:04 Done.
516 items.resize(kMaxSuggestionsCount);
517 }
518
519 cached_offline_page_downloads_.clear();
520 for (const OfflinePageItem* item : items) {
521 cached_offline_page_downloads_.push_back(*item);
522 }
523
524 if (old_dismissed_ids.size() != new_dismissed_ids.size()) {
525 StoreOfflinePageDismissedIDsToPrefs(new_dismissed_ids);
526 }
527 }
528
529 void DownloadSuggestionsProvider::InvalidateSuggestion(
530 const std::string& per_category_id) {
531 ContentSuggestion::ID suggestion_id(provided_category_, per_category_id);
532 observer()->OnSuggestionInvalidated(this, suggestion_id);
533
534 std::set<std::string> dismissed_ids =
535 ReadDismissedIDsFromPrefs(suggestion_id);
536 auto it = dismissed_ids.find(per_category_id);
537 if (it != dismissed_ids.end()) {
538 dismissed_ids.erase(it);
539 StoreDismissedIDsToPrefs(suggestion_id, dismissed_ids);
540 }
541
542 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id);
543 }
544
545 std::set<std::string>
546 DownloadSuggestionsProvider::ReadAssetDismissedIDsFromPrefs() const {
547 return prefs::ReadDismissedIDsFromPrefs(
548 *pref_service_, prefs::kDismissedAssetDownloadSuggestions);
549 }
550
551 void DownloadSuggestionsProvider::StoreAssetDismissedIDsToPrefs(
552 const std::set<std::string>& dismissed_ids) {
553 prefs::StoreDismissedIDsToPrefs(
554 pref_service_, prefs::kDismissedAssetDownloadSuggestions, dismissed_ids);
555 }
556
557 std::set<std::string>
558 DownloadSuggestionsProvider::ReadOfflinePageDismissedIDsFromPrefs() const {
559 return prefs::ReadDismissedIDsFromPrefs(
560 *pref_service_, prefs::kDismissedOfflinePageDownloadSuggestions);
561 }
562
563 void DownloadSuggestionsProvider::StoreOfflinePageDismissedIDsToPrefs(
564 const std::set<std::string>& dismissed_ids) {
565 prefs::StoreDismissedIDsToPrefs(
566 pref_service_, prefs::kDismissedOfflinePageDownloadSuggestions,
567 dismissed_ids);
568 }
569
570 std::set<std::string> DownloadSuggestionsProvider::ReadDismissedIDsFromPrefs(
571 const ContentSuggestion::ID& suggestion_id) const {
572 if (IsOfflinePageID(suggestion_id)) {
573 return ReadOfflinePageDismissedIDsFromPrefs();
574 } else {
Marc Treib 2016/10/11 12:07:40 nit: No "else" after "return" Or maybe just get r
vitaliii 2016/10/13 08:43:06 Done.
575 return ReadAssetDismissedIDsFromPrefs();
576 }
577 }
578
579 void DownloadSuggestionsProvider::StoreDismissedIDsToPrefs(
580 const ContentSuggestion::ID& suggestion_id,
581 const std::set<std::string>& dismissed_ids) {
582 if (IsOfflinePageID(suggestion_id)) {
583 StoreOfflinePageDismissedIDsToPrefs(dismissed_ids);
584 } else {
Marc Treib 2016/10/11 12:07:39 Also here
vitaliii 2016/10/13 08:43:05 Done.
585 StoreAssetDismissedIDsToPrefs(dismissed_ids);
586 }
587 }
588
589 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698