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

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

Issue 2629603002: [NTP::Downloads] Fetch assets once the manager is loaded. (Closed)
Patch Set: constructed DownloadHistory + tests. Created 3 years, 11 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 "chrome/browser/ntp_snippets/download_suggestions_provider.h" 5 #include "chrome/browser/ntp_snippets/download_suggestions_provider.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/feature_list.h" 11 #include "base/feature_list.h"
12 #include "base/guid.h" 12 #include "base/guid.h"
13 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
14 #include "base/stl_util.h" 14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h" 16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h" 17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/thread_task_runner_handle.h" 18 #include "base/threading/thread_task_runner_handle.h"
19 #include "base/time/time.h" 19 #include "base/time/time.h"
20 #include "base/values.h"
21 #include "chrome/common/chrome_features.h" 20 #include "chrome/common/chrome_features.h"
22 #include "chrome/grit/generated_resources.h" 21 #include "chrome/grit/generated_resources.h"
23 #include "components/ntp_snippets/features.h" 22 #include "components/ntp_snippets/features.h"
24 #include "components/ntp_snippets/pref_names.h" 23 #include "components/ntp_snippets/pref_names.h"
25 #include "components/ntp_snippets/pref_util.h" 24 #include "components/ntp_snippets/pref_util.h"
26 #include "components/offline_pages/core/offline_page_model_query.h" 25 #include "components/offline_pages/core/offline_page_model_query.h"
27 #include "components/prefs/pref_registry_simple.h" 26 #include "components/prefs/pref_registry_simple.h"
28 #include "components/prefs/pref_service.h" 27 #include "components/prefs/pref_service.h"
29 #include "components/variations/variations_associated_data.h" 28 #include "components/variations/variations_associated_data.h"
30 #include "ui/base/l10n/l10n_util.h" 29 #include "ui/base/l10n/l10n_util.h"
31 #include "ui/gfx/image/image.h" 30 #include "ui/gfx/image/image.h"
32 31
33 using base::ContainsValue;
34 using content::DownloadItem; 32 using content::DownloadItem;
35 using content::DownloadManager; 33 using content::DownloadManager;
36 using ntp_snippets::Category; 34 using ntp_snippets::Category;
37 using ntp_snippets::CategoryInfo; 35 using ntp_snippets::CategoryInfo;
38 using ntp_snippets::CategoryStatus; 36 using ntp_snippets::CategoryStatus;
39 using ntp_snippets::ContentSuggestion; 37 using ntp_snippets::ContentSuggestion;
40 using ntp_snippets::prefs::kDismissedAssetDownloadSuggestions; 38 using ntp_snippets::prefs::kDismissedAssetDownloadSuggestions;
41 using ntp_snippets::prefs::kDismissedOfflinePageDownloadSuggestions; 39 using ntp_snippets::prefs::kDismissedOfflinePageDownloadSuggestions;
42 using offline_pages::OfflinePageItem; 40 using offline_pages::OfflinePageItem;
43 using offline_pages::OfflinePageModelQuery; 41 using offline_pages::OfflinePageModelQuery;
44 using offline_pages::OfflinePageModelQueryBuilder; 42 using offline_pages::OfflinePageModelQueryBuilder;
45 43
46 namespace { 44 namespace {
47 45
48 const int kDefaultMaxSuggestionsCount = 5; 46 const int kDefaultMaxSuggestionsCount = 5;
49 const char kAssetDownloadsPrefix = 'D'; 47 const char kAssetDownloadsPrefix = 'D';
50 const char kOfflinePageDownloadsPrefix = 'O'; 48 const char kOfflinePageDownloadsPrefix = 'O';
51 49
52 const char* kMaxSuggestionsCountParamName = "downloads_max_count"; 50 const char* kMaxSuggestionsCountParamName = "downloads_max_count";
53 51
54 // Maximal number of dismissed asset download IDs stored at any time.
55 const int kMaxDismissedIdCount = 100;
56
57 bool CompareOfflinePagesMostRecentlyCreatedFirst(const OfflinePageItem& left, 52 bool CompareOfflinePagesMostRecentlyCreatedFirst(const OfflinePageItem& left,
58 const OfflinePageItem& right) { 53 const OfflinePageItem& right) {
59 return left.creation_time > right.creation_time; 54 return left.creation_time > right.creation_time;
60 } 55 }
61 56
62 int GetMaxSuggestionsCount() { 57 int GetMaxSuggestionsCount() {
63 bool assets_enabled = 58 bool assets_enabled =
64 base::FeatureList::IsEnabled(features::kAssetDownloadSuggestionsFeature); 59 base::FeatureList::IsEnabled(features::kAssetDownloadSuggestionsFeature);
65 return variations::GetVariationParamByFeatureAsInt( 60 return variations::GetVariationParamByFeatureAsInt(
66 assets_enabled ? features::kAssetDownloadSuggestionsFeature 61 assets_enabled ? features::kAssetDownloadSuggestionsFeature
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 OfflinePageModelQuery::Requirement::INCLUDE_MATCHING); 119 OfflinePageModelQuery::Requirement::INCLUDE_MATCHING);
125 return builder.Build(model->GetPolicyController()); 120 return builder.Build(model->GetPolicyController());
126 } 121 }
127 122
128 } // namespace 123 } // namespace
129 124
130 DownloadSuggestionsProvider::DownloadSuggestionsProvider( 125 DownloadSuggestionsProvider::DownloadSuggestionsProvider(
131 ContentSuggestionsProvider::Observer* observer, 126 ContentSuggestionsProvider::Observer* observer,
132 offline_pages::OfflinePageModel* offline_page_model, 127 offline_pages::OfflinePageModel* offline_page_model,
133 content::DownloadManager* download_manager, 128 content::DownloadManager* download_manager,
129 DownloadHistory* download_history,
134 PrefService* pref_service, 130 PrefService* pref_service,
135 bool download_manager_ui_enabled) 131 bool download_manager_ui_enabled)
136 : ContentSuggestionsProvider(observer), 132 : ContentSuggestionsProvider(observer),
137 category_status_(CategoryStatus::AVAILABLE_LOADING), 133 category_status_(CategoryStatus::AVAILABLE_LOADING),
138 provided_category_(Category::FromKnownCategory( 134 provided_category_(Category::FromKnownCategory(
139 ntp_snippets::KnownCategories::DOWNLOADS)), 135 ntp_snippets::KnownCategories::DOWNLOADS)),
140 offline_page_model_(offline_page_model), 136 offline_page_model_(offline_page_model),
141 download_manager_(download_manager), 137 download_manager_(download_manager),
138 download_history_(download_history),
142 pref_service_(pref_service), 139 pref_service_(pref_service),
143 download_manager_ui_enabled_(download_manager_ui_enabled), 140 download_manager_ui_enabled_(download_manager_ui_enabled),
141 is_asset_downloads_initialization_complete_(false),
144 weak_ptr_factory_(this) { 142 weak_ptr_factory_(this) {
145 observer->OnCategoryStatusChanged(this, provided_category_, category_status_); 143 observer->OnCategoryStatusChanged(this, provided_category_, category_status_);
146 144
147 DCHECK(offline_page_model_ || download_manager_); 145 DCHECK(offline_page_model_ || download_manager_);
148 if (offline_page_model_) { 146 if (offline_page_model_) {
149 offline_page_model_->AddObserver(this); 147 offline_page_model_->AddObserver(this);
150 } 148 }
151 149
152 if (download_manager_) { 150 if (download_manager_) {
153 download_manager_->AddObserver(this); 151 // We will start listening to download manager once it is loaded.
152 // May be nullptr in tests.
153 if (download_history_) {
154 download_history_->AddObserver(this);
155 }
156 } else {
157 download_history_ = nullptr;
154 } 158 }
155 159
156 // We explicitly fetch the asset downloads in case some of |OnDownloadCreated| 160 if (!download_manager_) {
157 // happened earlier and, therefore, were missed. 161 // Usually, all downloads are fetched when the download manager is loaded,
158 AsynchronouslyFetchAllDownloadsAndSubmitSuggestions(); 162 // but now it is disabled, so offline pages are fetched here instead.
163 AsynchronouslyFetchOfflinePagesDownloads(/*notify=*/true);
164 }
159 } 165 }
160 166
161 DownloadSuggestionsProvider::~DownloadSuggestionsProvider() { 167 DownloadSuggestionsProvider::~DownloadSuggestionsProvider() {
162 if (offline_page_model_) { 168 if (download_history_) {
163 offline_page_model_->RemoveObserver(this); 169 download_history_->RemoveObserver(this);
164 } 170 }
165 171
166 if (download_manager_) { 172 if (download_manager_) {
167 download_manager_->RemoveObserver(this); 173 download_manager_->RemoveObserver(this);
168 UnregisterDownloadItemObservers(); 174 UnregisterDownloadItemObservers();
169 } 175 }
176
177 if (offline_page_model_) {
178 offline_page_model_->RemoveObserver(this);
179 }
170 } 180 }
171 181
172 CategoryStatus DownloadSuggestionsProvider::GetCategoryStatus( 182 CategoryStatus DownloadSuggestionsProvider::GetCategoryStatus(
173 Category category) { 183 Category category) {
174 DCHECK_EQ(provided_category_, category); 184 DCHECK_EQ(provided_category_, category);
175 return category_status_; 185 return category_status_;
176 } 186 }
177 187
178 CategoryInfo DownloadSuggestionsProvider::GetCategoryInfo(Category category) { 188 CategoryInfo DownloadSuggestionsProvider::GetCategoryInfo(Category category) {
179 DCHECK_EQ(provided_category_, category); 189 DCHECK_EQ(provided_category_, category);
180 return CategoryInfo( 190 return CategoryInfo(
181 l10n_util::GetStringUTF16(IDS_NTP_DOWNLOAD_SUGGESTIONS_SECTION_HEADER), 191 l10n_util::GetStringUTF16(IDS_NTP_DOWNLOAD_SUGGESTIONS_SECTION_HEADER),
182 ntp_snippets::ContentSuggestionsCardLayout::MINIMAL_CARD, 192 ntp_snippets::ContentSuggestionsCardLayout::MINIMAL_CARD,
183 /*has_more_action=*/false, 193 /*has_more_action=*/false,
184 /*has_reload_action=*/false, 194 /*has_reload_action=*/false,
185 /*has_view_all_action=*/download_manager_ui_enabled_, 195 /*has_view_all_action=*/download_manager_ui_enabled_,
186 /*show_if_empty=*/false, 196 /*show_if_empty=*/false,
187 l10n_util::GetStringUTF16(IDS_NTP_DOWNLOADS_SUGGESTIONS_SECTION_EMPTY)); 197 l10n_util::GetStringUTF16(IDS_NTP_DOWNLOADS_SUGGESTIONS_SECTION_EMPTY));
188 } 198 }
189 199
190 void DownloadSuggestionsProvider::DismissSuggestion( 200 void DownloadSuggestionsProvider::DismissSuggestion(
191 const ContentSuggestion::ID& suggestion_id) { 201 const ContentSuggestion::ID& suggestion_id) {
192 DCHECK_EQ(provided_category_, suggestion_id.category()); 202 DCHECK_EQ(provided_category_, suggestion_id.category());
203 std::set<std::string> dismissed_ids =
204 ReadDismissedIDsFromPrefs(CorrespondsToOfflinePage(suggestion_id));
205 dismissed_ids.insert(suggestion_id.id_within_category());
206 StoreDismissedIDsToPrefs(CorrespondsToOfflinePage(suggestion_id),
207 dismissed_ids);
193 208
194 AddToDismissedStorageIfNeeded(suggestion_id);
195 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id); 209 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id);
196 } 210 }
197 211
198 void DownloadSuggestionsProvider::FetchSuggestionImage( 212 void DownloadSuggestionsProvider::FetchSuggestionImage(
199 const ContentSuggestion::ID& suggestion_id, 213 const ContentSuggestion::ID& suggestion_id,
200 const ntp_snippets::ImageFetchedCallback& callback) { 214 const ntp_snippets::ImageFetchedCallback& callback) {
201 // TODO(vitaliii): Fetch proper thumbnail from OfflinePageModel once it is 215 // TODO(vitaliii): Fetch proper thumbnail from OfflinePageModel once it is
202 // available there. 216 // available there.
203 // TODO(vitaliii): Provide site's favicon for assets downloads or file type. 217 // TODO(vitaliii): Provide site's favicon for assets downloads or file type.
204 // See crbug.com/631447. 218 // See crbug.com/631447.
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 weak_ptr_factory_.GetWeakPtr(), callback)); 269 weak_ptr_factory_.GetWeakPtr(), callback));
256 } else { 270 } else {
257 GetPagesMatchingQueryCallbackForGetDismissedSuggestions( 271 GetPagesMatchingQueryCallbackForGetDismissedSuggestions(
258 callback, std::vector<OfflinePageItem>()); 272 callback, std::vector<OfflinePageItem>());
259 } 273 }
260 } 274 }
261 275
262 void DownloadSuggestionsProvider::ClearDismissedSuggestionsForDebugging( 276 void DownloadSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
263 Category category) { 277 Category category) {
264 DCHECK_EQ(provided_category_, category); 278 DCHECK_EQ(provided_category_, category);
265 StoreAssetDismissedIDsToPrefs(std::vector<std::string>()); 279 StoreAssetDismissedIDsToPrefs(std::set<std::string>());
266 StoreOfflinePageDismissedIDsToPrefs(std::set<std::string>()); 280 StoreOfflinePageDismissedIDsToPrefs(std::set<std::string>());
267 AsynchronouslyFetchAllDownloadsAndSubmitSuggestions(); 281 AsynchronouslyFetchAllDownloadsAndSubmitSuggestions();
268 } 282 }
269 283
270 // static 284 // static
271 void DownloadSuggestionsProvider::RegisterProfilePrefs( 285 void DownloadSuggestionsProvider::RegisterProfilePrefs(
272 PrefRegistrySimple* registry) { 286 PrefRegistrySimple* registry) {
273 registry->RegisterListPref(kDismissedAssetDownloadSuggestions); 287 registry->RegisterListPref(kDismissedAssetDownloadSuggestions);
274 registry->RegisterListPref(kDismissedOfflinePageDownloadSuggestions); 288 registry->RegisterListPref(kDismissedOfflinePageDownloadSuggestions);
275 } 289 }
276 290
277 int DownloadSuggestionsProvider::GetMaxDismissedCountForTesting() {
278 return kMaxDismissedIdCount;
279 }
280
281 //////////////////////////////////////////////////////////////////////////////// 291 ////////////////////////////////////////////////////////////////////////////////
282 // Private methods 292 // Private methods
283 293
284 void DownloadSuggestionsProvider:: 294 void DownloadSuggestionsProvider::
285 GetPagesMatchingQueryCallbackForGetDismissedSuggestions( 295 GetPagesMatchingQueryCallbackForGetDismissedSuggestions(
286 const ntp_snippets::DismissedSuggestionsCallback& callback, 296 const ntp_snippets::DismissedSuggestionsCallback& callback,
287 const std::vector<OfflinePageItem>& offline_pages) const { 297 const std::vector<OfflinePageItem>& offline_pages) const {
288 std::set<std::string> dismissed_offline_ids = 298 std::set<std::string> dismissed_ids = ReadOfflinePageDismissedIDsFromPrefs();
289 ReadOfflinePageDismissedIDsFromPrefs();
290 std::vector<ContentSuggestion> suggestions; 299 std::vector<ContentSuggestion> suggestions;
291 for (const OfflinePageItem& item : offline_pages) { 300 for (const OfflinePageItem& item : offline_pages) {
292 if (dismissed_offline_ids.count( 301 if (dismissed_ids.count(GetOfflinePagePerCategoryID(item.offline_id))) {
293 GetOfflinePagePerCategoryID(item.offline_id))) {
294 suggestions.push_back(ConvertOfflinePage(item)); 302 suggestions.push_back(ConvertOfflinePage(item));
295 } 303 }
296 } 304 }
297 305
298 if (download_manager_) { 306 if (download_manager_) {
299 std::vector<DownloadItem*> all_downloads; 307 std::vector<DownloadItem*> all_downloads;
300 download_manager_->GetAllDownloads(&all_downloads); 308 download_manager_->GetAllDownloads(&all_downloads);
301 309
302 std::vector<std::string> dismissed_asset_ids = 310 dismissed_ids = ReadAssetDismissedIDsFromPrefs();
303 ReadAssetDismissedIDsFromPrefs();
304 311
305 for (const DownloadItem* item : all_downloads) { 312 for (const DownloadItem* item : all_downloads) {
306 if (ContainsValue(dismissed_asset_ids, 313 if (dismissed_ids.count(GetAssetDownloadPerCategoryID(item->GetId()))) {
307 GetAssetDownloadPerCategoryID(item->GetId()))) {
308 suggestions.push_back(ConvertDownloadItem(*item)); 314 suggestions.push_back(ConvertDownloadItem(*item));
309 } 315 }
310 } 316 }
311 } 317 }
312 318
313 callback.Run(std::move(suggestions)); 319 callback.Run(std::move(suggestions));
314 } 320 }
315 321
316 void DownloadSuggestionsProvider::OfflinePageModelLoaded( 322 void DownloadSuggestionsProvider::OfflinePageModelLoaded(
317 offline_pages::OfflinePageModel* model) { 323 offline_pages::OfflinePageModel* model) {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 const offline_pages::ClientId& client_id) { 360 const offline_pages::ClientId& client_id) {
355 DCHECK(offline_page_model_); 361 DCHECK(offline_page_model_);
356 if (IsClientIdForOfflinePageDownload( 362 if (IsClientIdForOfflinePageDownload(
357 offline_page_model_->GetPolicyController(), client_id)) { 363 offline_page_model_->GetPolicyController(), client_id)) {
358 InvalidateSuggestion(GetOfflinePagePerCategoryID(offline_id)); 364 InvalidateSuggestion(GetOfflinePagePerCategoryID(offline_id));
359 } 365 }
360 } 366 }
361 367
362 void DownloadSuggestionsProvider::OnDownloadCreated(DownloadManager* manager, 368 void DownloadSuggestionsProvider::OnDownloadCreated(DownloadManager* manager,
363 DownloadItem* item) { 369 DownloadItem* item) {
370 DCHECK(is_asset_downloads_initialization_complete_);
364 DCHECK_EQ(download_manager_, manager); 371 DCHECK_EQ(download_manager_, manager);
365 // This is called when new downloads are started and on startup for existing 372
366 // ones. We listen to each item to know when it is destroyed. 373 // This is called when new downloads are started. We listen to each item to
374 // know when it is finished or destroyed.
367 item->AddObserver(this); 375 item->AddObserver(this);
368 if (CacheAssetDownloadIfNeeded(item)) { 376 if (CacheAssetDownloadIfNeeded(item)) {
369 SubmitContentSuggestions(); 377 SubmitContentSuggestions();
370 } 378 }
371 } 379 }
372 380
373 void DownloadSuggestionsProvider::ManagerGoingDown(DownloadManager* manager) { 381 void DownloadSuggestionsProvider::ManagerGoingDown(DownloadManager* manager) {
374 DCHECK_EQ(download_manager_, manager); 382 DCHECK_EQ(download_manager_, manager);
375 UnregisterDownloadItemObservers(); 383 UnregisterDownloadItemObservers();
376 download_manager_ = nullptr; 384 download_manager_ = nullptr;
385 if (download_history_) {
386 download_history_->RemoveObserver(this);
387 download_history_ = nullptr;
388 }
377 } 389 }
378 390
379 void DownloadSuggestionsProvider::OnDownloadUpdated(DownloadItem* item) { 391 void DownloadSuggestionsProvider::OnDownloadUpdated(DownloadItem* item) {
380 if (ContainsValue(cached_asset_downloads_, item)) { 392 DCHECK(is_asset_downloads_initialization_complete_);
393 if (base::ContainsValue(cached_asset_downloads_, item)) {
381 if (item->GetFileExternallyRemoved()) { 394 if (item->GetFileExternallyRemoved()) {
382 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId())); 395 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId()));
383 } else { 396 } else {
384 // The download may have changed. 397 // The download may have changed.
385 SubmitContentSuggestions(); 398 SubmitContentSuggestions();
386 } 399 }
387 } else { 400 } else {
388 // Unfinished downloads may become completed. 401 // Unfinished downloads may become completed.
389 if (CacheAssetDownloadIfNeeded(item)) { 402 if (CacheAssetDownloadIfNeeded(item)) {
390 SubmitContentSuggestions(); 403 SubmitContentSuggestions();
391 } 404 }
392 } 405 }
393 } 406 }
394 407
395 void DownloadSuggestionsProvider::OnDownloadOpened(DownloadItem* item) { 408 void DownloadSuggestionsProvider::OnDownloadOpened(DownloadItem* item) {
396 // Ignored. 409 // Ignored.
397 } 410 }
398 411
399 void DownloadSuggestionsProvider::OnDownloadRemoved(DownloadItem* item) { 412 void DownloadSuggestionsProvider::OnDownloadRemoved(DownloadItem* item) {
400 // Ignored. We listen to |OnDownloadDestroyed| instead. The reason is that 413 // Ignored. We listen to |OnDownloadDestroyed| instead. The reason is that
401 // we may need to retrieve all downloads, but |OnDownloadRemoved| is called 414 // we may need to retrieve all downloads, but |OnDownloadRemoved| is called
402 // before the download is removed from the list. 415 // before the download is removed from the list.
403 } 416 }
404 417
405 void DownloadSuggestionsProvider::OnDownloadDestroyed( 418 void DownloadSuggestionsProvider::OnDownloadDestroyed(
406 content::DownloadItem* item) { 419 content::DownloadItem* item) {
420 DCHECK(is_asset_downloads_initialization_complete_);
421
407 item->RemoveObserver(this); 422 item->RemoveObserver(this);
408 423
409 if (!IsDownloadCompleted(*item)) { 424 if (!IsDownloadCompleted(*item)) {
410 return; 425 return;
411 } 426 }
412 // TODO(vitaliii): Implement a better way to clean up dismissed IDs (in case 427 // TODO(vitaliii): Implement a better way to clean up dismissed IDs (in case
413 // some calls are missed). 428 // some calls are missed).
414 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId())); 429 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId()));
415 } 430 }
416 431
432 void DownloadSuggestionsProvider::OnHistoryQueryComplete() {
433 is_asset_downloads_initialization_complete_ = true;
434 if (download_manager_) {
435 download_manager_->AddObserver(this);
436 }
437 AsynchronouslyFetchAllDownloadsAndSubmitSuggestions();
438 }
439
440 void DownloadSuggestionsProvider::OnDownloadHistoryDestroyed() {
441 DCHECK(download_history_);
442 download_history_->RemoveObserver(this);
443 download_history_ = nullptr;
444 }
445
417 void DownloadSuggestionsProvider::NotifyStatusChanged( 446 void DownloadSuggestionsProvider::NotifyStatusChanged(
418 CategoryStatus new_status) { 447 CategoryStatus new_status) {
419 DCHECK_NE(CategoryStatus::NOT_PROVIDED, category_status_); 448 DCHECK_NE(CategoryStatus::NOT_PROVIDED, category_status_);
420 DCHECK_NE(CategoryStatus::NOT_PROVIDED, new_status); 449 DCHECK_NE(CategoryStatus::NOT_PROVIDED, new_status);
421 if (category_status_ == new_status) { 450 if (category_status_ == new_status) {
422 return; 451 return;
423 } 452 }
424 category_status_ = new_status; 453 category_status_ = new_status;
425 observer()->OnCategoryStatusChanged(this, provided_category_, 454 observer()->OnCategoryStatusChanged(this, provided_category_,
426 category_status_); 455 category_status_);
(...skipping 24 matching lines...) Expand all
451 } 480 }
452 481
453 void DownloadSuggestionsProvider::FetchAssetsDownloads() { 482 void DownloadSuggestionsProvider::FetchAssetsDownloads() {
454 if (!download_manager_) { 483 if (!download_manager_) {
455 // The manager has gone down or was explicitly turned off. 484 // The manager has gone down or was explicitly turned off.
456 return; 485 return;
457 } 486 }
458 487
459 std::vector<DownloadItem*> all_downloads; 488 std::vector<DownloadItem*> all_downloads;
460 download_manager_->GetAllDownloads(&all_downloads); 489 download_manager_->GetAllDownloads(&all_downloads);
461 std::vector<std::string> old_dismissed_ids = ReadAssetDismissedIDsFromPrefs(); 490 std::set<std::string> old_dismissed_ids = ReadAssetDismissedIDsFromPrefs();
491 std::set<std::string> retained_dismissed_ids;
462 cached_asset_downloads_.clear(); 492 cached_asset_downloads_.clear();
463 for (const DownloadItem* item : all_downloads) { 493 for (DownloadItem* item : all_downloads) {
464 std::string within_category_id = 494 std::string within_category_id =
465 GetAssetDownloadPerCategoryID(item->GetId()); 495 GetAssetDownloadPerCategoryID(item->GetId());
466 if (!ContainsValue(old_dismissed_ids, within_category_id)) { 496 if (old_dismissed_ids.count(within_category_id)) {
467 if (IsDownloadCompleted(*item)) { 497 retained_dismissed_ids.insert(within_category_id);
468 cached_asset_downloads_.push_back(item); 498 } else if (IsDownloadCompleted(*item)) {
469 } 499 cached_asset_downloads_.push_back(item);
500 // We may already observe this item and, therefore, we remove the
501 // observer first.
502 item->RemoveObserver(this);
503 item->AddObserver(this);
470 } 504 }
471 } 505 }
472 506
473 // We do not prune dismissed IDs, because it is not possible to ensure that 507 if (old_dismissed_ids.size() != retained_dismissed_ids.size()) {
474 // the list of downloads is complete (i.e. DownloadManager has finished 508 StoreAssetDismissedIDsToPrefs(retained_dismissed_ids);
475 // reading them). 509 }
476 // TODO(vitaliii): Prune dismissed IDs once the |OnLoaded| notification is 510
477 // provided. See crbug.com/672758.
478 const int max_suggestions_count = GetMaxSuggestionsCount(); 511 const int max_suggestions_count = GetMaxSuggestionsCount();
479 if (static_cast<int>(cached_asset_downloads_.size()) > 512 if (static_cast<int>(cached_asset_downloads_.size()) >
480 max_suggestions_count) { 513 max_suggestions_count) {
481 // Partially sorts |downloads| such that: 514 // Partially sorts |downloads| such that:
482 // 1) The element at the index |max_suggestions_count| is changed to the 515 // 1) The element at the index |max_suggestions_count| is changed to the
483 // element which would occur on this position if |downloads| was sorted; 516 // element which would occur on this position if |downloads| was sorted;
484 // 2) All of the elements before index |max_suggestions_count| are less than 517 // 2) All of the elements before index |max_suggestions_count| are less than
485 // or equal to the elements after it. 518 // or equal to the elements after it.
486 std::nth_element(cached_asset_downloads_.begin(), 519 std::nth_element(cached_asset_downloads_.begin(),
487 cached_asset_downloads_.begin() + max_suggestions_count, 520 cached_asset_downloads_.begin() + max_suggestions_count,
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
565 suggestion.set_download_suggestion_extra(std::move(extra)); 598 suggestion.set_download_suggestion_extra(std::move(extra));
566 return suggestion; 599 return suggestion;
567 } 600 }
568 601
569 bool DownloadSuggestionsProvider::CacheAssetDownloadIfNeeded( 602 bool DownloadSuggestionsProvider::CacheAssetDownloadIfNeeded(
570 const content::DownloadItem* item) { 603 const content::DownloadItem* item) {
571 if (!IsDownloadCompleted(*item)) { 604 if (!IsDownloadCompleted(*item)) {
572 return false; 605 return false;
573 } 606 }
574 607
575 if (ContainsValue(cached_asset_downloads_, item)) { 608 if (base::ContainsValue(cached_asset_downloads_, item)) {
576 return false; 609 return false;
577 } 610 }
578 611
579 std::vector<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs(); 612 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs();
580 if (ContainsValue(dismissed_ids, 613 if (dismissed_ids.count(GetAssetDownloadPerCategoryID(item->GetId()))) {
581 GetAssetDownloadPerCategoryID(item->GetId()))) {
582 return false; 614 return false;
583 } 615 }
584 616
585 DCHECK_LE(static_cast<int>(cached_asset_downloads_.size()), 617 DCHECK_LE(static_cast<int>(cached_asset_downloads_.size()),
586 GetMaxSuggestionsCount()); 618 GetMaxSuggestionsCount());
587 if (static_cast<int>(cached_asset_downloads_.size()) == 619 if (static_cast<int>(cached_asset_downloads_.size()) ==
588 GetMaxSuggestionsCount()) { 620 GetMaxSuggestionsCount()) {
589 auto oldest = std::max_element( 621 auto oldest = std::max_element(
590 cached_asset_downloads_.begin(), cached_asset_downloads_.end(), 622 cached_asset_downloads_.begin(), cached_asset_downloads_.end(),
591 &CompareDownloadsMostRecentlyDownloadedFirst); 623 &CompareDownloadsMostRecentlyDownloadedFirst);
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
709 if (notify) { 741 if (notify) {
710 SubmitContentSuggestions(); 742 SubmitContentSuggestions();
711 } 743 }
712 } 744 }
713 745
714 void DownloadSuggestionsProvider::InvalidateSuggestion( 746 void DownloadSuggestionsProvider::InvalidateSuggestion(
715 const std::string& id_within_category) { 747 const std::string& id_within_category) {
716 ContentSuggestion::ID suggestion_id(provided_category_, id_within_category); 748 ContentSuggestion::ID suggestion_id(provided_category_, id_within_category);
717 observer()->OnSuggestionInvalidated(this, suggestion_id); 749 observer()->OnSuggestionInvalidated(this, suggestion_id);
718 750
719 RemoveFromDismissedStorageIfNeeded(suggestion_id); 751 std::set<std::string> dismissed_ids =
752 ReadDismissedIDsFromPrefs(CorrespondsToOfflinePage(suggestion_id));
753 auto it = dismissed_ids.find(id_within_category);
754 if (it != dismissed_ids.end()) {
755 dismissed_ids.erase(it);
756 StoreDismissedIDsToPrefs(CorrespondsToOfflinePage(suggestion_id),
757 dismissed_ids);
758 }
759
720 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id); 760 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id);
721 } 761 }
722 762
723 // TODO(vitaliii): Do not use std::vector, when we ensure that pruning happens 763 std::set<std::string>
724 // at the right time (crbug.com/672758).
725 std::vector<std::string>
726 DownloadSuggestionsProvider::ReadAssetDismissedIDsFromPrefs() const { 764 DownloadSuggestionsProvider::ReadAssetDismissedIDsFromPrefs() const {
727 std::vector<std::string> dismissed_ids; 765 return ntp_snippets::prefs::ReadDismissedIDsFromPrefs(
728 const base::ListValue* list = 766 *pref_service_, kDismissedAssetDownloadSuggestions);
729 pref_service_->GetList(kDismissedAssetDownloadSuggestions);
730 for (const std::unique_ptr<base::Value>& value : *list) {
731 std::string dismissed_id;
732 bool success = value->GetAsString(&dismissed_id);
733 DCHECK(success) << "Failed to parse dismissed id from prefs param "
734 << kDismissedAssetDownloadSuggestions << " into string.";
735 dismissed_ids.push_back(dismissed_id);
736 }
737 return dismissed_ids;
738 } 767 }
739 768
740 void DownloadSuggestionsProvider::StoreAssetDismissedIDsToPrefs( 769 void DownloadSuggestionsProvider::StoreAssetDismissedIDsToPrefs(
741 const std::vector<std::string>& dismissed_ids) { 770 const std::set<std::string>& dismissed_ids) {
742 DCHECK(std::all_of( 771 DCHECK(std::all_of(
743 dismissed_ids.begin(), dismissed_ids.end(), 772 dismissed_ids.begin(), dismissed_ids.end(),
744 [](const std::string& id) { return id[0] == kAssetDownloadsPrefix; })); 773 [](const std::string& id) { return id[0] == kAssetDownloadsPrefix; }));
745 774 ntp_snippets::prefs::StoreDismissedIDsToPrefs(
746 base::ListValue list; 775 pref_service_, kDismissedAssetDownloadSuggestions, dismissed_ids);
747 for (const std::string& dismissed_id : dismissed_ids) {
748 list.AppendString(dismissed_id);
749 }
750 pref_service_->Set(kDismissedAssetDownloadSuggestions, list);
751 } 776 }
752 777
753 std::set<std::string> 778 std::set<std::string>
754 DownloadSuggestionsProvider::ReadOfflinePageDismissedIDsFromPrefs() const { 779 DownloadSuggestionsProvider::ReadOfflinePageDismissedIDsFromPrefs() const {
755 return ntp_snippets::prefs::ReadDismissedIDsFromPrefs( 780 return ntp_snippets::prefs::ReadDismissedIDsFromPrefs(
756 *pref_service_, kDismissedOfflinePageDownloadSuggestions); 781 *pref_service_, kDismissedOfflinePageDownloadSuggestions);
757 } 782 }
758 783
759 void DownloadSuggestionsProvider::StoreOfflinePageDismissedIDsToPrefs( 784 void DownloadSuggestionsProvider::StoreOfflinePageDismissedIDsToPrefs(
760 const std::set<std::string>& dismissed_ids) { 785 const std::set<std::string>& dismissed_ids) {
761 DCHECK(std::all_of(dismissed_ids.begin(), dismissed_ids.end(), 786 DCHECK(std::all_of(dismissed_ids.begin(), dismissed_ids.end(),
762 [](const std::string& id) { 787 [](const std::string& id) {
763 return id[0] == kOfflinePageDownloadsPrefix; 788 return id[0] == kOfflinePageDownloadsPrefix;
764 })); 789 }));
765 ntp_snippets::prefs::StoreDismissedIDsToPrefs( 790 ntp_snippets::prefs::StoreDismissedIDsToPrefs(
766 pref_service_, kDismissedOfflinePageDownloadSuggestions, dismissed_ids); 791 pref_service_, kDismissedOfflinePageDownloadSuggestions, dismissed_ids);
767 } 792 }
768 793
769 void DownloadSuggestionsProvider::AddToDismissedStorageIfNeeded( 794 std::set<std::string> DownloadSuggestionsProvider::ReadDismissedIDsFromPrefs(
770 const ContentSuggestion::ID& suggestion_id) { 795 bool for_offline_page_downloads) const {
771 if (CorrespondsToOfflinePage(suggestion_id)) { 796 if (for_offline_page_downloads) {
772 std::set<std::string> dismissed_ids = 797 return ReadOfflinePageDismissedIDsFromPrefs();
773 ReadOfflinePageDismissedIDsFromPrefs(); 798 }
774 dismissed_ids.insert(suggestion_id.id_within_category()); 799 return ReadAssetDismissedIDsFromPrefs();
800 }
801
802 void DownloadSuggestionsProvider::StoreDismissedIDsToPrefs(
803 bool for_offline_page_downloads,
804 const std::set<std::string>& dismissed_ids) {
805 if (for_offline_page_downloads) {
775 StoreOfflinePageDismissedIDsToPrefs(dismissed_ids); 806 StoreOfflinePageDismissedIDsToPrefs(dismissed_ids);
776 } else { 807 } else {
777 std::vector<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs(); 808 StoreAssetDismissedIDsToPrefs(dismissed_ids);
778 // The suggestion may be double dismissed from previously opened NTPs.
779 if (!ContainsValue(dismissed_ids, suggestion_id.id_within_category())) {
780 dismissed_ids.push_back(suggestion_id.id_within_category());
781 // TODO(vitaliii): Remove this workaround once the dismissed ids are
782 // pruned. See crbug.com/672758.
783 while (dismissed_ids.size() > kMaxDismissedIdCount) {
784 dismissed_ids.erase(dismissed_ids.begin());
785 }
786 StoreAssetDismissedIDsToPrefs(dismissed_ids);
787 }
788 } 809 }
789 } 810 }
790 811
791 void DownloadSuggestionsProvider::RemoveFromDismissedStorageIfNeeded(
792 const ContentSuggestion::ID& suggestion_id) {
793 if (CorrespondsToOfflinePage(suggestion_id)) {
794 std::set<std::string> dismissed_ids =
795 ReadOfflinePageDismissedIDsFromPrefs();
796 if (dismissed_ids.count(suggestion_id.id_within_category())) {
797 dismissed_ids.erase(suggestion_id.id_within_category());
798 StoreOfflinePageDismissedIDsToPrefs(dismissed_ids);
799 }
800 } else {
801 std::vector<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs();
802 auto it = std::find(dismissed_ids.begin(), dismissed_ids.end(),
803 suggestion_id.id_within_category());
804 if (it != dismissed_ids.end()) {
805 dismissed_ids.erase(it);
806 StoreAssetDismissedIDsToPrefs(dismissed_ids);
807 }
808 }
809 }
810
811 void DownloadSuggestionsProvider::UnregisterDownloadItemObservers() { 812 void DownloadSuggestionsProvider::UnregisterDownloadItemObservers() {
812 DCHECK_NE(download_manager_, nullptr); 813 DCHECK_NE(download_manager_, nullptr);
813 814
814 std::vector<DownloadItem*> all_downloads; 815 std::vector<DownloadItem*> all_downloads;
815 download_manager_->GetAllDownloads(&all_downloads); 816 download_manager_->GetAllDownloads(&all_downloads);
816 817
817 for (DownloadItem* item : all_downloads) { 818 for (DownloadItem* item : all_downloads) {
818 item->RemoveObserver(this); 819 item->RemoveObserver(this);
819 } 820 }
820 } 821 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698