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/ntp_snippets_service.h" | 5 #include "components/ntp_snippets/remote/ntp_snippets_service.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <iterator> | 8 #include <iterator> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
186 snippets_fetcher_(std::move(snippets_fetcher)), | 186 snippets_fetcher_(std::move(snippets_fetcher)), |
187 image_fetcher_(std::move(image_fetcher)), | 187 image_fetcher_(std::move(image_fetcher)), |
188 image_decoder_(std::move(image_decoder)), | 188 image_decoder_(std::move(image_decoder)), |
189 database_(std::move(database)), | 189 database_(std::move(database)), |
190 snippets_status_service_(std::move(status_service)), | 190 snippets_status_service_(std::move(status_service)), |
191 fetch_when_ready_(false), | 191 fetch_when_ready_(false), |
192 nuke_when_initialized_(false), | 192 nuke_when_initialized_(false), |
193 thumbnail_requests_throttler_( | 193 thumbnail_requests_throttler_( |
194 pref_service, | 194 pref_service, |
195 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) { | 195 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) { |
196 // Articles category always exists; others will be added as needed. | 196 RestoreCategoriesFromPrefs(); |
197 categories_[articles_category_] = CategoryContent(); | 197 // The articles category always exists. Add it if we didn't get it from prefs. |
198 categories_[articles_category_].localized_title = | 198 // TODO(treib): Rethink this. |
199 l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER); | 199 if (!base::ContainsKey(categories_, articles_category_)) { |
200 observer->OnCategoryStatusChanged(this, articles_category_, | 200 categories_[articles_category_] = CategoryContent(); |
201 categories_[articles_category_].status); | 201 categories_[articles_category_].localized_title = |
202 l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER); | |
203 } | |
204 // Tell the observer about all the categories. | |
205 for (const auto& entry : categories_) { | |
206 observer->OnCategoryStatusChanged(this, entry.first, entry.second.status); | |
207 } | |
208 | |
202 if (database_->IsErrorState()) { | 209 if (database_->IsErrorState()) { |
203 EnterState(State::ERROR_OCCURRED); | 210 EnterState(State::ERROR_OCCURRED); |
204 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | 211 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); |
205 return; | 212 return; |
206 } | 213 } |
207 | 214 |
208 database_->SetErrorCallback(base::Bind(&NTPSnippetsService::OnDatabaseError, | 215 database_->SetErrorCallback(base::Bind(&NTPSnippetsService::OnDatabaseError, |
209 base::Unretained(this))); | 216 base::Unretained(this))); |
210 | 217 |
211 // We transition to other states while finalizing the initialization, when the | 218 // We transition to other states while finalizing the initialization, when the |
212 // database is done loading. | 219 // database is done loading. |
213 database_->LoadSnippets(base::Bind(&NTPSnippetsService::OnDatabaseLoaded, | 220 database_->LoadSnippets(base::Bind(&NTPSnippetsService::OnDatabaseLoaded, |
214 base::Unretained(this))); | 221 base::Unretained(this))); |
215 } | 222 } |
216 | 223 |
217 NTPSnippetsService::~NTPSnippetsService() = default; | 224 NTPSnippetsService::~NTPSnippetsService() = default; |
218 | 225 |
219 // static | 226 // static |
220 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { | 227 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { |
221 registry->RegisterListPref(prefs::kSnippetHosts); //TODO remove | 228 // TODO(treib): Add cleanup logic for prefs::kSnippetHosts, then remove it |
229 // completely after M56. | |
230 registry->RegisterListPref(prefs::kSnippetHosts); | |
231 registry->RegisterListPref(prefs::kRemoteSuggestionCategories); | |
222 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalWifi, 0); | 232 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalWifi, 0); |
223 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalFallback, | 233 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalFallback, |
224 0); | 234 0); |
225 | 235 |
226 NTPSnippetsStatusService::RegisterProfilePrefs(registry); | 236 NTPSnippetsStatusService::RegisterProfilePrefs(registry); |
227 } | 237 } |
228 | 238 |
229 void NTPSnippetsService::FetchSnippets(bool interactive_request) { | 239 void NTPSnippetsService::FetchSnippets(bool interactive_request) { |
230 if (ready()) | 240 if (ready()) |
231 FetchSnippetsFromHosts(std::set<std::string>(), interactive_request); | 241 FetchSnippetsFromHosts(std::set<std::string>(), interactive_request); |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
292 if (state_ != State::NOT_INITED || force) { | 302 if (state_ != State::NOT_INITED || force) { |
293 scheduler_->Unschedule(); | 303 scheduler_->Unschedule(); |
294 pref_service_->ClearPref(prefs::kSnippetBackgroundFetchingIntervalWifi); | 304 pref_service_->ClearPref(prefs::kSnippetBackgroundFetchingIntervalWifi); |
295 pref_service_->ClearPref( | 305 pref_service_->ClearPref( |
296 prefs::kSnippetBackgroundFetchingIntervalFallback); | 306 prefs::kSnippetBackgroundFetchingIntervalFallback); |
297 } | 307 } |
298 } | 308 } |
299 } | 309 } |
300 | 310 |
301 CategoryStatus NTPSnippetsService::GetCategoryStatus(Category category) { | 311 CategoryStatus NTPSnippetsService::GetCategoryStatus(Category category) { |
302 DCHECK(categories_.find(category) != categories_.end()); | 312 DCHECK(base::ContainsKey(categories_, category)); |
303 return categories_[category].status; | 313 return categories_[category].status; |
304 } | 314 } |
305 | 315 |
306 CategoryInfo NTPSnippetsService::GetCategoryInfo(Category category) { | 316 CategoryInfo NTPSnippetsService::GetCategoryInfo(Category category) { |
307 DCHECK(categories_.find(category) != categories_.end()); | 317 DCHECK(base::ContainsKey(categories_, category)); |
308 const CategoryContent& content = categories_[category]; | 318 const CategoryContent& content = categories_[category]; |
309 return CategoryInfo(content.localized_title, | 319 return CategoryInfo(content.localized_title, |
310 ContentSuggestionsCardLayout::FULL_CARD, | 320 ContentSuggestionsCardLayout::FULL_CARD, |
311 /* has_more_button */ false, | 321 /* has_more_button */ false, |
312 /* show_if_empty */ true); | 322 /* show_if_empty */ true); |
313 } | 323 } |
314 | 324 |
315 void NTPSnippetsService::DismissSuggestion( | 325 void NTPSnippetsService::DismissSuggestion( |
316 const ContentSuggestion::ID& suggestion_id) { | 326 const ContentSuggestion::ID& suggestion_id) { |
317 if (!ready()) | 327 if (!ready()) |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
356 if (!ready()) | 366 if (!ready()) |
357 nuke_when_initialized_ = true; | 367 nuke_when_initialized_ = true; |
358 else | 368 else |
359 NukeAllSnippets(); | 369 NukeAllSnippets(); |
360 } | 370 } |
361 | 371 |
362 void NTPSnippetsService::ClearCachedSuggestions(Category category) { | 372 void NTPSnippetsService::ClearCachedSuggestions(Category category) { |
363 if (!initialized()) | 373 if (!initialized()) |
364 return; | 374 return; |
365 | 375 |
366 if (categories_.find(category) == categories_.end()) | 376 if (!base::ContainsKey(categories_, category)) |
367 return; | 377 return; |
368 CategoryContent* content = &categories_[category]; | 378 CategoryContent* content = &categories_[category]; |
369 if (content->snippets.empty()) | 379 if (content->snippets.empty()) |
370 return; | 380 return; |
371 | 381 |
372 if (category == articles_category_) { | 382 if (category == articles_category_) { |
373 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); | 383 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
374 database_->DeleteImages(GetSnippetIDVector(content->snippets)); | 384 database_->DeleteImages(GetSnippetIDVector(content->snippets)); |
375 } | 385 } |
376 content->snippets.clear(); | 386 content->snippets.clear(); |
377 | 387 |
378 NotifyNewSuggestions(); | 388 NotifyNewSuggestions(); |
379 } | 389 } |
380 | 390 |
381 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( | 391 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( |
382 Category category, | 392 Category category, |
383 const DismissedSuggestionsCallback& callback) { | 393 const DismissedSuggestionsCallback& callback) { |
384 DCHECK(categories_.find(category) != categories_.end()); | 394 DCHECK(base::ContainsKey(categories_, category)); |
385 | 395 |
386 std::vector<ContentSuggestion> result; | 396 std::vector<ContentSuggestion> result; |
387 const CategoryContent& content = categories_[category]; | 397 const CategoryContent& content = categories_[category]; |
388 for (const std::unique_ptr<NTPSnippet>& snippet : content.dismissed) { | 398 for (const std::unique_ptr<NTPSnippet>& snippet : content.dismissed) { |
389 if (!snippet->is_complete()) | 399 if (!snippet->is_complete()) |
390 continue; | 400 continue; |
391 ContentSuggestion suggestion(category, snippet->id(), | 401 ContentSuggestion suggestion(category, snippet->id(), |
392 snippet->best_source().url); | 402 snippet->best_source().url); |
393 suggestion.set_amp_url(snippet->best_source().amp_url); | 403 suggestion.set_amp_url(snippet->best_source().amp_url); |
394 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); | 404 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); |
395 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); | 405 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); |
396 suggestion.set_publish_date(snippet->publish_date()); | 406 suggestion.set_publish_date(snippet->publish_date()); |
397 suggestion.set_publisher_name( | 407 suggestion.set_publisher_name( |
398 base::UTF8ToUTF16(snippet->best_source().publisher_name)); | 408 base::UTF8ToUTF16(snippet->best_source().publisher_name)); |
399 suggestion.set_score(snippet->score()); | 409 suggestion.set_score(snippet->score()); |
400 result.emplace_back(std::move(suggestion)); | 410 result.emplace_back(std::move(suggestion)); |
401 } | 411 } |
402 callback.Run(std::move(result)); | 412 callback.Run(std::move(result)); |
403 } | 413 } |
404 | 414 |
405 void NTPSnippetsService::ClearDismissedSuggestionsForDebugging( | 415 void NTPSnippetsService::ClearDismissedSuggestionsForDebugging( |
406 Category category) { | 416 Category category) { |
407 DCHECK(categories_.find(category) != categories_.end()); | 417 DCHECK(base::ContainsKey(categories_, category)); |
408 | 418 |
409 if (!initialized()) | 419 if (!initialized()) |
410 return; | 420 return; |
411 | 421 |
412 CategoryContent* content = &categories_[category]; | 422 CategoryContent* content = &categories_[category]; |
413 if (content->dismissed.empty()) | 423 if (content->dismissed.empty()) |
414 return; | 424 return; |
415 | 425 |
416 if (category == articles_category_) { | 426 if (category == articles_category_) { |
417 // The image got already deleted when the suggestion was dismissed. | 427 // The image got already deleted when the suggestion was dismissed. |
418 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); | 428 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); |
419 } | 429 } |
420 content->dismissed.clear(); | 430 content->dismissed.clear(); |
421 } | 431 } |
422 | 432 |
423 // static | 433 // static |
424 int NTPSnippetsService::GetMaxSnippetCountForTesting() { | 434 int NTPSnippetsService::GetMaxSnippetCountForTesting() { |
425 return kMaxSnippetCount; | 435 return kMaxSnippetCount; |
426 } | 436 } |
427 | 437 |
428 //////////////////////////////////////////////////////////////////////////////// | 438 //////////////////////////////////////////////////////////////////////////////// |
429 // Private methods | 439 // Private methods |
430 | 440 |
431 GURL NTPSnippetsService::FindSnippetImageUrl( | 441 GURL NTPSnippetsService::FindSnippetImageUrl( |
432 const ContentSuggestion::ID& suggestion_id) const { | 442 const ContentSuggestion::ID& suggestion_id) const { |
433 DCHECK(categories_.find(suggestion_id.category()) != categories_.end()); | 443 DCHECK(base::ContainsKey(categories_, suggestion_id.category())); |
434 | 444 |
435 const CategoryContent& content = categories_.at(suggestion_id.category()); | 445 const CategoryContent& content = categories_.at(suggestion_id.category()); |
436 const NTPSnippet* snippet = | 446 const NTPSnippet* snippet = |
437 content.FindSnippet(suggestion_id.id_within_category()); | 447 content.FindSnippet(suggestion_id.id_within_category()); |
438 if (!snippet) | 448 if (!snippet) |
439 return GURL(); | 449 return GURL(); |
440 return snippet->salient_image_url(); | 450 return snippet->salient_image_url(); |
441 } | 451 } |
442 | 452 |
443 // image_fetcher::ImageFetcherDelegate implementation. | 453 // image_fetcher::ImageFetcherDelegate implementation. |
(...skipping 16 matching lines...) Expand all Loading... | |
460 | 470 |
461 // Only cache the data in the DB, the actual serving is done in the callback | 471 // Only cache the data in the DB, the actual serving is done in the callback |
462 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()). | 472 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()). |
463 database_->SaveImage(id_within_category, image_data); | 473 database_->SaveImage(id_within_category, image_data); |
464 } | 474 } |
465 | 475 |
466 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { | 476 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { |
467 if (state_ == State::ERROR_OCCURRED) | 477 if (state_ == State::ERROR_OCCURRED) |
468 return; | 478 return; |
469 DCHECK(state_ == State::NOT_INITED); | 479 DCHECK(state_ == State::NOT_INITED); |
470 DCHECK_EQ(1u, categories_.size()); // Only articles category, so far. | 480 DCHECK(base::ContainsKey(categories_, articles_category_)); |
471 DCHECK(categories_.find(articles_category_) != categories_.end()); | |
472 | 481 |
473 // TODO(sfiera): support non-article categories in database. | 482 // TODO(treib): Support non-article categories in database. crbug.com/653476 |
474 CategoryContent* content = &categories_[articles_category_]; | 483 CategoryContent* content = &categories_[articles_category_]; |
475 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { | 484 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { |
476 if (snippet->is_dismissed()) | 485 if (snippet->is_dismissed()) |
477 content->dismissed.emplace_back(std::move(snippet)); | 486 content->dismissed.emplace_back(std::move(snippet)); |
478 else | 487 else |
479 content->snippets.emplace_back(std::move(snippet)); | 488 content->snippets.emplace_back(std::move(snippet)); |
480 } | 489 } |
481 | 490 |
482 std::sort(content->snippets.begin(), content->snippets.end(), | 491 std::sort(content->snippets.begin(), content->snippets.end(), |
483 [](const std::unique_ptr<NTPSnippet>& lhs, | 492 [](const std::unique_ptr<NTPSnippet>& lhs, |
(...skipping 12 matching lines...) Expand all Loading... | |
496 void NTPSnippetsService::OnDatabaseError() { | 505 void NTPSnippetsService::OnDatabaseError() { |
497 EnterState(State::ERROR_OCCURRED); | 506 EnterState(State::ERROR_OCCURRED); |
498 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | 507 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); |
499 } | 508 } |
500 | 509 |
501 void NTPSnippetsService::OnFetchFinished( | 510 void NTPSnippetsService::OnFetchFinished( |
502 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { | 511 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { |
503 if (!ready()) | 512 if (!ready()) |
504 return; | 513 return; |
505 | 514 |
515 // Mark all categories as not provided by the server in the latest fetch. The | |
516 // ones we got will be marked again below. | |
506 for (auto& item : categories_) { | 517 for (auto& item : categories_) { |
507 CategoryContent* content = &item.second; | 518 CategoryContent* content = &item.second; |
508 content->provided_by_server = false; | 519 content->provided_by_server = false; |
509 } | 520 } |
510 | 521 |
511 // Clear up expired dismissed snippets before we use them to filter new ones. | 522 // Clear up expired dismissed snippets before we use them to filter new ones. |
512 ClearExpiredDismissedSnippets(); | 523 ClearExpiredDismissedSnippets(); |
513 | 524 |
514 // If snippets were fetched successfully, update our |categories_| from each | 525 // If snippets were fetched successfully, update our |categories_| from each |
515 // category provided by the server. | 526 // category provided by the server. |
516 if (fetched_categories) { | 527 if (fetched_categories) { |
528 // TODO(treib): Reorder |categories_| to match the order we received from | |
529 // the server. crbug.com/653816 | |
517 // TODO(jkrcal): A bit hard to understand with so many variables called | 530 // TODO(jkrcal): A bit hard to understand with so many variables called |
518 // "*categor*". Isn't here some room for simplification? | 531 // "*categor*". Isn't here some room for simplification? |
519 for (NTPSnippetsFetcher::FetchedCategory& fetched_category : | 532 for (NTPSnippetsFetcher::FetchedCategory& fetched_category : |
520 *fetched_categories) { | 533 *fetched_categories) { |
521 Category category = fetched_category.category; | 534 Category category = fetched_category.category; |
522 | 535 |
523 // The ChromeReader backend doesn't provide category titles, so don't | 536 // The ChromeReader backend doesn't provide category titles, so don't |
524 // overwrite the existing title for ARTICLES if the new one is empty. | 537 // overwrite the existing title for ARTICLES if the new one is empty. |
525 // TODO(treib): Remove this check after we fully switch to the content | 538 // TODO(treib): Remove this check after we fully switch to the content |
526 // suggestions backend. | 539 // suggestions backend. |
(...skipping 11 matching lines...) Expand all Loading... | |
538 if (category == articles_category_) { | 551 if (category == articles_category_) { |
539 UMA_HISTOGRAM_SPARSE_SLOWLY( | 552 UMA_HISTOGRAM_SPARSE_SLOWLY( |
540 "NewTabPage.Snippets.NumArticlesFetched", | 553 "NewTabPage.Snippets.NumArticlesFetched", |
541 std::min(fetched_category.snippets.size(), | 554 std::min(fetched_category.snippets.size(), |
542 static_cast<size_t>(kMaxSnippetCount + 1))); | 555 static_cast<size_t>(kMaxSnippetCount + 1))); |
543 } | 556 } |
544 ReplaceSnippets(category, std::move(fetched_category.snippets)); | 557 ReplaceSnippets(category, std::move(fetched_category.snippets)); |
545 } | 558 } |
546 } | 559 } |
547 | 560 |
561 // We might have gotten new categories (or updated the titles of existing | |
562 // ones), so update the pref. | |
563 StoreCategoriesToPrefs(); | |
564 | |
548 for (const auto& item : categories_) { | 565 for (const auto& item : categories_) { |
549 Category category = item.first; | 566 Category category = item.first; |
550 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 567 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
551 } | 568 } |
552 | 569 |
553 // TODO(sfiera): equivalent metrics for non-articles. | 570 // TODO(sfiera): equivalent metrics for non-articles. |
554 const CategoryContent& content = categories_[articles_category_]; | 571 const CategoryContent& content = categories_[articles_category_]; |
555 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | 572 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", |
556 content.snippets.size()); | 573 content.snippets.size()); |
557 if (content.snippets.empty() && !content.dismissed.empty()) { | 574 if (content.snippets.empty() && !content.dismissed.empty()) { |
558 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 575 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", |
559 content.dismissed.size()); | 576 content.dismissed.size()); |
560 } | 577 } |
561 | 578 |
562 // TODO(sfiera): notify only when a category changed above. | 579 // TODO(sfiera): notify only when a category changed above. |
563 NotifyNewSuggestions(); | 580 NotifyNewSuggestions(); |
564 | 581 |
565 // Reschedule after a successful fetch. This resets all currently scheduled | 582 // Reschedule after a successful fetch. This resets all currently scheduled |
566 // fetches, to make sure the fallback interval triggers only if no wifi fetch | 583 // fetches, to make sure the fallback interval triggers only if no wifi fetch |
567 // succeeded, and also that we don't do a background fetch immediately after | 584 // succeeded, and also that we don't do a background fetch immediately after |
568 // a user-initiated one. | 585 // a user-initiated one. |
569 if (fetched_categories) | 586 if (fetched_categories) |
570 RescheduleFetching(true); | 587 RescheduleFetching(true); |
571 } | 588 } |
572 | 589 |
573 void NTPSnippetsService::ArchiveSnippets(Category category, | 590 void NTPSnippetsService::ArchiveSnippets(Category category, |
574 NTPSnippet::PtrVector* to_archive) { | 591 NTPSnippet::PtrVector* to_archive) { |
575 CategoryContent* content = &categories_[category]; | 592 CategoryContent* content = &categories_[category]; |
576 | 593 |
577 // TODO(sfiera): handle DB for non-articles too. | 594 // TODO(treib): Handle DB for non-articles too. crbug.com/653476 |
578 if (category == articles_category_) { | 595 if (category == articles_category_) { |
579 database_->DeleteSnippets(GetSnippetIDVector(*to_archive)); | 596 database_->DeleteSnippets(GetSnippetIDVector(*to_archive)); |
580 // Do not delete the thumbnail images as they are still handy on open NTPs. | 597 // Do not delete the thumbnail images as they are still handy on open NTPs. |
581 } | 598 } |
582 | 599 |
583 // Archive previous snippets - move them at the beginning of the list. | 600 // Archive previous snippets - move them at the beginning of the list. |
584 content->archived.insert(content->archived.begin(), | 601 content->archived.insert(content->archived.begin(), |
585 std::make_move_iterator(to_archive->begin()), | 602 std::make_move_iterator(to_archive->begin()), |
586 std::make_move_iterator(to_archive->end())); | 603 std::make_move_iterator(to_archive->end())); |
587 Compact(to_archive); | 604 Compact(to_archive); |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
685 if (content->snippets.empty() && content->dismissed.empty() && | 702 if (content->snippets.empty() && content->dismissed.empty() && |
686 category != articles_category_ && !content->provided_by_server) { | 703 category != articles_category_ && !content->provided_by_server) { |
687 categories_to_erase.push_back(category); | 704 categories_to_erase.push_back(category); |
688 } | 705 } |
689 } | 706 } |
690 | 707 |
691 for (Category category : categories_to_erase) { | 708 for (Category category : categories_to_erase) { |
692 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 709 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
693 categories_.erase(category); | 710 categories_.erase(category); |
694 } | 711 } |
712 | |
713 StoreCategoriesToPrefs(); | |
695 } | 714 } |
696 | 715 |
697 void NTPSnippetsService::ClearOrphanedImages() { | 716 void NTPSnippetsService::ClearOrphanedImages() { |
698 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); | 717 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); |
699 for (const auto& snippet_ptr : categories_[articles_category_].snippets) { | 718 for (const auto& snippet_ptr : categories_[articles_category_].snippets) { |
700 alive_snippets->insert(snippet_ptr->id()); | 719 alive_snippets->insert(snippet_ptr->id()); |
701 } | 720 } |
702 for (const auto& snippet_ptr : categories_[articles_category_].dismissed) { | 721 for (const auto& snippet_ptr : categories_[articles_category_].dismissed) { |
703 alive_snippets->insert(snippet_ptr->id()); | 722 alive_snippets->insert(snippet_ptr->id()); |
704 } | 723 } |
(...skipping 14 matching lines...) Expand all Loading... | |
719 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 738 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
720 | 739 |
721 // Remove the category entirely; it may or may not reappear. | 740 // Remove the category entirely; it may or may not reappear. |
722 if (category != articles_category_) | 741 if (category != articles_category_) |
723 categories_to_erase.push_back(category); | 742 categories_to_erase.push_back(category); |
724 } | 743 } |
725 | 744 |
726 for (Category category : categories_to_erase) { | 745 for (Category category : categories_to_erase) { |
727 categories_.erase(category); | 746 categories_.erase(category); |
728 } | 747 } |
748 | |
749 StoreCategoriesToPrefs(); | |
729 } | 750 } |
730 | 751 |
731 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase( | 752 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase( |
732 const ImageFetchedCallback& callback, | 753 const ImageFetchedCallback& callback, |
733 const ContentSuggestion::ID& suggestion_id, | 754 const ContentSuggestion::ID& suggestion_id, |
734 std::string data) { | 755 std::string data) { |
735 // |image_decoder_| is null in tests. | 756 // |image_decoder_| is null in tests. |
736 if (image_decoder_ && !data.empty()) { | 757 if (image_decoder_ && !data.empty()) { |
737 image_decoder_->DecodeImage( | 758 image_decoder_->DecodeImage( |
738 data, base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase, | 759 data, base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase, |
(...skipping 16 matching lines...) Expand all Loading... | |
755 | 776 |
756 // If decoding the image failed, delete the DB entry. | 777 // If decoding the image failed, delete the DB entry. |
757 database_->DeleteImage(suggestion_id.id_within_category()); | 778 database_->DeleteImage(suggestion_id.id_within_category()); |
758 | 779 |
759 FetchSnippetImageFromNetwork(suggestion_id, callback); | 780 FetchSnippetImageFromNetwork(suggestion_id, callback); |
760 } | 781 } |
761 | 782 |
762 void NTPSnippetsService::FetchSnippetImageFromNetwork( | 783 void NTPSnippetsService::FetchSnippetImageFromNetwork( |
763 const ContentSuggestion::ID& suggestion_id, | 784 const ContentSuggestion::ID& suggestion_id, |
764 const ImageFetchedCallback& callback) { | 785 const ImageFetchedCallback& callback) { |
765 if (categories_.find(suggestion_id.category()) == categories_.end()) { | 786 if (!base::ContainsKey(categories_, suggestion_id.category())) { |
766 OnSnippetImageDecodedFromNetwork( | 787 OnSnippetImageDecodedFromNetwork( |
767 callback, suggestion_id.id_within_category(), gfx::Image()); | 788 callback, suggestion_id.id_within_category(), gfx::Image()); |
768 return; | 789 return; |
769 } | 790 } |
770 | 791 |
771 GURL image_url = FindSnippetImageUrl(suggestion_id); | 792 GURL image_url = FindSnippetImageUrl(suggestion_id); |
772 | 793 |
773 if (image_url.is_empty() || | 794 if (image_url.is_empty() || |
774 !thumbnail_requests_throttler_.DemandQuotaForRequest( | 795 !thumbnail_requests_throttler_.DemandQuotaForRequest( |
775 /*interactive_request=*/true)) { | 796 /*interactive_request=*/true)) { |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
960 } | 981 } |
961 | 982 |
962 DVLOG(1) << "NotifyNewSuggestions(): " << result.size() | 983 DVLOG(1) << "NotifyNewSuggestions(): " << result.size() |
963 << " items in category " << category; | 984 << " items in category " << category; |
964 observer()->OnNewSuggestions(this, category, std::move(result)); | 985 observer()->OnNewSuggestions(this, category, std::move(result)); |
965 } | 986 } |
966 } | 987 } |
967 | 988 |
968 void NTPSnippetsService::UpdateCategoryStatus(Category category, | 989 void NTPSnippetsService::UpdateCategoryStatus(Category category, |
969 CategoryStatus status) { | 990 CategoryStatus status) { |
970 DCHECK(categories_.find(category) != categories_.end()); | 991 DCHECK(base::ContainsKey(categories_, category)); |
971 CategoryContent& content = categories_[category]; | 992 CategoryContent& content = categories_[category]; |
972 if (status == content.status) | 993 if (status == content.status) |
973 return; | 994 return; |
974 | 995 |
975 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " | 996 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " |
976 << static_cast<int>(content.status) << " -> " | 997 << static_cast<int>(content.status) << " -> " |
977 << static_cast<int>(status); | 998 << static_cast<int>(status); |
978 content.status = status; | 999 content.status = status; |
979 observer()->OnCategoryStatusChanged(this, category, content.status); | 1000 observer()->OnCategoryStatusChanged(this, category, content.status); |
980 } | 1001 } |
(...skipping 19 matching lines...) Expand all Loading... | |
1000 archived.begin(), archived.end(), | 1021 archived.begin(), archived.end(), |
1001 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { | 1022 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { |
1002 return snippet->id() == id_within_category; | 1023 return snippet->id() == id_within_category; |
1003 }); | 1024 }); |
1004 if (it != archived.end()) | 1025 if (it != archived.end()) |
1005 return it->get(); | 1026 return it->get(); |
1006 | 1027 |
1007 return nullptr; | 1028 return nullptr; |
1008 } | 1029 } |
1009 | 1030 |
1031 namespace { | |
1032 | |
1033 const char kCategoryContentId[] = "id"; | |
Bernhard Bauer
2016/10/07 16:21:27
Hm... Shouldn't the anonymous namespace go to the
Marc Treib
2016/10/10 08:33:43
That's certainly the common thing, though the styl
| |
1034 const char kCategoryContentTitle[] = "title"; | |
1035 const char kCategoryContentProvidedByServer[] = "provided_by_server"; | |
1036 | |
1037 } // namespace | |
1038 | |
1039 void NTPSnippetsService::RestoreCategoriesFromPrefs() { | |
1040 // This must only be called at startup, before there are any categories. | |
1041 DCHECK(categories_.empty()); | |
1042 | |
1043 const base::ListValue* list = | |
1044 pref_service_->GetList(prefs::kRemoteSuggestionCategories); | |
1045 for (const std::unique_ptr<base::Value>& entry : *list) { | |
1046 const base::DictionaryValue* dict = nullptr; | |
1047 if (!entry->GetAsDictionary(&dict)) { | |
1048 DLOG(WARNING) << "Invalid category pref value: " << *entry; | |
1049 continue; | |
1050 } | |
1051 int id = 0; | |
1052 if (!dict->GetInteger(kCategoryContentId, &id)) { | |
1053 DLOG(WARNING) << "Invalid category pref value, missing '" | |
1054 << kCategoryContentId << "': " << *entry; | |
1055 continue; | |
1056 } | |
1057 base::string16 title; | |
1058 if (!dict->GetString(kCategoryContentTitle, &title)) { | |
1059 DLOG(WARNING) << "Invalid category pref value, missing '" | |
1060 << kCategoryContentTitle << "': " << *entry; | |
1061 continue; | |
1062 } | |
1063 bool provided_by_server = false; | |
1064 if (!dict->GetBoolean(kCategoryContentProvidedByServer, | |
1065 &provided_by_server)) { | |
1066 DLOG(WARNING) << "Invalid category pref value, missing '" | |
1067 << kCategoryContentProvidedByServer << "': " << *entry; | |
1068 continue; | |
1069 } | |
1070 | |
1071 Category category = category_factory()->FromIDValue(id); | |
1072 categories_[category] = CategoryContent(); | |
1073 categories_[category].localized_title = title; | |
1074 categories_[category].provided_by_server = provided_by_server; | |
1075 } | |
1076 } | |
1077 | |
1078 void NTPSnippetsService::StoreCategoriesToPrefs() { | |
1079 // Collect all the CategoryContents. | |
1080 std::vector<std::pair<Category, const CategoryContent*>> to_store; | |
1081 for (const auto& entry : categories_) | |
1082 to_store.emplace_back(entry.first, &entry.second); | |
1083 // Sort them into the proper category order. | |
1084 std::sort(to_store.begin(), to_store.end(), | |
1085 [this](const std::pair<Category, const CategoryContent*>& left, | |
1086 const std::pair<Category, const CategoryContent*>& right) { | |
1087 return category_factory()->CompareCategories(left.first, | |
1088 right.first); | |
1089 }); | |
1090 // Convert the relevant info into a base::ListValue for storage. | |
1091 base::ListValue list; | |
1092 for (const auto& entry : to_store) { | |
1093 Category category = entry.first; | |
1094 const base::string16& title = entry.second->localized_title; | |
1095 bool provided_by_server = entry.second->provided_by_server; | |
1096 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
1097 dict->SetInteger(kCategoryContentId, category.id()); | |
1098 dict->SetString(kCategoryContentTitle, title); | |
1099 dict->SetBoolean(kCategoryContentProvidedByServer, provided_by_server); | |
1100 list.Append(std::move(dict)); | |
1101 } | |
1102 // Finally, store the result in the pref service. | |
1103 pref_service_->Set(prefs::kRemoteSuggestionCategories, list); | |
1104 } | |
1105 | |
1010 NTPSnippetsService::CategoryContent::CategoryContent() = default; | 1106 NTPSnippetsService::CategoryContent::CategoryContent() = default; |
1011 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = | 1107 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = |
1012 default; | 1108 default; |
1013 NTPSnippetsService::CategoryContent::~CategoryContent() = default; | 1109 NTPSnippetsService::CategoryContent::~CategoryContent() = default; |
1014 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: | 1110 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: |
1015 operator=(CategoryContent&&) = default; | 1111 operator=(CategoryContent&&) = default; |
1016 | 1112 |
1017 } // namespace ntp_snippets | 1113 } // namespace ntp_snippets |
OLD | NEW |