OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "components/ntp_snippets/remote/ntp_snippets_service.h" | 5 #include "components/ntp_snippets/remote/ntp_snippets_service.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <iterator> | 8 #include <iterator> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
235 void NTPSnippetsService::FetchSnippets(bool interactive_request) { | 235 void NTPSnippetsService::FetchSnippets(bool interactive_request) { |
236 if (ready()) | 236 if (ready()) |
237 FetchSnippetsFromHosts(std::set<std::string>(), interactive_request); | 237 FetchSnippetsFromHosts(std::set<std::string>(), interactive_request); |
238 else | 238 else |
239 fetch_when_ready_ = true; | 239 fetch_when_ready_ = true; |
240 } | 240 } |
241 | 241 |
242 void NTPSnippetsService::FetchSnippetsFromHosts( | 242 void NTPSnippetsService::FetchSnippetsFromHosts( |
243 const std::set<std::string>& hosts, | 243 const std::set<std::string>& hosts, |
244 bool interactive_request) { | 244 bool interactive_request) { |
245 FetchSnippetsFromHostsImpl(hosts, | |
246 interactive_request, | |
247 base::MakeUnique<ReplaceExistingSnippets>(this)); | |
248 } | |
249 | |
250 void NTPSnippetsService::FetchMore() { | |
251 FetchSnippetsFromHostsImpl(std::set<std::string>(), | |
252 /* interactive_request= */ true, | |
Marc Treib
2016/10/18 08:17:45
nit: misaligned
There's a few other formatting thi
fhorschig
2016/10/20 13:10:38
Done. Thank you!
| |
253 base::MakeUnique<AddNewSnippets>(this)); | |
254 } | |
255 | |
256 void NTPSnippetsService::FetchSnippetsFromHostsImpl( | |
257 const std::set<std::string>& hosts, | |
258 bool interactive_request, | |
259 std::unique_ptr<NewNTPSnippetsHandlingStrategy> strategy) { | |
245 if (!ready()) | 260 if (!ready()) |
246 return; | 261 return; |
247 | 262 |
248 // Empty categories are marked as loading; others are unchanged. | 263 MarkEmptyCategoriesAsLoading(); |
264 | |
265 std::set<std::string> excluded_ids; | |
266 strategy->CollectIdsToExclude(&excluded_ids); | |
267 | |
268 snippets_fetcher_->SetCallback( | |
269 base::BindOnce(&NTPSnippetsService::OnFetchFinished, | |
270 base::Unretained(this), | |
271 std::move(strategy))); | |
272 | |
273 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, | |
274 excluded_ids, kMaxSnippetCount, | |
275 interactive_request); | |
276 } | |
277 | |
278 void NTPSnippetsService::MarkEmptyCategoriesAsLoading() { | |
249 for (const auto& item : categories_) { | 279 for (const auto& item : categories_) { |
250 Category category = item.first; | 280 Category category = item.first; |
251 const CategoryContent& content = item.second; | 281 const CategoryContent& content = item.second; |
252 if (content.snippets.empty()) | 282 if (content.snippets.empty()) |
253 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); | 283 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); |
254 } | 284 } |
255 | |
256 std::set<std::string> excluded_ids; | |
257 for (const auto& item : categories_) { | |
258 const CategoryContent& content = item.second; | |
259 for (const auto& snippet : content.dismissed) | |
260 excluded_ids.insert(snippet->id()); | |
261 } | |
262 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, | |
263 excluded_ids, kMaxSnippetCount, | |
264 interactive_request); | |
265 } | 285 } |
266 | 286 |
267 void NTPSnippetsService::RescheduleFetching(bool force) { | 287 void NTPSnippetsService::RescheduleFetching(bool force) { |
268 // The scheduler only exists on Android so far, it's null on other platforms. | 288 // The scheduler only exists on Android so far, it's null on other platforms. |
269 if (!scheduler_) | 289 if (!scheduler_) |
270 return; | 290 return; |
271 | 291 |
272 if (ready()) { | 292 if (ready()) { |
273 base::TimeDelta old_interval_wifi = | 293 base::TimeDelta old_interval_wifi = |
274 base::TimeDelta::FromInternalValue(pref_service_->GetInt64( | 294 base::TimeDelta::FromInternalValue(pref_service_->GetInt64( |
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
513 ClearOrphanedImages(); | 533 ClearOrphanedImages(); |
514 FinishInitialization(); | 534 FinishInitialization(); |
515 } | 535 } |
516 | 536 |
517 void NTPSnippetsService::OnDatabaseError() { | 537 void NTPSnippetsService::OnDatabaseError() { |
518 EnterState(State::ERROR_OCCURRED); | 538 EnterState(State::ERROR_OCCURRED); |
519 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | 539 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); |
520 } | 540 } |
521 | 541 |
522 void NTPSnippetsService::OnFetchFinished( | 542 void NTPSnippetsService::OnFetchFinished( |
543 std::unique_ptr<NewNTPSnippetsHandlingStrategy> strategy, | |
523 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { | 544 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { |
524 if (!ready()) | 545 if (!ready()) |
525 return; | 546 return; |
526 | 547 |
527 // Mark all categories as not provided by the server in the latest fetch. The | 548 // Mark all categories as not provided by the server in the latest fetch. The |
528 // ones we got will be marked again below. | 549 // ones we got will be marked again below. |
529 for (auto& item : categories_) { | 550 for (auto& item : categories_) { |
530 CategoryContent* content = &item.second; | 551 CategoryContent* content = &item.second; |
531 content->provided_by_server = false; | 552 content->provided_by_server = false; |
532 } | 553 } |
(...skipping 24 matching lines...) Expand all Loading... | |
557 categories_[category].provided_by_server = true; | 578 categories_[category].provided_by_server = true; |
558 | 579 |
559 // TODO(tschumann): Remove this histogram once we only talk to the content | 580 // TODO(tschumann): Remove this histogram once we only talk to the content |
560 // suggestions cloud backend. | 581 // suggestions cloud backend. |
561 if (category == articles_category_) { | 582 if (category == articles_category_) { |
562 UMA_HISTOGRAM_SPARSE_SLOWLY( | 583 UMA_HISTOGRAM_SPARSE_SLOWLY( |
563 "NewTabPage.Snippets.NumArticlesFetched", | 584 "NewTabPage.Snippets.NumArticlesFetched", |
564 std::min(fetched_category.snippets.size(), | 585 std::min(fetched_category.snippets.size(), |
565 static_cast<size_t>(kMaxSnippetCount + 1))); | 586 static_cast<size_t>(kMaxSnippetCount + 1))); |
566 } | 587 } |
567 ReplaceSnippets(category, std::move(fetched_category.snippets)); | 588 StoreSnippets(category, |
589 std::move(fetched_category.snippets), | |
590 strategy.get()); | |
568 } | 591 } |
569 } | 592 } |
570 | 593 |
571 // We might have gotten new categories (or updated the titles of existing | 594 // We might have gotten new categories (or updated the titles of existing |
572 // ones), so update the pref. | 595 // ones), so update the pref. |
573 StoreCategoriesToPrefs(); | 596 StoreCategoriesToPrefs(); |
574 | 597 |
575 for (const auto& item : categories_) { | 598 for (const auto& item : categories_) { |
576 Category category = item.first; | 599 Category category = item.first; |
577 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 600 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
578 } | 601 } |
579 | 602 |
580 // TODO(sfiera): equivalent metrics for non-articles. | 603 // TODO(sfiera): equivalent metrics for non-articles. |
581 const CategoryContent& content = categories_[articles_category_]; | 604 const CategoryContent& content = categories_[articles_category_]; |
582 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | 605 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", |
583 content.snippets.size()); | 606 content.snippets.size()); |
584 if (content.snippets.empty() && !content.dismissed.empty()) { | 607 if (content.snippets.empty() && !content.dismissed.empty()) { |
585 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 608 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", |
586 content.dismissed.size()); | 609 content.dismissed.size()); |
587 } | 610 } |
588 | 611 |
589 // TODO(sfiera): notify only when a category changed above. | 612 // TODO(sfiera): notify only when a category changed above. |
590 NotifyNewSuggestions(); | 613 strategy->NotifySuggestions(); |
591 | 614 |
592 // Reschedule after a successful fetch. This resets all currently scheduled | 615 // Reschedule after a successful fetch. This resets all currently scheduled |
593 // fetches, to make sure the fallback interval triggers only if no wifi fetch | 616 // fetches, to make sure the fallback interval triggers only if no wifi fetch |
594 // succeeded, and also that we don't do a background fetch immediately after | 617 // succeeded, and also that we don't do a background fetch immediately after |
595 // a user-initiated one. | 618 // a user-initiated one. |
596 if (fetched_categories) | 619 if (fetched_categories) |
597 RescheduleFetching(true); | 620 RescheduleFetching(true); |
598 } | 621 } |
599 | 622 |
600 void NTPSnippetsService::ArchiveSnippets(Category category, | 623 void NTPSnippetsService::ArchiveSnippets(Category category, |
(...skipping 13 matching lines...) Expand all Loading... | |
614 if (content->archived.size() > kMaxArchivedSnippetCount) { | 637 if (content->archived.size() > kMaxArchivedSnippetCount) { |
615 NTPSnippet::PtrVector to_delete( | 638 NTPSnippet::PtrVector to_delete( |
616 std::make_move_iterator(content->archived.begin() + | 639 std::make_move_iterator(content->archived.begin() + |
617 kMaxArchivedSnippetCount), | 640 kMaxArchivedSnippetCount), |
618 std::make_move_iterator(content->archived.end())); | 641 std::make_move_iterator(content->archived.end())); |
619 content->archived.resize(kMaxArchivedSnippetCount); | 642 content->archived.resize(kMaxArchivedSnippetCount); |
620 database_->DeleteImages(GetSnippetIDVector(to_delete)); | 643 database_->DeleteImages(GetSnippetIDVector(to_delete)); |
621 } | 644 } |
622 } | 645 } |
623 | 646 |
624 void NTPSnippetsService::ReplaceSnippets(Category category, | 647 void NTPSnippetsService::AssignExpiryDates(NTPSnippet::PtrVector* snippets) { |
Marc Treib
2016/10/18 08:17:45
nit: this sets both publish and expiry dates. Also
fhorschig
2016/10/20 13:10:38
Done.
| |
625 NTPSnippet::PtrVector new_snippets) { | 648 for (std::unique_ptr<NTPSnippet>& snippet : *snippets) { |
626 DCHECK(ready()); | |
627 CategoryContent* content = &categories_[category]; | |
628 | |
629 // Remove new snippets that have been dismissed. | |
630 EraseMatchingSnippets(&new_snippets, content->dismissed); | |
631 | |
632 // Fill in default publish/expiry dates where required. | |
633 for (std::unique_ptr<NTPSnippet>& snippet : new_snippets) { | |
634 if (snippet->publish_date().is_null()) | 649 if (snippet->publish_date().is_null()) |
635 snippet->set_publish_date(base::Time::Now()); | 650 snippet->set_publish_date(base::Time::Now()); |
636 if (snippet->expiry_date().is_null()) { | 651 if (snippet->expiry_date().is_null()) { |
637 snippet->set_expiry_date( | 652 snippet->set_expiry_date( |
638 snippet->publish_date() + | 653 snippet->publish_date() + |
639 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins)); | 654 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins)); |
640 } | 655 } |
641 } | 656 } |
657 } | |
642 | 658 |
643 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | 659 void NTPSnippetsService::AddIncompleteSnippets( |
Marc Treib
2016/10/18 08:17:45
This has exactly the wrong name - it *removes* inc
fhorschig
2016/10/20 13:10:38
Done.
| |
644 switches::kAddIncompleteSnippets)) { | 660 NTPSnippet::PtrVector* snippets) { |
645 int num_new_snippets = new_snippets.size(); | 661 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
646 // Remove snippets that do not have all the info we need to display it to | 662 switches::kAddIncompleteSnippets)) |
647 // the user. | 663 return; |
Marc Treib
2016/10/18 08:17:45
nit: braces if either the condition or the body do
fhorschig
2016/10/20 13:10:38
Done.
| |
648 new_snippets.erase( | 664 |
649 std::remove_if(new_snippets.begin(), new_snippets.end(), | 665 int num_snippets = snippets->size(); |
650 [](const std::unique_ptr<NTPSnippet>& snippet) { | 666 // Remove snippets that do not have all the info we need to display it to |
651 return !snippet->is_complete(); | 667 // the user. |
652 }), | 668 snippets->erase( |
653 new_snippets.end()); | 669 std::remove_if(snippets->begin(), snippets->end(), |
654 int num_snippets_dismissed = num_new_snippets - new_snippets.size(); | 670 [](const std::unique_ptr<NTPSnippet>& snippet) { |
655 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", | 671 return !snippet->is_complete(); |
656 num_snippets_dismissed > 0); | 672 }), |
657 if (num_snippets_dismissed > 0) { | 673 snippets->end()); |
658 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", | 674 int num_snippets_dismissed = num_snippets - snippets->size(); |
659 num_snippets_dismissed); | 675 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", |
660 } | 676 num_snippets_dismissed > 0); |
677 if (num_snippets_dismissed > 0) { | |
678 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", | |
679 num_snippets_dismissed); | |
661 } | 680 } |
681 } | |
682 | |
683 void NTPSnippetsService::StoreSnippets( | |
684 const Category& category, | |
685 NTPSnippet::PtrVector new_snippets, | |
686 NewNTPSnippetsHandlingStrategy* strategy) { | |
687 DCHECK(ready()); | |
688 CategoryContent* content = &categories_[category]; | |
689 | |
690 // Remove new snippets that have been dismissed. | |
691 EraseMatchingSnippets(&new_snippets, content->dismissed); | |
692 | |
693 AssignExpiryDates(&new_snippets); | |
694 AddIncompleteSnippets(&new_snippets); | |
662 | 695 |
663 // Do not touch the current set of snippets if the newly fetched one is empty. | 696 // Do not touch the current set of snippets if the newly fetched one is empty. |
664 if (new_snippets.empty()) | 697 if (new_snippets.empty()) |
665 return; | 698 return; |
666 | 699 |
667 // It's entirely possible that the newly fetched snippets contain articles | 700 // It's entirely possible that the newly fetched snippets contain articles |
668 // that have been present before. | 701 // that have been present before. |
669 // Since archival removes snippets from the database (indexed by | 702 // Since archival removes snippets from the database (indexed by |
670 // snippet->id()), we need to make sure to only archive snippets that don't | 703 // snippet->id()), we need to make sure to only archive snippets that don't |
671 // appear with the same ID in the new suggestions (it's fine for additional | 704 // appear with the same ID in the new suggestions (it's fine for additional |
672 // IDs though). | 705 // IDs though). |
673 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); | 706 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); |
674 ArchiveSnippets(category, &content->snippets); | |
675 | 707 |
676 // Save new articles to the DB. | 708 // Save new articles to the DB. |
677 database_->SaveSnippets(new_snippets); | 709 database_->SaveSnippets(new_snippets); |
678 | 710 |
679 content->snippets = std::move(new_snippets); | 711 strategy->SaveNewSnippets(category, std::move(new_snippets)); |
680 } | 712 } |
681 | 713 |
682 void NTPSnippetsService::ClearExpiredDismissedSnippets() { | 714 void NTPSnippetsService::ClearExpiredDismissedSnippets() { |
683 std::vector<Category> categories_to_erase; | 715 std::vector<Category> categories_to_erase; |
684 | 716 |
685 const base::Time now = base::Time::Now(); | 717 const base::Time now = base::Time::Now(); |
686 | 718 |
687 for (auto& item : categories_) { | 719 for (auto& item : categories_) { |
688 Category category = item.first; | 720 Category category = item.first; |
689 CategoryContent* content = &item.second; | 721 CategoryContent* content = &item.second; |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
855 } | 887 } |
856 | 888 |
857 void NTPSnippetsService::FinishInitialization() { | 889 void NTPSnippetsService::FinishInitialization() { |
858 if (nuke_when_initialized_) { | 890 if (nuke_when_initialized_) { |
859 // We nuke here in addition to EnterStateReady, so that it happens even if | 891 // We nuke here in addition to EnterStateReady, so that it happens even if |
860 // we enter the DISABLED state below. | 892 // we enter the DISABLED state below. |
861 NukeAllSnippets(); | 893 NukeAllSnippets(); |
862 nuke_when_initialized_ = false; | 894 nuke_when_initialized_ = false; |
863 } | 895 } |
864 | 896 |
865 snippets_fetcher_->SetCallback( | |
866 base::Bind(&NTPSnippetsService::OnFetchFinished, base::Unretained(this))); | |
867 | |
868 // |image_fetcher_| can be null in tests. | 897 // |image_fetcher_| can be null in tests. |
869 if (image_fetcher_) { | 898 if (image_fetcher_) { |
870 image_fetcher_->SetImageFetcherDelegate(this); | 899 image_fetcher_->SetImageFetcherDelegate(this); |
871 image_fetcher_->SetDataUseServiceName( | 900 image_fetcher_->SetDataUseServiceName( |
872 data_use_measurement::DataUseUserData::NTP_SNIPPETS); | 901 data_use_measurement::DataUseUserData::NTP_SNIPPETS); |
873 } | 902 } |
874 | 903 |
875 // Note: Initializing the status service will run the callback right away with | 904 // Note: Initializing the status service will run the callback right away with |
876 // the current state. | 905 // the current state. |
877 snippets_status_service_->Init(base::Bind( | 906 snippets_status_service_->Init(base::Bind( |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1101 pref_service_->Set(prefs::kRemoteSuggestionCategories, list); | 1130 pref_service_->Set(prefs::kRemoteSuggestionCategories, list); |
1102 } | 1131 } |
1103 | 1132 |
1104 NTPSnippetsService::CategoryContent::CategoryContent() = default; | 1133 NTPSnippetsService::CategoryContent::CategoryContent() = default; |
1105 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = | 1134 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = |
1106 default; | 1135 default; |
1107 NTPSnippetsService::CategoryContent::~CategoryContent() = default; | 1136 NTPSnippetsService::CategoryContent::~CategoryContent() = default; |
1108 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: | 1137 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: |
1109 operator=(CategoryContent&&) = default; | 1138 operator=(CategoryContent&&) = default; |
1110 | 1139 |
1140 //////////////////////////////////////////////////////////////////////////////// | |
1141 // Snippet Handling Strategy Implementations | |
1142 | |
1143 NTPSnippetsService:: | |
1144 NewNTPSnippetsHandlingStrategy::NewNTPSnippetsHandlingStrategy( | |
1145 NTPSnippetsService* service) | |
1146 : service_(service) {} | |
1147 | |
1148 NTPSnippetsService::ReplaceExistingSnippets::ReplaceExistingSnippets( | |
1149 NTPSnippetsService* service) | |
1150 : NewNTPSnippetsHandlingStrategy(service) {} | |
1151 | |
1152 void NTPSnippetsService::ReplaceExistingSnippets::CollectIdsToExclude( | |
1153 std::set<std::string>* ids) { | |
1154 for (const auto& item : service_->categories_) { | |
1155 const CategoryContent& content = item.second; | |
1156 for (const auto& snippet : content.dismissed) | |
1157 ids->insert(snippet->id()); | |
1158 } | |
1159 } | |
1160 | |
1161 void NTPSnippetsService::ReplaceExistingSnippets::SaveNewSnippets( | |
1162 const Category& category, | |
1163 NTPSnippet::PtrVector new_snippets) { | |
1164 CategoryContent* content = &service_->categories_[category]; | |
1165 service_->ArchiveSnippets(category, &content->snippets); | |
1166 content->snippets = std::move(new_snippets); | |
1167 } | |
1168 | |
1169 void NTPSnippetsService::ReplaceExistingSnippets::NotifySuggestions() { | |
1170 service_->NotifyNewSuggestions(); | |
1171 } | |
1172 | |
1173 NTPSnippetsService::AddNewSnippets::AddNewSnippets(NTPSnippetsService* service) | |
1174 : NewNTPSnippetsHandlingStrategy(service) {} | |
1175 | |
1176 void NTPSnippetsService::AddNewSnippets::CollectIdsToExclude( | |
1177 std::set<std::string>* ids) { | |
1178 for (const auto& item : service_->categories_) { | |
1179 const CategoryContent& content = item.second; | |
1180 for (const auto& snippet : content.dismissed) | |
1181 ids->insert(snippet->id()); | |
1182 for (const auto& snippet : content.archived) | |
1183 ids->insert(snippet->id()); | |
1184 for (const auto& snippet : content.snippets) | |
1185 ids->insert(snippet->id()); | |
1186 } | |
1187 } | |
1188 | |
1189 void NTPSnippetsService::AddNewSnippets::SaveNewSnippets( | |
1190 const Category& category, | |
1191 NTPSnippet::PtrVector new_snippets) { | |
1192 CategoryContent* content = &service_->categories_[category]; | |
1193 content->snippets.insert(content->snippets.end(), | |
1194 std::make_move_iterator(new_snippets.begin()), | |
1195 std::make_move_iterator(new_snippets.end())); | |
1196 } | |
1197 | |
1198 void NTPSnippetsService::AddNewSnippets::NotifySuggestions() { | |
1199 // TODO(fhorschig): Notify additional Suggestions only. | |
1200 service_->NotifyNewSuggestions(); | |
1201 } | |
1202 | |
1111 } // namespace ntp_snippets | 1203 } // namespace ntp_snippets |
OLD | NEW |