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