| 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/ntp_snippets_service.h" | 5 #include "components/ntp_snippets/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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 using suggestions::SuggestionsService; | 43 using suggestions::SuggestionsService; |
| 44 | 44 |
| 45 namespace ntp_snippets { | 45 namespace ntp_snippets { |
| 46 | 46 |
| 47 namespace { | 47 namespace { |
| 48 | 48 |
| 49 // Number of snippets requested to the server. Consider replacing sparse UMA | 49 // Number of snippets requested to the server. Consider replacing sparse UMA |
| 50 // histograms with COUNTS() if this number increases beyond 50. | 50 // histograms with COUNTS() if this number increases beyond 50. |
| 51 const int kMaxSnippetCount = 10; | 51 const int kMaxSnippetCount = 10; |
| 52 | 52 |
| 53 // Number of archived snippets we keep around in memory. |
| 54 const int kMaxArchivedSnippetCount = 200; |
| 55 |
| 53 // Default values for snippets fetching intervals - once per day only. | 56 // Default values for snippets fetching intervals - once per day only. |
| 54 const int kDefaultFetchingIntervalWifiChargingSeconds = 0; | 57 const int kDefaultFetchingIntervalWifiChargingSeconds = 0; |
| 55 const int kDefaultFetchingIntervalWifiSeconds = 0; | 58 const int kDefaultFetchingIntervalWifiSeconds = 0; |
| 56 const int kDefaultFetchingIntervalFallbackSeconds = 24 * 60 * 60; | 59 const int kDefaultFetchingIntervalFallbackSeconds = 24 * 60 * 60; |
| 57 | 60 |
| 58 // Variation parameters than can override the default fetching intervals. | 61 // Variation parameters than can override the default fetching intervals. |
| 59 const char kFetchingIntervalWifiChargingParamName[] = | 62 const char kFetchingIntervalWifiChargingParamName[] = |
| 60 "fetching_interval_wifi_charging_seconds"; | 63 "fetching_interval_wifi_charging_seconds"; |
| 61 const char kFetchingIntervalWifiParamName[] = | 64 const char kFetchingIntervalWifiParamName[] = |
| 62 "fetching_interval_wifi_seconds"; | 65 "fetching_interval_wifi_seconds"; |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 | 173 |
| 171 void InsertAllIDs(const NTPSnippet::PtrVector& snippets, | 174 void InsertAllIDs(const NTPSnippet::PtrVector& snippets, |
| 172 std::set<std::string>* ids) { | 175 std::set<std::string>* ids) { |
| 173 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) { | 176 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) { |
| 174 ids->insert(snippet->id()); | 177 ids->insert(snippet->id()); |
| 175 for (const SnippetSource& source : snippet->sources()) | 178 for (const SnippetSource& source : snippet->sources()) |
| 176 ids->insert(source.url.spec()); | 179 ids->insert(source.url.spec()); |
| 177 } | 180 } |
| 178 } | 181 } |
| 179 | 182 |
| 183 bool IsSnippetInSet(const std::unique_ptr<NTPSnippet>& snippet, |
| 184 std::set<std::string> ids) { |
| 185 if (ids.count(snippet->id())) |
| 186 return true; |
| 187 for (const SnippetSource& source : snippet->sources()) { |
| 188 if (ids.count(source.url.spec())) |
| 189 return true; |
| 190 } |
| 191 return false; |
| 192 } |
| 193 |
| 194 void FilterSnippets(NTPSnippet::PtrVector* all_snippets, |
| 195 const NTPSnippet::PtrVector& snippets_to_filter_out) { |
| 196 std::set<std::string> filtered_snippet_ids; |
| 197 InsertAllIDs(snippets_to_filter_out, &filtered_snippet_ids); |
| 198 all_snippets->erase( |
| 199 std::remove_if( |
| 200 all_snippets->begin(), all_snippets->end(), |
| 201 [&filtered_snippet_ids](const std::unique_ptr<NTPSnippet>& snippet) { |
| 202 return IsSnippetInSet(snippet, filtered_snippet_ids); |
| 203 }), |
| 204 all_snippets->end()); |
| 205 } |
| 206 |
| 180 void Compact(NTPSnippet::PtrVector* snippets) { | 207 void Compact(NTPSnippet::PtrVector* snippets) { |
| 181 snippets->erase( | 208 snippets->erase( |
| 182 std::remove_if( | 209 std::remove_if( |
| 183 snippets->begin(), snippets->end(), | 210 snippets->begin(), snippets->end(), |
| 184 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), | 211 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), |
| 185 snippets->end()); | 212 snippets->end()); |
| 186 } | 213 } |
| 187 | 214 |
| 188 } // namespace | 215 } // namespace |
| 189 | 216 |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 427 } | 454 } |
| 428 | 455 |
| 429 // static | 456 // static |
| 430 int NTPSnippetsService::GetMaxSnippetCountForTesting() { | 457 int NTPSnippetsService::GetMaxSnippetCountForTesting() { |
| 431 return kMaxSnippetCount; | 458 return kMaxSnippetCount; |
| 432 } | 459 } |
| 433 | 460 |
| 434 //////////////////////////////////////////////////////////////////////////////// | 461 //////////////////////////////////////////////////////////////////////////////// |
| 435 // Private methods | 462 // Private methods |
| 436 | 463 |
| 464 GURL NTPSnippetsService::FindSnippetImageUrl( |
| 465 Category category, |
| 466 const std::string& snippet_id) const { |
| 467 DCHECK(categories_.find(category) != categories_.end()); |
| 468 |
| 469 const CategoryContent& content = categories_.at(category); |
| 470 // Search for the snippet in current and archived snippets. |
| 471 auto it = |
| 472 std::find_if(content.snippets.begin(), content.snippets.end(), |
| 473 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { |
| 474 return snippet->id() == snippet_id; |
| 475 }); |
| 476 if (it != content.snippets.end()) |
| 477 return it->get()->salient_image_url(); |
| 478 |
| 479 it = std::find_if(content.archived.begin(), content.archived.end(), |
| 480 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { |
| 481 return snippet->id() == snippet_id; |
| 482 }); |
| 483 if (it != content.archived.end()) |
| 484 return it->get()->salient_image_url(); |
| 485 |
| 486 return GURL(); |
| 487 } |
| 488 |
| 437 // image_fetcher::ImageFetcherDelegate implementation. | 489 // image_fetcher::ImageFetcherDelegate implementation. |
| 438 void NTPSnippetsService::OnImageDataFetched(const std::string& suggestion_id, | 490 void NTPSnippetsService::OnImageDataFetched(const std::string& suggestion_id, |
| 439 const std::string& image_data) { | 491 const std::string& image_data) { |
| 440 if (image_data.empty()) | 492 if (image_data.empty()) |
| 441 return; | 493 return; |
| 442 | 494 |
| 443 Category category = GetCategoryFromUniqueID(suggestion_id); | 495 Category category = GetCategoryFromUniqueID(suggestion_id); |
| 444 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | 496 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); |
| 445 | 497 |
| 446 auto category_it = categories_.find(category); | 498 if (categories_.find(category) == categories_.end()) |
| 447 if (category_it == categories_.end()) | |
| 448 return; | 499 return; |
| 449 | 500 |
| 450 const CategoryContent& content = category_it->second; | |
| 451 | |
| 452 // Only save the image if the corresponding snippet still exists. | 501 // Only save the image if the corresponding snippet still exists. |
| 453 auto it = | 502 if (FindSnippetImageUrl(category, snippet_id).is_empty()) |
| 454 std::find_if(content.snippets.begin(), content.snippets.end(), | |
| 455 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | |
| 456 return snippet->id() == snippet_id; | |
| 457 }); | |
| 458 if (it == content.snippets.end()) | |
| 459 return; | 503 return; |
| 460 | 504 |
| 505 // Only cache the data in the DB, the actual serving is done in the callback |
| 506 // provided to image_fetcher_ (OnSnippetImageDecodedFromNetwork()). |
| 461 database_->SaveImage(snippet_id, image_data); | 507 database_->SaveImage(snippet_id, image_data); |
| 462 } | 508 } |
| 463 | 509 |
| 464 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { | 510 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { |
| 465 if (state_ == State::ERROR_OCCURRED) | 511 if (state_ == State::ERROR_OCCURRED) |
| 466 return; | 512 return; |
| 467 DCHECK(state_ == State::NOT_INITED); | 513 DCHECK(state_ == State::NOT_INITED); |
| 468 DCHECK(categories_.size() == 1); // Only articles category, so far. | 514 DCHECK_EQ(1u, categories_.size()); // Only articles category, so far. |
| 469 DCHECK(categories_.find(articles_category_) != categories_.end()); | 515 DCHECK(categories_.find(articles_category_) != categories_.end()); |
| 470 | 516 |
| 471 // TODO(sfiera): support non-article categories in database. | 517 // TODO(sfiera): support non-article categories in database. |
| 472 CategoryContent* content = &categories_[articles_category_]; | 518 CategoryContent* content = &categories_[articles_category_]; |
| 473 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { | 519 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { |
| 474 if (snippet->is_dismissed()) | 520 if (snippet->is_dismissed()) |
| 475 content->dismissed.emplace_back(std::move(snippet)); | 521 content->dismissed.emplace_back(std::move(snippet)); |
| 476 else | 522 else |
| 477 content->snippets.emplace_back(std::move(snippet)); | 523 content->snippets.emplace_back(std::move(snippet)); |
| 478 } | 524 } |
| 479 | 525 |
| 480 std::sort(content->snippets.begin(), content->snippets.end(), | 526 std::sort(content->snippets.begin(), content->snippets.end(), |
| 481 [](const std::unique_ptr<NTPSnippet>& lhs, | 527 [](const std::unique_ptr<NTPSnippet>& lhs, |
| 482 const std::unique_ptr<NTPSnippet>& rhs) { | 528 const std::unique_ptr<NTPSnippet>& rhs) { |
| 483 return lhs->score() > rhs->score(); | 529 return lhs->score() > rhs->score(); |
| 484 }); | 530 }); |
| 485 | 531 |
| 486 ClearExpiredSnippets(); | 532 ClearExpiredSnippets(); |
| 533 ClearOrphanedImages(); |
| 487 FinishInitialization(); | 534 FinishInitialization(); |
| 488 } | 535 } |
| 489 | 536 |
| 490 void NTPSnippetsService::OnDatabaseError() { | 537 void NTPSnippetsService::OnDatabaseError() { |
| 491 EnterState(State::ERROR_OCCURRED); | 538 EnterState(State::ERROR_OCCURRED); |
| 492 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | 539 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); |
| 493 } | 540 } |
| 494 | 541 |
| 495 // TODO(dgn): name clash between content suggestions and suggestions hosts. | 542 // TODO(dgn): name clash between content suggestions and suggestions hosts. |
| 496 // method name should be changed. | 543 // method name should be changed. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 void NTPSnippetsService::OnFetchFinished( | 581 void NTPSnippetsService::OnFetchFinished( |
| 535 NTPSnippetsFetcher::OptionalSnippets snippets) { | 582 NTPSnippetsFetcher::OptionalSnippets snippets) { |
| 536 if (!ready()) | 583 if (!ready()) |
| 537 return; | 584 return; |
| 538 | 585 |
| 539 for (auto& item : categories_) { | 586 for (auto& item : categories_) { |
| 540 CategoryContent* content = &item.second; | 587 CategoryContent* content = &item.second; |
| 541 content->provided_by_server = false; | 588 content->provided_by_server = false; |
| 542 } | 589 } |
| 543 | 590 |
| 591 // Clear up expired dismissed snippets before we use them to filter new ones. |
| 592 ClearExpiredSnippets(); |
| 593 |
| 544 // If snippets were fetched successfully, update our |categories_| from each | 594 // If snippets were fetched successfully, update our |categories_| from each |
| 545 // category provided by the server. | 595 // category provided by the server. |
| 546 if (snippets) { | 596 if (snippets) { |
| 547 for (NTPSnippetsFetcher::FetchedCategory& fetched_category : *snippets) { | 597 for (NTPSnippetsFetcher::FetchedCategory& fetched_category : *snippets) { |
| 548 Category category = fetched_category.category; | 598 Category category = fetched_category.category; |
| 549 | 599 |
| 550 // TODO(sfiera): Avoid hard-coding articles category checks in so many | 600 // TODO(sfiera): Avoid hard-coding articles category checks in so many |
| 551 // places. | 601 // places. |
| 552 if (category != articles_category_) { | 602 if (category != articles_category_) { |
| 553 // Only update titles from server-side provided categories. | 603 // Only update titles from server-side provided categories. |
| 554 categories_[category].localized_title = | 604 categories_[category].localized_title = |
| 555 fetched_category.localized_title; | 605 fetched_category.localized_title; |
| 556 } | 606 } |
| 607 categories_[category].provided_by_server = true; |
| 557 | 608 |
| 558 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); | 609 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); |
| 559 // TODO(sfiera): histograms for server categories. | 610 // TODO(sfiera): histograms for server categories. |
| 560 // Sparse histogram used because the number of snippets is small (bound by | 611 // Sparse histogram used because the number of snippets is small (bound by |
| 561 // kMaxSnippetCount). | 612 // kMaxSnippetCount). |
| 562 if (category == articles_category_) { | 613 if (category == articles_category_) { |
| 563 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", | 614 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", |
| 564 fetched_category.snippets.size()); | 615 fetched_category.snippets.size()); |
| 565 } | 616 } |
| 566 | 617 |
| 567 MergeSnippets(category, std::move(fetched_category.snippets)); | 618 ReplaceSnippets(category, std::move(fetched_category.snippets)); |
| 568 | |
| 569 // If there are more snippets than we want to show, delete the extra ones. | |
| 570 CategoryContent* content = &categories_[category]; | |
| 571 content->provided_by_server = true; | |
| 572 if (content->snippets.size() > kMaxSnippetCount) { | |
| 573 NTPSnippet::PtrVector to_delete( | |
| 574 std::make_move_iterator(content->snippets.begin() + | |
| 575 kMaxSnippetCount), | |
| 576 std::make_move_iterator(content->snippets.end())); | |
| 577 content->snippets.resize(kMaxSnippetCount); | |
| 578 if (category == articles_category_) | |
| 579 database_->DeleteSnippets(to_delete); | |
| 580 } | |
| 581 } | 619 } |
| 582 } | 620 } |
| 583 | 621 |
| 584 // Trigger expiration. This probably won't expire any current snippets (old | |
| 585 // ones should have already been expired by the timer, and new ones shouldn't | |
| 586 // have expired yet), but it will update the timer for the next run. | |
| 587 ClearExpiredSnippets(); | |
| 588 | |
| 589 for (const auto& item : categories_) { | 622 for (const auto& item : categories_) { |
| 590 Category category = item.first; | 623 Category category = item.first; |
| 591 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 624 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
| 592 } | 625 } |
| 593 | 626 |
| 594 // TODO(sfiera): equivalent metrics for non-articles. | 627 // TODO(sfiera): equivalent metrics for non-articles. |
| 595 const CategoryContent& content = categories_[articles_category_]; | 628 const CategoryContent& content = categories_[articles_category_]; |
| 596 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | 629 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", |
| 597 content.snippets.size()); | 630 content.snippets.size()); |
| 598 if (content.snippets.empty() && !content.dismissed.empty()) { | 631 if (content.snippets.empty() && !content.dismissed.empty()) { |
| 599 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 632 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", |
| 600 content.dismissed.size()); | 633 content.dismissed.size()); |
| 601 } | 634 } |
| 602 | 635 |
| 603 // TODO(sfiera): notify only when a category changed above. | 636 // TODO(sfiera): notify only when a category changed above. |
| 604 NotifyNewSuggestions(); | 637 NotifyNewSuggestions(); |
| 605 } | 638 } |
| 606 | 639 |
| 607 void NTPSnippetsService::MergeSnippets(Category category, | 640 void NTPSnippetsService::ReplaceSnippets(Category category, |
| 608 NTPSnippet::PtrVector new_snippets) { | 641 NTPSnippet::PtrVector new_snippets) { |
| 609 DCHECK(ready()); | 642 DCHECK(ready()); |
| 610 CategoryContent* content = &categories_[category]; | 643 CategoryContent* content = &categories_[category]; |
| 611 | 644 |
| 612 // Remove new snippets that we already have, or that have been dismissed. | 645 // Remove new snippets that have been dismissed. |
| 613 std::set<std::string> old_snippet_ids; | 646 FilterSnippets(&new_snippets, content->dismissed); |
| 614 InsertAllIDs(content->dismissed, &old_snippet_ids); | |
| 615 InsertAllIDs(content->snippets, &old_snippet_ids); | |
| 616 new_snippets.erase( | |
| 617 std::remove_if( | |
| 618 new_snippets.begin(), new_snippets.end(), | |
| 619 [&old_snippet_ids](const std::unique_ptr<NTPSnippet>& snippet) { | |
| 620 if (old_snippet_ids.count(snippet->id())) | |
| 621 return true; | |
| 622 for (const SnippetSource& source : snippet->sources()) { | |
| 623 if (old_snippet_ids.count(source.url.spec())) | |
| 624 return true; | |
| 625 } | |
| 626 return false; | |
| 627 }), | |
| 628 new_snippets.end()); | |
| 629 | 647 |
| 630 // Fill in default publish/expiry dates where required. | 648 // Fill in default publish/expiry dates where required. |
| 631 for (std::unique_ptr<NTPSnippet>& snippet : new_snippets) { | 649 for (std::unique_ptr<NTPSnippet>& snippet : new_snippets) { |
| 632 if (snippet->publish_date().is_null()) | 650 if (snippet->publish_date().is_null()) |
| 633 snippet->set_publish_date(base::Time::Now()); | 651 snippet->set_publish_date(base::Time::Now()); |
| 634 if (snippet->expiry_date().is_null()) { | 652 if (snippet->expiry_date().is_null()) { |
| 635 snippet->set_expiry_date( | 653 snippet->set_expiry_date( |
| 636 snippet->publish_date() + | 654 snippet->publish_date() + |
| 637 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins)); | 655 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins)); |
| 638 } | 656 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 653 new_snippets.end()); | 671 new_snippets.end()); |
| 654 int num_snippets_dismissed = num_new_snippets - new_snippets.size(); | 672 int num_snippets_dismissed = num_new_snippets - new_snippets.size(); |
| 655 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", | 673 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", |
| 656 num_snippets_dismissed > 0); | 674 num_snippets_dismissed > 0); |
| 657 if (num_snippets_dismissed > 0) { | 675 if (num_snippets_dismissed > 0) { |
| 658 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", | 676 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", |
| 659 num_snippets_dismissed); | 677 num_snippets_dismissed); |
| 660 } | 678 } |
| 661 } | 679 } |
| 662 | 680 |
| 663 // Save new articles to the DB. | 681 if (new_snippets.empty()) |
| 664 // TODO(sfiera): save non-articles to DB too. | 682 return; |
| 665 if (category == articles_category_) | 683 |
| 684 // Remove current snippets that have been fetched again. We do not need to |
| 685 // archive those as they will be in the new current set. |
| 686 FilterSnippets(&content->snippets, new_snippets); |
| 687 |
| 688 // TODO(sfiera): handle DB for non-articles too. |
| 689 if (category == articles_category_) { |
| 690 // Remove previous snippets from the DB. |
| 691 database_->DeleteSnippets(content->snippets); |
| 692 // Save new articles to the DB. |
| 666 database_->SaveSnippets(new_snippets); | 693 database_->SaveSnippets(new_snippets); |
| 694 } |
| 667 | 695 |
| 668 // Insert the new snippets at the front. | 696 // Archive previous snippets - move them at the beginning of the list. |
| 697 content->archived.insert(content->archived.begin(), |
| 698 std::make_move_iterator(content->snippets.begin()), |
| 699 std::make_move_iterator(content->snippets.end())); |
| 700 Compact(&content->snippets); |
| 701 |
| 702 // Insert new snippets. |
| 669 content->snippets.insert(content->snippets.begin(), | 703 content->snippets.insert(content->snippets.begin(), |
| 670 std::make_move_iterator(new_snippets.begin()), | 704 std::make_move_iterator(new_snippets.begin()), |
| 671 std::make_move_iterator(new_snippets.end())); | 705 std::make_move_iterator(new_snippets.end())); |
| 706 |
| 707 // If there are more archived snippets than we want to keep, delete the |
| 708 // oldest ones by their fetch time (which are always in the back). |
| 709 if (content->archived.size() > kMaxArchivedSnippetCount) { |
| 710 NTPSnippet::PtrVector to_delete( |
| 711 std::make_move_iterator(content->archived.begin() + |
| 712 kMaxArchivedSnippetCount), |
| 713 std::make_move_iterator(content->archived.end())); |
| 714 content->archived.resize(kMaxArchivedSnippetCount); |
| 715 if (category == articles_category_) |
| 716 database_->DeleteImages(to_delete); |
| 717 } |
| 672 } | 718 } |
| 673 | 719 |
| 674 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const { | 720 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const { |
| 675 std::set<std::string> hosts; | 721 std::set<std::string> hosts; |
| 676 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts); | 722 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts); |
| 677 for (const auto& value : *list) { | 723 for (const auto& value : *list) { |
| 678 std::string str; | 724 std::string str; |
| 679 bool success = value->GetAsString(&str); | 725 bool success = value->GetAsString(&str); |
| 680 DCHECK(success) << "Failed to parse snippet host from prefs"; | 726 DCHECK(success) << "Failed to parse snippet host from prefs"; |
| 681 hosts.insert(std::move(str)); | 727 hosts.insert(std::move(str)); |
| 682 } | 728 } |
| 683 return hosts; | 729 return hosts; |
| 684 } | 730 } |
| 685 | 731 |
| 686 void NTPSnippetsService::StoreSnippetHostsToPrefs( | 732 void NTPSnippetsService::StoreSnippetHostsToPrefs( |
| 687 const std::set<std::string>& hosts) { | 733 const std::set<std::string>& hosts) { |
| 688 base::ListValue list; | 734 base::ListValue list; |
| 689 for (const std::string& host : hosts) | 735 for (const std::string& host : hosts) |
| 690 list.AppendString(host); | 736 list.AppendString(host); |
| 691 pref_service_->Set(prefs::kSnippetHosts, list); | 737 pref_service_->Set(prefs::kSnippetHosts, list); |
| 692 } | 738 } |
| 693 | 739 |
| 694 void NTPSnippetsService::ClearExpiredSnippets() { | 740 void NTPSnippetsService::ClearExpiredSnippets() { |
| 695 std::vector<Category> categories_to_erase; | 741 std::vector<Category> categories_to_erase; |
| 696 | 742 |
| 697 const base::Time expiry = base::Time::Now(); | 743 const base::Time now = base::Time::Now(); |
| 698 base::Time next_expiry = base::Time::Max(); | |
| 699 | 744 |
| 700 for (auto& item : categories_) { | 745 for (auto& item : categories_) { |
| 701 Category category = item.first; | 746 Category category = item.first; |
| 702 CategoryContent* content = &item.second; | 747 CategoryContent* content = &item.second; |
| 703 | 748 |
| 704 // Move expired snippets over into |to_delete|. | |
| 705 NTPSnippet::PtrVector to_delete; | 749 NTPSnippet::PtrVector to_delete; |
| 706 for (std::unique_ptr<NTPSnippet>& snippet : content->snippets) { | 750 // Move expired dismissed snippets over into |to_delete|. |
| 707 if (snippet->expiry_date() <= expiry) | |
| 708 to_delete.emplace_back(std::move(snippet)); | |
| 709 } | |
| 710 Compact(&content->snippets); | |
| 711 | |
| 712 // Move expired dismissed snippets over into |to_delete| as well. | |
| 713 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) { | 751 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) { |
| 714 if (snippet->expiry_date() <= expiry) | 752 if (snippet->expiry_date() <= now) |
| 715 to_delete.emplace_back(std::move(snippet)); | 753 to_delete.emplace_back(std::move(snippet)); |
| 716 } | 754 } |
| 717 Compact(&content->dismissed); | 755 Compact(&content->dismissed); |
| 718 | 756 |
| 719 // Finally, actually delete the removed snippets from the DB. | 757 // Finally, actually delete the removed snippets from the DB. |
| 720 if (category == articles_category_) | 758 if (category == articles_category_) |
| 721 database_->DeleteSnippets(to_delete); | 759 database_->DeleteSnippets(to_delete); |
| 722 | 760 |
| 723 if (content->snippets.empty() && content->dismissed.empty()) { | 761 if (content->snippets.empty() && content->dismissed.empty()) { |
| 724 if ((category != articles_category_) && !content->provided_by_server) | 762 if ((category != articles_category_) && !content->provided_by_server) |
| 725 categories_to_erase.push_back(category); | 763 categories_to_erase.push_back(category); |
| 726 continue; | 764 continue; |
| 727 } | 765 } |
| 728 | |
| 729 for (const auto& snippet : content->snippets) { | |
| 730 if (snippet->expiry_date() < next_expiry) | |
| 731 next_expiry = snippet->expiry_date(); | |
| 732 } | |
| 733 for (const auto& snippet : content->dismissed) { | |
| 734 if (snippet->expiry_date() < next_expiry) | |
| 735 next_expiry = snippet->expiry_date(); | |
| 736 } | |
| 737 } | 766 } |
| 738 | 767 |
| 739 for (Category category : categories_to_erase) { | 768 for (Category category : categories_to_erase) { |
| 740 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 769 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
| 741 categories_.erase(category); | 770 categories_.erase(category); |
| 742 } | 771 } |
| 772 } |
| 743 | 773 |
| 744 // Unless there are no snippets left, schedule a timer for the next expiry. | 774 void NTPSnippetsService::ClearOrphanedImages() { |
| 745 DCHECK_GT(next_expiry, expiry); | 775 // TODO(jkrcal): Implement. |
| 746 if (next_expiry < base::Time::Max()) { | |
| 747 expiry_timer_.Start(FROM_HERE, next_expiry - expiry, | |
| 748 base::Bind(&NTPSnippetsService::ClearExpiredSnippets, | |
| 749 base::Unretained(this))); | |
| 750 } | |
| 751 } | 776 } |
| 752 | 777 |
| 753 void NTPSnippetsService::NukeAllSnippets() { | 778 void NTPSnippetsService::NukeAllSnippets() { |
| 754 std::vector<Category> categories_to_erase; | 779 std::vector<Category> categories_to_erase; |
| 755 | 780 |
| 756 // Empty the ARTICLES category and remove all others, since they may or may | 781 // Empty the ARTICLES category and remove all others, since they may or may |
| 757 // not be personalized. | 782 // not be personalized. |
| 758 for (const auto& item : categories_) { | 783 for (const auto& item : categories_) { |
| 759 Category category = item.first; | 784 Category category = item.first; |
| 760 | 785 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 815 | 840 |
| 816 FetchSnippetImageFromNetwork(suggestion_id, callback); | 841 FetchSnippetImageFromNetwork(suggestion_id, callback); |
| 817 } | 842 } |
| 818 | 843 |
| 819 void NTPSnippetsService::FetchSnippetImageFromNetwork( | 844 void NTPSnippetsService::FetchSnippetImageFromNetwork( |
| 820 const std::string& suggestion_id, | 845 const std::string& suggestion_id, |
| 821 const ImageFetchedCallback& callback) { | 846 const ImageFetchedCallback& callback) { |
| 822 Category category = GetCategoryFromUniqueID(suggestion_id); | 847 Category category = GetCategoryFromUniqueID(suggestion_id); |
| 823 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | 848 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); |
| 824 | 849 |
| 825 auto category_it = categories_.find(category); | 850 if (categories_.find(category) == categories_.end()) { |
| 826 if (category_it == categories_.end()) { | |
| 827 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); | 851 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); |
| 828 return; | 852 return; |
| 829 } | 853 } |
| 830 | 854 |
| 831 const CategoryContent& content = category_it->second; | 855 GURL image_url = FindSnippetImageUrl(category, snippet_id); |
| 832 auto it = | |
| 833 std::find_if(content.snippets.begin(), content.snippets.end(), | |
| 834 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | |
| 835 return snippet->id() == snippet_id; | |
| 836 }); | |
| 837 | 856 |
| 838 if (it == content.snippets.end() || | 857 if (image_url.is_empty() || |
| 839 !thumbnail_requests_throttler_.DemandQuotaForRequest( | 858 !thumbnail_requests_throttler_.DemandQuotaForRequest( |
| 840 /*interactive_request=*/true)) { | 859 /*interactive_request=*/true)) { |
| 841 // Return an empty image. Directly, this is never synchronous with the | 860 // Return an empty image. Directly, this is never synchronous with the |
| 842 // original FetchSuggestionImage() call - an asynchronous database query has | 861 // original FetchSuggestionImage() call - an asynchronous database query has |
| 843 // happened in the meantime. | 862 // happened in the meantime. |
| 844 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); | 863 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); |
| 845 return; | 864 return; |
| 846 } | 865 } |
| 847 | 866 |
| 848 const NTPSnippet& snippet = *it->get(); | |
| 849 | |
| 850 // TODO(jkrcal): We probably should rename OnImageDataFetched() to | |
| 851 // CacheImageData(). This would document that this is actually independent | |
| 852 // from the individual fetch-flow. | |
| 853 // The image fetcher calls OnImageDataFetched() with the raw data (this object | |
| 854 // is an ImageFetcherDelegate) and then also | |
| 855 // OnSnippetImageDecodedFromNetwork() after the raw data gets decoded. | |
| 856 image_fetcher_->StartOrQueueNetworkRequest( | 867 image_fetcher_->StartOrQueueNetworkRequest( |
| 857 suggestion_id, snippet.salient_image_url(), | 868 suggestion_id, image_url, |
| 858 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork, | 869 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork, |
| 859 base::Unretained(this), callback)); | 870 base::Unretained(this), callback)); |
| 860 } | 871 } |
| 861 | 872 |
| 862 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork( | 873 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork( |
| 863 const ImageFetchedCallback& callback, | 874 const ImageFetchedCallback& callback, |
| 864 const std::string& suggestion_id, | 875 const std::string& suggestion_id, |
| 865 const gfx::Image& image) { | 876 const gfx::Image& image) { |
| 866 callback.Run(image); | 877 callback.Run(image); |
| 867 } | 878 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 900 if (category != articles_category_) { | 911 if (category != articles_category_) { |
| 901 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 912 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
| 902 categories_to_erase.push_back(category); | 913 categories_to_erase.push_back(category); |
| 903 } | 914 } |
| 904 } | 915 } |
| 905 | 916 |
| 906 for (Category category : categories_to_erase) { | 917 for (Category category : categories_to_erase) { |
| 907 categories_.erase(category); | 918 categories_.erase(category); |
| 908 } | 919 } |
| 909 | 920 |
| 910 expiry_timer_.Stop(); | |
| 911 suggestions_service_subscription_.reset(); | 921 suggestions_service_subscription_.reset(); |
| 912 RescheduleFetching(); | 922 RescheduleFetching(); |
| 913 } | 923 } |
| 914 | 924 |
| 915 void NTPSnippetsService::EnterStateError() { | 925 void NTPSnippetsService::EnterStateError() { |
| 916 expiry_timer_.Stop(); | |
| 917 suggestions_service_subscription_.reset(); | 926 suggestions_service_subscription_.reset(); |
| 918 RescheduleFetching(); | 927 RescheduleFetching(); |
| 919 snippets_status_service_.reset(); | 928 snippets_status_service_.reset(); |
| 920 } | 929 } |
| 921 | 930 |
| 922 void NTPSnippetsService::FinishInitialization() { | 931 void NTPSnippetsService::FinishInitialization() { |
| 923 if (nuke_after_load_) { | 932 if (nuke_after_load_) { |
| 924 NukeAllSnippets(); | 933 NukeAllSnippets(); |
| 925 nuke_after_load_ = false; | 934 nuke_after_load_ = false; |
| 926 } | 935 } |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1061 } | 1070 } |
| 1062 | 1071 |
| 1063 NTPSnippetsService::CategoryContent::CategoryContent() = default; | 1072 NTPSnippetsService::CategoryContent::CategoryContent() = default; |
| 1064 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = | 1073 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = |
| 1065 default; | 1074 default; |
| 1066 NTPSnippetsService::CategoryContent::~CategoryContent() = default; | 1075 NTPSnippetsService::CategoryContent::~CategoryContent() = default; |
| 1067 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: | 1076 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: |
| 1068 operator=(CategoryContent&&) = default; | 1077 operator=(CategoryContent&&) = default; |
| 1069 | 1078 |
| 1070 } // namespace ntp_snippets | 1079 } // namespace ntp_snippets |
| OLD | NEW |