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

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

Issue 2395273003: [NTP Snippets] Persist remote categories in prefs (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/ntp_snippets/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
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
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
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
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
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.
(...skipping 22 matching lines...) Expand all
538 if (category == articles_category_) { 549 if (category == articles_category_) {
539 UMA_HISTOGRAM_SPARSE_SLOWLY( 550 UMA_HISTOGRAM_SPARSE_SLOWLY(
540 "NewTabPage.Snippets.NumArticlesFetched", 551 "NewTabPage.Snippets.NumArticlesFetched",
541 std::min(fetched_category.snippets.size(), 552 std::min(fetched_category.snippets.size(),
542 static_cast<size_t>(kMaxSnippetCount + 1))); 553 static_cast<size_t>(kMaxSnippetCount + 1)));
543 } 554 }
544 ReplaceSnippets(category, std::move(fetched_category.snippets)); 555 ReplaceSnippets(category, std::move(fetched_category.snippets));
545 } 556 }
546 } 557 }
547 558
559 // We might have gotten new categories (or updated the titles of existing
560 // ones), so update the pref.
561 StoreCategoriesToPrefs();
562
548 for (const auto& item : categories_) { 563 for (const auto& item : categories_) {
549 Category category = item.first; 564 Category category = item.first;
550 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); 565 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
551 } 566 }
552 567
553 // TODO(sfiera): equivalent metrics for non-articles. 568 // TODO(sfiera): equivalent metrics for non-articles.
554 const CategoryContent& content = categories_[articles_category_]; 569 const CategoryContent& content = categories_[articles_category_];
555 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", 570 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
556 content.snippets.size()); 571 content.snippets.size());
557 if (content.snippets.empty() && !content.dismissed.empty()) { 572 if (content.snippets.empty() && !content.dismissed.empty()) {
558 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", 573 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
559 content.dismissed.size()); 574 content.dismissed.size());
560 } 575 }
561 576
562 // TODO(sfiera): notify only when a category changed above. 577 // TODO(sfiera): notify only when a category changed above.
563 NotifyNewSuggestions(); 578 NotifyNewSuggestions();
564 579
565 // Reschedule after a successful fetch. This resets all currently scheduled 580 // Reschedule after a successful fetch. This resets all currently scheduled
566 // fetches, to make sure the fallback interval triggers only if no wifi fetch 581 // 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 582 // succeeded, and also that we don't do a background fetch immediately after
568 // a user-initiated one. 583 // a user-initiated one.
569 if (fetched_categories) 584 if (fetched_categories)
570 RescheduleFetching(true); 585 RescheduleFetching(true);
571 } 586 }
572 587
573 void NTPSnippetsService::ArchiveSnippets(Category category, 588 void NTPSnippetsService::ArchiveSnippets(Category category,
574 NTPSnippet::PtrVector* to_archive) { 589 NTPSnippet::PtrVector* to_archive) {
575 CategoryContent* content = &categories_[category]; 590 CategoryContent* content = &categories_[category];
576 591
577 // TODO(sfiera): handle DB for non-articles too. 592 // TODO(treib): Handle DB for non-articles too. crbug.com/653476
578 if (category == articles_category_) { 593 if (category == articles_category_) {
579 database_->DeleteSnippets(GetSnippetIDVector(*to_archive)); 594 database_->DeleteSnippets(GetSnippetIDVector(*to_archive));
580 // Do not delete the thumbnail images as they are still handy on open NTPs. 595 // Do not delete the thumbnail images as they are still handy on open NTPs.
581 } 596 }
582 597
583 // Archive previous snippets - move them at the beginning of the list. 598 // Archive previous snippets - move them at the beginning of the list.
584 content->archived.insert(content->archived.begin(), 599 content->archived.insert(content->archived.begin(),
585 std::make_move_iterator(to_archive->begin()), 600 std::make_move_iterator(to_archive->begin()),
586 std::make_move_iterator(to_archive->end())); 601 std::make_move_iterator(to_archive->end()));
587 Compact(to_archive); 602 Compact(to_archive);
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
685 if (content->snippets.empty() && content->dismissed.empty() && 700 if (content->snippets.empty() && content->dismissed.empty() &&
686 category != articles_category_ && !content->provided_by_server) { 701 category != articles_category_ && !content->provided_by_server) {
687 categories_to_erase.push_back(category); 702 categories_to_erase.push_back(category);
688 } 703 }
689 } 704 }
690 705
691 for (Category category : categories_to_erase) { 706 for (Category category : categories_to_erase) {
692 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); 707 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED);
693 categories_.erase(category); 708 categories_.erase(category);
694 } 709 }
710
711 StoreCategoriesToPrefs();
695 } 712 }
696 713
697 void NTPSnippetsService::ClearOrphanedImages() { 714 void NTPSnippetsService::ClearOrphanedImages() {
698 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); 715 auto alive_snippets = base::MakeUnique<std::set<std::string>>();
699 for (const auto& snippet_ptr : categories_[articles_category_].snippets) { 716 for (const auto& snippet_ptr : categories_[articles_category_].snippets) {
700 alive_snippets->insert(snippet_ptr->id()); 717 alive_snippets->insert(snippet_ptr->id());
701 } 718 }
702 for (const auto& snippet_ptr : categories_[articles_category_].dismissed) { 719 for (const auto& snippet_ptr : categories_[articles_category_].dismissed) {
703 alive_snippets->insert(snippet_ptr->id()); 720 alive_snippets->insert(snippet_ptr->id());
704 } 721 }
(...skipping 14 matching lines...) Expand all
719 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); 736 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED);
720 737
721 // Remove the category entirely; it may or may not reappear. 738 // Remove the category entirely; it may or may not reappear.
722 if (category != articles_category_) 739 if (category != articles_category_)
723 categories_to_erase.push_back(category); 740 categories_to_erase.push_back(category);
724 } 741 }
725 742
726 for (Category category : categories_to_erase) { 743 for (Category category : categories_to_erase) {
727 categories_.erase(category); 744 categories_.erase(category);
728 } 745 }
746
747 StoreCategoriesToPrefs();
729 } 748 }
730 749
731 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase( 750 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase(
732 const ImageFetchedCallback& callback, 751 const ImageFetchedCallback& callback,
733 const ContentSuggestion::ID& suggestion_id, 752 const ContentSuggestion::ID& suggestion_id,
734 std::string data) { 753 std::string data) {
735 // |image_decoder_| is null in tests. 754 // |image_decoder_| is null in tests.
736 if (image_decoder_ && !data.empty()) { 755 if (image_decoder_ && !data.empty()) {
737 image_decoder_->DecodeImage( 756 image_decoder_->DecodeImage(
738 data, base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase, 757 data, base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase,
(...skipping 16 matching lines...) Expand all
755 774
756 // If decoding the image failed, delete the DB entry. 775 // If decoding the image failed, delete the DB entry.
757 database_->DeleteImage(suggestion_id.id_within_category()); 776 database_->DeleteImage(suggestion_id.id_within_category());
758 777
759 FetchSnippetImageFromNetwork(suggestion_id, callback); 778 FetchSnippetImageFromNetwork(suggestion_id, callback);
760 } 779 }
761 780
762 void NTPSnippetsService::FetchSnippetImageFromNetwork( 781 void NTPSnippetsService::FetchSnippetImageFromNetwork(
763 const ContentSuggestion::ID& suggestion_id, 782 const ContentSuggestion::ID& suggestion_id,
764 const ImageFetchedCallback& callback) { 783 const ImageFetchedCallback& callback) {
765 if (categories_.find(suggestion_id.category()) == categories_.end()) { 784 if (!base::ContainsKey(categories_, suggestion_id.category())) {
766 OnSnippetImageDecodedFromNetwork( 785 OnSnippetImageDecodedFromNetwork(
767 callback, suggestion_id.id_within_category(), gfx::Image()); 786 callback, suggestion_id.id_within_category(), gfx::Image());
768 return; 787 return;
769 } 788 }
770 789
771 GURL image_url = FindSnippetImageUrl(suggestion_id); 790 GURL image_url = FindSnippetImageUrl(suggestion_id);
772 791
773 if (image_url.is_empty() || 792 if (image_url.is_empty() ||
774 !thumbnail_requests_throttler_.DemandQuotaForRequest( 793 !thumbnail_requests_throttler_.DemandQuotaForRequest(
775 /*interactive_request=*/true)) { 794 /*interactive_request=*/true)) {
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
960 } 979 }
961 980
962 DVLOG(1) << "NotifyNewSuggestions(): " << result.size() 981 DVLOG(1) << "NotifyNewSuggestions(): " << result.size()
963 << " items in category " << category; 982 << " items in category " << category;
964 observer()->OnNewSuggestions(this, category, std::move(result)); 983 observer()->OnNewSuggestions(this, category, std::move(result));
965 } 984 }
966 } 985 }
967 986
968 void NTPSnippetsService::UpdateCategoryStatus(Category category, 987 void NTPSnippetsService::UpdateCategoryStatus(Category category,
969 CategoryStatus status) { 988 CategoryStatus status) {
970 DCHECK(categories_.find(category) != categories_.end()); 989 DCHECK(base::ContainsKey(categories_, category));
971 CategoryContent& content = categories_[category]; 990 CategoryContent& content = categories_[category];
972 if (status == content.status) 991 if (status == content.status)
973 return; 992 return;
974 993
975 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " 994 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": "
976 << static_cast<int>(content.status) << " -> " 995 << static_cast<int>(content.status) << " -> "
977 << static_cast<int>(status); 996 << static_cast<int>(status);
978 content.status = status; 997 content.status = status;
979 observer()->OnCategoryStatusChanged(this, category, content.status); 998 observer()->OnCategoryStatusChanged(this, category, content.status);
980 } 999 }
(...skipping 19 matching lines...) Expand all
1000 archived.begin(), archived.end(), 1019 archived.begin(), archived.end(),
1001 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { 1020 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) {
1002 return snippet->id() == id_within_category; 1021 return snippet->id() == id_within_category;
1003 }); 1022 });
1004 if (it != archived.end()) 1023 if (it != archived.end())
1005 return it->get(); 1024 return it->get();
1006 1025
1007 return nullptr; 1026 return nullptr;
1008 } 1027 }
1009 1028
1029 namespace {
1030
1031 const char kCategoryContentId[] = "id";
1032 const char kCategoryContentTitle[] = "title";
1033 const char kCategoryContentProvidedByServer[] = "provided_by_server";
1034
1035 } // namespace
1036
1037 void NTPSnippetsService::RestoreCategoriesFromPrefs() {
1038 // This must only be called at startup, before there are any categories.
1039 DCHECK(categories_.empty());
1040
1041 const base::ListValue* list =
1042 pref_service_->GetList(prefs::kRemoteSuggestionCategories);
1043 for (const std::unique_ptr<base::Value>& entry : *list) {
1044 const base::DictionaryValue* dict = nullptr;
1045 if (!entry->GetAsDictionary(&dict)) {
1046 DLOG(WARNING) << "Invalid category pref value: " << *entry;
1047 continue;
1048 }
1049 int id = 0;
1050 if (!dict->GetInteger(kCategoryContentId, &id)) {
1051 DLOG(WARNING) << "Invalid category pref value, missing '"
1052 << kCategoryContentId << "': " << *entry;
1053 continue;
1054 }
1055 base::string16 title;
1056 if (!dict->GetString(kCategoryContentTitle, &title)) {
1057 DLOG(WARNING) << "Invalid category pref value, missing '"
1058 << kCategoryContentTitle << "': " << *entry;
1059 continue;
1060 }
1061 bool provided_by_server = false;
1062 if (!dict->GetBoolean(kCategoryContentProvidedByServer,
1063 &provided_by_server)) {
1064 DLOG(WARNING) << "Invalid category pref value, missing '"
1065 << kCategoryContentProvidedByServer << "': " << *entry;
1066 continue;
1067 }
1068
1069 Category category = category_factory()->FromIDValue(id);
1070 categories_[category] = CategoryContent();
1071 categories_[category].localized_title = title;
1072 categories_[category].provided_by_server = provided_by_server;
1073 }
1074 }
1075
1076 void NTPSnippetsService::StoreCategoriesToPrefs() {
1077 // Collect all the CategoryContents.
1078 std::vector<std::pair<Category, const CategoryContent*>> to_store;
1079 for (const auto& entry : categories_)
1080 to_store.emplace_back(entry.first, &entry.second);
1081 // Sort them into the proper category order.
1082 std::sort(to_store.begin(), to_store.end(),
1083 [this](const std::pair<Category, const CategoryContent*>& left,
1084 const std::pair<Category, const CategoryContent*>& right) {
1085 return category_factory()->CompareCategories(left.first,
1086 right.first);
1087 });
1088 // Convert the relevant info into a base::ListValue for storage.
1089 base::ListValue list;
1090 for (const auto& entry : to_store) {
1091 Category category = entry.first;
1092 const base::string16& title = entry.second->localized_title;
1093 bool provided_by_server = entry.second->provided_by_server;
1094 auto dict = base::MakeUnique<base::DictionaryValue>();
1095 dict->SetInteger(kCategoryContentId, category.id());
1096 dict->SetString(kCategoryContentTitle, title);
1097 dict->SetBoolean(kCategoryContentProvidedByServer, provided_by_server);
1098 list.Append(std::move(dict));
1099 }
1100 // Finally, store the result in the pref service.
1101 pref_service_->Set(prefs::kRemoteSuggestionCategories, list);
1102 }
1103
1010 NTPSnippetsService::CategoryContent::CategoryContent() = default; 1104 NTPSnippetsService::CategoryContent::CategoryContent() = default;
1011 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = 1105 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) =
1012 default; 1106 default;
1013 NTPSnippetsService::CategoryContent::~CategoryContent() = default; 1107 NTPSnippetsService::CategoryContent::~CategoryContent() = default;
1014 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: 1108 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent::
1015 operator=(CategoryContent&&) = default; 1109 operator=(CategoryContent&&) = default;
1016 1110
1017 } // namespace ntp_snippets 1111 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698