| 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 |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/location.h" | 12 #include "base/location.h" |
| 13 #include "base/memory/ptr_util.h" |
| 13 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
| 14 #include "base/metrics/sparse_histogram.h" | 15 #include "base/metrics/sparse_histogram.h" |
| 15 #include "base/path_service.h" | 16 #include "base/path_service.h" |
| 16 #include "base/stl_util.h" | 17 #include "base/stl_util.h" |
| 17 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
| 19 #include "base/task_runner_util.h" | 20 #include "base/task_runner_util.h" |
| 20 #include "base/time/time.h" | 21 #include "base/time/time.h" |
| 21 #include "base/values.h" | 22 #include "base/values.h" |
| 22 #include "components/data_use_measurement/core/data_use_user_data.h" | 23 #include "components/data_use_measurement/core/data_use_user_data.h" |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 122 std::set<std::string> GetAllIDs(const NTPSnippet::PtrVector& snippets) { | 123 std::set<std::string> GetAllIDs(const NTPSnippet::PtrVector& snippets) { |
| 123 std::set<std::string> ids; | 124 std::set<std::string> ids; |
| 124 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) { | 125 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) { |
| 125 ids.insert(snippet->id()); | 126 ids.insert(snippet->id()); |
| 126 for (const SnippetSource& source : snippet->sources()) | 127 for (const SnippetSource& source : snippet->sources()) |
| 127 ids.insert(source.url.spec()); | 128 ids.insert(source.url.spec()); |
| 128 } | 129 } |
| 129 return ids; | 130 return ids; |
| 130 } | 131 } |
| 131 | 132 |
| 132 std::set<std::string> GetMainIDs(const NTPSnippet::PtrVector& snippets) { | 133 std::set<std::string> GetSnippetIDSet(const NTPSnippet::PtrVector& snippets) { |
| 133 std::set<std::string> ids; | 134 std::set<std::string> ids; |
| 134 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) | 135 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) |
| 135 ids.insert(snippet->id()); | 136 ids.insert(snippet->id()); |
| 136 return ids; | 137 return ids; |
| 137 } | 138 } |
| 138 | 139 |
| 140 std::unique_ptr<std::vector<std::string>> GetSnippetIDVector( |
| 141 const NTPSnippet::PtrVector& snippets) { |
| 142 auto result = base::MakeUnique<std::vector<std::string>>(); |
| 143 for (const auto& snippet : snippets) { |
| 144 result->push_back(snippet->id()); |
| 145 } |
| 146 return result; |
| 147 } |
| 148 |
| 139 bool IsSnippetInSet(const std::unique_ptr<NTPSnippet>& snippet, | 149 bool IsSnippetInSet(const std::unique_ptr<NTPSnippet>& snippet, |
| 140 const std::set<std::string>& ids, | 150 const std::set<std::string>& ids, |
| 141 bool match_all_ids) { | 151 bool match_all_ids) { |
| 142 if (ids.count(snippet->id())) | 152 if (ids.count(snippet->id())) |
| 143 return true; | 153 return true; |
| 144 if (!match_all_ids) | 154 if (!match_all_ids) |
| 145 return false; | 155 return false; |
| 146 for (const SnippetSource& source : snippet->sources()) { | 156 for (const SnippetSource& source : snippet->sources()) { |
| 147 if (ids.count(source.url.spec())) | 157 if (ids.count(source.url.spec())) |
| 148 return true; | 158 return true; |
| (...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 375 if (!initialized()) | 385 if (!initialized()) |
| 376 return; | 386 return; |
| 377 | 387 |
| 378 if (categories_.find(category) == categories_.end()) | 388 if (categories_.find(category) == categories_.end()) |
| 379 return; | 389 return; |
| 380 CategoryContent* content = &categories_[category]; | 390 CategoryContent* content = &categories_[category]; |
| 381 if (content->snippets.empty()) | 391 if (content->snippets.empty()) |
| 382 return; | 392 return; |
| 383 | 393 |
| 384 if (category == articles_category_) { | 394 if (category == articles_category_) { |
| 385 database_->DeleteSnippets(content->snippets); | 395 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
| 386 database_->DeleteImages(content->snippets); | 396 database_->DeleteImages(GetSnippetIDVector(content->snippets)); |
| 387 } | 397 } |
| 388 content->snippets.clear(); | 398 content->snippets.clear(); |
| 389 | 399 |
| 390 NotifyNewSuggestions(); | 400 NotifyNewSuggestions(); |
| 391 } | 401 } |
| 392 | 402 |
| 393 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( | 403 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( |
| 394 Category category, | 404 Category category, |
| 395 const DismissedSuggestionsCallback& callback) { | 405 const DismissedSuggestionsCallback& callback) { |
| 396 DCHECK(categories_.find(category) != categories_.end()); | 406 DCHECK(categories_.find(category) != categories_.end()); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 420 | 430 |
| 421 if (!initialized()) | 431 if (!initialized()) |
| 422 return; | 432 return; |
| 423 | 433 |
| 424 CategoryContent* content = &categories_[category]; | 434 CategoryContent* content = &categories_[category]; |
| 425 if (content->dismissed.empty()) | 435 if (content->dismissed.empty()) |
| 426 return; | 436 return; |
| 427 | 437 |
| 428 if (category == articles_category_) { | 438 if (category == articles_category_) { |
| 429 // The image got already deleted when the suggestion was dismissed. | 439 // The image got already deleted when the suggestion was dismissed. |
| 430 database_->DeleteSnippets(content->dismissed); | 440 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); |
| 431 } | 441 } |
| 432 content->dismissed.clear(); | 442 content->dismissed.clear(); |
| 433 } | 443 } |
| 434 | 444 |
| 435 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { | 445 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { |
| 436 // |suggestions_service_| can be null in tests. | 446 // |suggestions_service_| can be null in tests. |
| 437 if (!suggestions_service_) | 447 if (!suggestions_service_) |
| 438 return std::set<std::string>(); | 448 return std::set<std::string>(); |
| 439 | 449 |
| 440 // TODO(treib): This should just call GetSnippetHostsFromPrefs. | 450 // TODO(treib): This should just call GetSnippetHostsFromPrefs. |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 500 else | 510 else |
| 501 content->snippets.emplace_back(std::move(snippet)); | 511 content->snippets.emplace_back(std::move(snippet)); |
| 502 } | 512 } |
| 503 | 513 |
| 504 std::sort(content->snippets.begin(), content->snippets.end(), | 514 std::sort(content->snippets.begin(), content->snippets.end(), |
| 505 [](const std::unique_ptr<NTPSnippet>& lhs, | 515 [](const std::unique_ptr<NTPSnippet>& lhs, |
| 506 const std::unique_ptr<NTPSnippet>& rhs) { | 516 const std::unique_ptr<NTPSnippet>& rhs) { |
| 507 return lhs->score() > rhs->score(); | 517 return lhs->score() > rhs->score(); |
| 508 }); | 518 }); |
| 509 | 519 |
| 520 // TODO(tschumann): If I move ClearExpiredDismisedSnippets() to the beginning |
| 521 // of the function, it essentially does nothing but tests are still green. Fix |
| 522 // this! |
| 510 ClearExpiredDismissedSnippets(); | 523 ClearExpiredDismissedSnippets(); |
| 511 ClearOrphanedImages(); | 524 ClearOrphanedImages(); |
| 512 FinishInitialization(); | 525 FinishInitialization(); |
| 513 } | 526 } |
| 514 | 527 |
| 515 void NTPSnippetsService::OnDatabaseError() { | 528 void NTPSnippetsService::OnDatabaseError() { |
| 516 EnterState(State::ERROR_OCCURRED); | 529 EnterState(State::ERROR_OCCURRED); |
| 517 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | 530 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); |
| 518 } | 531 } |
| 519 | 532 |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 586 categories_[category].provided_by_server = true; | 599 categories_[category].provided_by_server = true; |
| 587 | 600 |
| 588 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); | 601 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); |
| 589 // TODO(sfiera): histograms for server categories. | 602 // TODO(sfiera): histograms for server categories. |
| 590 // Sparse histogram used because the number of snippets is small (bound by | 603 // Sparse histogram used because the number of snippets is small (bound by |
| 591 // kMaxSnippetCount). | 604 // kMaxSnippetCount). |
| 592 if (category == articles_category_) { | 605 if (category == articles_category_) { |
| 593 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", | 606 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", |
| 594 fetched_category.snippets.size()); | 607 fetched_category.snippets.size()); |
| 595 } | 608 } |
| 596 | |
| 597 ReplaceSnippets(category, std::move(fetched_category.snippets)); | 609 ReplaceSnippets(category, std::move(fetched_category.snippets)); |
| 598 } | 610 } |
| 599 } | 611 } |
| 600 | 612 |
| 601 for (const auto& item : categories_) { | 613 for (const auto& item : categories_) { |
| 602 Category category = item.first; | 614 Category category = item.first; |
| 603 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 615 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
| 604 } | 616 } |
| 605 | 617 |
| 606 // TODO(sfiera): equivalent metrics for non-articles. | 618 // TODO(sfiera): equivalent metrics for non-articles. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 622 if (snippets) | 634 if (snippets) |
| 623 RescheduleFetching(true); | 635 RescheduleFetching(true); |
| 624 } | 636 } |
| 625 | 637 |
| 626 void NTPSnippetsService::ArchiveSnippets(Category category, | 638 void NTPSnippetsService::ArchiveSnippets(Category category, |
| 627 NTPSnippet::PtrVector* to_archive) { | 639 NTPSnippet::PtrVector* to_archive) { |
| 628 CategoryContent* content = &categories_[category]; | 640 CategoryContent* content = &categories_[category]; |
| 629 | 641 |
| 630 // TODO(sfiera): handle DB for non-articles too. | 642 // TODO(sfiera): handle DB for non-articles too. |
| 631 if (category == articles_category_) { | 643 if (category == articles_category_) { |
| 632 database_->DeleteSnippets(*to_archive); | 644 database_->DeleteSnippets(GetSnippetIDVector(*to_archive)); |
| 633 // Do not delete the thumbnail images as they are still handy on open NTPs. | 645 // Do not delete the thumbnail images as they are still handy on open NTPs. |
| 634 } | 646 } |
| 635 | 647 |
| 636 // Archive previous snippets - move them at the beginning of the list. | 648 // Archive previous snippets - move them at the beginning of the list. |
| 637 content->archived.insert(content->archived.begin(), | 649 content->archived.insert(content->archived.begin(), |
| 638 std::make_move_iterator(to_archive->begin()), | 650 std::make_move_iterator(to_archive->begin()), |
| 639 std::make_move_iterator(to_archive->end())); | 651 std::make_move_iterator(to_archive->end())); |
| 640 Compact(to_archive); | 652 Compact(to_archive); |
| 641 | 653 |
| 642 // If there are more archived snippets than we want to keep, delete the | 654 // If there are more archived snippets than we want to keep, delete the |
| 643 // oldest ones by their fetch time (which are always in the back). | 655 // oldest ones by their fetch time (which are always in the back). |
| 644 if (content->archived.size() > kMaxArchivedSnippetCount) { | 656 if (content->archived.size() > kMaxArchivedSnippetCount) { |
| 645 NTPSnippet::PtrVector to_delete( | 657 NTPSnippet::PtrVector to_delete( |
| 646 std::make_move_iterator(content->archived.begin() + | 658 std::make_move_iterator(content->archived.begin() + |
| 647 kMaxArchivedSnippetCount), | 659 kMaxArchivedSnippetCount), |
| 648 std::make_move_iterator(content->archived.end())); | 660 std::make_move_iterator(content->archived.end())); |
| 649 content->archived.resize(kMaxArchivedSnippetCount); | 661 content->archived.resize(kMaxArchivedSnippetCount); |
| 650 if (category == articles_category_) | 662 if (category == articles_category_) |
| 651 database_->DeleteImages(to_delete); | 663 database_->DeleteImages(GetSnippetIDVector(to_delete)); |
| 652 } | 664 } |
| 653 } | 665 } |
| 654 | 666 |
| 655 void NTPSnippetsService::ReplaceSnippets(Category category, | 667 void NTPSnippetsService::ReplaceSnippets(Category category, |
| 656 NTPSnippet::PtrVector new_snippets) { | 668 NTPSnippet::PtrVector new_snippets) { |
| 657 DCHECK(ready()); | 669 DCHECK(ready()); |
| 658 CategoryContent* content = &categories_[category]; | 670 CategoryContent* content = &categories_[category]; |
| 659 | 671 |
| 660 // Remove new snippets that have been dismissed. | 672 // Remove new snippets that have been dismissed. |
| 661 EraseMatchingSnippets(&new_snippets, GetAllIDs(content->dismissed), | 673 EraseMatchingSnippets(&new_snippets, GetAllIDs(content->dismissed), |
| (...skipping 29 matching lines...) Expand all Loading... |
| 691 num_snippets_dismissed); | 703 num_snippets_dismissed); |
| 692 } | 704 } |
| 693 } | 705 } |
| 694 | 706 |
| 695 // Do not touch the current set of snippets if the newly fetched one is empty. | 707 // Do not touch the current set of snippets if the newly fetched one is empty. |
| 696 if (new_snippets.empty()) | 708 if (new_snippets.empty()) |
| 697 return; | 709 return; |
| 698 | 710 |
| 699 // Remove current snippets that have been fetched again. We do not need to | 711 // Remove current snippets that have been fetched again. We do not need to |
| 700 // archive those as they will be in the new current set. | 712 // archive those as they will be in the new current set. |
| 701 EraseMatchingSnippets(&content->snippets, GetMainIDs(new_snippets), | 713 EraseMatchingSnippets(&content->snippets, GetSnippetIDSet(new_snippets), |
| 702 /*match_all_ids=*/false); | 714 /*match_all_ids=*/false); |
| 703 | 715 |
| 704 ArchiveSnippets(category, &content->snippets); | 716 ArchiveSnippets(category, &content->snippets); |
| 705 | 717 |
| 706 // TODO(sfiera): handle DB for non-articles too. | 718 // TODO(sfiera): handle DB for non-articles too. |
| 707 if (category == articles_category_) { | 719 if (category == articles_category_) { |
| 708 // Save new articles to the DB. | 720 // Save new articles to the DB. |
| 709 database_->SaveSnippets(new_snippets); | 721 database_->SaveSnippets(new_snippets); |
| 710 } | 722 } |
| 711 | 723 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 745 // Move expired dismissed snippets over into |to_delete|. | 757 // Move expired dismissed snippets over into |to_delete|. |
| 746 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) { | 758 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) { |
| 747 if (snippet->expiry_date() <= now) | 759 if (snippet->expiry_date() <= now) |
| 748 to_delete.emplace_back(std::move(snippet)); | 760 to_delete.emplace_back(std::move(snippet)); |
| 749 } | 761 } |
| 750 Compact(&content->dismissed); | 762 Compact(&content->dismissed); |
| 751 | 763 |
| 752 // Delete the removed article suggestions from the DB. | 764 // Delete the removed article suggestions from the DB. |
| 753 if (category == articles_category_) { | 765 if (category == articles_category_) { |
| 754 // The image got already deleted when the suggestion was dismissed. | 766 // The image got already deleted when the suggestion was dismissed. |
| 755 database_->DeleteSnippets(to_delete); | 767 database_->DeleteSnippets(GetSnippetIDVector(to_delete)); |
| 756 } | 768 } |
| 757 | 769 |
| 758 if (content->snippets.empty() && content->dismissed.empty() && | 770 if (content->snippets.empty() && content->dismissed.empty() && |
| 759 category != articles_category_ && !content->provided_by_server) { | 771 category != articles_category_ && !content->provided_by_server) { |
| 760 categories_to_erase.push_back(category); | 772 categories_to_erase.push_back(category); |
| 761 } | 773 } |
| 762 } | 774 } |
| 763 | 775 |
| 764 for (Category category : categories_to_erase) { | 776 for (Category category : categories_to_erase) { |
| 765 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 777 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
| 766 categories_.erase(category); | 778 categories_.erase(category); |
| 767 } | 779 } |
| 768 } | 780 } |
| 769 | 781 |
| 770 void NTPSnippetsService::ClearOrphanedImages() { | 782 void NTPSnippetsService::ClearOrphanedImages() { |
| 771 // TODO(jkrcal): Implement. crbug.com/649009 | 783 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); |
| 784 for (const auto& snippet_ptr : categories_[articles_category_].snippets) { |
| 785 alive_snippets->insert(snippet_ptr->id()); |
| 786 } |
| 787 for (const auto& snippet_ptr : categories_[articles_category_].dismissed) { |
| 788 alive_snippets->insert(snippet_ptr->id()); |
| 789 } |
| 790 database_->GarbageCollectImages(std::move(alive_snippets)); |
| 772 } | 791 } |
| 773 | 792 |
| 774 void NTPSnippetsService::NukeAllSnippets() { | 793 void NTPSnippetsService::NukeAllSnippets() { |
| 775 std::vector<Category> categories_to_erase; | 794 std::vector<Category> categories_to_erase; |
| 776 | 795 |
| 777 // Empty the ARTICLES category and remove all others, since they may or may | 796 // Empty the ARTICLES category and remove all others, since they may or may |
| 778 // not be personalized. | 797 // not be personalized. |
| 779 for (const auto& item : categories_) { | 798 for (const auto& item : categories_) { |
| 780 Category category = item.first; | 799 Category category = item.first; |
| 781 | 800 |
| (...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1084 } | 1103 } |
| 1085 | 1104 |
| 1086 NTPSnippetsService::CategoryContent::CategoryContent() = default; | 1105 NTPSnippetsService::CategoryContent::CategoryContent() = default; |
| 1087 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = | 1106 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = |
| 1088 default; | 1107 default; |
| 1089 NTPSnippetsService::CategoryContent::~CategoryContent() = default; | 1108 NTPSnippetsService::CategoryContent::~CategoryContent() = default; |
| 1090 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: | 1109 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: |
| 1091 operator=(CategoryContent&&) = default; | 1110 operator=(CategoryContent&&) = default; |
| 1092 | 1111 |
| 1093 } // namespace ntp_snippets | 1112 } // namespace ntp_snippets |
| OLD | NEW |