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; | |
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 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) { | |
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 Loading... | |
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 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(); | |
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 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; | |
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 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; |
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 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 |