| OLD | NEW |
| 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/clock.h" |
| 19 #include "base/time/time.h" | 20 #include "base/time/time.h" |
| 20 #include "chrome/common/chrome_features.h" | 21 #include "chrome/common/chrome_features.h" |
| 21 #include "chrome/grit/generated_resources.h" | 22 #include "chrome/grit/generated_resources.h" |
| 22 #include "components/ntp_snippets/features.h" | 23 #include "components/ntp_snippets/features.h" |
| 23 #include "components/ntp_snippets/pref_names.h" | 24 #include "components/ntp_snippets/pref_names.h" |
| 24 #include "components/ntp_snippets/pref_util.h" | 25 #include "components/ntp_snippets/pref_util.h" |
| 25 #include "components/offline_pages/core/offline_page_model_query.h" | 26 #include "components/offline_pages/core/offline_page_model_query.h" |
| 26 #include "components/prefs/pref_registry_simple.h" | 27 #include "components/prefs/pref_registry_simple.h" |
| 27 #include "components/prefs/pref_service.h" | 28 #include "components/prefs/pref_service.h" |
| 28 #include "components/variations/variations_associated_data.h" | 29 #include "components/variations/variations_associated_data.h" |
| 29 #include "ui/base/l10n/l10n_util.h" | 30 #include "ui/base/l10n/l10n_util.h" |
| 30 #include "ui/gfx/image/image.h" | 31 #include "ui/gfx/image/image.h" |
| 31 | 32 |
| 32 using content::DownloadItem; | 33 using content::DownloadItem; |
| 33 using content::DownloadManager; | 34 using content::DownloadManager; |
| 34 using ntp_snippets::Category; | 35 using ntp_snippets::Category; |
| 35 using ntp_snippets::CategoryInfo; | 36 using ntp_snippets::CategoryInfo; |
| 36 using ntp_snippets::CategoryStatus; | 37 using ntp_snippets::CategoryStatus; |
| 37 using ntp_snippets::ContentSuggestion; | 38 using ntp_snippets::ContentSuggestion; |
| 38 using ntp_snippets::prefs::kDismissedAssetDownloadSuggestions; | 39 using ntp_snippets::prefs::kDismissedAssetDownloadSuggestions; |
| 39 using ntp_snippets::prefs::kDismissedOfflinePageDownloadSuggestions; | 40 using ntp_snippets::prefs::kDismissedOfflinePageDownloadSuggestions; |
| 40 using offline_pages::OfflinePageItem; | 41 using offline_pages::OfflinePageItem; |
| 41 using offline_pages::OfflinePageModelQuery; | 42 using offline_pages::OfflinePageModelQuery; |
| 42 using offline_pages::OfflinePageModelQueryBuilder; | 43 using offline_pages::OfflinePageModelQueryBuilder; |
| 43 | 44 |
| 44 namespace { | 45 namespace { |
| 45 | 46 |
| 46 const int kDefaultMaxSuggestionsCount = 5; | 47 const int kDefaultMaxSuggestionsCount = 5; |
| 48 const int kDefaultMaxDownloadAgeHours = 6 * 7 * 24; // 6 weeks |
| 47 const char kAssetDownloadsPrefix = 'D'; | 49 const char kAssetDownloadsPrefix = 'D'; |
| 48 const char kOfflinePageDownloadsPrefix = 'O'; | 50 const char kOfflinePageDownloadsPrefix = 'O'; |
| 49 | 51 |
| 52 // NOTE: You must set variation param values for both features (one of them may |
| 53 // be disabled in future). |
| 50 const char* kMaxSuggestionsCountParamName = "downloads_max_count"; | 54 const char* kMaxSuggestionsCountParamName = "downloads_max_count"; |
| 55 const char* kMaxDownloadAgeHoursParamName = "downloads_max_age_hours"; |
| 51 | 56 |
| 52 bool CompareOfflinePagesMostRecentlyCreatedFirst(const OfflinePageItem& left, | 57 const base::Feature& GetEnabledDownloadsFeature() { |
| 53 const OfflinePageItem& right) { | 58 bool assets_enabled = |
| 54 return left.creation_time > right.creation_time; | 59 base::FeatureList::IsEnabled(features::kAssetDownloadSuggestionsFeature); |
| 60 DCHECK(assets_enabled || |
| 61 base::FeatureList::IsEnabled( |
| 62 features::kOfflinePageDownloadSuggestionsFeature)); |
| 63 return assets_enabled ? features::kAssetDownloadSuggestionsFeature |
| 64 : features::kOfflinePageDownloadSuggestionsFeature; |
| 55 } | 65 } |
| 56 | 66 |
| 57 int GetMaxSuggestionsCount() { | 67 int GetMaxSuggestionsCount() { |
| 58 bool assets_enabled = | 68 // One cannot get a variation param from a disabled feature, so the enabled |
| 59 base::FeatureList::IsEnabled(features::kAssetDownloadSuggestionsFeature); | 69 // one is taken. |
| 60 return variations::GetVariationParamByFeatureAsInt( | 70 return variations::GetVariationParamByFeatureAsInt( |
| 61 assets_enabled ? features::kAssetDownloadSuggestionsFeature | 71 GetEnabledDownloadsFeature(), kMaxSuggestionsCountParamName, |
| 62 : features::kOfflinePageDownloadSuggestionsFeature, | 72 kDefaultMaxSuggestionsCount); |
| 63 kMaxSuggestionsCountParamName, kDefaultMaxSuggestionsCount); | 73 } |
| 74 |
| 75 int GetMaxDownloadAgeHours() { |
| 76 // One cannot get a variation param from a disabled feature, so the enabled |
| 77 // one is taken. |
| 78 return variations::GetVariationParamByFeatureAsInt( |
| 79 GetEnabledDownloadsFeature(), kMaxDownloadAgeHoursParamName, |
| 80 kDefaultMaxDownloadAgeHours); |
| 81 } |
| 82 |
| 83 base::Time GetOfflinePagePublishedTime(const OfflinePageItem& item) { |
| 84 return item.creation_time; |
| 85 } |
| 86 |
| 87 bool CompareOfflinePagesMostRecentlyPublishedFirst( |
| 88 const OfflinePageItem& left, |
| 89 const OfflinePageItem& right) { |
| 90 return GetOfflinePagePublishedTime(left) > GetOfflinePagePublishedTime(right); |
| 64 } | 91 } |
| 65 | 92 |
| 66 std::string GetOfflinePagePerCategoryID(int64_t raw_offline_page_id) { | 93 std::string GetOfflinePagePerCategoryID(int64_t raw_offline_page_id) { |
| 67 // Raw ID is prefixed in order to avoid conflicts with asset downloads. | 94 // Raw ID is prefixed in order to avoid conflicts with asset downloads. |
| 68 return std::string(1, kOfflinePageDownloadsPrefix) + | 95 return std::string(1, kOfflinePageDownloadsPrefix) + |
| 69 base::IntToString(raw_offline_page_id); | 96 base::IntToString(raw_offline_page_id); |
| 70 } | 97 } |
| 71 | 98 |
| 72 std::string GetAssetDownloadPerCategoryID(uint32_t raw_download_id) { | 99 std::string GetAssetDownloadPerCategoryID(uint32_t raw_download_id) { |
| 73 // Raw ID is prefixed in order to avoid conflicts with offline page downloads. | 100 // Raw ID is prefixed in order to avoid conflicts with offline page downloads. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 84 return true; | 111 return true; |
| 85 } | 112 } |
| 86 if (id_within_category[0] == kAssetDownloadsPrefix) { | 113 if (id_within_category[0] == kAssetDownloadsPrefix) { |
| 87 return false; | 114 return false; |
| 88 } | 115 } |
| 89 } | 116 } |
| 90 NOTREACHED() << "Unknown id_within_category " << id_within_category; | 117 NOTREACHED() << "Unknown id_within_category " << id_within_category; |
| 91 return false; | 118 return false; |
| 92 } | 119 } |
| 93 | 120 |
| 94 bool IsDownloadCompleted(const DownloadItem& item) { | 121 bool IsAssetDownloadCompleted(const DownloadItem& item) { |
| 95 return item.GetState() == DownloadItem::DownloadState::COMPLETE && | 122 return item.GetState() == DownloadItem::DownloadState::COMPLETE && |
| 96 !item.GetFileExternallyRemoved(); | 123 !item.GetFileExternallyRemoved(); |
| 97 } | 124 } |
| 98 | 125 |
| 99 base::Time GetAssetDownloadPublishedTime(const DownloadItem& item) { | 126 base::Time GetAssetDownloadPublishedTime(const DownloadItem& item) { |
| 100 return item.GetStartTime(); | 127 return item.GetStartTime(); |
| 101 } | 128 } |
| 102 | 129 |
| 103 bool CompareDownloadsMostRecentlyDownloadedFirst(const DownloadItem* left, | 130 bool CompareDownloadsMostRecentlyPublishedFirst(const DownloadItem* left, |
| 104 const DownloadItem* right) { | 131 const DownloadItem* right) { |
| 105 return GetAssetDownloadPublishedTime(*left) > | 132 return GetAssetDownloadPublishedTime(*left) > |
| 106 GetAssetDownloadPublishedTime(*right); | 133 GetAssetDownloadPublishedTime(*right); |
| 107 } | 134 } |
| 108 | 135 |
| 109 bool IsClientIdForOfflinePageDownload( | 136 bool IsClientIdForOfflinePageDownload( |
| 110 offline_pages::ClientPolicyController* policy_controller, | 137 offline_pages::ClientPolicyController* policy_controller, |
| 111 const offline_pages::ClientId& client_id) { | 138 const offline_pages::ClientId& client_id) { |
| 112 return policy_controller->IsSupportedByDownload(client_id.name_space); | 139 return policy_controller->IsSupportedByDownload(client_id.name_space); |
| 113 } | 140 } |
| 114 | 141 |
| 115 std::unique_ptr<OfflinePageModelQuery> BuildOfflinePageDownloadsQuery( | 142 std::unique_ptr<OfflinePageModelQuery> BuildOfflinePageDownloadsQuery( |
| 116 offline_pages::OfflinePageModel* model) { | 143 offline_pages::OfflinePageModel* model) { |
| 117 OfflinePageModelQueryBuilder builder; | 144 OfflinePageModelQueryBuilder builder; |
| 118 builder.RequireSupportedByDownload( | 145 builder.RequireSupportedByDownload( |
| 119 OfflinePageModelQuery::Requirement::INCLUDE_MATCHING); | 146 OfflinePageModelQuery::Requirement::INCLUDE_MATCHING); |
| 120 return builder.Build(model->GetPolicyController()); | 147 return builder.Build(model->GetPolicyController()); |
| 121 } | 148 } |
| 122 | 149 |
| 123 } // namespace | 150 } // namespace |
| 124 | 151 |
| 125 DownloadSuggestionsProvider::DownloadSuggestionsProvider( | 152 DownloadSuggestionsProvider::DownloadSuggestionsProvider( |
| 126 ContentSuggestionsProvider::Observer* observer, | 153 ContentSuggestionsProvider::Observer* observer, |
| 127 offline_pages::OfflinePageModel* offline_page_model, | 154 offline_pages::OfflinePageModel* offline_page_model, |
| 128 content::DownloadManager* download_manager, | 155 content::DownloadManager* download_manager, |
| 129 DownloadHistory* download_history, | 156 DownloadHistory* download_history, |
| 130 PrefService* pref_service) | 157 PrefService* pref_service, |
| 158 std::unique_ptr<base::Clock> clock) |
| 131 : ContentSuggestionsProvider(observer), | 159 : ContentSuggestionsProvider(observer), |
| 132 category_status_(CategoryStatus::AVAILABLE_LOADING), | 160 category_status_(CategoryStatus::AVAILABLE_LOADING), |
| 133 provided_category_(Category::FromKnownCategory( | 161 provided_category_(Category::FromKnownCategory( |
| 134 ntp_snippets::KnownCategories::DOWNLOADS)), | 162 ntp_snippets::KnownCategories::DOWNLOADS)), |
| 135 offline_page_model_(offline_page_model), | 163 offline_page_model_(offline_page_model), |
| 136 download_manager_(download_manager), | 164 download_manager_(download_manager), |
| 137 download_history_(download_history), | 165 download_history_(download_history), |
| 138 pref_service_(pref_service), | 166 pref_service_(pref_service), |
| 167 clock_(std::move(clock)), |
| 139 is_asset_downloads_initialization_complete_(false), | 168 is_asset_downloads_initialization_complete_(false), |
| 140 weak_ptr_factory_(this) { | 169 weak_ptr_factory_(this) { |
| 141 observer->OnCategoryStatusChanged(this, provided_category_, category_status_); | 170 observer->OnCategoryStatusChanged(this, provided_category_, category_status_); |
| 142 | 171 |
| 143 DCHECK(offline_page_model_ || download_manager_); | 172 DCHECK(offline_page_model_ || download_manager_); |
| 144 if (offline_page_model_) { | 173 if (offline_page_model_) { |
| 145 offline_page_model_->AddObserver(this); | 174 offline_page_model_->AddObserver(this); |
| 146 } | 175 } |
| 147 | 176 |
| 148 if (download_manager_) { | 177 if (download_manager_) { |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 0U); | 369 0U); |
| 341 | 370 |
| 342 int max_suggestions_count = GetMaxSuggestionsCount(); | 371 int max_suggestions_count = GetMaxSuggestionsCount(); |
| 343 if (static_cast<int>(cached_offline_page_downloads_.size()) < | 372 if (static_cast<int>(cached_offline_page_downloads_.size()) < |
| 344 max_suggestions_count) { | 373 max_suggestions_count) { |
| 345 cached_offline_page_downloads_.push_back(added_page); | 374 cached_offline_page_downloads_.push_back(added_page); |
| 346 } else if (max_suggestions_count > 0) { | 375 } else if (max_suggestions_count > 0) { |
| 347 auto oldest_page_iterator = | 376 auto oldest_page_iterator = |
| 348 std::max_element(cached_offline_page_downloads_.begin(), | 377 std::max_element(cached_offline_page_downloads_.begin(), |
| 349 cached_offline_page_downloads_.end(), | 378 cached_offline_page_downloads_.end(), |
| 350 &CompareOfflinePagesMostRecentlyCreatedFirst); | 379 &CompareOfflinePagesMostRecentlyPublishedFirst); |
| 351 *oldest_page_iterator = added_page; | 380 *oldest_page_iterator = added_page; |
| 352 } | 381 } |
| 353 | 382 |
| 354 SubmitContentSuggestions(); | 383 SubmitContentSuggestions(); |
| 355 } | 384 } |
| 356 | 385 |
| 357 void DownloadSuggestionsProvider::OfflinePageDeleted( | 386 void DownloadSuggestionsProvider::OfflinePageDeleted( |
| 358 int64_t offline_id, | 387 int64_t offline_id, |
| 359 const offline_pages::ClientId& client_id) { | 388 const offline_pages::ClientId& client_id) { |
| 360 DCHECK(offline_page_model_); | 389 DCHECK(offline_page_model_); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 413 // we may need to retrieve all downloads, but |OnDownloadRemoved| is called | 442 // we may need to retrieve all downloads, but |OnDownloadRemoved| is called |
| 414 // before the download is removed from the list. | 443 // before the download is removed from the list. |
| 415 } | 444 } |
| 416 | 445 |
| 417 void DownloadSuggestionsProvider::OnDownloadDestroyed( | 446 void DownloadSuggestionsProvider::OnDownloadDestroyed( |
| 418 content::DownloadItem* item) { | 447 content::DownloadItem* item) { |
| 419 DCHECK(is_asset_downloads_initialization_complete_); | 448 DCHECK(is_asset_downloads_initialization_complete_); |
| 420 | 449 |
| 421 item->RemoveObserver(this); | 450 item->RemoveObserver(this); |
| 422 | 451 |
| 423 if (!IsDownloadCompleted(*item)) { | 452 if (!IsAssetDownloadCompleted(*item)) { |
| 424 return; | 453 return; |
| 425 } | 454 } |
| 426 // TODO(vitaliii): Implement a better way to clean up dismissed IDs (in case | 455 // TODO(vitaliii): Implement a better way to clean up dismissed IDs (in case |
| 427 // some calls are missed). | 456 // some calls are missed). |
| 428 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId())); | 457 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId())); |
| 429 } | 458 } |
| 430 | 459 |
| 431 void DownloadSuggestionsProvider::OnHistoryQueryComplete() { | 460 void DownloadSuggestionsProvider::OnHistoryQueryComplete() { |
| 432 is_asset_downloads_initialization_complete_ = true; | 461 is_asset_downloads_initialization_complete_ = true; |
| 433 if (download_manager_) { | 462 if (download_manager_) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 487 std::vector<DownloadItem*> all_downloads; | 516 std::vector<DownloadItem*> all_downloads; |
| 488 download_manager_->GetAllDownloads(&all_downloads); | 517 download_manager_->GetAllDownloads(&all_downloads); |
| 489 std::set<std::string> old_dismissed_ids = ReadAssetDismissedIDsFromPrefs(); | 518 std::set<std::string> old_dismissed_ids = ReadAssetDismissedIDsFromPrefs(); |
| 490 std::set<std::string> retained_dismissed_ids; | 519 std::set<std::string> retained_dismissed_ids; |
| 491 cached_asset_downloads_.clear(); | 520 cached_asset_downloads_.clear(); |
| 492 for (DownloadItem* item : all_downloads) { | 521 for (DownloadItem* item : all_downloads) { |
| 493 std::string within_category_id = | 522 std::string within_category_id = |
| 494 GetAssetDownloadPerCategoryID(item->GetId()); | 523 GetAssetDownloadPerCategoryID(item->GetId()); |
| 495 if (old_dismissed_ids.count(within_category_id)) { | 524 if (old_dismissed_ids.count(within_category_id)) { |
| 496 retained_dismissed_ids.insert(within_category_id); | 525 retained_dismissed_ids.insert(within_category_id); |
| 497 } else if (IsDownloadCompleted(*item)) { | 526 } else if (IsAssetDownloadCompleted(*item) && |
| 527 !IsDownloadOutdated(GetAssetDownloadPublishedTime(*item))) { |
| 498 cached_asset_downloads_.push_back(item); | 528 cached_asset_downloads_.push_back(item); |
| 499 // We may already observe this item and, therefore, we remove the | 529 // We may already observe this item and, therefore, we remove the |
| 500 // observer first. | 530 // observer first. |
| 501 item->RemoveObserver(this); | 531 item->RemoveObserver(this); |
| 502 item->AddObserver(this); | 532 item->AddObserver(this); |
| 503 } | 533 } |
| 504 } | 534 } |
| 505 | 535 |
| 506 if (old_dismissed_ids.size() != retained_dismissed_ids.size()) { | 536 if (old_dismissed_ids.size() != retained_dismissed_ids.size()) { |
| 507 StoreAssetDismissedIDsToPrefs(retained_dismissed_ids); | 537 StoreAssetDismissedIDsToPrefs(retained_dismissed_ids); |
| 508 } | 538 } |
| 509 | 539 |
| 510 const int max_suggestions_count = GetMaxSuggestionsCount(); | 540 const int max_suggestions_count = GetMaxSuggestionsCount(); |
| 511 if (static_cast<int>(cached_asset_downloads_.size()) > | 541 if (static_cast<int>(cached_asset_downloads_.size()) > |
| 512 max_suggestions_count) { | 542 max_suggestions_count) { |
| 513 // Partially sorts |downloads| such that: | 543 // Partially sorts |downloads| such that: |
| 514 // 1) The element at the index |max_suggestions_count| is changed to the | 544 // 1) The element at the index |max_suggestions_count| is changed to the |
| 515 // element which would occur on this position if |downloads| was sorted; | 545 // element which would occur on this position if |downloads| was sorted; |
| 516 // 2) All of the elements before index |max_suggestions_count| are less than | 546 // 2) All of the elements before index |max_suggestions_count| are less than |
| 517 // or equal to the elements after it. | 547 // or equal to the elements after it. |
| 518 std::nth_element(cached_asset_downloads_.begin(), | 548 std::nth_element(cached_asset_downloads_.begin(), |
| 519 cached_asset_downloads_.begin() + max_suggestions_count, | 549 cached_asset_downloads_.begin() + max_suggestions_count, |
| 520 cached_asset_downloads_.end(), | 550 cached_asset_downloads_.end(), |
| 521 &CompareDownloadsMostRecentlyDownloadedFirst); | 551 &CompareDownloadsMostRecentlyPublishedFirst); |
| 522 cached_asset_downloads_.resize(max_suggestions_count); | 552 cached_asset_downloads_.resize(max_suggestions_count); |
| 523 } | 553 } |
| 524 } | 554 } |
| 525 | 555 |
| 526 void DownloadSuggestionsProvider:: | 556 void DownloadSuggestionsProvider:: |
| 527 AsynchronouslyFetchAllDownloadsAndSubmitSuggestions() { | 557 AsynchronouslyFetchAllDownloadsAndSubmitSuggestions() { |
| 528 FetchAssetsDownloads(); | 558 FetchAssetsDownloads(); |
| 529 AsynchronouslyFetchOfflinePagesDownloads(/*notify=*/true); | 559 AsynchronouslyFetchOfflinePagesDownloads(/*notify=*/true); |
| 530 } | 560 } |
| 531 | 561 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 563 offline_page.offline_id)), | 593 offline_page.offline_id)), |
| 564 offline_page.url); | 594 offline_page.url); |
| 565 | 595 |
| 566 if (offline_page.title.empty()) { | 596 if (offline_page.title.empty()) { |
| 567 // TODO(vitaliii): Remove this fallback once the OfflinePageModel provides | 597 // TODO(vitaliii): Remove this fallback once the OfflinePageModel provides |
| 568 // titles for all (relevant) OfflinePageItems. | 598 // titles for all (relevant) OfflinePageItems. |
| 569 suggestion.set_title(base::UTF8ToUTF16(offline_page.url.spec())); | 599 suggestion.set_title(base::UTF8ToUTF16(offline_page.url.spec())); |
| 570 } else { | 600 } else { |
| 571 suggestion.set_title(offline_page.title); | 601 suggestion.set_title(offline_page.title); |
| 572 } | 602 } |
| 573 suggestion.set_publish_date(offline_page.creation_time); | 603 suggestion.set_publish_date(GetOfflinePagePublishedTime(offline_page)); |
| 574 suggestion.set_publisher_name(base::UTF8ToUTF16(offline_page.url.host())); | 604 suggestion.set_publisher_name(base::UTF8ToUTF16(offline_page.url.host())); |
| 575 auto extra = base::MakeUnique<ntp_snippets::DownloadSuggestionExtra>(); | 605 auto extra = base::MakeUnique<ntp_snippets::DownloadSuggestionExtra>(); |
| 576 extra->is_download_asset = false; | 606 extra->is_download_asset = false; |
| 577 extra->offline_page_id = offline_page.offline_id; | 607 extra->offline_page_id = offline_page.offline_id; |
| 578 suggestion.set_download_suggestion_extra(std::move(extra)); | 608 suggestion.set_download_suggestion_extra(std::move(extra)); |
| 579 return suggestion; | 609 return suggestion; |
| 580 } | 610 } |
| 581 | 611 |
| 582 ContentSuggestion DownloadSuggestionsProvider::ConvertDownloadItem( | 612 ContentSuggestion DownloadSuggestionsProvider::ConvertDownloadItem( |
| 583 const DownloadItem& download_item) const { | 613 const DownloadItem& download_item) const { |
| 584 ContentSuggestion suggestion( | 614 ContentSuggestion suggestion( |
| 585 ContentSuggestion::ID(provided_category_, GetAssetDownloadPerCategoryID( | 615 ContentSuggestion::ID(provided_category_, GetAssetDownloadPerCategoryID( |
| 586 download_item.GetId())), | 616 download_item.GetId())), |
| 587 download_item.GetOriginalUrl()); | 617 download_item.GetOriginalUrl()); |
| 588 suggestion.set_title( | 618 suggestion.set_title( |
| 589 download_item.GetTargetFilePath().BaseName().LossyDisplayName()); | 619 download_item.GetTargetFilePath().BaseName().LossyDisplayName()); |
| 590 suggestion.set_publish_date(GetAssetDownloadPublishedTime(download_item)); | 620 suggestion.set_publish_date(GetAssetDownloadPublishedTime(download_item)); |
| 591 suggestion.set_publisher_name( | 621 suggestion.set_publisher_name( |
| 592 base::UTF8ToUTF16(download_item.GetURL().host())); | 622 base::UTF8ToUTF16(download_item.GetURL().host())); |
| 593 auto extra = base::MakeUnique<ntp_snippets::DownloadSuggestionExtra>(); | 623 auto extra = base::MakeUnique<ntp_snippets::DownloadSuggestionExtra>(); |
| 594 extra->target_file_path = download_item.GetTargetFilePath(); | 624 extra->target_file_path = download_item.GetTargetFilePath(); |
| 595 extra->mime_type = download_item.GetMimeType(); | 625 extra->mime_type = download_item.GetMimeType(); |
| 596 extra->is_download_asset = true; | 626 extra->is_download_asset = true; |
| 597 suggestion.set_download_suggestion_extra(std::move(extra)); | 627 suggestion.set_download_suggestion_extra(std::move(extra)); |
| 598 return suggestion; | 628 return suggestion; |
| 599 } | 629 } |
| 600 | 630 |
| 631 bool DownloadSuggestionsProvider::IsDownloadOutdated( |
| 632 const base::Time& published_time) { |
| 633 // TODO(vitaliii): Also consider last access time here once it is collected |
| 634 // for asset downloads. |
| 635 return published_time < |
| 636 clock_->Now() - base::TimeDelta::FromHours(GetMaxDownloadAgeHours()); |
| 637 } |
| 638 |
| 601 bool DownloadSuggestionsProvider::CacheAssetDownloadIfNeeded( | 639 bool DownloadSuggestionsProvider::CacheAssetDownloadIfNeeded( |
| 602 const content::DownloadItem* item) { | 640 const content::DownloadItem* item) { |
| 603 if (!IsDownloadCompleted(*item)) { | 641 if (!IsAssetDownloadCompleted(*item)) { |
| 604 return false; | 642 return false; |
| 605 } | 643 } |
| 606 | 644 |
| 607 if (base::ContainsValue(cached_asset_downloads_, item)) { | 645 if (base::ContainsValue(cached_asset_downloads_, item)) { |
| 608 return false; | 646 return false; |
| 609 } | 647 } |
| 610 | 648 |
| 611 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs(); | 649 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs(); |
| 612 if (dismissed_ids.count(GetAssetDownloadPerCategoryID(item->GetId()))) { | 650 if (dismissed_ids.count(GetAssetDownloadPerCategoryID(item->GetId()))) { |
| 613 return false; | 651 return false; |
| 614 } | 652 } |
| 615 | 653 |
| 616 DCHECK_LE(static_cast<int>(cached_asset_downloads_.size()), | 654 DCHECK_LE(static_cast<int>(cached_asset_downloads_.size()), |
| 617 GetMaxSuggestionsCount()); | 655 GetMaxSuggestionsCount()); |
| 618 if (static_cast<int>(cached_asset_downloads_.size()) == | 656 if (static_cast<int>(cached_asset_downloads_.size()) == |
| 619 GetMaxSuggestionsCount()) { | 657 GetMaxSuggestionsCount()) { |
| 620 auto oldest = std::max_element( | 658 auto oldest = std::max_element(cached_asset_downloads_.begin(), |
| 621 cached_asset_downloads_.begin(), cached_asset_downloads_.end(), | 659 cached_asset_downloads_.end(), |
| 622 &CompareDownloadsMostRecentlyDownloadedFirst); | 660 &CompareDownloadsMostRecentlyPublishedFirst); |
| 623 if (GetAssetDownloadPublishedTime(*item) <= | 661 if (GetAssetDownloadPublishedTime(*item) <= |
| 624 GetAssetDownloadPublishedTime(**oldest)) { | 662 GetAssetDownloadPublishedTime(**oldest)) { |
| 625 return false; | 663 return false; |
| 626 } | 664 } |
| 627 | 665 |
| 628 *oldest = item; | 666 *oldest = item; |
| 629 } else { | 667 } else { |
| 630 cached_asset_downloads_.push_back(item); | 668 cached_asset_downloads_.push_back(item); |
| 631 } | 669 } |
| 632 | 670 |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 699 DCHECK(!offline_page_model_ || offline_page_model_->is_loaded()); | 737 DCHECK(!offline_page_model_ || offline_page_model_->is_loaded()); |
| 700 | 738 |
| 701 std::set<std::string> old_dismissed_ids = | 739 std::set<std::string> old_dismissed_ids = |
| 702 ReadOfflinePageDismissedIDsFromPrefs(); | 740 ReadOfflinePageDismissedIDsFromPrefs(); |
| 703 std::set<std::string> retained_dismissed_ids; | 741 std::set<std::string> retained_dismissed_ids; |
| 704 std::vector<const OfflinePageItem*> items; | 742 std::vector<const OfflinePageItem*> items; |
| 705 // Filtering out dismissed items and pruning dismissed IDs. | 743 // Filtering out dismissed items and pruning dismissed IDs. |
| 706 for (const OfflinePageItem& item : all_download_offline_pages) { | 744 for (const OfflinePageItem& item : all_download_offline_pages) { |
| 707 std::string id_within_category = | 745 std::string id_within_category = |
| 708 GetOfflinePagePerCategoryID(item.offline_id); | 746 GetOfflinePagePerCategoryID(item.offline_id); |
| 709 if (!old_dismissed_ids.count(id_within_category)) { | 747 if (old_dismissed_ids.count(id_within_category)) { |
| 710 items.push_back(&item); | 748 retained_dismissed_ids.insert(id_within_category); |
| 711 } else { | 749 } else { |
| 712 retained_dismissed_ids.insert(id_within_category); | 750 if (!IsDownloadOutdated(GetOfflinePagePublishedTime(item))) { |
| 751 items.push_back(&item); |
| 752 } |
| 713 } | 753 } |
| 714 } | 754 } |
| 715 | 755 |
| 716 const int max_suggestions_count = GetMaxSuggestionsCount(); | 756 const int max_suggestions_count = GetMaxSuggestionsCount(); |
| 717 if (static_cast<int>(items.size()) > max_suggestions_count) { | 757 if (static_cast<int>(items.size()) > max_suggestions_count) { |
| 718 // Partially sorts |items| such that: | 758 // Partially sorts |items| such that: |
| 719 // 1) The element at the index |max_suggestions_count| is changed to the | 759 // 1) The element at the index |max_suggestions_count| is changed to the |
| 720 // element which would occur on this position if |items| was sorted; | 760 // element which would occur on this position if |items| was sorted; |
| 721 // 2) All of the elements before index |max_suggestions_count| are less than | 761 // 2) All of the elements before index |max_suggestions_count| are less than |
| 722 // or equal to the elements after it. | 762 // or equal to the elements after it. |
| 723 std::nth_element( | 763 std::nth_element( |
| 724 items.begin(), items.begin() + max_suggestions_count, items.end(), | 764 items.begin(), items.begin() + max_suggestions_count, items.end(), |
| 725 [](const OfflinePageItem* left, const OfflinePageItem* right) { | 765 [](const OfflinePageItem* left, const OfflinePageItem* right) { |
| 726 return CompareOfflinePagesMostRecentlyCreatedFirst(*left, *right); | 766 return CompareOfflinePagesMostRecentlyPublishedFirst(*left, *right); |
| 727 }); | 767 }); |
| 728 items.resize(max_suggestions_count); | 768 items.resize(max_suggestions_count); |
| 729 } | 769 } |
| 730 | 770 |
| 731 cached_offline_page_downloads_.clear(); | 771 cached_offline_page_downloads_.clear(); |
| 732 for (const OfflinePageItem* item : items) { | 772 for (const OfflinePageItem* item : items) { |
| 733 cached_offline_page_downloads_.push_back(*item); | 773 cached_offline_page_downloads_.push_back(*item); |
| 734 } | 774 } |
| 735 | 775 |
| 736 if (old_dismissed_ids.size() != retained_dismissed_ids.size()) { | 776 if (old_dismissed_ids.size() != retained_dismissed_ids.size()) { |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 811 void DownloadSuggestionsProvider::UnregisterDownloadItemObservers() { | 851 void DownloadSuggestionsProvider::UnregisterDownloadItemObservers() { |
| 812 DCHECK_NE(download_manager_, nullptr); | 852 DCHECK_NE(download_manager_, nullptr); |
| 813 | 853 |
| 814 std::vector<DownloadItem*> all_downloads; | 854 std::vector<DownloadItem*> all_downloads; |
| 815 download_manager_->GetAllDownloads(&all_downloads); | 855 download_manager_->GetAllDownloads(&all_downloads); |
| 816 | 856 |
| 817 for (DownloadItem* item : all_downloads) { | 857 for (DownloadItem* item : all_downloads) { |
| 818 item->RemoveObserver(this); | 858 item->RemoveObserver(this); |
| 819 } | 859 } |
| 820 } | 860 } |
| OLD | NEW |