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

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: Marc's comments 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;
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 const 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 EraseMatchingSnippets(
195 NTPSnippet::PtrVector* snippets,
Marc Treib 2016/09/22 11:27:06 nit: This can go on the previous line
jkrcal 2016/09/22 12:50:05 Done.
196 const NTPSnippet::PtrVector& matching) {
197 std::set<std::string> matching_ids;
198 InsertAllIDs(matching, &matching_ids);
199 snippets->erase(
200 std::remove_if(
201 snippets->begin(), snippets->end(),
202 [&matching_ids](const std::unique_ptr<NTPSnippet>& snippet) {
203 return IsSnippetInSet(snippet, matching_ids);
204 }),
205 snippets->end());
206 }
207
180 void Compact(NTPSnippet::PtrVector* snippets) { 208 void Compact(NTPSnippet::PtrVector* snippets) {
181 snippets->erase( 209 snippets->erase(
182 std::remove_if( 210 std::remove_if(
183 snippets->begin(), snippets->end(), 211 snippets->begin(), snippets->end(),
184 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), 212 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }),
185 snippets->end()); 213 snippets->end());
186 } 214 }
187 215
188 } // namespace 216 } // namespace
189 217
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 void NTPSnippetsService::ClearCachedSuggestions(Category category) { 390 void NTPSnippetsService::ClearCachedSuggestions(Category category) {
363 if (!initialized()) 391 if (!initialized())
364 return; 392 return;
365 393
366 if (categories_.find(category) == categories_.end()) 394 if (categories_.find(category) == categories_.end())
367 return; 395 return;
368 CategoryContent* content = &categories_[category]; 396 CategoryContent* content = &categories_[category];
369 if (content->snippets.empty()) 397 if (content->snippets.empty())
370 return; 398 return;
371 399
372 if (category == articles_category_) 400 if (category == articles_category_) {
373 database_->DeleteSnippets(content->snippets); 401 database_->DeleteSnippets(content->snippets);
402 database_->DeleteImages(content->snippets);
403 }
374 content->snippets.clear(); 404 content->snippets.clear();
375 405
376 NotifyNewSuggestions(); 406 NotifyNewSuggestions();
377 } 407 }
378 408
379 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( 409 void NTPSnippetsService::GetDismissedSuggestionsForDebugging(
380 Category category, 410 Category category,
381 const DismissedSuggestionsCallback& callback) { 411 const DismissedSuggestionsCallback& callback) {
382 DCHECK(categories_.find(category) != categories_.end()); 412 DCHECK(categories_.find(category) != categories_.end());
383 413
(...skipping 20 matching lines...) Expand all
404 Category category) { 434 Category category) {
405 DCHECK(categories_.find(category) != categories_.end()); 435 DCHECK(categories_.find(category) != categories_.end());
406 436
407 if (!initialized()) 437 if (!initialized())
408 return; 438 return;
409 439
410 CategoryContent* content = &categories_[category]; 440 CategoryContent* content = &categories_[category];
411 if (content->dismissed.empty()) 441 if (content->dismissed.empty())
412 return; 442 return;
413 443
414 if (category == articles_category_) 444 if (category == articles_category_) {
445 // Image gets deleted when each snippet gets dismissed.
Marc Treib 2016/09/22 11:27:06 nit: This sounds like something that will happen i
jkrcal 2016/09/22 12:50:05 Done.
415 database_->DeleteSnippets(content->dismissed); 446 database_->DeleteSnippets(content->dismissed);
447 }
416 content->dismissed.clear(); 448 content->dismissed.clear();
417 } 449 }
418 450
419 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { 451 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const {
420 // |suggestions_service_| can be null in tests. 452 // |suggestions_service_| can be null in tests.
421 if (!suggestions_service_) 453 if (!suggestions_service_)
422 return std::set<std::string>(); 454 return std::set<std::string>();
423 455
424 // TODO(treib): This should just call GetSnippetHostsFromPrefs. 456 // TODO(treib): This should just call GetSnippetHostsFromPrefs.
425 return GetSuggestionsHostsImpl( 457 return GetSuggestionsHostsImpl(
426 suggestions_service_->GetSuggestionsDataFromCache()); 458 suggestions_service_->GetSuggestionsDataFromCache());
427 } 459 }
428 460
429 // static 461 // static
430 int NTPSnippetsService::GetMaxSnippetCountForTesting() { 462 int NTPSnippetsService::GetMaxSnippetCountForTesting() {
431 return kMaxSnippetCount; 463 return kMaxSnippetCount;
432 } 464 }
433 465
434 //////////////////////////////////////////////////////////////////////////////// 466 ////////////////////////////////////////////////////////////////////////////////
435 // Private methods 467 // Private methods
436 468
469 GURL NTPSnippetsService::FindSnippetImageUrl(
470 Category category,
471 const std::string& snippet_id) const {
472 DCHECK(categories_.find(category) != categories_.end());
473
474 const CategoryContent& content = categories_.at(category);
475 // Search for the snippet in current and archived snippets.
476 auto it =
477 std::find_if(content.snippets.begin(), content.snippets.end(),
478 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) {
479 return snippet->id() == snippet_id;
480 });
481 if (it != content.snippets.end())
482 return (*it)->salient_image_url();
483
484 it = std::find_if(content.archived.begin(), content.archived.end(),
485 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) {
486 return snippet->id() == snippet_id;
487 });
488 if (it != content.archived.end())
489 return (*it)->salient_image_url();
490
491 return GURL();
492 }
493
437 // image_fetcher::ImageFetcherDelegate implementation. 494 // image_fetcher::ImageFetcherDelegate implementation.
438 void NTPSnippetsService::OnImageDataFetched(const std::string& suggestion_id, 495 void NTPSnippetsService::OnImageDataFetched(const std::string& suggestion_id,
439 const std::string& image_data) { 496 const std::string& image_data) {
440 if (image_data.empty()) 497 if (image_data.empty())
441 return; 498 return;
442 499
443 Category category = GetCategoryFromUniqueID(suggestion_id); 500 Category category = GetCategoryFromUniqueID(suggestion_id);
444 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); 501 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
445 502
446 auto category_it = categories_.find(category); 503 if (categories_.find(category) == categories_.end())
447 if (category_it == categories_.end())
448 return; 504 return;
449 505
450 const CategoryContent& content = category_it->second;
451
452 // Only save the image if the corresponding snippet still exists. 506 // Only save the image if the corresponding snippet still exists.
453 auto it = 507 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; 508 return;
460 509
510 // Only cache the data in the DB, the actual serving is done in the callback
511 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()).
461 database_->SaveImage(snippet_id, image_data); 512 database_->SaveImage(snippet_id, image_data);
462 } 513 }
463 514
464 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { 515 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) {
465 if (state_ == State::ERROR_OCCURRED) 516 if (state_ == State::ERROR_OCCURRED)
466 return; 517 return;
467 DCHECK(state_ == State::NOT_INITED); 518 DCHECK(state_ == State::NOT_INITED);
468 DCHECK(categories_.size() == 1); // Only articles category, so far. 519 DCHECK_EQ(1u, categories_.size()); // Only articles category, so far.
469 DCHECK(categories_.find(articles_category_) != categories_.end()); 520 DCHECK(categories_.find(articles_category_) != categories_.end());
470 521
471 // TODO(sfiera): support non-article categories in database. 522 // TODO(sfiera): support non-article categories in database.
472 CategoryContent* content = &categories_[articles_category_]; 523 CategoryContent* content = &categories_[articles_category_];
473 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { 524 for (std::unique_ptr<NTPSnippet>& snippet : snippets) {
474 if (snippet->is_dismissed()) 525 if (snippet->is_dismissed())
475 content->dismissed.emplace_back(std::move(snippet)); 526 content->dismissed.emplace_back(std::move(snippet));
476 else 527 else
477 content->snippets.emplace_back(std::move(snippet)); 528 content->snippets.emplace_back(std::move(snippet));
478 } 529 }
479 530
480 std::sort(content->snippets.begin(), content->snippets.end(), 531 std::sort(content->snippets.begin(), content->snippets.end(),
481 [](const std::unique_ptr<NTPSnippet>& lhs, 532 [](const std::unique_ptr<NTPSnippet>& lhs,
482 const std::unique_ptr<NTPSnippet>& rhs) { 533 const std::unique_ptr<NTPSnippet>& rhs) {
483 return lhs->score() > rhs->score(); 534 return lhs->score() > rhs->score();
484 }); 535 });
485 536
486 ClearExpiredSnippets(); 537 ClearExpiredDismissedSnippets();
538 ClearOrphanedImages();
487 FinishInitialization(); 539 FinishInitialization();
488 } 540 }
489 541
490 void NTPSnippetsService::OnDatabaseError() { 542 void NTPSnippetsService::OnDatabaseError() {
491 EnterState(State::ERROR_OCCURRED); 543 EnterState(State::ERROR_OCCURRED);
492 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); 544 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR);
493 } 545 }
494 546
495 // TODO(dgn): name clash between content suggestions and suggestions hosts. 547 // TODO(dgn): name clash between content suggestions and suggestions hosts.
496 // method name should be changed. 548 // method name should be changed.
(...skipping 14 matching lines...) Expand all
511 // and apply the same logic to them here. Maybe never? 563 // and apply the same logic to them here. Maybe never?
512 // 564 //
513 // First, move them over into |to_delete|. 565 // First, move them over into |to_delete|.
514 CategoryContent* content = &categories_[articles_category_]; 566 CategoryContent* content = &categories_[articles_category_];
515 NTPSnippet::PtrVector to_delete; 567 NTPSnippet::PtrVector to_delete;
516 for (std::unique_ptr<NTPSnippet>& snippet : content->snippets) { 568 for (std::unique_ptr<NTPSnippet>& snippet : content->snippets) {
517 if (!hosts.count(snippet->best_source().url.host())) 569 if (!hosts.count(snippet->best_source().url.host()))
518 to_delete.emplace_back(std::move(snippet)); 570 to_delete.emplace_back(std::move(snippet));
519 } 571 }
520 Compact(&content->snippets); 572 Compact(&content->snippets);
521 // Then delete the removed snippets from the database. 573 ArchiveSnippets(articles_category_, &to_delete);
522 database_->DeleteSnippets(to_delete);
523 574
524 StoreSnippetHostsToPrefs(hosts); 575 StoreSnippetHostsToPrefs(hosts);
525 576
526 // We removed some suggestions, so we want to let the client know about that. 577 // We removed some suggestions, so we want to let the client know about that.
527 // The fetch might take a long time or not complete so we don't want to wait 578 // The fetch might take a long time or not complete so we don't want to wait
528 // for its callback. 579 // for its callback.
529 NotifyNewSuggestions(); 580 NotifyNewSuggestions();
530 581
531 FetchSnippetsFromHosts(hosts, /*force_request=*/false); 582 FetchSnippetsFromHosts(hosts, /*force_request=*/false);
532 } 583 }
533 584
534 void NTPSnippetsService::OnFetchFinished( 585 void NTPSnippetsService::OnFetchFinished(
535 NTPSnippetsFetcher::OptionalSnippets snippets) { 586 NTPSnippetsFetcher::OptionalSnippets snippets) {
536 if (!ready()) 587 if (!ready())
537 return; 588 return;
538 589
539 for (auto& item : categories_) { 590 for (auto& item : categories_) {
540 CategoryContent* content = &item.second; 591 CategoryContent* content = &item.second;
541 content->provided_by_server = false; 592 content->provided_by_server = false;
542 } 593 }
543 594
595 // Clear up expired dismissed snippets before we use them to filter new ones.
596 ClearExpiredDismissedSnippets();
597
544 // If snippets were fetched successfully, update our |categories_| from each 598 // If snippets were fetched successfully, update our |categories_| from each
545 // category provided by the server. 599 // category provided by the server.
546 if (snippets) { 600 if (snippets) {
601 // TODO(jkrcal): A bit hard to understand with so many variables called
602 // "*categor*". Isn't here some room for simplification?
547 for (NTPSnippetsFetcher::FetchedCategory& fetched_category : *snippets) { 603 for (NTPSnippetsFetcher::FetchedCategory& fetched_category : *snippets) {
548 Category category = fetched_category.category; 604 Category category = fetched_category.category;
549 605
550 // TODO(sfiera): Avoid hard-coding articles category checks in so many 606 // TODO(sfiera): Avoid hard-coding articles category checks in so many
551 // places. 607 // places.
552 if (category != articles_category_) { 608 if (category != articles_category_) {
553 // Only update titles from server-side provided categories. 609 // Only update titles from server-side provided categories.
554 categories_[category].localized_title = 610 categories_[category].localized_title =
555 fetched_category.localized_title; 611 fetched_category.localized_title;
556 } 612 }
613 categories_[category].provided_by_server = true;
557 614
558 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); 615 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount));
559 // TODO(sfiera): histograms for server categories. 616 // TODO(sfiera): histograms for server categories.
560 // Sparse histogram used because the number of snippets is small (bound by 617 // Sparse histogram used because the number of snippets is small (bound by
561 // kMaxSnippetCount). 618 // kMaxSnippetCount).
562 if (category == articles_category_) { 619 if (category == articles_category_) {
563 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", 620 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched",
564 fetched_category.snippets.size()); 621 fetched_category.snippets.size());
565 } 622 }
566 623
567 MergeSnippets(category, std::move(fetched_category.snippets)); 624 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 } 625 }
582 } 626 }
583 627
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_) { 628 for (const auto& item : categories_) {
590 Category category = item.first; 629 Category category = item.first;
591 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); 630 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
592 } 631 }
593 632
594 // TODO(sfiera): equivalent metrics for non-articles. 633 // TODO(sfiera): equivalent metrics for non-articles.
595 const CategoryContent& content = categories_[articles_category_]; 634 const CategoryContent& content = categories_[articles_category_];
596 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", 635 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
597 content.snippets.size()); 636 content.snippets.size());
598 if (content.snippets.empty() && !content.dismissed.empty()) { 637 if (content.snippets.empty() && !content.dismissed.empty()) {
599 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", 638 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
600 content.dismissed.size()); 639 content.dismissed.size());
601 } 640 }
602 641
603 // TODO(sfiera): notify only when a category changed above. 642 // TODO(sfiera): notify only when a category changed above.
604 NotifyNewSuggestions(); 643 NotifyNewSuggestions();
605 } 644 }
606 645
607 void NTPSnippetsService::MergeSnippets(Category category, 646 void NTPSnippetsService::ArchiveSnippets(Category category,
608 NTPSnippet::PtrVector new_snippets) { 647 NTPSnippet::PtrVector* to_archive) {
648 CategoryContent* content = &categories_[category];
649
650 // TODO(sfiera): handle DB for non-articles too.
651 if (category == articles_category_) {
652 database_->DeleteSnippets(*to_archive);
653 // TODO(jkrcal): remove when orphaned images are deleted at start.
654 // crbug.com/649009
655 database_->DeleteImages(*to_archive);
Marc Treib 2016/09/22 11:27:06 Hm. So when there is an open NTP that has a snippe
jkrcal 2016/09/22 12:50:05 Okay :) Removed the line.
656 }
657
658 // Archive previous snippets - move them at the beginning of the list.
659 content->archived.insert(content->archived.begin(),
660 std::make_move_iterator(to_archive->begin()),
661 std::make_move_iterator(to_archive->end()));
662 Compact(to_archive);
663
664 // If there are more archived snippets than we want to keep, delete the
665 // oldest ones by their fetch time (which are always in the back).
666 if (content->archived.size() > kMaxArchivedSnippetCount) {
667 NTPSnippet::PtrVector to_delete(
668 std::make_move_iterator(content->archived.begin() +
669 kMaxArchivedSnippetCount),
670 std::make_move_iterator(content->archived.end()));
671 content->archived.resize(kMaxArchivedSnippetCount);
672 if (category == articles_category_)
673 database_->DeleteImages(to_delete);
674 }
675 }
676
677 void NTPSnippetsService::ReplaceSnippets(Category category,
678 NTPSnippet::PtrVector new_snippets) {
609 DCHECK(ready()); 679 DCHECK(ready());
610 CategoryContent* content = &categories_[category]; 680 CategoryContent* content = &categories_[category];
611 681
612 // Remove new snippets that we already have, or that have been dismissed. 682 // Remove new snippets that have been dismissed.
613 std::set<std::string> old_snippet_ids; 683 EraseMatchingSnippets(&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 684
630 // Fill in default publish/expiry dates where required. 685 // Fill in default publish/expiry dates where required.
631 for (std::unique_ptr<NTPSnippet>& snippet : new_snippets) { 686 for (std::unique_ptr<NTPSnippet>& snippet : new_snippets) {
632 if (snippet->publish_date().is_null()) 687 if (snippet->publish_date().is_null())
633 snippet->set_publish_date(base::Time::Now()); 688 snippet->set_publish_date(base::Time::Now());
634 if (snippet->expiry_date().is_null()) { 689 if (snippet->expiry_date().is_null()) {
635 snippet->set_expiry_date( 690 snippet->set_expiry_date(
636 snippet->publish_date() + 691 snippet->publish_date() +
637 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins)); 692 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins));
638 } 693 }
(...skipping 14 matching lines...) Expand all
653 new_snippets.end()); 708 new_snippets.end());
654 int num_snippets_dismissed = num_new_snippets - new_snippets.size(); 709 int num_snippets_dismissed = num_new_snippets - new_snippets.size();
655 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", 710 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch",
656 num_snippets_dismissed > 0); 711 num_snippets_dismissed > 0);
657 if (num_snippets_dismissed > 0) { 712 if (num_snippets_dismissed > 0) {
658 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", 713 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets",
659 num_snippets_dismissed); 714 num_snippets_dismissed);
660 } 715 }
661 } 716 }
662 717
663 // Save new articles to the DB. 718 if (new_snippets.empty())
664 // TODO(sfiera): save non-articles to DB too. 719 return;
665 if (category == articles_category_) 720
721 // Remove current snippets that have been fetched again. We do not need to
722 // archive those as they will be in the new current set.
723 EraseMatchingSnippets(&content->snippets, new_snippets);
Marc Treib 2016/09/22 11:27:06 Hm. For this purpose, should we only check the pri
jkrcal 2016/09/22 12:50:05 Done.
724
725 ArchiveSnippets(category, &content->snippets);
726
727 // TODO(sfiera): handle DB for non-articles too.
728 if (category == articles_category_) {
729 // Save new articles to the DB.
666 database_->SaveSnippets(new_snippets); 730 database_->SaveSnippets(new_snippets);
731 }
667 732
668 // Insert the new snippets at the front. 733 // Insert new snippets.
669 content->snippets.insert(content->snippets.begin(), 734 content->snippets.insert(content->snippets.begin(),
670 std::make_move_iterator(new_snippets.begin()), 735 std::make_move_iterator(new_snippets.begin()),
671 std::make_move_iterator(new_snippets.end())); 736 std::make_move_iterator(new_snippets.end()));
Marc Treib 2016/09/22 11:27:06 Would content->snippets = std::move(new_snippets);
jkrcal 2016/09/22 12:50:05 Done.
672 } 737 }
673 738
674 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const { 739 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const {
675 std::set<std::string> hosts; 740 std::set<std::string> hosts;
676 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts); 741 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts);
677 for (const auto& value : *list) { 742 for (const auto& value : *list) {
678 std::string str; 743 std::string str;
679 bool success = value->GetAsString(&str); 744 bool success = value->GetAsString(&str);
680 DCHECK(success) << "Failed to parse snippet host from prefs"; 745 DCHECK(success) << "Failed to parse snippet host from prefs";
681 hosts.insert(std::move(str)); 746 hosts.insert(std::move(str));
682 } 747 }
683 return hosts; 748 return hosts;
684 } 749 }
685 750
686 void NTPSnippetsService::StoreSnippetHostsToPrefs( 751 void NTPSnippetsService::StoreSnippetHostsToPrefs(
687 const std::set<std::string>& hosts) { 752 const std::set<std::string>& hosts) {
688 base::ListValue list; 753 base::ListValue list;
689 for (const std::string& host : hosts) 754 for (const std::string& host : hosts)
690 list.AppendString(host); 755 list.AppendString(host);
691 pref_service_->Set(prefs::kSnippetHosts, list); 756 pref_service_->Set(prefs::kSnippetHosts, list);
692 } 757 }
693 758
694 void NTPSnippetsService::ClearExpiredSnippets() { 759 void NTPSnippetsService::ClearExpiredDismissedSnippets() {
695 std::vector<Category> categories_to_erase; 760 std::vector<Category> categories_to_erase;
696 761
697 const base::Time expiry = base::Time::Now(); 762 const base::Time now = base::Time::Now();
698 base::Time next_expiry = base::Time::Max();
699 763
700 for (auto& item : categories_) { 764 for (auto& item : categories_) {
701 Category category = item.first; 765 Category category = item.first;
702 CategoryContent* content = &item.second; 766 CategoryContent* content = &item.second;
703 767
704 // Move expired snippets over into |to_delete|.
705 NTPSnippet::PtrVector to_delete; 768 NTPSnippet::PtrVector to_delete;
706 for (std::unique_ptr<NTPSnippet>& snippet : content->snippets) { 769 // 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) { 770 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) {
714 if (snippet->expiry_date() <= expiry) 771 if (snippet->expiry_date() <= now)
715 to_delete.emplace_back(std::move(snippet)); 772 to_delete.emplace_back(std::move(snippet));
716 } 773 }
717 Compact(&content->dismissed); 774 Compact(&content->dismissed);
718 775
719 // Finally, actually delete the removed snippets from the DB. 776 // Delete the removed article suggestions from the DB.
720 if (category == articles_category_) 777 if (category == articles_category_) {
778 // Image gets deleted when each snippet gets dismissed.
Marc Treib 2016/09/22 11:27:06 Same as the identical comment above :)
jkrcal 2016/09/22 12:50:05 Done.
721 database_->DeleteSnippets(to_delete); 779 database_->DeleteSnippets(to_delete);
780 }
722 781
723 if (content->snippets.empty() && content->dismissed.empty()) { 782 if (content->snippets.empty() && content->dismissed.empty()) {
724 if ((category != articles_category_) && !content->provided_by_server) 783 if ((category != articles_category_) && !content->provided_by_server)
Marc Treib 2016/09/22 11:27:06 nit: Now you could merge the two "if"s
jkrcal 2016/09/22 12:50:05 Done.
725 categories_to_erase.push_back(category); 784 categories_to_erase.push_back(category);
726 continue;
727 }
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 } 785 }
737 } 786 }
738 787
739 for (Category category : categories_to_erase) { 788 for (Category category : categories_to_erase) {
740 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); 789 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED);
741 categories_.erase(category); 790 categories_.erase(category);
742 } 791 }
792 }
743 793
744 // Unless there are no snippets left, schedule a timer for the next expiry. 794 void NTPSnippetsService::ClearOrphanedImages() {
745 DCHECK_GT(next_expiry, expiry); 795 // TODO(jkrcal): Implement. crbug.com/649009
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 } 796 }
752 797
753 void NTPSnippetsService::NukeAllSnippets() { 798 void NTPSnippetsService::NukeAllSnippets() {
754 std::vector<Category> categories_to_erase; 799 std::vector<Category> categories_to_erase;
755 800
756 // Empty the ARTICLES category and remove all others, since they may or may 801 // Empty the ARTICLES category and remove all others, since they may or may
757 // not be personalized. 802 // not be personalized.
758 for (const auto& item : categories_) { 803 for (const auto& item : categories_) {
759 Category category = item.first; 804 Category category = item.first;
760 805
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
815 860
816 FetchSnippetImageFromNetwork(suggestion_id, callback); 861 FetchSnippetImageFromNetwork(suggestion_id, callback);
817 } 862 }
818 863
819 void NTPSnippetsService::FetchSnippetImageFromNetwork( 864 void NTPSnippetsService::FetchSnippetImageFromNetwork(
820 const std::string& suggestion_id, 865 const std::string& suggestion_id,
821 const ImageFetchedCallback& callback) { 866 const ImageFetchedCallback& callback) {
822 Category category = GetCategoryFromUniqueID(suggestion_id); 867 Category category = GetCategoryFromUniqueID(suggestion_id);
823 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); 868 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
824 869
825 auto category_it = categories_.find(category); 870 if (categories_.find(category) == categories_.end()) {
826 if (category_it == categories_.end()) {
827 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); 871 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image());
828 return; 872 return;
829 } 873 }
830 874
831 const CategoryContent& content = category_it->second; 875 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 876
838 if (it == content.snippets.end() || 877 if (image_url.is_empty() ||
839 !thumbnail_requests_throttler_.DemandQuotaForRequest( 878 !thumbnail_requests_throttler_.DemandQuotaForRequest(
840 /*interactive_request=*/true)) { 879 /*interactive_request=*/true)) {
841 // Return an empty image. Directly, this is never synchronous with the 880 // Return an empty image. Directly, this is never synchronous with the
842 // original FetchSuggestionImage() call - an asynchronous database query has 881 // original FetchSuggestionImage() call - an asynchronous database query has
843 // happened in the meantime. 882 // happened in the meantime.
844 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); 883 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image());
845 return; 884 return;
846 } 885 }
847 886
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( 887 image_fetcher_->StartOrQueueNetworkRequest(
857 suggestion_id, snippet.salient_image_url(), 888 suggestion_id, image_url,
858 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork, 889 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork,
859 base::Unretained(this), callback)); 890 base::Unretained(this), callback));
860 } 891 }
861 892
862 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork( 893 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork(
863 const ImageFetchedCallback& callback, 894 const ImageFetchedCallback& callback,
864 const std::string& suggestion_id, 895 const std::string& suggestion_id,
865 const gfx::Image& image) { 896 const gfx::Image& image) {
866 callback.Run(image); 897 callback.Run(image);
867 } 898 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
900 if (category != articles_category_) { 931 if (category != articles_category_) {
901 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); 932 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED);
902 categories_to_erase.push_back(category); 933 categories_to_erase.push_back(category);
903 } 934 }
904 } 935 }
905 936
906 for (Category category : categories_to_erase) { 937 for (Category category : categories_to_erase) {
907 categories_.erase(category); 938 categories_.erase(category);
908 } 939 }
909 940
910 expiry_timer_.Stop();
911 suggestions_service_subscription_.reset(); 941 suggestions_service_subscription_.reset();
912 RescheduleFetching(); 942 RescheduleFetching();
913 } 943 }
914 944
915 void NTPSnippetsService::EnterStateError() { 945 void NTPSnippetsService::EnterStateError() {
916 expiry_timer_.Stop();
917 suggestions_service_subscription_.reset(); 946 suggestions_service_subscription_.reset();
918 RescheduleFetching(); 947 RescheduleFetching();
919 snippets_status_service_.reset(); 948 snippets_status_service_.reset();
920 } 949 }
921 950
922 void NTPSnippetsService::FinishInitialization() { 951 void NTPSnippetsService::FinishInitialization() {
923 if (nuke_after_load_) { 952 if (nuke_after_load_) {
924 NukeAllSnippets(); 953 NukeAllSnippets();
925 nuke_after_load_ = false; 954 nuke_after_load_ = false;
926 } 955 }
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
1061 } 1090 }
1062 1091
1063 NTPSnippetsService::CategoryContent::CategoryContent() = default; 1092 NTPSnippetsService::CategoryContent::CategoryContent() = default;
1064 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = 1093 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) =
1065 default; 1094 default;
1066 NTPSnippetsService::CategoryContent::~CategoryContent() = default; 1095 NTPSnippetsService::CategoryContent::~CategoryContent() = default;
1067 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: 1096 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent::
1068 operator=(CategoryContent&&) = default; 1097 operator=(CategoryContent&&) = default;
1069 1098
1070 } // namespace ntp_snippets 1099 } // namespace ntp_snippets
OLDNEW
« components/ntp_snippets/category_factory.h ('K') | « components/ntp_snippets/ntp_snippets_service.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698