| 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 366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 377 void NTPSnippetsService::ClearCachedSuggestions(Category category) { | 377 void NTPSnippetsService::ClearCachedSuggestions(Category category) { |
| 378 if (!initialized()) | 378 if (!initialized()) |
| 379 return; | 379 return; |
| 380 | 380 |
| 381 if (!base::ContainsKey(categories_, category)) | 381 if (!base::ContainsKey(categories_, category)) |
| 382 return; | 382 return; |
| 383 CategoryContent* content = &categories_[category]; | 383 CategoryContent* content = &categories_[category]; |
| 384 if (content->snippets.empty()) | 384 if (content->snippets.empty()) |
| 385 return; | 385 return; |
| 386 | 386 |
| 387 if (category == articles_category_) { | 387 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
| 388 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); | 388 database_->DeleteImages(GetSnippetIDVector(content->snippets)); |
| 389 database_->DeleteImages(GetSnippetIDVector(content->snippets)); | |
| 390 } | |
| 391 content->snippets.clear(); | 389 content->snippets.clear(); |
| 392 | 390 |
| 393 NotifyNewSuggestions(); | 391 NotifyNewSuggestions(); |
| 394 } | 392 } |
| 395 | 393 |
| 396 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( | 394 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( |
| 397 Category category, | 395 Category category, |
| 398 const DismissedSuggestionsCallback& callback) { | 396 const DismissedSuggestionsCallback& callback) { |
| 399 DCHECK(base::ContainsKey(categories_, category)); | 397 DCHECK(base::ContainsKey(categories_, category)); |
| 400 | 398 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 421 Category category) { | 419 Category category) { |
| 422 DCHECK(base::ContainsKey(categories_, category)); | 420 DCHECK(base::ContainsKey(categories_, category)); |
| 423 | 421 |
| 424 if (!initialized()) | 422 if (!initialized()) |
| 425 return; | 423 return; |
| 426 | 424 |
| 427 CategoryContent* content = &categories_[category]; | 425 CategoryContent* content = &categories_[category]; |
| 428 if (content->dismissed.empty()) | 426 if (content->dismissed.empty()) |
| 429 return; | 427 return; |
| 430 | 428 |
| 431 if (category == articles_category_) { | 429 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); |
| 432 // The image got already deleted when the suggestion was dismissed. | 430 // The image got already deleted when the suggestion was dismissed. |
| 433 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); | 431 |
| 434 } | |
| 435 content->dismissed.clear(); | 432 content->dismissed.clear(); |
| 436 } | 433 } |
| 437 | 434 |
| 438 // static | 435 // static |
| 439 int NTPSnippetsService::GetMaxSnippetCountForTesting() { | 436 int NTPSnippetsService::GetMaxSnippetCountForTesting() { |
| 440 return kMaxSnippetCount; | 437 return kMaxSnippetCount; |
| 441 } | 438 } |
| 442 | 439 |
| 443 //////////////////////////////////////////////////////////////////////////////// | 440 //////////////////////////////////////////////////////////////////////////////// |
| 444 // Private methods | 441 // Private methods |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 477 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()). | 474 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()). |
| 478 database_->SaveImage(id_within_category, image_data); | 475 database_->SaveImage(id_within_category, image_data); |
| 479 } | 476 } |
| 480 | 477 |
| 481 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { | 478 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { |
| 482 if (state_ == State::ERROR_OCCURRED) | 479 if (state_ == State::ERROR_OCCURRED) |
| 483 return; | 480 return; |
| 484 DCHECK(state_ == State::NOT_INITED); | 481 DCHECK(state_ == State::NOT_INITED); |
| 485 DCHECK(base::ContainsKey(categories_, articles_category_)); | 482 DCHECK(base::ContainsKey(categories_, articles_category_)); |
| 486 | 483 |
| 487 // TODO(treib): Support non-article categories in database. crbug.com/653476 | 484 NTPSnippet::PtrVector to_delete; |
| 488 CategoryContent* content = &categories_[articles_category_]; | |
| 489 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { | 485 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { |
| 486 Category snippet_category = |
| 487 category_factory()->FromRemoteCategory(snippet->remote_category_id()); |
| 488 // We should already know about the category. |
| 489 if (!base::ContainsKey(categories_, snippet_category)) { |
| 490 DLOG(WARNING) << "Loaded a suggestion for unknown category " |
| 491 << snippet_category << " from the DB; deleting"; |
| 492 to_delete.emplace_back(std::move(snippet)); |
| 493 continue; |
| 494 } |
| 495 |
| 496 CategoryContent* content = &categories_[snippet_category]; |
| 490 if (snippet->is_dismissed()) | 497 if (snippet->is_dismissed()) |
| 491 content->dismissed.emplace_back(std::move(snippet)); | 498 content->dismissed.emplace_back(std::move(snippet)); |
| 492 else | 499 else |
| 493 content->snippets.emplace_back(std::move(snippet)); | 500 content->snippets.emplace_back(std::move(snippet)); |
| 494 } | 501 } |
| 502 if (!to_delete.empty()) { |
| 503 database_->DeleteSnippets(GetSnippetIDVector(to_delete)); |
| 504 database_->DeleteImages(GetSnippetIDVector(to_delete)); |
| 505 } |
| 495 | 506 |
| 496 std::sort(content->snippets.begin(), content->snippets.end(), | 507 // Sort the suggestions in each category. |
| 497 [](const std::unique_ptr<NTPSnippet>& lhs, | 508 // TODO(treib): Persist the actual order in the DB somehow? crbug.com/654409 |
| 498 const std::unique_ptr<NTPSnippet>& rhs) { | 509 for (auto& entry : categories_) { |
| 499 return lhs->score() > rhs->score(); | 510 CategoryContent* content = &entry.second; |
| 500 }); | 511 std::sort(content->snippets.begin(), content->snippets.end(), |
| 512 [](const std::unique_ptr<NTPSnippet>& lhs, |
| 513 const std::unique_ptr<NTPSnippet>& rhs) { |
| 514 return lhs->score() > rhs->score(); |
| 515 }); |
| 516 } |
| 501 | 517 |
| 502 // TODO(tschumann): If I move ClearExpiredDismisedSnippets() to the beginning | 518 // TODO(tschumann): If I move ClearExpiredDismisedSnippets() to the beginning |
| 503 // of the function, it essentially does nothing but tests are still green. Fix | 519 // of the function, it essentially does nothing but tests are still green. Fix |
| 504 // this! | 520 // this! |
| 505 ClearExpiredDismissedSnippets(); | 521 ClearExpiredDismissedSnippets(); |
| 506 ClearOrphanedImages(); | 522 ClearOrphanedImages(); |
| 507 FinishInitialization(); | 523 FinishInitialization(); |
| 508 } | 524 } |
| 509 | 525 |
| 510 void NTPSnippetsService::OnDatabaseError() { | 526 void NTPSnippetsService::OnDatabaseError() { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 535 // TODO(jkrcal): A bit hard to understand with so many variables called | 551 // TODO(jkrcal): A bit hard to understand with so many variables called |
| 536 // "*categor*". Isn't here some room for simplification? | 552 // "*categor*". Isn't here some room for simplification? |
| 537 for (NTPSnippetsFetcher::FetchedCategory& fetched_category : | 553 for (NTPSnippetsFetcher::FetchedCategory& fetched_category : |
| 538 *fetched_categories) { | 554 *fetched_categories) { |
| 539 Category category = fetched_category.category; | 555 Category category = fetched_category.category; |
| 540 | 556 |
| 541 // The ChromeReader backend doesn't provide category titles, so don't | 557 // The ChromeReader backend doesn't provide category titles, so don't |
| 542 // overwrite the existing title for ARTICLES if the new one is empty. | 558 // overwrite the existing title for ARTICLES if the new one is empty. |
| 543 // TODO(treib): Remove this check after we fully switch to the content | 559 // TODO(treib): Remove this check after we fully switch to the content |
| 544 // suggestions backend. | 560 // suggestions backend. |
| 545 // TODO(sfiera): Avoid hard-coding articles category checks in so many | |
| 546 // places. | |
| 547 if (category != articles_category_ || | 561 if (category != articles_category_ || |
| 548 !fetched_category.localized_title.empty()) { | 562 !fetched_category.localized_title.empty()) { |
| 549 categories_[category].localized_title = | 563 categories_[category].localized_title = |
| 550 fetched_category.localized_title; | 564 fetched_category.localized_title; |
| 551 } | 565 } |
| 552 categories_[category].provided_by_server = true; | 566 categories_[category].provided_by_server = true; |
| 553 | 567 |
| 554 // TODO(tschumann): Remove this histogram once we only talk to the content | 568 // TODO(tschumann): Remove this histogram once we only talk to the content |
| 555 // suggestions cloud backend. | 569 // suggestions cloud backend. |
| 556 if (category == articles_category_) { | 570 if (category == articles_category_) { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 589 // succeeded, and also that we don't do a background fetch immediately after | 603 // succeeded, and also that we don't do a background fetch immediately after |
| 590 // a user-initiated one. | 604 // a user-initiated one. |
| 591 if (fetched_categories) | 605 if (fetched_categories) |
| 592 RescheduleFetching(true); | 606 RescheduleFetching(true); |
| 593 } | 607 } |
| 594 | 608 |
| 595 void NTPSnippetsService::ArchiveSnippets(Category category, | 609 void NTPSnippetsService::ArchiveSnippets(Category category, |
| 596 NTPSnippet::PtrVector* to_archive) { | 610 NTPSnippet::PtrVector* to_archive) { |
| 597 CategoryContent* content = &categories_[category]; | 611 CategoryContent* content = &categories_[category]; |
| 598 | 612 |
| 599 // TODO(treib): Handle DB for non-articles too. crbug.com/653476 | 613 database_->DeleteSnippets(GetSnippetIDVector(*to_archive)); |
| 600 if (category == articles_category_) { | 614 // Do not delete the thumbnail images as they are still handy on open NTPs. |
| 601 database_->DeleteSnippets(GetSnippetIDVector(*to_archive)); | |
| 602 // Do not delete the thumbnail images as they are still handy on open NTPs. | |
| 603 } | |
| 604 | 615 |
| 605 // Archive previous snippets - move them at the beginning of the list. | 616 // Archive previous snippets - move them at the beginning of the list. |
| 606 content->archived.insert(content->archived.begin(), | 617 content->archived.insert(content->archived.begin(), |
| 607 std::make_move_iterator(to_archive->begin()), | 618 std::make_move_iterator(to_archive->begin()), |
| 608 std::make_move_iterator(to_archive->end())); | 619 std::make_move_iterator(to_archive->end())); |
| 609 Compact(to_archive); | 620 Compact(to_archive); |
| 610 | 621 |
| 611 // If there are more archived snippets than we want to keep, delete the | 622 // If there are more archived snippets than we want to keep, delete the |
| 612 // oldest ones by their fetch time (which are always in the back). | 623 // oldest ones by their fetch time (which are always in the back). |
| 613 if (content->archived.size() > kMaxArchivedSnippetCount) { | 624 if (content->archived.size() > kMaxArchivedSnippetCount) { |
| 614 NTPSnippet::PtrVector to_delete( | 625 NTPSnippet::PtrVector to_delete( |
| 615 std::make_move_iterator(content->archived.begin() + | 626 std::make_move_iterator(content->archived.begin() + |
| 616 kMaxArchivedSnippetCount), | 627 kMaxArchivedSnippetCount), |
| 617 std::make_move_iterator(content->archived.end())); | 628 std::make_move_iterator(content->archived.end())); |
| 618 content->archived.resize(kMaxArchivedSnippetCount); | 629 content->archived.resize(kMaxArchivedSnippetCount); |
| 619 if (category == articles_category_) | 630 database_->DeleteImages(GetSnippetIDVector(to_delete)); |
| 620 database_->DeleteImages(GetSnippetIDVector(to_delete)); | |
| 621 } | 631 } |
| 622 } | 632 } |
| 623 | 633 |
| 624 void NTPSnippetsService::ReplaceSnippets(Category category, | 634 void NTPSnippetsService::ReplaceSnippets(Category category, |
| 625 NTPSnippet::PtrVector new_snippets) { | 635 NTPSnippet::PtrVector new_snippets) { |
| 626 DCHECK(ready()); | 636 DCHECK(ready()); |
| 627 CategoryContent* content = &categories_[category]; | 637 CategoryContent* content = &categories_[category]; |
| 628 | 638 |
| 629 // Remove new snippets that have been dismissed. | 639 // Remove new snippets that have been dismissed. |
| 630 EraseMatchingSnippets(&new_snippets, GetAllIDs(content->dismissed), | 640 EraseMatchingSnippets(&new_snippets, GetAllIDs(content->dismissed), |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 665 if (new_snippets.empty()) | 675 if (new_snippets.empty()) |
| 666 return; | 676 return; |
| 667 | 677 |
| 668 // Remove current snippets that have been fetched again. We do not need to | 678 // Remove current snippets that have been fetched again. We do not need to |
| 669 // archive those as they will be in the new current set. | 679 // archive those as they will be in the new current set. |
| 670 EraseMatchingSnippets(&content->snippets, GetSnippetIDSet(new_snippets), | 680 EraseMatchingSnippets(&content->snippets, GetSnippetIDSet(new_snippets), |
| 671 /*match_all_ids=*/false); | 681 /*match_all_ids=*/false); |
| 672 | 682 |
| 673 ArchiveSnippets(category, &content->snippets); | 683 ArchiveSnippets(category, &content->snippets); |
| 674 | 684 |
| 675 // TODO(sfiera): handle DB for non-articles too. | 685 // Save new articles to the DB. |
| 676 if (category == articles_category_) { | 686 database_->SaveSnippets(new_snippets); |
| 677 // Save new articles to the DB. | |
| 678 database_->SaveSnippets(new_snippets); | |
| 679 } | |
| 680 | 687 |
| 681 content->snippets = std::move(new_snippets); | 688 content->snippets = std::move(new_snippets); |
| 682 } | 689 } |
| 683 | 690 |
| 684 void NTPSnippetsService::ClearExpiredDismissedSnippets() { | 691 void NTPSnippetsService::ClearExpiredDismissedSnippets() { |
| 685 std::vector<Category> categories_to_erase; | 692 std::vector<Category> categories_to_erase; |
| 686 | 693 |
| 687 const base::Time now = base::Time::Now(); | 694 const base::Time now = base::Time::Now(); |
| 688 | 695 |
| 689 for (auto& item : categories_) { | 696 for (auto& item : categories_) { |
| 690 Category category = item.first; | 697 Category category = item.first; |
| 691 CategoryContent* content = &item.second; | 698 CategoryContent* content = &item.second; |
| 692 | 699 |
| 693 NTPSnippet::PtrVector to_delete; | 700 NTPSnippet::PtrVector to_delete; |
| 694 // Move expired dismissed snippets over into |to_delete|. | 701 // Move expired dismissed snippets over into |to_delete|. |
| 695 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) { | 702 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) { |
| 696 if (snippet->expiry_date() <= now) | 703 if (snippet->expiry_date() <= now) |
| 697 to_delete.emplace_back(std::move(snippet)); | 704 to_delete.emplace_back(std::move(snippet)); |
| 698 } | 705 } |
| 699 Compact(&content->dismissed); | 706 Compact(&content->dismissed); |
| 700 | 707 |
| 701 // Delete the removed article suggestions from the DB. | 708 // Delete the removed article suggestions from the DB. |
| 702 if (category == articles_category_) { | 709 database_->DeleteSnippets(GetSnippetIDVector(to_delete)); |
| 703 // The image got already deleted when the suggestion was dismissed. | 710 // The image got already deleted when the suggestion was dismissed. |
| 704 database_->DeleteSnippets(GetSnippetIDVector(to_delete)); | |
| 705 } | |
| 706 | 711 |
| 707 if (content->snippets.empty() && content->dismissed.empty() && | 712 if (content->snippets.empty() && content->dismissed.empty() && |
| 708 category != articles_category_ && !content->provided_by_server) { | 713 category != articles_category_ && !content->provided_by_server) { |
| 709 categories_to_erase.push_back(category); | 714 categories_to_erase.push_back(category); |
| 710 } | 715 } |
| 711 } | 716 } |
| 712 | 717 |
| 713 for (Category category : categories_to_erase) { | 718 for (Category category : categories_to_erase) { |
| 714 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 719 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
| 715 categories_.erase(category); | 720 categories_.erase(category); |
| 716 } | 721 } |
| 717 | 722 |
| 718 StoreCategoriesToPrefs(); | 723 StoreCategoriesToPrefs(); |
| 719 } | 724 } |
| 720 | 725 |
| 721 void NTPSnippetsService::ClearOrphanedImages() { | 726 void NTPSnippetsService::ClearOrphanedImages() { |
| 722 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); | 727 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); |
| 723 for (const auto& snippet_ptr : categories_[articles_category_].snippets) { | 728 for (const auto& entry : categories_) { |
| 724 alive_snippets->insert(snippet_ptr->id()); | 729 const CategoryContent& content = entry.second; |
| 725 } | 730 for (const auto& snippet_ptr : content.snippets) { |
| 726 for (const auto& snippet_ptr : categories_[articles_category_].dismissed) { | 731 alive_snippets->insert(snippet_ptr->id()); |
| 727 alive_snippets->insert(snippet_ptr->id()); | 732 } |
| 733 for (const auto& snippet_ptr : content.dismissed) { |
| 734 alive_snippets->insert(snippet_ptr->id()); |
| 735 } |
| 728 } | 736 } |
| 729 database_->GarbageCollectImages(std::move(alive_snippets)); | 737 database_->GarbageCollectImages(std::move(alive_snippets)); |
| 730 } | 738 } |
| 731 | 739 |
| 732 void NTPSnippetsService::NukeAllSnippets() { | 740 void NTPSnippetsService::NukeAllSnippets() { |
| 733 std::vector<Category> categories_to_erase; | 741 std::vector<Category> categories_to_erase; |
| 734 | 742 |
| 735 // Empty the ARTICLES category and remove all others, since they may or may | 743 // Empty the ARTICLES category and remove all others, since they may or may |
| 736 // not be personalized. | 744 // not be personalized. |
| 737 for (const auto& item : categories_) { | 745 for (const auto& item : categories_) { |
| (...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1101 } | 1109 } |
| 1102 | 1110 |
| 1103 NTPSnippetsService::CategoryContent::CategoryContent() = default; | 1111 NTPSnippetsService::CategoryContent::CategoryContent() = default; |
| 1104 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = | 1112 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = |
| 1105 default; | 1113 default; |
| 1106 NTPSnippetsService::CategoryContent::~CategoryContent() = default; | 1114 NTPSnippetsService::CategoryContent::~CategoryContent() = default; |
| 1107 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: | 1115 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: |
| 1108 operator=(CategoryContent&&) = default; | 1116 operator=(CategoryContent&&) = default; |
| 1109 | 1117 |
| 1110 } // namespace ntp_snippets | 1118 } // namespace ntp_snippets |
| OLD | NEW |