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

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

Issue 2051823003: [NTP Snippets] More reliable service initialization (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 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
« no previous file with comments | « components/ntp_snippets/ntp_snippets_service.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/ntp_snippets_service.h" 5 #include "components/ntp_snippets/ntp_snippets_service.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 #include <utility> 9 #include <utility>
10 10
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 database_(std::move(database)), 200 database_(std::move(database)),
201 fetch_after_load_(false) { 201 fetch_after_load_(false) {
202 // TODO(dgn) should be removed after branch point (https:://crbug.com/617585). 202 // TODO(dgn) should be removed after branch point (https:://crbug.com/617585).
203 ClearDeprecatedPrefs(); 203 ClearDeprecatedPrefs();
204 204
205 if (explicitly_disabled_) { 205 if (explicitly_disabled_) {
206 EnterState(State::DISABLED); 206 EnterState(State::DISABLED);
207 return; 207 return;
208 } 208 }
209 209
210 snippets_fetcher_->SetCallback(base::Bind( 210 // We transition to other states while finalizing the initialization, when the
211 &NTPSnippetsService::OnFetchFinished, base::Unretained(this))); 211 // database is done loading.
212
213 // |sync_service_| can be null in tests or if sync is disabled.
214 // This is a service we want to keep listening to all the time, independently
215 // from the state, since it will allow us to enable or disable the snippets
216 // service.
217 if (sync_service_)
218 sync_service_observer_.Add(sync_service_);
219
220 // Will trigger the transition to the READY state.
221 database_->Load(base::Bind(&NTPSnippetsService::OnDatabaseLoaded, 212 database_->Load(base::Bind(&NTPSnippetsService::OnDatabaseLoaded,
222 base::Unretained(this))); 213 base::Unretained(this)));
223 } 214 }
224 215
225 NTPSnippetsService::~NTPSnippetsService() { 216 NTPSnippetsService::~NTPSnippetsService() {
226 DCHECK(state_ == State::SHUT_DOWN); 217 DCHECK(state_ == State::SHUT_DOWN);
227 } 218 }
228 219
229 // static 220 // static
230 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { 221 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) {
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
382 373
383 // static 374 // static
384 int NTPSnippetsService::GetMaxSnippetCountForTesting() { 375 int NTPSnippetsService::GetMaxSnippetCountForTesting() {
385 return kMaxSnippetCount; 376 return kMaxSnippetCount;
386 } 377 }
387 378
388 //////////////////////////////////////////////////////////////////////////////// 379 ////////////////////////////////////////////////////////////////////////////////
389 // Private methods 380 // Private methods
390 381
391 void NTPSnippetsService::OnStateChanged() { 382 void NTPSnippetsService::OnStateChanged() {
392 if (!initialized()) 383 if (state_ == State::SHUT_DOWN)
393 return; 384 return;
394 385
395 DVLOG(1) << "[OnStateChanged]"; 386 DVLOG(1) << "[OnStateChanged]";
396 EnterState(GetStateForDependenciesStatus()); 387 EnterState(GetStateForDependenciesStatus());
397 } 388 }
398 389
399 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { 390 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) {
400 DCHECK(state_ == State::NOT_INITED || state_ == State::SHUT_DOWN); 391 DCHECK(state_ == State::NOT_INITED || state_ == State::SHUT_DOWN);
401 if (state_ == State::SHUT_DOWN) 392 if (state_ == State::SHUT_DOWN)
402 return; 393 return;
403 394
404 DCHECK(snippets_.empty()); 395 DCHECK(snippets_.empty());
405 DCHECK(discarded_snippets_.empty()); 396 DCHECK(discarded_snippets_.empty());
406 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { 397 for (std::unique_ptr<NTPSnippet>& snippet : snippets) {
407 if (snippet->is_discarded()) 398 if (snippet->is_discarded())
408 discarded_snippets_.emplace_back(std::move(snippet)); 399 discarded_snippets_.emplace_back(std::move(snippet));
409 else 400 else
410 snippets_.emplace_back(std::move(snippet)); 401 snippets_.emplace_back(std::move(snippet));
411 } 402 }
412 std::sort(snippets_.begin(), snippets_.end(), 403 std::sort(snippets_.begin(), snippets_.end(),
413 [](const std::unique_ptr<NTPSnippet>& lhs, 404 [](const std::unique_ptr<NTPSnippet>& lhs,
414 const std::unique_ptr<NTPSnippet>& rhs) { 405 const std::unique_ptr<NTPSnippet>& rhs) {
415 return lhs->score() > rhs->score(); 406 return lhs->score() > rhs->score();
416 }); 407 });
417 408
418 // Change state after we started loading the snippets. During startup, the 409 ClearExpiredSnippets();
419 // Sync service might not be completely loaded when we initialize this 410 FinishInitialization();
420 // service. Whether we had snippets in the last season is used to guess if
421 // we should enable the service or not. See |GetStateForDependenciesStatus|.
422 EnterState(GetStateForDependenciesStatus());
423
424 LoadingSnippetsFinished();
425
426 // Start a fetch if we don't have any snippets yet, or a fetch was requested
427 // earlier.
428 if (snippets_.empty() || fetch_after_load_) {
429 fetch_after_load_ = false;
430 FetchSnippets();
431 }
432 } 411 }
433 412
434 void NTPSnippetsService::OnSuggestionsChanged( 413 void NTPSnippetsService::OnSuggestionsChanged(
435 const SuggestionsProfile& suggestions) { 414 const SuggestionsProfile& suggestions) {
436 DCHECK(initialized()); 415 DCHECK(initialized());
437 416
438 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); 417 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions);
439 if (hosts == GetSnippetHostsFromPrefs()) 418 if (hosts == GetSnippetHostsFromPrefs())
440 return; 419 return;
441 420
(...skipping 24 matching lines...) Expand all
466 return; 445 return;
467 446
468 if (snippets) { 447 if (snippets) {
469 // Sparse histogram used because the number of snippets is small (bound by 448 // Sparse histogram used because the number of snippets is small (bound by
470 // kMaxSnippetCount). 449 // kMaxSnippetCount).
471 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); 450 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount));
472 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", 451 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched",
473 snippets->size()); 452 snippets->size());
474 MergeSnippets(std::move(*snippets)); 453 MergeSnippets(std::move(*snippets));
475 } 454 }
476 LoadingSnippetsFinished(); 455
456 ClearExpiredSnippets();
457
458 // If there are still more snippets than we want to show, move the extra ones
459 // over into |to_delete|.
460 NTPSnippet::PtrVector to_delete;
461 if (snippets_.size() > kMaxSnippetCount) {
462 to_delete.insert(
463 to_delete.end(),
464 std::make_move_iterator(snippets_.begin() + kMaxSnippetCount),
465 std::make_move_iterator(snippets_.end()));
466 snippets_.resize(kMaxSnippetCount);
467 }
468 database_->Delete(to_delete);
469
470 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
471 snippets_.size());
472 if (snippets_.empty() && !discarded_snippets_.empty()) {
473 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
474 discarded_snippets_.size());
475 }
476
477 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
478 NTPSnippetsServiceLoaded());
477 } 479 }
478 480
479 void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) { 481 void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) {
480 DCHECK(ready()); 482 DCHECK(ready());
481 483
482 // Remove new snippets that we already have, or that have been discarded. 484 // Remove new snippets that we already have, or that have been discarded.
483 std::set<std::string> old_snippet_ids; 485 std::set<std::string> old_snippet_ids;
484 InsertAllIDs(discarded_snippets_, &old_snippet_ids); 486 InsertAllIDs(discarded_snippets_, &old_snippet_ids);
485 InsertAllIDs(snippets_, &old_snippet_ids); 487 InsertAllIDs(snippets_, &old_snippet_ids);
486 new_snippets.erase( 488 new_snippets.erase(
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
552 } 554 }
553 555
554 void NTPSnippetsService::StoreSnippetHostsToPrefs( 556 void NTPSnippetsService::StoreSnippetHostsToPrefs(
555 const std::set<std::string>& hosts) { 557 const std::set<std::string>& hosts) {
556 base::ListValue list; 558 base::ListValue list;
557 for (const std::string& host : hosts) 559 for (const std::string& host : hosts)
558 list.AppendString(host); 560 list.AppendString(host);
559 pref_service_->Set(prefs::kSnippetHosts, list); 561 pref_service_->Set(prefs::kSnippetHosts, list);
560 } 562 }
561 563
562 void NTPSnippetsService::LoadingSnippetsFinished() { 564 void NTPSnippetsService::ClearExpiredSnippets() {
563 DCHECK(ready());
564
565 // Remove expired snippets.
566 base::Time expiry = base::Time::Now(); 565 base::Time expiry = base::Time::Now();
567 566
568 // Move expired snippets over into |to_delete|. 567 // Move expired snippets over into |to_delete|.
569 NTPSnippet::PtrVector to_delete; 568 NTPSnippet::PtrVector to_delete;
570 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) { 569 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) {
571 if (snippet->expiry_date() <= expiry) 570 if (snippet->expiry_date() <= expiry)
572 to_delete.emplace_back(std::move(snippet)); 571 to_delete.emplace_back(std::move(snippet));
573 } 572 }
574 Compact(&snippets_); 573 Compact(&snippets_);
575 574
576 // If there are still more snippets than we want to show, move the extra ones
577 // over into |to_delete| as well.
578 if (snippets_.size() > kMaxSnippetCount) {
579 to_delete.insert(
580 to_delete.end(),
581 std::make_move_iterator(snippets_.begin() + kMaxSnippetCount),
582 std::make_move_iterator(snippets_.end()));
583 snippets_.resize(kMaxSnippetCount);
584 }
585
586 // Move expired discarded snippets over into |to_delete| as well. 575 // Move expired discarded snippets over into |to_delete| as well.
587 for (std::unique_ptr<NTPSnippet>& snippet : discarded_snippets_) { 576 for (std::unique_ptr<NTPSnippet>& snippet : discarded_snippets_) {
588 if (snippet->expiry_date() <= expiry) 577 if (snippet->expiry_date() <= expiry)
589 to_delete.emplace_back(std::move(snippet)); 578 to_delete.emplace_back(std::move(snippet));
590 } 579 }
591 Compact(&discarded_snippets_); 580 Compact(&discarded_snippets_);
592 581
593 // Finally, actually delete the removed snippets from the DB. 582 // Finally, actually delete the removed snippets from the DB.
594 database_->Delete(to_delete); 583 database_->Delete(to_delete);
595 584
596 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
597 snippets_.size());
598 if (snippets_.empty() && !discarded_snippets_.empty()) {
599 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
600 discarded_snippets_.size());
601 }
602
603 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
604 NTPSnippetsServiceLoaded());
605
606 // If there are any snippets left, schedule a timer for the next expiry. 585 // If there are any snippets left, schedule a timer for the next expiry.
607 if (snippets_.empty() && discarded_snippets_.empty()) 586 if (snippets_.empty() && discarded_snippets_.empty())
608 return; 587 return;
609 588
610 base::Time next_expiry = base::Time::Max(); 589 base::Time next_expiry = base::Time::Max();
611 for (const auto& snippet : snippets_) { 590 for (const auto& snippet : snippets_) {
612 if (snippet->expiry_date() < next_expiry) 591 if (snippet->expiry_date() < next_expiry)
613 next_expiry = snippet->expiry_date(); 592 next_expiry = snippet->expiry_date();
614 } 593 }
615 for (const auto& snippet : discarded_snippets_) { 594 for (const auto& snippet : discarded_snippets_) {
616 if (snippet->expiry_date() < next_expiry) 595 if (snippet->expiry_date() < next_expiry)
617 next_expiry = snippet->expiry_date(); 596 next_expiry = snippet->expiry_date();
618 } 597 }
619 DCHECK_GT(next_expiry, expiry); 598 DCHECK_GT(next_expiry, expiry);
620 expiry_timer_.Start(FROM_HERE, next_expiry - expiry, 599 expiry_timer_.Start(FROM_HERE, next_expiry - expiry,
621 base::Bind(&NTPSnippetsService::LoadingSnippetsFinished, 600 base::Bind(&NTPSnippetsService::ClearExpiredSnippets,
622 base::Unretained(this))); 601 base::Unretained(this)));
623 } 602 }
624 603
625 void NTPSnippetsService::EnterStateEnabled(bool fetch_snippets) { 604 void NTPSnippetsService::EnterStateEnabled(bool fetch_snippets) {
626 if (fetch_snippets) 605 if (fetch_snippets)
627 FetchSnippets(); 606 FetchSnippets();
628 607
629 // If host restrictions are enabled, register for host list updates. 608 // If host restrictions are enabled, register for host list updates.
630 // |suggestions_service_| can be null in tests. 609 // |suggestions_service_| can be null in tests.
631 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) { 610 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) {
(...skipping 21 matching lines...) Expand all
653 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, 632 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
654 NTPSnippetsServiceShutdown()); 633 NTPSnippetsServiceShutdown());
655 634
656 expiry_timer_.Stop(); 635 expiry_timer_.Stop();
657 suggestions_service_subscription_.reset(); 636 suggestions_service_subscription_.reset();
658 637
659 if (sync_service_) 638 if (sync_service_)
660 sync_service_observer_.Remove(sync_service_); 639 sync_service_observer_.Remove(sync_service_);
661 } 640 }
662 641
642 void NTPSnippetsService::FinishInitialization() {
643 snippets_fetcher_->SetCallback(
644 base::Bind(&NTPSnippetsService::OnFetchFinished, base::Unretained(this)));
645
646 // |sync_service_| can be null in tests or if sync is disabled.
647 // This is a service we want to keep listening to all the time, independently
648 // from the state, since it will allow us to enable or disable the snippets
649 // service.
650 if (sync_service_)
651 sync_service_observer_.Add(sync_service_);
652
653 // Change state after we started loading the snippets. During startup, the
654 // Sync service might not be completely loaded when we initialize this
655 // service, so we might stay in the NOT_INITED state until the sync state is
656 // updated. See |GetStateForDependenciesStatus|.
657 EnterState(GetStateForDependenciesStatus());
658
659 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
660 NTPSnippetsServiceLoaded());
661
662 // Start a fetch if we don't have any snippets yet, or a fetch was requested
663 // earlier.
664 if (ready() && (snippets_.empty() || fetch_after_load_)) {
665 fetch_after_load_ = false;
666 FetchSnippets();
667 }
668 }
669
663 NTPSnippetsService::State NTPSnippetsService::GetStateForDependenciesStatus() { 670 NTPSnippetsService::State NTPSnippetsService::GetStateForDependenciesStatus() {
664 switch (GetDisabledReason()) { 671 switch (GetDisabledReason()) {
665 case DisabledReason::NONE: 672 case DisabledReason::NONE:
666 return State::READY; 673 return State::READY;
667 674
668 case DisabledReason::HISTORY_SYNC_STATE_UNKNOWN: 675 case DisabledReason::HISTORY_SYNC_STATE_UNKNOWN:
669 // HistorySync is not initialized yet, so we don't know what the actual 676 // HistorySync is not initialized yet, so we don't know what the actual
670 // state is. However, because we retreived the previous snippets from the 677 // state is and we just return the current one. If things change,
671 // database, if we got something, we know that the service was previously 678 // |OnStateChanged| will call this function again to update the state.
672 // enabled, so we just restore that state. If things changed,
673 // |OnStateChanged| will call this function again to fix the state.
674 DVLOG(1) << "Sync configuration incomplete, continuing based on the " 679 DVLOG(1) << "Sync configuration incomplete, continuing based on the "
675 "current state."; 680 "current state.";
676 return snippets_.empty() ? State::DISABLED : State::READY; 681 return state_;
677 682
678 case DisabledReason::EXPLICITLY_DISABLED: 683 case DisabledReason::EXPLICITLY_DISABLED:
679 case DisabledReason::HISTORY_SYNC_DISABLED: 684 case DisabledReason::HISTORY_SYNC_DISABLED:
680 return State::DISABLED; 685 return State::DISABLED;
681 } 686 }
682 687
683 // All cases should be handled by the above switch 688 // All cases should be handled by the above switch
684 NOTREACHED(); 689 NOTREACHED();
685 return State::DISABLED; 690 return State::DISABLED;
686 } 691 }
687 692
688 void NTPSnippetsService::EnterState(State state) { 693 void NTPSnippetsService::EnterState(State state) {
694 if (state == state_)
695 return;
696
689 switch (state) { 697 switch (state) {
690 case State::NOT_INITED: 698 case State::NOT_INITED:
691 // Initial state, it should not be possible to get back there. 699 // Initial state, it should not be possible to get back there.
692 NOTREACHED(); 700 NOTREACHED();
693 return; 701 return;
694 702
695 case State::READY: { 703 case State::READY: {
696 DCHECK(state_ == State::NOT_INITED || state_ == State::READY || 704 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED);
697 state_ == State::DISABLED);
698 if (state_ == State::READY)
699 return;
700 705
701 // If the service was previously disabled, we will need to start a fetch 706 // If the service was previously disabled, we will need to start a fetch
702 // because otherwise there won't be any. 707 // because otherwise there won't be any.
703 bool fetch_snippets = state_ == State::DISABLED; 708 bool fetch_snippets = state_ == State::DISABLED || fetch_after_load_;
704 DVLOG(1) << "Entering state: READY"; 709 DVLOG(1) << "Entering state: READY";
705 state_ = State::READY; 710 state_ = State::READY;
711 fetch_after_load_ = false;
706 EnterStateEnabled(fetch_snippets); 712 EnterStateEnabled(fetch_snippets);
707 return; 713 return;
708 } 714 }
709 715
710 case State::DISABLED: 716 case State::DISABLED:
711 DCHECK(state_ == State::NOT_INITED || state_ == State::READY || 717 DCHECK(state_ == State::NOT_INITED || state_ == State::READY);
712 state_ == State::DISABLED);
713 if (state_ == State::DISABLED)
714 return;
715 718
716 DVLOG(1) << "Entering state: DISABLED"; 719 DVLOG(1) << "Entering state: DISABLED";
717 state_ = State::DISABLED; 720 state_ = State::DISABLED;
718 EnterStateDisabled(); 721 EnterStateDisabled();
719 return; 722 return;
720 723
721 case State::SHUT_DOWN: 724 case State::SHUT_DOWN:
722 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED ||
723 state_ == State::READY);
724 DVLOG(1) << "Entering state: SHUT_DOWN"; 725 DVLOG(1) << "Entering state: SHUT_DOWN";
725 state_ = State::SHUT_DOWN; 726 state_ = State::SHUT_DOWN;
726 EnterStateShutdown(); 727 EnterStateShutdown();
727 return; 728 return;
728 } 729 }
729 } 730 }
730 731
731 void NTPSnippetsService::ClearDeprecatedPrefs() { 732 void NTPSnippetsService::ClearDeprecatedPrefs() {
732 pref_service_->ClearPref(prefs::kDeprecatedSnippets); 733 pref_service_->ClearPref(prefs::kDeprecatedSnippets);
733 pref_service_->ClearPref(prefs::kDeprecatedDiscardedSnippets); 734 pref_service_->ClearPref(prefs::kDeprecatedDiscardedSnippets);
734 } 735 }
735 736
736 } // namespace ntp_snippets 737 } // namespace ntp_snippets
OLDNEW
« no previous file with comments | « components/ntp_snippets/ntp_snippets_service.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698