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

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

Issue 2355393002: New snippets now replace old snippets and do not merge (Closed)
Patch Set: Minor polish 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/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
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;
Marc Treib 2016/09/21 19:10:16 Out of curiosity: Any particular reason for this n
jkrcal 2016/09/22 09:12:42 Not really. I was not sure if 100 is large enough
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
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) {
Marc Treib 2016/09/21 19:10:16 const &
jkrcal 2016/09/22 09:12:42 Oh, shit :) Thanks!
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,
Marc Treib 2016/09/21 19:10:16 Hm, I find this name somewhat misleading, but I ca
jkrcal 2016/09/22 09:12:42 Better, done.
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 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
363 if (!initialized()) 390 if (!initialized())
364 return; 391 return;
365 392
366 if (categories_.find(category) == categories_.end()) 393 if (categories_.find(category) == categories_.end())
367 return; 394 return;
368 CategoryContent* content = &categories_[category]; 395 CategoryContent* content = &categories_[category];
369 if (content->snippets.empty()) 396 if (content->snippets.empty())
370 return; 397 return;
371 398
372 if (category == articles_category_) 399 if (category == articles_category_)
373 database_->DeleteSnippets(content->snippets); 400 database_->DeleteSnippets(content->snippets);
Marc Treib 2016/09/21 19:10:17 Also delete the images? (Please go over all databa
jkrcal 2016/09/22 09:12:42 I was not sure. But I can delete them :) Actually,
374 content->snippets.clear(); 401 content->snippets.clear();
375 402
376 NotifyNewSuggestions(); 403 NotifyNewSuggestions();
377 } 404 }
378 405
379 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( 406 void NTPSnippetsService::GetDismissedSuggestionsForDebugging(
380 Category category, 407 Category category,
381 const DismissedSuggestionsCallback& callback) { 408 const DismissedSuggestionsCallback& callback) {
382 DCHECK(categories_.find(category) != categories_.end()); 409 DCHECK(categories_.find(category) != categories_.end());
383 410
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
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();
Marc Treib 2016/09/21 19:10:16 (*it)->salient_image_url() ?
jkrcal 2016/09/22 09:12:42 Done.
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();
Marc Treib 2016/09/21 19:10:17 Same here
jkrcal 2016/09/22 09:12:42 Done.
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()).
Marc Treib 2016/09/21 19:10:16 nit: pipes around image_fetcher_
jkrcal 2016/09/22 09:12:42 Done.
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();
Marc Treib 2016/09/21 19:10:16 Didn't you rename this to ClearExpiredDismissedSni
jkrcal 2016/09/22 09:12:42 More like forgot to hit "compile" :)
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
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;
Marc Treib 2016/09/21 19:10:17 Not your doing, but this is super hard to read, wi
jkrcal 2016/09/22 09:12:42 Agreed. I also had hard time understanding what is
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
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;
Marc Treib 2016/09/21 19:10:17 I don't think returning here is correct, e.g. the
jkrcal 2016/09/22 09:12:42 I do not agree. If we get a new fetch that is prac
Marc Treib 2016/09/22 11:27:06 Okay, makes sense - mind adding a brief comment to
jkrcal 2016/09/22 12:50:05 Done.
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.
Marc Treib 2016/09/21 19:10:16 nitty nit: "Finally" doesn't apply anymore.
jkrcal 2016/09/22 09:12:42 Done.
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;
Marc Treib 2016/09/21 19:10:16 The "continue" doesn't do anything anymore.
jkrcal 2016/09/22 09:12:42 Huh, good point :)
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.
Marc Treib 2016/09/21 19:10:16 Have a bug to reference? :)
jkrcal 2016/09/22 09:12:42 Done.
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698