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

Side by Side Diff: components/ntp_snippets/remote/ntp_snippets_service.cc

Issue 2421463002: FetchMore functionality backend (Closed)
Patch Set: Strategy pattern to handle differences in fetching procedure. Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698