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

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

Issue 2360263002: [NTPSnippets] Show all downloads on the NTP (3/3): Downloads provider. (Closed)
Patch Set: the freshest rebase. Created 4 years, 1 month 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/download_suggestions_provider.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/feature_list.h"
11 #include "base/guid.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "base/time/time.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "components/ntp_snippets/pref_names.h"
20 #include "components/ntp_snippets/pref_util.h"
21 #include "components/offline_pages/client_namespace_constants.h"
22 #include "components/prefs/pref_registry_simple.h"
23 #include "components/prefs/pref_service.h"
24 #include "net/base/filename_util.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/gfx/image/image.h"
27
28 using content::DownloadItem;
29 using content::DownloadManager;
30 using ntp_snippets::Category;
31 using ntp_snippets::CategoryInfo;
32 using ntp_snippets::CategoryStatus;
33 using ntp_snippets::ContentSuggestion;
34 using ntp_snippets::prefs::kDismissedAssetDownloadSuggestions;
35 using ntp_snippets::prefs::kDismissedOfflinePageDownloadSuggestions;
36 using offline_pages::OfflinePageItem;
37
38 namespace {
39
40 // TODO(vitaliii): Make this configurable via a variation param. See
41 // crbug.com/654800.
42 const int kMaxSuggestionsCount = 5;
43 const char kAssetDownloadsPrefix = 'D';
44 const char kOfflinePageDownloadsPrefix = 'O';
45
46 std::string GetOfflinePagePerCategoryID(int64_t raw_offline_page_id) {
47 // Raw ID is prefixed in order to avoid conflicts with asset downloads.
48 return std::string(1, kOfflinePageDownloadsPrefix) +
49 base::IntToString(raw_offline_page_id);
50 }
51
52 std::string GetAssetDownloadPerCategoryID(uint32_t raw_download_id) {
53 // Raw ID is prefixed in order to avoid conflicts with offline page downloads.
54 return std::string(1, kAssetDownloadsPrefix) +
55 base::UintToString(raw_download_id);
56 }
57
58 // Determines whether |suggestion_id| corresponds to offline page suggestion or
59 // asset download based on |id_within_category| prefix.
60 bool CorrespondsToOfflinePage(const ContentSuggestion::ID& suggestion_id) {
61 const std::string& id_within_category = suggestion_id.id_within_category();
62 if (!id_within_category.empty()) {
63 if (id_within_category[0] == kOfflinePageDownloadsPrefix)
64 return true;
65 if (id_within_category[0] == kAssetDownloadsPrefix)
66 return false;
67 }
68 NOTREACHED() << "Unknown id_within_category " << id_within_category;
69 return false;
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;
76 }
77
78 bool IsDownloadCompleted(const DownloadItem& item) {
79 return item.GetState() == DownloadItem::DownloadState::COMPLETE &&
80 !item.GetFileExternallyRemoved();
81 }
82
83 struct OrderDownloadsMostRecentlyDownloadedFirst {
84 bool operator()(const DownloadItem* left, const DownloadItem* right) const {
85 return left->GetEndTime() > right->GetEndTime();
86 }
87 };
88
89 } // namespace
90
91 DownloadSuggestionsProvider::DownloadSuggestionsProvider(
92 ContentSuggestionsProvider::Observer* observer,
93 ntp_snippets::CategoryFactory* category_factory,
94 scoped_refptr<ntp_snippets::OfflinePageProxy> offline_page_proxy,
95 content::DownloadManager* download_manager,
96 PrefService* pref_service,
97 bool download_manager_ui_enabled)
98 : ContentSuggestionsProvider(observer, category_factory),
99 category_status_(CategoryStatus::AVAILABLE_LOADING),
100 provided_category_(category_factory->FromKnownCategory(
101 ntp_snippets::KnownCategories::DOWNLOADS)),
102 offline_page_proxy_(std::move(offline_page_proxy)),
103 download_manager_(download_manager),
104 pref_service_(pref_service),
105 download_manager_ui_enabled_(download_manager_ui_enabled),
106 weak_ptr_factory_(this) {
107 observer->OnCategoryStatusChanged(this, provided_category_, category_status_);
108 offline_page_proxy_->AddObserver(this);
109 if (download_manager_)
110 download_manager_->AddObserver(this);
111 // No need to explicitly fetch the asset downloads, since for each of them
112 // |OnDownloadCreated| is fired.
113 AsynchronouslyFetchOfflinePagesDownloads(/*notify=*/true);
114 }
115
116 DownloadSuggestionsProvider::~DownloadSuggestionsProvider() {
117 offline_page_proxy_->RemoveObserver(this);
118 if (download_manager_) {
119 download_manager_->RemoveObserver(this);
120 UnregisterDownloadItemObservers();
121 }
122 }
123
124 CategoryStatus DownloadSuggestionsProvider::GetCategoryStatus(
125 Category category) {
126 DCHECK_EQ(provided_category_, category);
127 return category_status_;
128 }
129
130 CategoryInfo DownloadSuggestionsProvider::GetCategoryInfo(Category category) {
131 DCHECK_EQ(provided_category_, category);
132 // TODO(vitaliii): Do not show "More" button when there is no downloads UI.
133 // See crbug.com/660030.
134 return CategoryInfo(
135 l10n_util::GetStringUTF16(IDS_NTP_DOWNLOAD_SUGGESTIONS_SECTION_HEADER),
136 ntp_snippets::ContentSuggestionsCardLayout::MINIMAL_CARD,
137 /*has_more_button=*/download_manager_ui_enabled_,
138 /*show_if_empty=*/false,
139 l10n_util::GetStringUTF16(IDS_NTP_DOWNLOADS_SUGGESTIONS_SECTION_EMPTY));
140 }
141
142 void DownloadSuggestionsProvider::DismissSuggestion(
143 const ContentSuggestion::ID& suggestion_id) {
144 DCHECK_EQ(provided_category_, suggestion_id.category());
145 std::set<std::string> dismissed_ids =
146 ReadDismissedIDsFromPrefs(CorrespondsToOfflinePage(suggestion_id));
147 dismissed_ids.insert(suggestion_id.id_within_category());
148 StoreDismissedIDsToPrefs(CorrespondsToOfflinePage(suggestion_id),
149 dismissed_ids);
150
151 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id);
152 }
153
154 void DownloadSuggestionsProvider::FetchSuggestionImage(
155 const ContentSuggestion::ID& suggestion_id,
156 const ImageFetchedCallback& callback) {
157 // TODO(vitaliii): Fetch proper thumbnail from OfflinePageModel once it is
158 // available there.
159 // TODO(vitaliii): Provide site's favicon for assets downloads. See
160 // crbug.com/631447.
161 base::ThreadTaskRunnerHandle::Get()->PostTask(
162 FROM_HERE, base::Bind(callback, gfx::Image()));
163 }
164
165 void DownloadSuggestionsProvider::ClearHistory(
166 base::Time begin,
167 base::Time end,
168 const base::Callback<bool(const GURL& url)>& filter) {
169 cached_offline_page_downloads_.clear();
170 cached_asset_downloads_.clear();
171 // This will trigger an asynchronous re-fetch.
172 ClearDismissedSuggestionsForDebugging(provided_category_);
173 }
174
175 void DownloadSuggestionsProvider::ClearCachedSuggestions(Category category) {
176 DCHECK_EQ(provided_category_, category);
177 // Ignored. The internal caches are not stored on disk and they are just
178 // partial copies of the data stored at OfflinePage model and DownloadManager.
179 // If it is cleared there, it will be cleared in these caches as well.
180 }
181
182 void DownloadSuggestionsProvider::GetDismissedSuggestionsForDebugging(
183 Category category,
184 const DismissedSuggestionsCallback& callback) {
185 DCHECK_EQ(provided_category_, category);
186
187 offline_page_proxy_->GetAllPages(
188 base::Bind(&DownloadSuggestionsProvider::
189 GetAllPagesCallbackForGetDismissedSuggestions,
190 weak_ptr_factory_.GetWeakPtr(), callback));
191 }
192
193 void DownloadSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
194 Category category) {
195 DCHECK_EQ(provided_category_, category);
196 StoreAssetDismissedIDsToPrefs(std::set<std::string>());
197 StoreOfflinePageDismissedIDsToPrefs(std::set<std::string>());
198 AsynchronouslyFetchAllDownloadsAndSubmitSuggestions();
199 }
200
201 // static
202 void DownloadSuggestionsProvider::RegisterProfilePrefs(
203 PrefRegistrySimple* registry) {
204 registry->RegisterListPref(kDismissedAssetDownloadSuggestions);
205 registry->RegisterListPref(kDismissedOfflinePageDownloadSuggestions);
206 }
207
208 ////////////////////////////////////////////////////////////////////////////////
209 // Private methods
210
211 void DownloadSuggestionsProvider::GetAllPagesCallbackForGetDismissedSuggestions(
212 const DismissedSuggestionsCallback& callback,
213 const std::vector<OfflinePageItem>& offline_pages) const {
214 std::set<std::string> dismissed_ids = ReadOfflinePageDismissedIDsFromPrefs();
215 std::vector<ContentSuggestion> suggestions;
216 for (const OfflinePageItem& item : offline_pages) {
217 if (dismissed_ids.count(GetOfflinePagePerCategoryID(item.offline_id)))
218 suggestions.push_back(ConvertOfflinePage(item));
219 }
220
221 if (download_manager_) {
222 std::vector<DownloadItem*> all_downloads;
223 download_manager_->GetAllDownloads(&all_downloads);
224
225 dismissed_ids = ReadAssetDismissedIDsFromPrefs();
226
227 for (const DownloadItem* item : all_downloads) {
228 if (dismissed_ids.count(GetAssetDownloadPerCategoryID(item->GetId())))
229 suggestions.push_back(ConvertDownloadItem(*item));
230 }
231 }
232
233 callback.Run(std::move(suggestions));
234 }
235
236 void DownloadSuggestionsProvider::OfflinePageModelChanged(
237 const std::vector<offline_pages::OfflinePageItem>& offline_pages) {
238 UpdateOfflinePagesCache(/*notify=*/true, offline_pages);
239 }
240
241 void DownloadSuggestionsProvider::OfflinePageDeleted(
242 int64_t offline_id,
243 const offline_pages::ClientId& client_id) {
244 if (IsOfflinePageDownload(client_id))
245 InvalidateSuggestion(GetOfflinePagePerCategoryID(offline_id));
246 }
247
248 void DownloadSuggestionsProvider::OnDownloadCreated(DownloadManager* manager,
249 DownloadItem* item) {
250 DCHECK_EQ(download_manager_, manager);
251 // This is called when new downloads are started and on startup for existing
252 // ones. We listen to each item to know when it is destroyed.
253 item->AddObserver(this);
254 if (CacheAssetDownloadIfNeeded(item))
255 SubmitContentSuggestions();
256 }
257
258 void DownloadSuggestionsProvider::ManagerGoingDown(DownloadManager* manager) {
259 DCHECK_EQ(download_manager_, manager);
260 UnregisterDownloadItemObservers();
261 download_manager_ = nullptr;
262 }
263
264 void DownloadSuggestionsProvider::OnDownloadUpdated(DownloadItem* item) {
265 if (base::ContainsValue(cached_asset_downloads_, item)) {
266 if (item->GetFileExternallyRemoved()) {
267 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId()));
268 } else {
269 // The download may have changed.
270 SubmitContentSuggestions();
271 }
272 } else {
273 // Unfinished downloads may become completed.
274 if (CacheAssetDownloadIfNeeded(item))
275 SubmitContentSuggestions();
276 }
277 }
278
279 void DownloadSuggestionsProvider::OnDownloadOpened(DownloadItem* item) {
280 // Ignored.
281 }
282
283 void DownloadSuggestionsProvider::OnDownloadRemoved(DownloadItem* item) {
284 // Ignored. We listen to |OnDownloadDestroyed| instead. The reason is that
285 // we may need to retrieve all downloads, but |OnDownloadRemoved| is called
286 // before the download is removed from the list.
287 }
288
289 void DownloadSuggestionsProvider::OnDownloadDestroyed(
290 content::DownloadItem* item) {
291 item->RemoveObserver(this);
292
293 if (!IsDownloadCompleted(*item))
294 return;
295 // TODO(vitaliii): Implement a better way to clean up dismissed IDs (in case
296 // some calls are missed).
297 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId()));
298 }
299
300 void DownloadSuggestionsProvider::NotifyStatusChanged(
301 CategoryStatus new_status) {
302 DCHECK_NE(CategoryStatus::NOT_PROVIDED, category_status_);
303 DCHECK_NE(CategoryStatus::NOT_PROVIDED, new_status);
304 if (category_status_ == new_status)
305 return;
306 category_status_ = new_status;
307 observer()->OnCategoryStatusChanged(this, provided_category_,
308 category_status_);
309 }
310
311 void DownloadSuggestionsProvider::AsynchronouslyFetchOfflinePagesDownloads(
312 bool notify) {
313 offline_page_proxy_->GetAllPages(
314 base::Bind(&DownloadSuggestionsProvider::UpdateOfflinePagesCache,
315 weak_ptr_factory_.GetWeakPtr(), notify));
316 }
317
318 void DownloadSuggestionsProvider::FetchAssetsDownloads() {
319 if (!download_manager_) {
320 // The manager has gone down.
321 return;
322 }
323
324 std::vector<DownloadItem*> all_downloads;
325 download_manager_->GetAllDownloads(&all_downloads);
326 std::set<std::string> old_dismissed_ids = ReadAssetDismissedIDsFromPrefs();
327 std::set<std::string> retained_dismissed_ids;
328 cached_asset_downloads_.clear();
329 for (const DownloadItem* item : all_downloads) {
330 std::string within_category_id =
331 GetAssetDownloadPerCategoryID(item->GetId());
332 if (!old_dismissed_ids.count(within_category_id)) {
333 if (IsDownloadCompleted(*item))
334 cached_asset_downloads_.push_back(item);
335 } else {
336 retained_dismissed_ids.insert(within_category_id);
337 }
338 }
339
340 if (old_dismissed_ids.size() != retained_dismissed_ids.size())
341 StoreAssetDismissedIDsToPrefs(retained_dismissed_ids);
342
343 if (static_cast<int>(cached_asset_downloads_.size()) > kMaxSuggestionsCount) {
344 // Partially sorts |downloads| such that:
345 // 1) The element at the index |kMaxSuggestionsCount| is changed to the
346 // element which would occur on this position if |downloads| was sorted;
347 // 2) All of the elements before index |kMaxSuggestionsCount| are less than
348 // or equal to the elements after it.
349 std::nth_element(cached_asset_downloads_.begin(),
350 cached_asset_downloads_.begin() + kMaxSuggestionsCount,
351 cached_asset_downloads_.end(),
352 OrderDownloadsMostRecentlyDownloadedFirst());
353 cached_asset_downloads_.resize(kMaxSuggestionsCount);
354 }
355 }
356
357 void DownloadSuggestionsProvider::
358 AsynchronouslyFetchAllDownloadsAndSubmitSuggestions() {
359 FetchAssetsDownloads();
360 AsynchronouslyFetchOfflinePagesDownloads(/*notify=*/true);
361 }
362
363 void DownloadSuggestionsProvider::SubmitContentSuggestions() {
364 NotifyStatusChanged(CategoryStatus::AVAILABLE);
365
366 std::vector<ContentSuggestion> suggestions;
367 for (const OfflinePageItem& item : cached_offline_page_downloads_)
368 suggestions.push_back(ConvertOfflinePage(item));
369
370 for (const DownloadItem* item : cached_asset_downloads_)
371 suggestions.push_back(ConvertDownloadItem(*item));
372
373 std::sort(suggestions.begin(), suggestions.end(),
374 [](const ContentSuggestion& left, const ContentSuggestion& right) {
375 return left.publish_date() > right.publish_date();
376 });
377
378 // TODO(vitaliii): Use resize() here. In order to do so, mark
379 // ContentSuggestion move constructor noexcept.
380 while (suggestions.size() > kMaxSuggestionsCount)
381 suggestions.pop_back();
382
383 observer()->OnNewSuggestions(this, provided_category_,
384 std::move(suggestions));
385 }
386
387 ContentSuggestion DownloadSuggestionsProvider::ConvertOfflinePage(
388 const OfflinePageItem& offline_page) const {
389 // TODO(vitaliii): Make sure the URL is actually opened as an offline URL even
390 // when the user is online. See crbug.com/641568.
391 ContentSuggestion suggestion(
392 ContentSuggestion::ID(provided_category_, GetOfflinePagePerCategoryID(
393 offline_page.offline_id)),
394 offline_page.url);
395
396 if (offline_page.title.empty()) {
397 // TODO(vitaliii): Remove this fallback once the OfflinePageModel provides
398 // titles for all (relevant) OfflinePageItems.
399 suggestion.set_title(base::UTF8ToUTF16(offline_page.url.spec()));
400 } else {
401 suggestion.set_title(offline_page.title);
402 }
403 suggestion.set_publish_date(offline_page.creation_time);
404 suggestion.set_publisher_name(base::UTF8ToUTF16(offline_page.url.host()));
405 return suggestion;
406 }
407
408 ContentSuggestion DownloadSuggestionsProvider::ConvertDownloadItem(
409 const DownloadItem& download_item) const {
410 // TODO(vitaliii): Ensure that files are opened in browser, but not downloaded
411 // again. See crbug.com/641568.
412 ContentSuggestion suggestion(
413 ContentSuggestion::ID(provided_category_, GetAssetDownloadPerCategoryID(
414 download_item.GetId())),
415 net::FilePathToFileURL(download_item.GetTargetFilePath()));
416 // TODO(vitaliii): Set proper title.
417 suggestion.set_title(
418 download_item.GetTargetFilePath().BaseName().LossyDisplayName());
419 suggestion.set_publish_date(download_item.GetEndTime());
420 suggestion.set_publisher_name(
421 base::UTF8ToUTF16(download_item.GetURL().host()));
422 // TODO(vitaliii): Set suggestion icon.
423 return suggestion;
424 }
425
426 bool DownloadSuggestionsProvider::CacheAssetDownloadIfNeeded(
427 const content::DownloadItem* item) {
428 if (!IsDownloadCompleted(*item))
429 return false;
430
431 if (base::ContainsValue(cached_asset_downloads_, item))
432 return false;
433
434 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs();
435 if (dismissed_ids.count(GetAssetDownloadPerCategoryID(item->GetId())))
436 return false;
437
438 DCHECK_LE(static_cast<int>(cached_asset_downloads_.size()),
439 kMaxSuggestionsCount);
440 if (cached_asset_downloads_.size() == kMaxSuggestionsCount) {
441 auto oldest = std::max_element(cached_asset_downloads_.begin(),
442 cached_asset_downloads_.end(),
443 OrderDownloadsMostRecentlyDownloadedFirst());
444 if (item->GetEndTime() <= (*oldest)->GetEndTime())
445 return false;
446
447 *oldest = item;
448 } else {
449 cached_asset_downloads_.push_back(item);
450 }
451
452 return true;
453 }
454
455 bool DownloadSuggestionsProvider::RemoveSuggestionFromCacheIfPresent(
456 const ContentSuggestion::ID& suggestion_id) {
457 DCHECK_EQ(provided_category_, suggestion_id.category());
458 if (CorrespondsToOfflinePage(suggestion_id)) {
459 auto matching =
460 std::find_if(cached_offline_page_downloads_.begin(),
461 cached_offline_page_downloads_.end(),
462 [&suggestion_id](const OfflinePageItem& item) {
463 return GetOfflinePagePerCategoryID(item.offline_id) ==
464 suggestion_id.id_within_category();
465 });
466 if (matching != cached_offline_page_downloads_.end()) {
467 cached_offline_page_downloads_.erase(matching);
468 return true;
469 }
470 return false;
471 }
472
473 auto matching = std::find_if(
474 cached_asset_downloads_.begin(), cached_asset_downloads_.end(),
475 [&suggestion_id](const DownloadItem* item) {
476 return GetAssetDownloadPerCategoryID(item->GetId()) ==
477 suggestion_id.id_within_category();
478 });
479 if (matching != cached_asset_downloads_.end()) {
480 cached_asset_downloads_.erase(matching);
481 return true;
482 }
483 return false;
484 }
485
486 void DownloadSuggestionsProvider::
487 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(
488 const ContentSuggestion::ID& suggestion_id) {
489 DCHECK_EQ(provided_category_, suggestion_id.category());
490 if (!RemoveSuggestionFromCacheIfPresent(suggestion_id))
491 return;
492
493 if (CorrespondsToOfflinePage(suggestion_id)) {
494 if (cached_offline_page_downloads_.size() == kMaxSuggestionsCount - 1) {
495 // Previously there were |kMaxSuggestionsCount| cached suggestion,
496 // therefore, overall there may be more than |kMaxSuggestionsCount|
497 // suggestions in the model and now one of them may be cached instead of
498 // the removed one. Even though, the suggestions are not immediately
499 // used the cache has to be kept up to date, because it may be used when
500 // other data source is updated.
501 AsynchronouslyFetchOfflinePagesDownloads(/*notify=*/false);
502 }
503 } else {
504 if (cached_asset_downloads_.size() == kMaxSuggestionsCount - 1) {
505 // The same as the case above.
506 FetchAssetsDownloads();
507 }
508 }
509 }
510
511 void DownloadSuggestionsProvider::UpdateOfflinePagesCache(
512 bool notify,
513 const std::vector<offline_pages::OfflinePageItem>& all_offline_pages) {
514 std::set<std::string> old_dismissed_ids =
515 ReadOfflinePageDismissedIDsFromPrefs();
516 std::set<std::string> retained_dismissed_ids;
517 std::vector<const OfflinePageItem*> items;
518 // Filtering out dismissed items and pruning dismissed IDs.
519 for (const OfflinePageItem& item : all_offline_pages) {
520 if (!IsOfflinePageDownload(item.client_id))
521 continue;
522
523 std::string id_within_category =
524 GetOfflinePagePerCategoryID(item.offline_id);
525 if (!old_dismissed_ids.count(id_within_category))
526 items.push_back(&item);
527 else
528 retained_dismissed_ids.insert(id_within_category);
529 }
530
531 if (static_cast<int>(items.size()) > kMaxSuggestionsCount) {
532 // Partially sorts |items| such that:
533 // 1) The element at the index |kMaxSuggestionsCount| is changed to the
534 // element which would occur on this position if |items| was sorted;
535 // 2) All of the elements before index |kMaxSuggestionsCount| are less than
536 // or equal to the elements after it.
537 std::nth_element(
538 items.begin(), items.begin() + kMaxSuggestionsCount, items.end(),
539 [](const OfflinePageItem* left, const OfflinePageItem* right) {
540 return left->creation_time > right->creation_time;
541 });
542 items.resize(kMaxSuggestionsCount);
543 }
544
545 cached_offline_page_downloads_.clear();
546 for (const OfflinePageItem* item : items)
547 cached_offline_page_downloads_.push_back(*item);
548
549 if (old_dismissed_ids.size() != retained_dismissed_ids.size())
550 StoreOfflinePageDismissedIDsToPrefs(retained_dismissed_ids);
551
552 if (notify)
553 SubmitContentSuggestions();
554 }
555
556 void DownloadSuggestionsProvider::InvalidateSuggestion(
557 const std::string& id_within_category) {
558 ContentSuggestion::ID suggestion_id(provided_category_, id_within_category);
559 observer()->OnSuggestionInvalidated(this, suggestion_id);
560
561 std::set<std::string> dismissed_ids =
562 ReadDismissedIDsFromPrefs(CorrespondsToOfflinePage(suggestion_id));
563 auto it = dismissed_ids.find(id_within_category);
564 if (it != dismissed_ids.end()) {
565 dismissed_ids.erase(it);
566 StoreDismissedIDsToPrefs(CorrespondsToOfflinePage(suggestion_id),
567 dismissed_ids);
568 }
569
570 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id);
571 }
572
573 std::set<std::string>
574 DownloadSuggestionsProvider::ReadAssetDismissedIDsFromPrefs() const {
575 return ntp_snippets::prefs::ReadDismissedIDsFromPrefs(
576 *pref_service_, kDismissedAssetDownloadSuggestions);
577 }
578
579 void DownloadSuggestionsProvider::StoreAssetDismissedIDsToPrefs(
580 const std::set<std::string>& dismissed_ids) {
581 DCHECK(std::all_of(
582 dismissed_ids.begin(), dismissed_ids.end(),
583 [](const std::string& id) { return id[0] == kAssetDownloadsPrefix; }));
584 ntp_snippets::prefs::StoreDismissedIDsToPrefs(
585 pref_service_, kDismissedAssetDownloadSuggestions, dismissed_ids);
586 }
587
588 std::set<std::string>
589 DownloadSuggestionsProvider::ReadOfflinePageDismissedIDsFromPrefs() const {
590 return ntp_snippets::prefs::ReadDismissedIDsFromPrefs(
591 *pref_service_, kDismissedOfflinePageDownloadSuggestions);
592 }
593
594 void DownloadSuggestionsProvider::StoreOfflinePageDismissedIDsToPrefs(
595 const std::set<std::string>& dismissed_ids) {
596 DCHECK(std::all_of(dismissed_ids.begin(), dismissed_ids.end(),
597 [](const std::string& id) {
598 return id[0] == kOfflinePageDownloadsPrefix;
599 }));
600 ntp_snippets::prefs::StoreDismissedIDsToPrefs(
601 pref_service_, kDismissedOfflinePageDownloadSuggestions, dismissed_ids);
602 }
603
604 std::set<std::string> DownloadSuggestionsProvider::ReadDismissedIDsFromPrefs(
605 bool for_offline_page_downloads) const {
606 if (for_offline_page_downloads)
607 return ReadOfflinePageDismissedIDsFromPrefs();
608 return ReadAssetDismissedIDsFromPrefs();
609 }
610
611 // TODO(vitaliii): Store one set instead of two. See crbug.com/656024.
612 void DownloadSuggestionsProvider::StoreDismissedIDsToPrefs(
613 bool for_offline_page_downloads,
614 const std::set<std::string>& dismissed_ids) {
615 if (for_offline_page_downloads)
616 StoreOfflinePageDismissedIDsToPrefs(dismissed_ids);
617 else
618 StoreAssetDismissedIDsToPrefs(dismissed_ids);
619 }
620
621 void DownloadSuggestionsProvider::UnregisterDownloadItemObservers() {
622 DCHECK_NE(download_manager_, nullptr);
623
624 std::vector<DownloadItem*> all_downloads;
625 download_manager_->GetAllDownloads(&all_downloads);
626
627 for (DownloadItem* item : all_downloads)
628 item->RemoveObserver(this);
629 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698