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

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

Issue 2377663002: [NTP Snippets] Introduce ContentSuggestion::ID (Closed)
Patch Set: rebase 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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « components/ntp_snippets/ntp_snippets_service.h ('k') | components/ntp_snippets/ntp_snippets_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698