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

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

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

Powered by Google App Engine
This is Rietveld 408576698