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/remote/remote_suggestions_provider.h" | 5 #include "components/ntp_snippets/remote/remote_suggestions_provider.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <iterator> | 8 #include <iterator> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 value_hours = kDefaultFetchingIntervalActiveSuggestionsConsumer[index]; | 94 value_hours = kDefaultFetchingIntervalActiveSuggestionsConsumer[index]; |
95 param_name = kFetchingIntervalParamNameActiveSuggestionsConsumer[index]; | 95 param_name = kFetchingIntervalParamNameActiveSuggestionsConsumer[index]; |
96 break; | 96 break; |
97 } | 97 } |
98 | 98 |
99 // The default value can be overridden by a variation parameter. | 99 // The default value can be overridden by a variation parameter. |
100 std::string param_value_str = variations::GetVariationParamValueByFeature( | 100 std::string param_value_str = variations::GetVariationParamValueByFeature( |
101 ntp_snippets::kArticleSuggestionsFeature, param_name); | 101 ntp_snippets::kArticleSuggestionsFeature, param_name); |
102 if (!param_value_str.empty()) { | 102 if (!param_value_str.empty()) { |
103 double param_value_hours = 0.0; | 103 double param_value_hours = 0.0; |
104 if (base::StringToDouble(param_value_str, ¶m_value_hours)) | 104 if (base::StringToDouble(param_value_str, ¶m_value_hours)) { |
105 value_hours = param_value_hours; | 105 value_hours = param_value_hours; |
106 else | 106 } else { |
107 LOG(WARNING) << "Invalid value for variation parameter " << param_name; | 107 LOG(WARNING) << "Invalid value for variation parameter " << param_name; |
| 108 } |
108 } | 109 } |
109 | 110 |
110 return base::TimeDelta::FromSecondsD(value_hours * 3600.0); | 111 return base::TimeDelta::FromSecondsD(value_hours * 3600.0); |
111 } | 112 } |
112 | 113 |
113 std::unique_ptr<std::vector<std::string>> GetSnippetIDVector( | 114 std::unique_ptr<std::vector<std::string>> GetSnippetIDVector( |
114 const NTPSnippet::PtrVector& snippets) { | 115 const NTPSnippet::PtrVector& snippets) { |
115 auto result = base::MakeUnique<std::vector<std::string>>(); | 116 auto result = base::MakeUnique<std::vector<std::string>>(); |
116 for (const auto& snippet : snippets) { | 117 for (const auto& snippet : snippets) { |
117 result->push_back(snippet->id()); | 118 result->push_back(snippet->id()); |
118 } | 119 } |
119 return result; | 120 return result; |
120 } | 121 } |
121 | 122 |
122 bool HasIntersection(const std::vector<std::string>& a, | 123 bool HasIntersection(const std::vector<std::string>& a, |
123 const std::set<std::string>& b) { | 124 const std::set<std::string>& b) { |
124 for (const std::string& item : a) { | 125 for (const std::string& item : a) { |
125 if (base::ContainsValue(b, item)) | 126 if (base::ContainsValue(b, item)) { |
126 return true; | 127 return true; |
| 128 } |
127 } | 129 } |
128 return false; | 130 return false; |
129 } | 131 } |
130 | 132 |
131 void EraseByPrimaryID(NTPSnippet::PtrVector* snippets, | 133 void EraseByPrimaryID(NTPSnippet::PtrVector* snippets, |
132 const std::vector<std::string>& ids) { | 134 const std::vector<std::string>& ids) { |
133 std::set<std::string> ids_lookup(ids.begin(), ids.end()); | 135 std::set<std::string> ids_lookup(ids.begin(), ids.end()); |
134 snippets->erase( | 136 snippets->erase( |
135 std::remove_if(snippets->begin(), snippets->end(), | 137 std::remove_if(snippets->begin(), snippets->end(), |
136 [&ids_lookup](const std::unique_ptr<NTPSnippet>& snippet) { | 138 [&ids_lookup](const std::unique_ptr<NTPSnippet>& snippet) { |
(...skipping 21 matching lines...) Expand all Loading... |
158 void RemoveNullPointers(NTPSnippet::PtrVector* snippets) { | 160 void RemoveNullPointers(NTPSnippet::PtrVector* snippets) { |
159 snippets->erase( | 161 snippets->erase( |
160 std::remove_if( | 162 std::remove_if( |
161 snippets->begin(), snippets->end(), | 163 snippets->begin(), snippets->end(), |
162 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), | 164 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), |
163 snippets->end()); | 165 snippets->end()); |
164 } | 166 } |
165 | 167 |
166 void AssignExpiryAndPublishDates(NTPSnippet::PtrVector* snippets) { | 168 void AssignExpiryAndPublishDates(NTPSnippet::PtrVector* snippets) { |
167 for (std::unique_ptr<NTPSnippet>& snippet : *snippets) { | 169 for (std::unique_ptr<NTPSnippet>& snippet : *snippets) { |
168 if (snippet->publish_date().is_null()) | 170 if (snippet->publish_date().is_null()) { |
169 snippet->set_publish_date(base::Time::Now()); | 171 snippet->set_publish_date(base::Time::Now()); |
| 172 } |
170 if (snippet->expiry_date().is_null()) { | 173 if (snippet->expiry_date().is_null()) { |
171 snippet->set_expiry_date( | 174 snippet->set_expiry_date( |
172 snippet->publish_date() + | 175 snippet->publish_date() + |
173 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins)); | 176 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins)); |
174 } | 177 } |
175 } | 178 } |
176 } | 179 } |
177 | 180 |
178 void RemoveIncompleteSnippets(NTPSnippet::PtrVector* snippets) { | 181 void RemoveIncompleteSnippets(NTPSnippet::PtrVector* snippets) { |
179 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 182 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
(...skipping 20 matching lines...) Expand all Loading... |
200 | 203 |
201 std::vector<ContentSuggestion> ConvertToContentSuggestions( | 204 std::vector<ContentSuggestion> ConvertToContentSuggestions( |
202 Category category, | 205 Category category, |
203 const NTPSnippet::PtrVector& snippets) { | 206 const NTPSnippet::PtrVector& snippets) { |
204 std::vector<ContentSuggestion> result; | 207 std::vector<ContentSuggestion> result; |
205 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) { | 208 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) { |
206 // TODO(sfiera): if a snippet is not going to be displayed, move it | 209 // TODO(sfiera): if a snippet is not going to be displayed, move it |
207 // directly to content.dismissed on fetch. Otherwise, we might prune | 210 // directly to content.dismissed on fetch. Otherwise, we might prune |
208 // other snippets to get down to kMaxSnippetCount, only to hide one of the | 211 // other snippets to get down to kMaxSnippetCount, only to hide one of the |
209 // incomplete ones we kept. | 212 // incomplete ones we kept. |
210 if (!snippet->is_complete()) | 213 if (!snippet->is_complete()) { |
211 continue; | 214 continue; |
| 215 } |
212 ContentSuggestion suggestion(category, snippet->id(), | 216 ContentSuggestion suggestion(category, snippet->id(), |
213 snippet->best_source().url); | 217 snippet->best_source().url); |
214 suggestion.set_amp_url(snippet->best_source().amp_url); | 218 suggestion.set_amp_url(snippet->best_source().amp_url); |
215 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); | 219 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); |
216 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); | 220 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); |
217 suggestion.set_publish_date(snippet->publish_date()); | 221 suggestion.set_publish_date(snippet->publish_date()); |
218 suggestion.set_publisher_name( | 222 suggestion.set_publisher_name( |
219 base::UTF8ToUTF16(snippet->best_source().publisher_name)); | 223 base::UTF8ToUTF16(snippet->best_source().publisher_name)); |
220 suggestion.set_score(snippet->score()); | 224 suggestion.set_score(snippet->score()); |
221 result.emplace_back(std::move(suggestion)); | 225 result.emplace_back(std::move(suggestion)); |
222 } | 226 } |
223 return result; | 227 return result; |
224 } | 228 } |
225 | 229 |
226 void CallWithEmptyResults(FetchDoneCallback callback, Status status) { | 230 void CallWithEmptyResults(FetchDoneCallback callback, Status status) { |
227 if (callback.is_null()) | 231 if (callback.is_null()) { |
228 return; | 232 return; |
| 233 } |
229 callback.Run(status, std::vector<ContentSuggestion>()); | 234 callback.Run(status, std::vector<ContentSuggestion>()); |
230 } | 235 } |
231 | 236 |
232 } // namespace | 237 } // namespace |
233 | 238 |
234 RemoteSuggestionsProvider::RemoteSuggestionsProvider( | 239 RemoteSuggestionsProvider::RemoteSuggestionsProvider( |
235 Observer* observer, | 240 Observer* observer, |
236 CategoryFactory* category_factory, | 241 CategoryFactory* category_factory, |
237 PrefService* pref_service, | 242 PrefService* pref_service, |
238 const std::string& application_language_code, | 243 const std::string& application_language_code, |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
299 registry->RegisterListPref(kDeprecatedSnippetHostsPref); | 304 registry->RegisterListPref(kDeprecatedSnippetHostsPref); |
300 registry->RegisterListPref(prefs::kRemoteSuggestionCategories); | 305 registry->RegisterListPref(prefs::kRemoteSuggestionCategories); |
301 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalWifi, 0); | 306 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalWifi, 0); |
302 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalFallback, | 307 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalFallback, |
303 0); | 308 0); |
304 | 309 |
305 NTPSnippetsStatusService::RegisterProfilePrefs(registry); | 310 NTPSnippetsStatusService::RegisterProfilePrefs(registry); |
306 } | 311 } |
307 | 312 |
308 void RemoteSuggestionsProvider::FetchSnippets(bool interactive_request) { | 313 void RemoteSuggestionsProvider::FetchSnippets(bool interactive_request) { |
309 if (ready()) | 314 if (ready()) { |
310 FetchSnippetsFromHosts(std::set<std::string>(), interactive_request); | 315 FetchSnippetsFromHosts(std::set<std::string>(), interactive_request); |
311 else | 316 } else { |
312 fetch_when_ready_ = true; | 317 fetch_when_ready_ = true; |
| 318 } |
313 } | 319 } |
314 | 320 |
315 void RemoteSuggestionsProvider::FetchSnippetsFromHosts( | 321 void RemoteSuggestionsProvider::FetchSnippetsFromHosts( |
316 const std::set<std::string>& hosts, | 322 const std::set<std::string>& hosts, |
317 bool interactive_request) { | 323 bool interactive_request) { |
318 // TODO(tschumann): FetchSnippets() and FetchSnippetsFromHost() implement the | 324 // TODO(tschumann): FetchSnippets() and FetchSnippetsFromHost() implement the |
319 // fetch logic when called by the background fetcher or interactive "reload" | 325 // fetch logic when called by the background fetcher or interactive "reload" |
320 // requests. Fetch() is right now only called for the fetch-more use case. | 326 // requests. Fetch() is right now only called for the fetch-more use case. |
321 // The names are confusing and we need to clean them up. | 327 // The names are confusing and we need to clean them up. |
322 if (!ready()) | 328 if (!ready()) { |
323 return; | 329 return; |
| 330 } |
324 MarkEmptyCategoriesAsLoading(); | 331 MarkEmptyCategoriesAsLoading(); |
325 | 332 |
326 NTPSnippetsFetcher::Params params = | 333 NTPSnippetsFetcher::Params params = |
327 BuildFetchParams(/*exclude_archived_suggestions=*/true); | 334 BuildFetchParams(/*exclude_archived_suggestions=*/true); |
328 params.hosts = hosts; | 335 params.hosts = hosts; |
329 params.interactive_request = interactive_request; | 336 params.interactive_request = interactive_request; |
330 snippets_fetcher_->FetchSnippets( | 337 snippets_fetcher_->FetchSnippets( |
331 params, base::BindOnce(&RemoteSuggestionsProvider::OnFetchFinished, | 338 params, base::BindOnce(&RemoteSuggestionsProvider::OnFetchFinished, |
332 base::Unretained(this))); | 339 base::Unretained(this))); |
333 } | 340 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
374 } | 381 } |
375 } | 382 } |
376 } | 383 } |
377 return result; | 384 return result; |
378 } | 385 } |
379 | 386 |
380 void RemoteSuggestionsProvider::MarkEmptyCategoriesAsLoading() { | 387 void RemoteSuggestionsProvider::MarkEmptyCategoriesAsLoading() { |
381 for (const auto& item : category_contents_) { | 388 for (const auto& item : category_contents_) { |
382 Category category = item.first; | 389 Category category = item.first; |
383 const CategoryContent& content = item.second; | 390 const CategoryContent& content = item.second; |
384 if (content.snippets.empty()) | 391 if (content.snippets.empty()) { |
385 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); | 392 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); |
| 393 } |
386 } | 394 } |
387 } | 395 } |
388 | 396 |
389 void RemoteSuggestionsProvider::RescheduleFetching(bool force) { | 397 void RemoteSuggestionsProvider::RescheduleFetching(bool force) { |
390 // The scheduler only exists on Android so far, it's null on other platforms. | 398 // The scheduler only exists on Android so far, it's null on other platforms. |
391 if (!scheduler_) | 399 if (!scheduler_) { |
392 return; | 400 return; |
| 401 } |
393 | 402 |
394 if (ready()) { | 403 if (ready()) { |
395 base::TimeDelta old_interval_wifi = base::TimeDelta::FromInternalValue( | 404 base::TimeDelta old_interval_wifi = base::TimeDelta::FromInternalValue( |
396 pref_service_->GetInt64(prefs::kSnippetBackgroundFetchingIntervalWifi)); | 405 pref_service_->GetInt64(prefs::kSnippetBackgroundFetchingIntervalWifi)); |
397 base::TimeDelta old_interval_fallback = | 406 base::TimeDelta old_interval_fallback = |
398 base::TimeDelta::FromInternalValue(pref_service_->GetInt64( | 407 base::TimeDelta::FromInternalValue(pref_service_->GetInt64( |
399 prefs::kSnippetBackgroundFetchingIntervalFallback)); | 408 prefs::kSnippetBackgroundFetchingIntervalFallback)); |
400 UserClassifier::UserClass user_class = user_classifier_->GetUserClass(); | 409 UserClassifier::UserClass user_class = user_classifier_->GetUserClass(); |
401 base::TimeDelta interval_wifi = | 410 base::TimeDelta interval_wifi = |
402 GetFetchingInterval(/*is_wifi=*/true, user_class); | 411 GetFetchingInterval(/*is_wifi=*/true, user_class); |
(...skipping 28 matching lines...) Expand all Loading... |
431 } | 440 } |
432 | 441 |
433 CategoryInfo RemoteSuggestionsProvider::GetCategoryInfo(Category category) { | 442 CategoryInfo RemoteSuggestionsProvider::GetCategoryInfo(Category category) { |
434 auto content_it = category_contents_.find(category); | 443 auto content_it = category_contents_.find(category); |
435 DCHECK(content_it != category_contents_.end()); | 444 DCHECK(content_it != category_contents_.end()); |
436 return content_it->second.info; | 445 return content_it->second.info; |
437 } | 446 } |
438 | 447 |
439 void RemoteSuggestionsProvider::DismissSuggestion( | 448 void RemoteSuggestionsProvider::DismissSuggestion( |
440 const ContentSuggestion::ID& suggestion_id) { | 449 const ContentSuggestion::ID& suggestion_id) { |
441 if (!ready()) | 450 if (!ready()) { |
442 return; | 451 return; |
| 452 } |
443 | 453 |
444 auto content_it = category_contents_.find(suggestion_id.category()); | 454 auto content_it = category_contents_.find(suggestion_id.category()); |
445 DCHECK(content_it != category_contents_.end()); | 455 DCHECK(content_it != category_contents_.end()); |
446 CategoryContent* content = &content_it->second; | 456 CategoryContent* content = &content_it->second; |
447 DismissSuggestionFromCategoryContent(content, | 457 DismissSuggestionFromCategoryContent(content, |
448 suggestion_id.id_within_category()); | 458 suggestion_id.id_within_category()); |
449 } | 459 } |
450 | 460 |
451 void RemoteSuggestionsProvider::FetchSuggestionImage( | 461 void RemoteSuggestionsProvider::FetchSuggestionImage( |
452 const ContentSuggestion::ID& suggestion_id, | 462 const ContentSuggestion::ID& suggestion_id, |
453 const ImageFetchedCallback& callback) { | 463 const ImageFetchedCallback& callback) { |
454 database_->LoadImage( | 464 database_->LoadImage( |
455 suggestion_id.id_within_category(), | 465 suggestion_id.id_within_category(), |
456 base::Bind(&RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase, | 466 base::Bind(&RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase, |
457 base::Unretained(this), callback, suggestion_id)); | 467 base::Unretained(this), callback, suggestion_id)); |
458 } | 468 } |
459 | 469 |
460 void RemoteSuggestionsProvider::ClearHistory( | 470 void RemoteSuggestionsProvider::ClearHistory( |
461 base::Time begin, | 471 base::Time begin, |
462 base::Time end, | 472 base::Time end, |
463 const base::Callback<bool(const GURL& url)>& filter) { | 473 const base::Callback<bool(const GURL& url)>& filter) { |
464 // Both time range and the filter are ignored and all suggestions are removed, | 474 // Both time range and the filter are ignored and all suggestions are removed, |
465 // because it is not known which history entries were used for the suggestions | 475 // because it is not known which history entries were used for the suggestions |
466 // personalization. | 476 // personalization. |
467 if (!ready()) | 477 if (!ready()) { |
468 nuke_when_initialized_ = true; | 478 nuke_when_initialized_ = true; |
469 else | 479 } else { |
470 NukeAllSnippets(); | 480 NukeAllSnippets(); |
| 481 } |
471 } | 482 } |
472 | 483 |
473 void RemoteSuggestionsProvider::ClearCachedSuggestions(Category category) { | 484 void RemoteSuggestionsProvider::ClearCachedSuggestions(Category category) { |
474 if (!initialized()) | 485 if (!initialized()) { |
475 return; | 486 return; |
| 487 } |
476 | 488 |
477 auto content_it = category_contents_.find(category); | 489 auto content_it = category_contents_.find(category); |
478 if (content_it == category_contents_.end()) { | 490 if (content_it == category_contents_.end()) { |
479 return; | 491 return; |
480 } | 492 } |
481 CategoryContent* content = &content_it->second; | 493 CategoryContent* content = &content_it->second; |
482 if (content->snippets.empty()) | 494 if (content->snippets.empty()) { |
483 return; | 495 return; |
| 496 } |
484 | 497 |
485 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); | 498 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
486 database_->DeleteImages(GetSnippetIDVector(content->snippets)); | 499 database_->DeleteImages(GetSnippetIDVector(content->snippets)); |
487 content->snippets.clear(); | 500 content->snippets.clear(); |
488 | 501 |
489 if (IsCategoryStatusAvailable(content->status)) | 502 if (IsCategoryStatusAvailable(content->status)) { |
490 NotifyNewSuggestions(category, *content); | 503 NotifyNewSuggestions(category, *content); |
| 504 } |
491 } | 505 } |
492 | 506 |
493 void RemoteSuggestionsProvider::GetDismissedSuggestionsForDebugging( | 507 void RemoteSuggestionsProvider::GetDismissedSuggestionsForDebugging( |
494 Category category, | 508 Category category, |
495 const DismissedSuggestionsCallback& callback) { | 509 const DismissedSuggestionsCallback& callback) { |
496 auto content_it = category_contents_.find(category); | 510 auto content_it = category_contents_.find(category); |
497 DCHECK(content_it != category_contents_.end()); | 511 DCHECK(content_it != category_contents_.end()); |
498 callback.Run( | 512 callback.Run( |
499 ConvertToContentSuggestions(category, content_it->second.dismissed)); | 513 ConvertToContentSuggestions(category, content_it->second.dismissed)); |
500 } | 514 } |
501 | 515 |
502 void RemoteSuggestionsProvider::ClearDismissedSuggestionsForDebugging( | 516 void RemoteSuggestionsProvider::ClearDismissedSuggestionsForDebugging( |
503 Category category) { | 517 Category category) { |
504 auto content_it = category_contents_.find(category); | 518 auto content_it = category_contents_.find(category); |
505 DCHECK(content_it != category_contents_.end()); | 519 DCHECK(content_it != category_contents_.end()); |
506 CategoryContent* content = &content_it->second; | 520 CategoryContent* content = &content_it->second; |
507 | 521 |
508 if (!initialized()) | 522 if (!initialized()) { |
509 return; | 523 return; |
| 524 } |
510 | 525 |
511 if (content->dismissed.empty()) | 526 if (content->dismissed.empty()) { |
512 return; | 527 return; |
| 528 } |
513 | 529 |
514 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); | 530 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); |
515 // The image got already deleted when the suggestion was dismissed. | 531 // The image got already deleted when the suggestion was dismissed. |
516 | 532 |
517 content->dismissed.clear(); | 533 content->dismissed.clear(); |
518 } | 534 } |
519 | 535 |
520 // static | 536 // static |
521 int RemoteSuggestionsProvider::GetMaxSnippetCountForTesting() { | 537 int RemoteSuggestionsProvider::GetMaxSnippetCountForTesting() { |
522 return kMaxSnippetCount; | 538 return kMaxSnippetCount; |
523 } | 539 } |
524 | 540 |
525 //////////////////////////////////////////////////////////////////////////////// | 541 //////////////////////////////////////////////////////////////////////////////// |
526 // Private methods | 542 // Private methods |
527 | 543 |
528 GURL RemoteSuggestionsProvider::FindSnippetImageUrl( | 544 GURL RemoteSuggestionsProvider::FindSnippetImageUrl( |
529 const ContentSuggestion::ID& suggestion_id) const { | 545 const ContentSuggestion::ID& suggestion_id) const { |
530 DCHECK(base::ContainsKey(category_contents_, suggestion_id.category())); | 546 DCHECK(base::ContainsKey(category_contents_, suggestion_id.category())); |
531 | 547 |
532 const CategoryContent& content = | 548 const CategoryContent& content = |
533 category_contents_.at(suggestion_id.category()); | 549 category_contents_.at(suggestion_id.category()); |
534 const NTPSnippet* snippet = | 550 const NTPSnippet* snippet = |
535 content.FindSnippet(suggestion_id.id_within_category()); | 551 content.FindSnippet(suggestion_id.id_within_category()); |
536 if (!snippet) | 552 if (!snippet) { |
537 return GURL(); | 553 return GURL(); |
| 554 } |
538 return snippet->salient_image_url(); | 555 return snippet->salient_image_url(); |
539 } | 556 } |
540 | 557 |
541 // image_fetcher::ImageFetcherDelegate implementation. | 558 // image_fetcher::ImageFetcherDelegate implementation. |
542 void RemoteSuggestionsProvider::OnImageDataFetched( | 559 void RemoteSuggestionsProvider::OnImageDataFetched( |
543 const std::string& id_within_category, | 560 const std::string& id_within_category, |
544 const std::string& image_data) { | 561 const std::string& image_data) { |
545 if (image_data.empty()) | 562 if (image_data.empty()) { |
546 return; | 563 return; |
| 564 } |
547 | 565 |
548 // Only save the image if the corresponding snippet still exists. | 566 // Only save the image if the corresponding snippet still exists. |
549 bool found = false; | 567 bool found = false; |
550 for (const auto& entry : category_contents_) { | 568 for (const auto& entry : category_contents_) { |
551 if (entry.second.FindSnippet(id_within_category)) { | 569 if (entry.second.FindSnippet(id_within_category)) { |
552 found = true; | 570 found = true; |
553 break; | 571 break; |
554 } | 572 } |
555 } | 573 } |
556 if (!found) | 574 if (!found) { |
557 return; | 575 return; |
| 576 } |
558 | 577 |
559 // Only cache the data in the DB, the actual serving is done in the callback | 578 // Only cache the data in the DB, the actual serving is done in the callback |
560 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()). | 579 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()). |
561 database_->SaveImage(id_within_category, image_data); | 580 database_->SaveImage(id_within_category, image_data); |
562 } | 581 } |
563 | 582 |
564 void RemoteSuggestionsProvider::OnDatabaseLoaded( | 583 void RemoteSuggestionsProvider::OnDatabaseLoaded( |
565 NTPSnippet::PtrVector snippets) { | 584 NTPSnippet::PtrVector snippets) { |
566 if (state_ == State::ERROR_OCCURRED) | 585 if (state_ == State::ERROR_OCCURRED) { |
567 return; | 586 return; |
| 587 } |
568 DCHECK(state_ == State::NOT_INITED); | 588 DCHECK(state_ == State::NOT_INITED); |
569 DCHECK(base::ContainsKey(category_contents_, articles_category_)); | 589 DCHECK(base::ContainsKey(category_contents_, articles_category_)); |
570 | 590 |
571 base::TimeDelta database_load_time = | 591 base::TimeDelta database_load_time = |
572 base::TimeTicks::Now() - database_load_start_; | 592 base::TimeTicks::Now() - database_load_start_; |
573 UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Snippets.DatabaseLoadTime", | 593 UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Snippets.DatabaseLoadTime", |
574 database_load_time); | 594 database_load_time); |
575 | 595 |
576 NTPSnippet::PtrVector to_delete; | 596 NTPSnippet::PtrVector to_delete; |
577 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { | 597 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { |
578 Category snippet_category = | 598 Category snippet_category = |
579 category_factory()->FromRemoteCategory(snippet->remote_category_id()); | 599 category_factory()->FromRemoteCategory(snippet->remote_category_id()); |
580 auto content_it = category_contents_.find(snippet_category); | 600 auto content_it = category_contents_.find(snippet_category); |
581 // We should already know about the category. | 601 // We should already know about the category. |
582 if (content_it == category_contents_.end()) { | 602 if (content_it == category_contents_.end()) { |
583 DLOG(WARNING) << "Loaded a suggestion for unknown category " | 603 DLOG(WARNING) << "Loaded a suggestion for unknown category " |
584 << snippet_category << " from the DB; deleting"; | 604 << snippet_category << " from the DB; deleting"; |
585 to_delete.emplace_back(std::move(snippet)); | 605 to_delete.emplace_back(std::move(snippet)); |
586 continue; | 606 continue; |
587 } | 607 } |
588 CategoryContent* content = &content_it->second; | 608 CategoryContent* content = &content_it->second; |
589 if (snippet->is_dismissed()) | 609 if (snippet->is_dismissed()) { |
590 content->dismissed.emplace_back(std::move(snippet)); | 610 content->dismissed.emplace_back(std::move(snippet)); |
591 else | 611 } else { |
592 content->snippets.emplace_back(std::move(snippet)); | 612 content->snippets.emplace_back(std::move(snippet)); |
| 613 } |
593 } | 614 } |
594 if (!to_delete.empty()) { | 615 if (!to_delete.empty()) { |
595 database_->DeleteSnippets(GetSnippetIDVector(to_delete)); | 616 database_->DeleteSnippets(GetSnippetIDVector(to_delete)); |
596 database_->DeleteImages(GetSnippetIDVector(to_delete)); | 617 database_->DeleteImages(GetSnippetIDVector(to_delete)); |
597 } | 618 } |
598 | 619 |
599 // Sort the suggestions in each category. | 620 // Sort the suggestions in each category. |
600 // TODO(treib): Persist the actual order in the DB somehow? crbug.com/654409 | 621 // TODO(treib): Persist the actual order in the DB somehow? crbug.com/654409 |
601 for (auto& entry : category_contents_) { | 622 for (auto& entry : category_contents_) { |
602 CategoryContent* content = &entry.second; | 623 CategoryContent* content = &entry.second; |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
746 content.snippets.size()); | 767 content.snippets.size()); |
747 if (content.snippets.empty() && !content.dismissed.empty()) { | 768 if (content.snippets.empty() && !content.dismissed.empty()) { |
748 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 769 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", |
749 content.dismissed.size()); | 770 content.dismissed.size()); |
750 } | 771 } |
751 | 772 |
752 // Reschedule after a successful fetch. This resets all currently scheduled | 773 // Reschedule after a successful fetch. This resets all currently scheduled |
753 // fetches, to make sure the fallback interval triggers only if no wifi fetch | 774 // fetches, to make sure the fallback interval triggers only if no wifi fetch |
754 // succeeded, and also that we don't do a background fetch immediately after | 775 // succeeded, and also that we don't do a background fetch immediately after |
755 // a user-initiated one. | 776 // a user-initiated one. |
756 if (fetched_categories) | 777 if (fetched_categories) { |
757 RescheduleFetching(true); | 778 RescheduleFetching(true); |
| 779 } |
758 } | 780 } |
759 | 781 |
760 void RemoteSuggestionsProvider::ArchiveSnippets( | 782 void RemoteSuggestionsProvider::ArchiveSnippets( |
761 CategoryContent* content, | 783 CategoryContent* content, |
762 NTPSnippet::PtrVector* to_archive) { | 784 NTPSnippet::PtrVector* to_archive) { |
763 // Archive previous snippets - move them at the beginning of the list. | 785 // Archive previous snippets - move them at the beginning of the list. |
764 content->archived.insert(content->archived.begin(), | 786 content->archived.insert(content->archived.begin(), |
765 std::make_move_iterator(to_archive->begin()), | 787 std::make_move_iterator(to_archive->begin()), |
766 std::make_move_iterator(to_archive->end())); | 788 std::make_move_iterator(to_archive->end())); |
767 to_archive->clear(); | 789 to_archive->clear(); |
(...skipping 21 matching lines...) Expand all Loading... |
789 | 811 |
790 void RemoteSuggestionsProvider::IntegrateSnippets( | 812 void RemoteSuggestionsProvider::IntegrateSnippets( |
791 CategoryContent* content, | 813 CategoryContent* content, |
792 NTPSnippet::PtrVector new_snippets) { | 814 NTPSnippet::PtrVector new_snippets) { |
793 DCHECK(ready()); | 815 DCHECK(ready()); |
794 | 816 |
795 // Do not touch the current set of snippets if the newly fetched one is empty. | 817 // Do not touch the current set of snippets if the newly fetched one is empty. |
796 // TODO(tschumann): This should go. If we get empty results we should update | 818 // TODO(tschumann): This should go. If we get empty results we should update |
797 // accordingly and remove the old one (only of course if this was not received | 819 // accordingly and remove the old one (only of course if this was not received |
798 // through a fetch-more). | 820 // through a fetch-more). |
799 if (new_snippets.empty()) | 821 if (new_snippets.empty()) { |
800 return; | 822 return; |
| 823 } |
801 | 824 |
802 // It's entirely possible that the newly fetched snippets contain articles | 825 // It's entirely possible that the newly fetched snippets contain articles |
803 // that have been present before. | 826 // that have been present before. |
804 // We need to make sure to only delete and archive snippets that don't | 827 // We need to make sure to only delete and archive snippets that don't |
805 // appear with the same ID in the new suggestions (it's fine for additional | 828 // appear with the same ID in the new suggestions (it's fine for additional |
806 // IDs though). | 829 // IDs though). |
807 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); | 830 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); |
808 // Do not delete the thumbnail images as they are still handy on open NTPs. | 831 // Do not delete the thumbnail images as they are still handy on open NTPs. |
809 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); | 832 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
810 // Note, that ArchiveSnippets will clear |content->snippets|. | 833 // Note, that ArchiveSnippets will clear |content->snippets|. |
811 ArchiveSnippets(content, &content->snippets); | 834 ArchiveSnippets(content, &content->snippets); |
812 | 835 |
813 database_->SaveSnippets(new_snippets); | 836 database_->SaveSnippets(new_snippets); |
814 | 837 |
815 content->snippets = std::move(new_snippets); | 838 content->snippets = std::move(new_snippets); |
816 } | 839 } |
817 | 840 |
818 void RemoteSuggestionsProvider::DismissSuggestionFromCategoryContent( | 841 void RemoteSuggestionsProvider::DismissSuggestionFromCategoryContent( |
819 CategoryContent* content, | 842 CategoryContent* content, |
820 const std::string& id_within_category) { | 843 const std::string& id_within_category) { |
821 auto it = std::find_if( | 844 auto it = std::find_if( |
822 content->snippets.begin(), content->snippets.end(), | 845 content->snippets.begin(), content->snippets.end(), |
823 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { | 846 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { |
824 return snippet->id() == id_within_category; | 847 return snippet->id() == id_within_category; |
825 }); | 848 }); |
826 if (it == content->snippets.end()) | 849 if (it == content->snippets.end()) { |
827 return; | 850 return; |
| 851 } |
828 | 852 |
829 (*it)->set_dismissed(true); | 853 (*it)->set_dismissed(true); |
830 | 854 |
831 database_->SaveSnippet(**it); | 855 database_->SaveSnippet(**it); |
832 | 856 |
833 content->dismissed.push_back(std::move(*it)); | 857 content->dismissed.push_back(std::move(*it)); |
834 content->snippets.erase(it); | 858 content->snippets.erase(it); |
835 } | 859 } |
836 | 860 |
837 void RemoteSuggestionsProvider::ClearExpiredDismissedSnippets() { | 861 void RemoteSuggestionsProvider::ClearExpiredDismissedSnippets() { |
838 std::vector<Category> categories_to_erase; | 862 std::vector<Category> categories_to_erase; |
839 | 863 |
840 const base::Time now = base::Time::Now(); | 864 const base::Time now = base::Time::Now(); |
841 | 865 |
842 for (auto& item : category_contents_) { | 866 for (auto& item : category_contents_) { |
843 Category category = item.first; | 867 Category category = item.first; |
844 CategoryContent* content = &item.second; | 868 CategoryContent* content = &item.second; |
845 | 869 |
846 NTPSnippet::PtrVector to_delete; | 870 NTPSnippet::PtrVector to_delete; |
847 // Move expired dismissed snippets over into |to_delete|. | 871 // Move expired dismissed snippets over into |to_delete|. |
848 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) { | 872 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) { |
849 if (snippet->expiry_date() <= now) | 873 if (snippet->expiry_date() <= now) { |
850 to_delete.emplace_back(std::move(snippet)); | 874 to_delete.emplace_back(std::move(snippet)); |
| 875 } |
851 } | 876 } |
852 RemoveNullPointers(&content->dismissed); | 877 RemoveNullPointers(&content->dismissed); |
853 | 878 |
854 // Delete the images. | 879 // Delete the images. |
855 database_->DeleteImages(GetSnippetIDVector(to_delete)); | 880 database_->DeleteImages(GetSnippetIDVector(to_delete)); |
856 // Delete the removed article suggestions from the DB. | 881 // Delete the removed article suggestions from the DB. |
857 database_->DeleteSnippets(GetSnippetIDVector(to_delete)); | 882 database_->DeleteSnippets(GetSnippetIDVector(to_delete)); |
858 | 883 |
859 if (content->snippets.empty() && content->dismissed.empty() && | 884 if (content->snippets.empty() && content->dismissed.empty() && |
860 category != articles_category_ && | 885 category != articles_category_ && |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
892 // not be personalized. | 917 // not be personalized. |
893 for (const auto& item : category_contents_) { | 918 for (const auto& item : category_contents_) { |
894 Category category = item.first; | 919 Category category = item.first; |
895 | 920 |
896 ClearCachedSuggestions(category); | 921 ClearCachedSuggestions(category); |
897 ClearDismissedSuggestionsForDebugging(category); | 922 ClearDismissedSuggestionsForDebugging(category); |
898 | 923 |
899 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 924 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
900 | 925 |
901 // Remove the category entirely; it may or may not reappear. | 926 // Remove the category entirely; it may or may not reappear. |
902 if (category != articles_category_) | 927 if (category != articles_category_) { |
903 categories_to_erase.push_back(category); | 928 categories_to_erase.push_back(category); |
| 929 } |
904 } | 930 } |
905 | 931 |
906 for (Category category : categories_to_erase) { | 932 for (Category category : categories_to_erase) { |
907 category_contents_.erase(category); | 933 category_contents_.erase(category); |
908 } | 934 } |
909 | 935 |
910 StoreCategoriesToPrefs(); | 936 StoreCategoriesToPrefs(); |
911 } | 937 } |
912 | 938 |
913 void RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase( | 939 void RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase( |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
994 // network request. | 1020 // network request. |
995 FetchSnippets(/*interactive_request=*/false); | 1021 FetchSnippets(/*interactive_request=*/false); |
996 fetch_when_ready_ = false; | 1022 fetch_when_ready_ = false; |
997 } | 1023 } |
998 | 1024 |
999 for (const auto& item : category_contents_) { | 1025 for (const auto& item : category_contents_) { |
1000 Category category = item.first; | 1026 Category category = item.first; |
1001 const CategoryContent& content = item.second; | 1027 const CategoryContent& content = item.second; |
1002 // FetchSnippets has set the status to |AVAILABLE_LOADING| if relevant, | 1028 // FetchSnippets has set the status to |AVAILABLE_LOADING| if relevant, |
1003 // otherwise we transition to |AVAILABLE| here. | 1029 // otherwise we transition to |AVAILABLE| here. |
1004 if (content.status != CategoryStatus::AVAILABLE_LOADING) | 1030 if (content.status != CategoryStatus::AVAILABLE_LOADING) { |
1005 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 1031 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
| 1032 } |
1006 } | 1033 } |
1007 } | 1034 } |
1008 | 1035 |
1009 void RemoteSuggestionsProvider::EnterStateDisabled() { | 1036 void RemoteSuggestionsProvider::EnterStateDisabled() { |
1010 NukeAllSnippets(); | 1037 NukeAllSnippets(); |
1011 } | 1038 } |
1012 | 1039 |
1013 void RemoteSuggestionsProvider::EnterStateError() { | 1040 void RemoteSuggestionsProvider::EnterStateError() { |
1014 snippets_status_service_.reset(); | 1041 snippets_status_service_.reset(); |
1015 } | 1042 } |
(...skipping 19 matching lines...) Expand all Loading... |
1035 base::Bind(&RemoteSuggestionsProvider::OnSnippetsStatusChanged, | 1062 base::Bind(&RemoteSuggestionsProvider::OnSnippetsStatusChanged, |
1036 base::Unretained(this))); | 1063 base::Unretained(this))); |
1037 | 1064 |
1038 // Always notify here even if we got nothing from the database, because we | 1065 // Always notify here even if we got nothing from the database, because we |
1039 // don't know how long the fetch will take or if it will even complete. | 1066 // don't know how long the fetch will take or if it will even complete. |
1040 for (const auto& item : category_contents_) { | 1067 for (const auto& item : category_contents_) { |
1041 Category category = item.first; | 1068 Category category = item.first; |
1042 const CategoryContent& content = item.second; | 1069 const CategoryContent& content = item.second; |
1043 // Note: We might be in a non-available status here, e.g. DISABLED due to | 1070 // Note: We might be in a non-available status here, e.g. DISABLED due to |
1044 // enterprise policy. | 1071 // enterprise policy. |
1045 if (IsCategoryStatusAvailable(content.status)) | 1072 if (IsCategoryStatusAvailable(content.status)) { |
1046 NotifyNewSuggestions(category, content); | 1073 NotifyNewSuggestions(category, content); |
| 1074 } |
1047 } | 1075 } |
1048 } | 1076 } |
1049 | 1077 |
1050 void RemoteSuggestionsProvider::OnSnippetsStatusChanged( | 1078 void RemoteSuggestionsProvider::OnSnippetsStatusChanged( |
1051 SnippetsStatus old_snippets_status, | 1079 SnippetsStatus old_snippets_status, |
1052 SnippetsStatus new_snippets_status) { | 1080 SnippetsStatus new_snippets_status) { |
1053 switch (new_snippets_status) { | 1081 switch (new_snippets_status) { |
1054 case SnippetsStatus::ENABLED_AND_SIGNED_IN: | 1082 case SnippetsStatus::ENABLED_AND_SIGNED_IN: |
1055 if (old_snippets_status == SnippetsStatus::ENABLED_AND_SIGNED_OUT) { | 1083 if (old_snippets_status == SnippetsStatus::ENABLED_AND_SIGNED_OUT) { |
1056 DCHECK(state_ == State::READY); | 1084 DCHECK(state_ == State::READY); |
(...skipping 26 matching lines...) Expand all Loading... |
1083 break; | 1111 break; |
1084 | 1112 |
1085 case SnippetsStatus::SIGNED_OUT_AND_DISABLED: | 1113 case SnippetsStatus::SIGNED_OUT_AND_DISABLED: |
1086 EnterState(State::DISABLED); | 1114 EnterState(State::DISABLED); |
1087 UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT); | 1115 UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT); |
1088 break; | 1116 break; |
1089 } | 1117 } |
1090 } | 1118 } |
1091 | 1119 |
1092 void RemoteSuggestionsProvider::EnterState(State state) { | 1120 void RemoteSuggestionsProvider::EnterState(State state) { |
1093 if (state == state_) | 1121 if (state == state_) { |
1094 return; | 1122 return; |
| 1123 } |
1095 | 1124 |
1096 UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.EnteredState", | 1125 UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.EnteredState", |
1097 static_cast<int>(state), | 1126 static_cast<int>(state), |
1098 static_cast<int>(State::COUNT)); | 1127 static_cast<int>(State::COUNT)); |
1099 | 1128 |
1100 switch (state) { | 1129 switch (state) { |
1101 case State::NOT_INITED: | 1130 case State::NOT_INITED: |
1102 // Initial state, it should not be possible to get back there. | 1131 // Initial state, it should not be possible to get back there. |
1103 NOTREACHED(); | 1132 NOTREACHED(); |
1104 break; | 1133 break; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1146 << " items in category " << category; | 1175 << " items in category " << category; |
1147 observer()->OnNewSuggestions(this, category, std::move(result)); | 1176 observer()->OnNewSuggestions(this, category, std::move(result)); |
1148 } | 1177 } |
1149 | 1178 |
1150 void RemoteSuggestionsProvider::UpdateCategoryStatus(Category category, | 1179 void RemoteSuggestionsProvider::UpdateCategoryStatus(Category category, |
1151 CategoryStatus status) { | 1180 CategoryStatus status) { |
1152 auto content_it = category_contents_.find(category); | 1181 auto content_it = category_contents_.find(category); |
1153 DCHECK(content_it != category_contents_.end()); | 1182 DCHECK(content_it != category_contents_.end()); |
1154 CategoryContent& content = content_it->second; | 1183 CategoryContent& content = content_it->second; |
1155 | 1184 |
1156 if (status == content.status) | 1185 if (status == content.status) { |
1157 return; | 1186 return; |
| 1187 } |
1158 | 1188 |
1159 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " | 1189 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " |
1160 << static_cast<int>(content.status) << " -> " | 1190 << static_cast<int>(content.status) << " -> " |
1161 << static_cast<int>(status); | 1191 << static_cast<int>(status); |
1162 content.status = status; | 1192 content.status = status; |
1163 observer()->OnCategoryStatusChanged(this, category, content.status); | 1193 observer()->OnCategoryStatusChanged(this, category, content.status); |
1164 } | 1194 } |
1165 | 1195 |
1166 void RemoteSuggestionsProvider::UpdateAllCategoryStatus(CategoryStatus status) { | 1196 void RemoteSuggestionsProvider::UpdateAllCategoryStatus(CategoryStatus status) { |
1167 for (const auto& category : category_contents_) { | 1197 for (const auto& category : category_contents_) { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1263 : BuildRemoteCategoryInfo(title, allow_fetching_more_results); | 1293 : BuildRemoteCategoryInfo(title, allow_fetching_more_results); |
1264 CategoryContent* content = UpdateCategoryInfo(category, info); | 1294 CategoryContent* content = UpdateCategoryInfo(category, info); |
1265 content->included_in_last_server_response = | 1295 content->included_in_last_server_response = |
1266 included_in_last_server_response; | 1296 included_in_last_server_response; |
1267 } | 1297 } |
1268 } | 1298 } |
1269 | 1299 |
1270 void RemoteSuggestionsProvider::StoreCategoriesToPrefs() { | 1300 void RemoteSuggestionsProvider::StoreCategoriesToPrefs() { |
1271 // Collect all the CategoryContents. | 1301 // Collect all the CategoryContents. |
1272 std::vector<std::pair<Category, const CategoryContent*>> to_store; | 1302 std::vector<std::pair<Category, const CategoryContent*>> to_store; |
1273 for (const auto& entry : category_contents_) | 1303 for (const auto& entry : category_contents_) { |
1274 to_store.emplace_back(entry.first, &entry.second); | 1304 to_store.emplace_back(entry.first, &entry.second); |
| 1305 } |
1275 // Sort them into the proper category order. | 1306 // Sort them into the proper category order. |
1276 std::sort(to_store.begin(), to_store.end(), | 1307 std::sort(to_store.begin(), to_store.end(), |
1277 [this](const std::pair<Category, const CategoryContent*>& left, | 1308 [this](const std::pair<Category, const CategoryContent*>& left, |
1278 const std::pair<Category, const CategoryContent*>& right) { | 1309 const std::pair<Category, const CategoryContent*>& right) { |
1279 return category_factory()->CompareCategories(left.first, | 1310 return category_factory()->CompareCategories(left.first, |
1280 right.first); | 1311 right.first); |
1281 }); | 1312 }); |
1282 // Convert the relevant info into a base::ListValue for storage. | 1313 // Convert the relevant info into a base::ListValue for storage. |
1283 base::ListValue list; | 1314 base::ListValue list; |
1284 for (const auto& entry : to_store) { | 1315 for (const auto& entry : to_store) { |
(...skipping 20 matching lines...) Expand all Loading... |
1305 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) = | 1336 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) = |
1306 default; | 1337 default; |
1307 | 1338 |
1308 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default; | 1339 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default; |
1309 | 1340 |
1310 RemoteSuggestionsProvider::CategoryContent& | 1341 RemoteSuggestionsProvider::CategoryContent& |
1311 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) = | 1342 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) = |
1312 default; | 1343 default; |
1313 | 1344 |
1314 } // namespace ntp_snippets | 1345 } // namespace ntp_snippets |
OLD | NEW |