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 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
314 | 314 |
315 CategoryInfo NTPSnippetsService::GetCategoryInfo(Category category) { | 315 CategoryInfo NTPSnippetsService::GetCategoryInfo(Category category) { |
316 DCHECK(categories_.find(category) != categories_.end()); | 316 DCHECK(categories_.find(category) != categories_.end()); |
317 const CategoryContent& content = categories_[category]; | 317 const CategoryContent& content = categories_[category]; |
318 return CategoryInfo(content.localized_title, | 318 return CategoryInfo(content.localized_title, |
319 ContentSuggestionsCardLayout::FULL_CARD, | 319 ContentSuggestionsCardLayout::FULL_CARD, |
320 /* has_more_button */ false, | 320 /* has_more_button */ false, |
321 /* show_if_empty */ true); | 321 /* show_if_empty */ true); |
322 } | 322 } |
323 | 323 |
324 void NTPSnippetsService::DismissSuggestion(const std::string& suggestion_id) { | 324 void NTPSnippetsService::DismissSuggestion( |
325 const ContentSuggestion::ID& suggestion_id) { | |
325 if (!ready()) | 326 if (!ready()) |
326 return; | 327 return; |
327 | 328 |
328 Category category = GetCategoryFromUniqueID(suggestion_id); | 329 DCHECK(categories_.find(suggestion_id.category()) != categories_.end()); |
Bernhard Bauer
2016/09/28 09:45:33
Nit: Using base::Contains() for this (from base/st
Marc Treib
2016/09/28 11:14:09
Done.
| |
329 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | |
330 | 330 |
331 DCHECK(categories_.find(category) != categories_.end()); | 331 CategoryContent* content = &categories_[suggestion_id.category()]; |
332 | 332 auto it = std::find_if( |
333 CategoryContent* content = &categories_[category]; | 333 content->snippets.begin(), content->snippets.end(), |
334 auto it = | 334 [&suggestion_id](const std::unique_ptr<NTPSnippet>& snippet) { |
335 std::find_if(content->snippets.begin(), content->snippets.end(), | 335 return snippet->id() == suggestion_id.within_category_id(); |
336 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | 336 }); |
337 return snippet->id() == snippet_id; | |
338 }); | |
339 if (it == content->snippets.end()) | 337 if (it == content->snippets.end()) |
340 return; | 338 return; |
341 | 339 |
342 (*it)->set_dismissed(true); | 340 (*it)->set_dismissed(true); |
343 | 341 |
344 database_->SaveSnippet(**it); | 342 database_->SaveSnippet(**it); |
345 database_->DeleteImage(snippet_id); | 343 database_->DeleteImage(suggestion_id.within_category_id()); |
346 | 344 |
347 content->dismissed.push_back(std::move(*it)); | 345 content->dismissed.push_back(std::move(*it)); |
348 content->snippets.erase(it); | 346 content->snippets.erase(it); |
349 } | 347 } |
350 | 348 |
351 void NTPSnippetsService::FetchSuggestionImage( | 349 void NTPSnippetsService::FetchSuggestionImage( |
352 const std::string& suggestion_id, | 350 const ContentSuggestion::ID& suggestion_id, |
353 const ImageFetchedCallback& callback) { | 351 const ImageFetchedCallback& callback) { |
354 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | |
355 database_->LoadImage( | 352 database_->LoadImage( |
356 snippet_id, | 353 suggestion_id.within_category_id(), |
357 base::Bind(&NTPSnippetsService::OnSnippetImageFetchedFromDatabase, | 354 base::Bind(&NTPSnippetsService::OnSnippetImageFetchedFromDatabase, |
358 base::Unretained(this), callback, suggestion_id)); | 355 base::Unretained(this), callback, suggestion_id)); |
359 } | 356 } |
360 | 357 |
361 void NTPSnippetsService::ClearHistory( | 358 void NTPSnippetsService::ClearHistory( |
362 base::Time begin, | 359 base::Time begin, |
363 base::Time end, | 360 base::Time end, |
364 const base::Callback<bool(const GURL& url)>& filter) { | 361 const base::Callback<bool(const GURL& url)>& filter) { |
365 // Both time range and the filter are ignored and all suggestions are removed, | 362 // Both time range and the filter are ignored and all suggestions are removed, |
366 // because it is not known which history entries were used for the suggestions | 363 // because it is not known which history entries were used for the suggestions |
(...skipping 26 matching lines...) Expand all Loading... | |
393 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( | 390 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( |
394 Category category, | 391 Category category, |
395 const DismissedSuggestionsCallback& callback) { | 392 const DismissedSuggestionsCallback& callback) { |
396 DCHECK(categories_.find(category) != categories_.end()); | 393 DCHECK(categories_.find(category) != categories_.end()); |
397 | 394 |
398 std::vector<ContentSuggestion> result; | 395 std::vector<ContentSuggestion> result; |
399 const CategoryContent& content = categories_[category]; | 396 const CategoryContent& content = categories_[category]; |
400 for (const std::unique_ptr<NTPSnippet>& snippet : content.dismissed) { | 397 for (const std::unique_ptr<NTPSnippet>& snippet : content.dismissed) { |
401 if (!snippet->is_complete()) | 398 if (!snippet->is_complete()) |
402 continue; | 399 continue; |
403 ContentSuggestion suggestion(MakeUniqueID(category, snippet->id()), | 400 ContentSuggestion suggestion(category, snippet->id(), |
404 snippet->best_source().url); | 401 snippet->best_source().url); |
405 suggestion.set_amp_url(snippet->best_source().amp_url); | 402 suggestion.set_amp_url(snippet->best_source().amp_url); |
406 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); | 403 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); |
407 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); | 404 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); |
408 suggestion.set_publish_date(snippet->publish_date()); | 405 suggestion.set_publish_date(snippet->publish_date()); |
409 suggestion.set_publisher_name( | 406 suggestion.set_publisher_name( |
410 base::UTF8ToUTF16(snippet->best_source().publisher_name)); | 407 base::UTF8ToUTF16(snippet->best_source().publisher_name)); |
411 suggestion.set_score(snippet->score()); | 408 suggestion.set_score(snippet->score()); |
412 result.emplace_back(std::move(suggestion)); | 409 result.emplace_back(std::move(suggestion)); |
413 } | 410 } |
(...skipping 30 matching lines...) Expand all Loading... | |
444 | 441 |
445 // static | 442 // static |
446 int NTPSnippetsService::GetMaxSnippetCountForTesting() { | 443 int NTPSnippetsService::GetMaxSnippetCountForTesting() { |
447 return kMaxSnippetCount; | 444 return kMaxSnippetCount; |
448 } | 445 } |
449 | 446 |
450 //////////////////////////////////////////////////////////////////////////////// | 447 //////////////////////////////////////////////////////////////////////////////// |
451 // Private methods | 448 // Private methods |
452 | 449 |
453 GURL NTPSnippetsService::FindSnippetImageUrl( | 450 GURL NTPSnippetsService::FindSnippetImageUrl( |
454 Category category, | 451 const ContentSuggestion::ID& suggestion_id) const { |
455 const std::string& snippet_id) const { | 452 DCHECK(categories_.find(suggestion_id.category()) != categories_.end()); |
456 DCHECK(categories_.find(category) != categories_.end()); | |
457 | 453 |
458 const CategoryContent& content = categories_.at(category); | 454 const CategoryContent& content = categories_.at(suggestion_id.category()); |
459 // Search for the snippet in current and archived snippets. | 455 const NTPSnippet* snippet = |
460 auto it = | 456 content.FindSnippet(suggestion_id.within_category_id()); |
461 std::find_if(content.snippets.begin(), content.snippets.end(), | 457 if (!snippet) |
462 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | 458 return GURL(); |
463 return snippet->id() == snippet_id; | 459 return snippet->salient_image_url(); |
464 }); | |
465 if (it != content.snippets.end()) | |
466 return (*it)->salient_image_url(); | |
467 | |
468 it = std::find_if(content.archived.begin(), content.archived.end(), | |
469 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | |
470 return snippet->id() == snippet_id; | |
471 }); | |
472 if (it != content.archived.end()) | |
473 return (*it)->salient_image_url(); | |
474 | |
475 return GURL(); | |
476 } | 460 } |
477 | 461 |
478 // image_fetcher::ImageFetcherDelegate implementation. | 462 // image_fetcher::ImageFetcherDelegate implementation. |
479 void NTPSnippetsService::OnImageDataFetched(const std::string& suggestion_id, | 463 void NTPSnippetsService::OnImageDataFetched( |
480 const std::string& image_data) { | 464 const std::string& within_category_id, |
465 const std::string& image_data) { | |
481 if (image_data.empty()) | 466 if (image_data.empty()) |
482 return; | 467 return; |
483 | 468 |
484 Category category = GetCategoryFromUniqueID(suggestion_id); | |
485 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | |
486 | |
487 if (categories_.find(category) == categories_.end()) | |
488 return; | |
489 | |
490 // Only save the image if the corresponding snippet still exists. | 469 // Only save the image if the corresponding snippet still exists. |
491 if (FindSnippetImageUrl(category, snippet_id).is_empty()) | 470 bool found = false; |
471 for (const std::pair<const Category, CategoryContent>& entry : categories_) { | |
472 if (entry.second.FindSnippet(within_category_id)) { | |
473 found = true; | |
474 break; | |
475 } | |
476 } | |
477 if (!found) | |
492 return; | 478 return; |
493 | 479 |
494 // Only cache the data in the DB, the actual serving is done in the callback | 480 // Only cache the data in the DB, the actual serving is done in the callback |
495 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()). | 481 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()). |
496 database_->SaveImage(snippet_id, image_data); | 482 database_->SaveImage(within_category_id, image_data); |
497 } | 483 } |
498 | 484 |
499 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { | 485 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { |
500 if (state_ == State::ERROR_OCCURRED) | 486 if (state_ == State::ERROR_OCCURRED) |
501 return; | 487 return; |
502 DCHECK(state_ == State::NOT_INITED); | 488 DCHECK(state_ == State::NOT_INITED); |
503 DCHECK_EQ(1u, categories_.size()); // Only articles category, so far. | 489 DCHECK_EQ(1u, categories_.size()); // Only articles category, so far. |
504 DCHECK(categories_.find(articles_category_) != categories_.end()); | 490 DCHECK(categories_.find(articles_category_) != categories_.end()); |
505 | 491 |
506 // TODO(sfiera): support non-article categories in database. | 492 // TODO(sfiera): support non-article categories in database. |
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
812 } | 798 } |
813 } | 799 } |
814 | 800 |
815 for (Category category : categories_to_erase) { | 801 for (Category category : categories_to_erase) { |
816 categories_.erase(category); | 802 categories_.erase(category); |
817 } | 803 } |
818 } | 804 } |
819 | 805 |
820 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase( | 806 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase( |
821 const ImageFetchedCallback& callback, | 807 const ImageFetchedCallback& callback, |
822 const std::string& suggestion_id, | 808 const ContentSuggestion::ID& suggestion_id, |
823 std::string data) { | 809 std::string data) { |
824 // |image_decoder_| is null in tests. | 810 // |image_decoder_| is null in tests. |
825 if (image_decoder_ && !data.empty()) { | 811 if (image_decoder_ && !data.empty()) { |
826 image_decoder_->DecodeImage( | 812 image_decoder_->DecodeImage( |
827 data, base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase, | 813 data, base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase, |
828 base::Unretained(this), callback, suggestion_id)); | 814 base::Unretained(this), callback, suggestion_id)); |
829 return; | 815 return; |
830 } | 816 } |
831 | 817 |
832 // Fetching from the DB failed; start a network fetch. | 818 // Fetching from the DB failed; start a network fetch. |
833 FetchSnippetImageFromNetwork(suggestion_id, callback); | 819 FetchSnippetImageFromNetwork(suggestion_id, callback); |
834 } | 820 } |
835 | 821 |
836 void NTPSnippetsService::OnSnippetImageDecodedFromDatabase( | 822 void NTPSnippetsService::OnSnippetImageDecodedFromDatabase( |
837 const ImageFetchedCallback& callback, | 823 const ImageFetchedCallback& callback, |
838 const std::string& suggestion_id, | 824 const ContentSuggestion::ID& suggestion_id, |
839 const gfx::Image& image) { | 825 const gfx::Image& image) { |
840 if (!image.IsEmpty()) { | 826 if (!image.IsEmpty()) { |
841 callback.Run(image); | 827 callback.Run(image); |
842 return; | 828 return; |
843 } | 829 } |
844 | 830 |
845 // If decoding the image failed, delete the DB entry. | 831 // If decoding the image failed, delete the DB entry. |
846 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | 832 database_->DeleteImage(suggestion_id.within_category_id()); |
847 database_->DeleteImage(snippet_id); | |
848 | 833 |
849 FetchSnippetImageFromNetwork(suggestion_id, callback); | 834 FetchSnippetImageFromNetwork(suggestion_id, callback); |
850 } | 835 } |
851 | 836 |
852 void NTPSnippetsService::FetchSnippetImageFromNetwork( | 837 void NTPSnippetsService::FetchSnippetImageFromNetwork( |
853 const std::string& suggestion_id, | 838 const ContentSuggestion::ID& suggestion_id, |
854 const ImageFetchedCallback& callback) { | 839 const ImageFetchedCallback& callback) { |
855 Category category = GetCategoryFromUniqueID(suggestion_id); | 840 if (categories_.find(suggestion_id.category()) == categories_.end()) { |
856 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | 841 OnSnippetImageDecodedFromNetwork( |
857 | 842 callback, suggestion_id.within_category_id(), gfx::Image()); |
858 if (categories_.find(category) == categories_.end()) { | |
859 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); | |
860 return; | 843 return; |
861 } | 844 } |
862 | 845 |
863 GURL image_url = FindSnippetImageUrl(category, snippet_id); | 846 GURL image_url = FindSnippetImageUrl(suggestion_id); |
864 | 847 |
865 if (image_url.is_empty() || | 848 if (image_url.is_empty() || |
866 !thumbnail_requests_throttler_.DemandQuotaForRequest( | 849 !thumbnail_requests_throttler_.DemandQuotaForRequest( |
867 /*interactive_request=*/true)) { | 850 /*interactive_request=*/true)) { |
868 // Return an empty image. Directly, this is never synchronous with the | 851 // Return an empty image. Directly, this is never synchronous with the |
869 // original FetchSuggestionImage() call - an asynchronous database query has | 852 // original FetchSuggestionImage() call - an asynchronous database query has |
870 // happened in the meantime. | 853 // happened in the meantime. |
871 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); | 854 OnSnippetImageDecodedFromNetwork( |
855 callback, suggestion_id.within_category_id(), gfx::Image()); | |
872 return; | 856 return; |
873 } | 857 } |
874 | 858 |
875 image_fetcher_->StartOrQueueNetworkRequest( | 859 image_fetcher_->StartOrQueueNetworkRequest( |
876 suggestion_id, image_url, | 860 suggestion_id.within_category_id(), image_url, |
877 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork, | 861 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork, |
878 base::Unretained(this), callback)); | 862 base::Unretained(this), callback)); |
879 } | 863 } |
880 | 864 |
881 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork( | 865 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork( |
882 const ImageFetchedCallback& callback, | 866 const ImageFetchedCallback& callback, |
883 const std::string& suggestion_id, | 867 const std::string& within_category_id, |
884 const gfx::Image& image) { | 868 const gfx::Image& image) { |
885 callback.Run(image); | 869 callback.Run(image); |
886 } | 870 } |
887 | 871 |
888 void NTPSnippetsService::EnterStateReady() { | 872 void NTPSnippetsService::EnterStateReady() { |
889 if (nuke_when_initialized_) { | 873 if (nuke_when_initialized_) { |
890 NukeAllSnippets(); | 874 NukeAllSnippets(); |
891 nuke_when_initialized_ = false; | 875 nuke_when_initialized_ = false; |
892 } | 876 } |
893 | 877 |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1019 const CategoryContent& content = item.second; | 1003 const CategoryContent& content = item.second; |
1020 | 1004 |
1021 std::vector<ContentSuggestion> result; | 1005 std::vector<ContentSuggestion> result; |
1022 for (const std::unique_ptr<NTPSnippet>& snippet : content.snippets) { | 1006 for (const std::unique_ptr<NTPSnippet>& snippet : content.snippets) { |
1023 // TODO(sfiera): if a snippet is not going to be displayed, move it | 1007 // TODO(sfiera): if a snippet is not going to be displayed, move it |
1024 // directly to content.dismissed on fetch. Otherwise, we might prune | 1008 // directly to content.dismissed on fetch. Otherwise, we might prune |
1025 // other snippets to get down to kMaxSnippetCount, only to hide one of the | 1009 // other snippets to get down to kMaxSnippetCount, only to hide one of the |
1026 // incomplete ones we kept. | 1010 // incomplete ones we kept. |
1027 if (!snippet->is_complete()) | 1011 if (!snippet->is_complete()) |
1028 continue; | 1012 continue; |
1029 ContentSuggestion suggestion(MakeUniqueID(category, snippet->id()), | 1013 ContentSuggestion suggestion(category, snippet->id(), |
1030 snippet->best_source().url); | 1014 snippet->best_source().url); |
1031 suggestion.set_amp_url(snippet->best_source().amp_url); | 1015 suggestion.set_amp_url(snippet->best_source().amp_url); |
1032 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); | 1016 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); |
1033 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); | 1017 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); |
1034 suggestion.set_publish_date(snippet->publish_date()); | 1018 suggestion.set_publish_date(snippet->publish_date()); |
1035 suggestion.set_publisher_name( | 1019 suggestion.set_publisher_name( |
1036 base::UTF8ToUTF16(snippet->best_source().publisher_name)); | 1020 base::UTF8ToUTF16(snippet->best_source().publisher_name)); |
1037 suggestion.set_score(snippet->score()); | 1021 suggestion.set_score(snippet->score()); |
1038 result.emplace_back(std::move(suggestion)); | 1022 result.emplace_back(std::move(suggestion)); |
1039 } | 1023 } |
(...skipping 17 matching lines...) Expand all Loading... | |
1057 content.status = status; | 1041 content.status = status; |
1058 observer()->OnCategoryStatusChanged(this, category, content.status); | 1042 observer()->OnCategoryStatusChanged(this, category, content.status); |
1059 } | 1043 } |
1060 | 1044 |
1061 void NTPSnippetsService::UpdateAllCategoryStatus(CategoryStatus status) { | 1045 void NTPSnippetsService::UpdateAllCategoryStatus(CategoryStatus status) { |
1062 for (const auto& category : categories_) { | 1046 for (const auto& category : categories_) { |
1063 UpdateCategoryStatus(category.first, status); | 1047 UpdateCategoryStatus(category.first, status); |
1064 } | 1048 } |
1065 } | 1049 } |
1066 | 1050 |
1051 const NTPSnippet* NTPSnippetsService::CategoryContent::FindSnippet( | |
1052 const std::string& within_category_id) const { | |
1053 // Search for the snippet in current and archived snippets. | |
1054 auto it = std::find_if( | |
1055 snippets.begin(), snippets.end(), | |
1056 [&within_category_id](const std::unique_ptr<NTPSnippet>& snippet) { | |
1057 return snippet->id() == within_category_id; | |
1058 }); | |
1059 if (it != snippets.end()) | |
1060 return it->get(); | |
1061 | |
1062 it = std::find_if( | |
1063 archived.begin(), archived.end(), | |
1064 [&within_category_id](const std::unique_ptr<NTPSnippet>& snippet) { | |
1065 return snippet->id() == within_category_id; | |
1066 }); | |
1067 if (it != archived.end()) | |
1068 return it->get(); | |
1069 | |
1070 return nullptr; | |
1071 } | |
1072 | |
1067 NTPSnippetsService::CategoryContent::CategoryContent() = default; | 1073 NTPSnippetsService::CategoryContent::CategoryContent() = default; |
1068 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = | 1074 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = |
1069 default; | 1075 default; |
1070 NTPSnippetsService::CategoryContent::~CategoryContent() = default; | 1076 NTPSnippetsService::CategoryContent::~CategoryContent() = default; |
1071 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: | 1077 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: |
1072 operator=(CategoryContent&&) = default; | 1078 operator=(CategoryContent&&) = default; |
1073 | 1079 |
1074 } // namespace ntp_snippets | 1080 } // namespace ntp_snippets |
OLD | NEW |