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 |