Chromium Code Reviews| 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/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 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 252 FetchSnippetsFromHosts(GetSuggestionsHosts(), force_request); | 252 FetchSnippetsFromHosts(GetSuggestionsHosts(), force_request); |
| 253 else | 253 else |
| 254 fetch_after_load_ = true; | 254 fetch_after_load_ = true; |
| 255 } | 255 } |
| 256 | 256 |
| 257 void NTPSnippetsService::FetchSnippetsFromHosts( | 257 void NTPSnippetsService::FetchSnippetsFromHosts( |
| 258 const std::set<std::string>& hosts, | 258 const std::set<std::string>& hosts, |
| 259 bool force_request) { | 259 bool force_request) { |
| 260 if (!ready()) | 260 if (!ready()) |
| 261 return; | 261 return; |
| 262 | |
| 263 if (snippets_.empty()) | |
| 264 UpdateCategoryStatus(ContentSuggestionsCategoryStatus::AVAILABLE_LOADING); | |
|
Marc Treib
2016/08/01 09:03:31
Hm, so this status effectively means "no suggestio
dgn
2016/08/01 10:54:40
Yes, that's how I understand it and the doc of the
| |
| 265 | |
| 262 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, | 266 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, |
| 263 kMaxSnippetCount, force_request); | 267 kMaxSnippetCount, force_request); |
| 264 } | 268 } |
| 265 | 269 |
| 266 void NTPSnippetsService::RescheduleFetching() { | 270 void NTPSnippetsService::RescheduleFetching() { |
| 267 // The scheduler only exists on Android so far, it's null on other platforms. | 271 // The scheduler only exists on Android so far, it's null on other platforms. |
| 268 if (!scheduler_) | 272 if (!scheduler_) |
| 269 return; | 273 return; |
| 270 | 274 |
| 271 if (ready()) { | 275 if (ready()) { |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 409 }); | 413 }); |
| 410 | 414 |
| 411 ClearExpiredSnippets(); | 415 ClearExpiredSnippets(); |
| 412 FinishInitialization(); | 416 FinishInitialization(); |
| 413 } | 417 } |
| 414 | 418 |
| 415 void NTPSnippetsService::OnDatabaseError() { | 419 void NTPSnippetsService::OnDatabaseError() { |
| 416 EnterState(State::SHUT_DOWN, ContentSuggestionsCategoryStatus::LOADING_ERROR); | 420 EnterState(State::SHUT_DOWN, ContentSuggestionsCategoryStatus::LOADING_ERROR); |
| 417 } | 421 } |
| 418 | 422 |
| 423 // TODO(dgn): name clash between content suggestions and suggestions hosts. | |
| 424 // method name should be changed. | |
| 419 void NTPSnippetsService::OnSuggestionsChanged( | 425 void NTPSnippetsService::OnSuggestionsChanged( |
| 420 const SuggestionsProfile& suggestions) { | 426 const SuggestionsProfile& suggestions) { |
| 421 DCHECK(initialized()); | 427 DCHECK(initialized()); |
| 422 | 428 |
| 423 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); | 429 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); |
| 424 if (hosts == GetSnippetHostsFromPrefs()) | 430 if (hosts == GetSnippetHostsFromPrefs()) |
| 425 return; | 431 return; |
| 426 | 432 |
| 427 // Remove existing snippets that aren't in the suggestions anymore. | 433 // Remove existing snippets that aren't in the suggestions anymore. |
| 428 // TODO(treib,maybelle): If there is another source with an allowed host, | 434 // TODO(treib,maybelle): If there is another source with an allowed host, |
| 429 // then we should fall back to that. | 435 // then we should fall back to that. |
| 430 // First, move them over into |to_delete|. | 436 // First, move them over into |to_delete|. |
| 431 NTPSnippet::PtrVector to_delete; | 437 NTPSnippet::PtrVector to_delete; |
| 432 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) { | 438 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) { |
| 433 if (!hosts.count(snippet->best_source().url.host())) | 439 if (!hosts.count(snippet->best_source().url.host())) |
| 434 to_delete.emplace_back(std::move(snippet)); | 440 to_delete.emplace_back(std::move(snippet)); |
| 435 } | 441 } |
| 436 Compact(&snippets_); | 442 Compact(&snippets_); |
| 437 // Then delete the removed snippets from the database. | 443 // Then delete the removed snippets from the database. |
| 438 database_->DeleteSnippets(to_delete); | 444 database_->DeleteSnippets(to_delete); |
| 439 | 445 |
| 440 StoreSnippetHostsToPrefs(hosts); | 446 StoreSnippetHostsToPrefs(hosts); |
| 441 | 447 |
| 448 // We removed some suggestions, so we want to let the client know about that. | |
| 449 // The fetch might take a long time or not complete so we don't want to wait | |
| 450 // for its callback. | |
| 442 NotifyNewSuggestions(); | 451 NotifyNewSuggestions(); |
| 443 | 452 |
| 444 FetchSnippetsFromHosts(hosts, /*force_request=*/false); | 453 FetchSnippetsFromHosts(hosts, /*force_request=*/false); |
| 445 } | 454 } |
| 446 | 455 |
| 447 void NTPSnippetsService::OnFetchFinished( | 456 void NTPSnippetsService::OnFetchFinished( |
| 448 NTPSnippetsFetcher::OptionalSnippets snippets) { | 457 NTPSnippetsFetcher::OptionalSnippets snippets) { |
| 449 if (!ready()) | 458 if (!ready()) |
| 450 return; | 459 return; |
| 451 | 460 |
| 461 DCHECK(category_status_ == ContentSuggestionsCategoryStatus::AVAILABLE || | |
|
Philipp Keck
2016/08/01 09:14:10
What if we shut down during a fetch? Or if the use
Marc Treib
2016/08/01 09:34:34
I thought about that too :) But there's a ready()
| |
| 462 category_status_ == | |
| 463 ContentSuggestionsCategoryStatus::AVAILABLE_LOADING); | |
| 464 | |
| 452 if (snippets) { | 465 if (snippets) { |
| 453 // Sparse histogram used because the number of snippets is small (bound by | 466 // Sparse histogram used because the number of snippets is small (bound by |
| 454 // kMaxSnippetCount). | 467 // kMaxSnippetCount). |
| 455 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); | 468 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); |
| 456 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", | 469 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", |
| 457 snippets->size()); | 470 snippets->size()); |
| 458 MergeSnippets(std::move(*snippets)); | 471 MergeSnippets(std::move(*snippets)); |
| 459 } | 472 } |
| 460 | 473 |
| 461 ClearExpiredSnippets(); | 474 ClearExpiredSnippets(); |
| 462 | 475 |
| 463 // If there are more snippets than we want to show, delete the extra ones. | 476 // If there are more snippets than we want to show, delete the extra ones. |
| 464 if (snippets_.size() > kMaxSnippetCount) { | 477 if (snippets_.size() > kMaxSnippetCount) { |
| 465 NTPSnippet::PtrVector to_delete( | 478 NTPSnippet::PtrVector to_delete( |
| 466 std::make_move_iterator(snippets_.begin() + kMaxSnippetCount), | 479 std::make_move_iterator(snippets_.begin() + kMaxSnippetCount), |
| 467 std::make_move_iterator(snippets_.end())); | 480 std::make_move_iterator(snippets_.end())); |
| 468 snippets_.resize(kMaxSnippetCount); | 481 snippets_.resize(kMaxSnippetCount); |
| 469 database_->DeleteSnippets(to_delete); | 482 database_->DeleteSnippets(to_delete); |
| 470 } | 483 } |
| 471 | 484 |
| 472 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | 485 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", |
| 473 snippets_.size()); | 486 snippets_.size()); |
| 474 if (snippets_.empty() && !dismissed_snippets_.empty()) { | 487 if (snippets_.empty() && !dismissed_snippets_.empty()) { |
| 475 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 488 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", |
| 476 dismissed_snippets_.size()); | 489 dismissed_snippets_.size()); |
| 477 } | 490 } |
| 478 | 491 |
| 492 UpdateCategoryStatus(ContentSuggestionsCategoryStatus::AVAILABLE); | |
| 479 NotifyNewSuggestions(); | 493 NotifyNewSuggestions(); |
| 480 } | 494 } |
| 481 | 495 |
| 482 void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) { | 496 void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) { |
| 483 DCHECK(ready()); | 497 DCHECK(ready()); |
| 484 | 498 |
| 485 // Remove new snippets that we already have, or that have been dismissed. | 499 // Remove new snippets that we already have, or that have been dismissed. |
| 486 std::set<std::string> old_snippet_ids; | 500 std::set<std::string> old_snippet_ids; |
| 487 InsertAllIDs(dismissed_snippets_, &old_snippet_ids); | 501 InsertAllIDs(dismissed_snippets_, &old_snippet_ids); |
| 488 InsertAllIDs(snippets_, &old_snippet_ids); | 502 InsertAllIDs(snippets_, &old_snippet_ids); |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 651 | 665 |
| 652 const NTPSnippet& snippet = *it->get(); | 666 const NTPSnippet& snippet = *it->get(); |
| 653 image_fetcher_->StartOrQueueNetworkRequest( | 667 image_fetcher_->StartOrQueueNetworkRequest( |
| 654 snippet.id(), snippet.salient_image_url(), callback); | 668 snippet.id(), snippet.salient_image_url(), callback); |
| 655 } | 669 } |
| 656 | 670 |
| 657 void NTPSnippetsService::EnterStateEnabled(bool fetch_snippets) { | 671 void NTPSnippetsService::EnterStateEnabled(bool fetch_snippets) { |
| 658 if (fetch_snippets) | 672 if (fetch_snippets) |
| 659 FetchSnippets(/*force_request=*/false); | 673 FetchSnippets(/*force_request=*/false); |
| 660 | 674 |
| 675 // FetchSnippets should set the status to |AVAILABLE_LOADING| if relevant, | |
| 676 // otherwise we transition to |AVAILABLE| here. | |
| 677 if (category_status_ != ContentSuggestionsCategoryStatus::AVAILABLE_LOADING) | |
| 678 UpdateCategoryStatus(ContentSuggestionsCategoryStatus::AVAILABLE); | |
| 679 | |
| 661 // If host restrictions are enabled, register for host list updates. | 680 // If host restrictions are enabled, register for host list updates. |
| 662 // |suggestions_service_| can be null in tests. | 681 // |suggestions_service_| can be null in tests. |
| 663 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) { | 682 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) { |
| 664 suggestions_service_subscription_ = | 683 suggestions_service_subscription_ = |
| 665 suggestions_service_->AddCallback(base::Bind( | 684 suggestions_service_->AddCallback(base::Bind( |
| 666 &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this))); | 685 &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this))); |
| 667 } | 686 } |
| 668 | 687 |
| 669 RescheduleFetching(); | 688 RescheduleFetching(); |
| 670 } | 689 } |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 698 image_fetcher_->SetImageFetcherDelegate(this); | 717 image_fetcher_->SetImageFetcherDelegate(this); |
| 699 image_fetcher_->SetDataUseServiceName( | 718 image_fetcher_->SetDataUseServiceName( |
| 700 data_use_measurement::DataUseUserData::NTP_SNIPPETS); | 719 data_use_measurement::DataUseUserData::NTP_SNIPPETS); |
| 701 } | 720 } |
| 702 | 721 |
| 703 // Note: Initializing the status service will run the callback right away with | 722 // Note: Initializing the status service will run the callback right away with |
| 704 // the current state. | 723 // the current state. |
| 705 snippets_status_service_->Init(base::Bind( | 724 snippets_status_service_->Init(base::Bind( |
| 706 &NTPSnippetsService::OnDisabledReasonChanged, base::Unretained(this))); | 725 &NTPSnippetsService::OnDisabledReasonChanged, base::Unretained(this))); |
| 707 | 726 |
| 727 // Always notify here even if we got nothing from the database, because we | |
| 728 // don't know how long the fetch will take or if it will even complete. | |
| 708 NotifyNewSuggestions(); | 729 NotifyNewSuggestions(); |
| 709 } | 730 } |
| 710 | 731 |
| 711 void NTPSnippetsService::OnDisabledReasonChanged( | 732 void NTPSnippetsService::OnDisabledReasonChanged( |
| 712 DisabledReason disabled_reason) { | 733 DisabledReason disabled_reason) { |
| 713 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 734 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, |
| 714 NTPSnippetsServiceDisabledReasonChanged(disabled_reason)); | 735 NTPSnippetsServiceDisabledReasonChanged(disabled_reason)); |
| 715 | 736 |
| 716 switch (disabled_reason) { | 737 switch (disabled_reason) { |
| 717 case DisabledReason::NONE: | 738 case DisabledReason::NONE: |
| 718 EnterState(State::READY, ContentSuggestionsCategoryStatus::AVAILABLE); | 739 // Do not change the status. That will be done in EnterStateEnabled() |
| 740 EnterState(State::READY, category_status_); | |
| 719 break; | 741 break; |
| 720 | 742 |
| 721 case DisabledReason::EXPLICITLY_DISABLED: | 743 case DisabledReason::EXPLICITLY_DISABLED: |
| 722 EnterState( | 744 EnterState( |
| 723 State::DISABLED, | 745 State::DISABLED, |
| 724 ContentSuggestionsCategoryStatus::CATEGORY_EXPLICITLY_DISABLED); | 746 ContentSuggestionsCategoryStatus::CATEGORY_EXPLICITLY_DISABLED); |
| 725 break; | 747 break; |
| 726 | 748 |
| 727 case DisabledReason::SIGNED_OUT: | 749 case DisabledReason::SIGNED_OUT: |
| 728 EnterState(State::DISABLED, ContentSuggestionsCategoryStatus::SIGNED_OUT); | 750 EnterState(State::DISABLED, ContentSuggestionsCategoryStatus::SIGNED_OUT); |
| 729 break; | 751 break; |
| 730 } | 752 } |
| 731 } | 753 } |
| 732 | 754 |
| 733 void NTPSnippetsService::EnterState(State state, | 755 void NTPSnippetsService::EnterState(State state, |
| 734 ContentSuggestionsCategoryStatus status) { | 756 ContentSuggestionsCategoryStatus status) { |
| 735 if (status != category_status_) { | 757 UpdateCategoryStatus(status); |
| 736 category_status_ = status; | |
| 737 NotifyCategoryStatusChanged(); | |
| 738 } | |
| 739 | 758 |
| 740 if (state == state_) | 759 if (state == state_) |
| 741 return; | 760 return; |
| 742 | 761 |
| 743 switch (state) { | 762 switch (state) { |
| 744 case State::NOT_INITED: | 763 case State::NOT_INITED: |
| 745 // Initial state, it should not be possible to get back there. | 764 // Initial state, it should not be possible to get back there. |
| 746 NOTREACHED(); | 765 NOTREACHED(); |
| 747 return; | 766 return; |
| 748 | 767 |
| 749 case State::READY: { | 768 case State::READY: { |
| 750 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED); | 769 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED); |
| 751 | 770 |
| 752 bool fetch_snippets = snippets_.empty() || fetch_after_load_; | 771 bool fetch_snippets = snippets_.empty() || fetch_after_load_; |
| 753 DVLOG(1) << "Entering state: READY"; | 772 DVLOG(1) << "Entering state: READY"; |
| 754 state_ = State::READY; | 773 state_ = State::READY; |
| 755 fetch_after_load_ = false; | 774 fetch_after_load_ = false; |
| 756 EnterStateEnabled(fetch_snippets); | 775 EnterStateEnabled(fetch_snippets); |
| 757 return; | 776 return; |
| 758 } | 777 } |
| 759 | 778 |
| 760 case State::DISABLED: | 779 case State::DISABLED: |
| 761 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); | 780 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); |
| 762 | |
| 763 DVLOG(1) << "Entering state: DISABLED"; | 781 DVLOG(1) << "Entering state: DISABLED"; |
| 764 state_ = State::DISABLED; | 782 state_ = State::DISABLED; |
| 765 EnterStateDisabled(); | 783 EnterStateDisabled(); |
| 766 return; | 784 return; |
| 767 | 785 |
| 768 case State::SHUT_DOWN: | 786 case State::SHUT_DOWN: |
| 769 DVLOG(1) << "Entering state: SHUT_DOWN"; | 787 DVLOG(1) << "Entering state: SHUT_DOWN"; |
| 770 state_ = State::SHUT_DOWN; | 788 state_ = State::SHUT_DOWN; |
| 771 EnterStateShutdown(); | 789 EnterStateShutdown(); |
| 772 return; | 790 return; |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 793 suggestion.set_snippet_text(snippet->snippet()); | 811 suggestion.set_snippet_text(snippet->snippet()); |
| 794 suggestion.set_publish_date(snippet->publish_date()); | 812 suggestion.set_publish_date(snippet->publish_date()); |
| 795 suggestion.set_publisher_name(snippet->best_source().publisher_name); | 813 suggestion.set_publisher_name(snippet->best_source().publisher_name); |
| 796 suggestion.set_score(snippet->score()); | 814 suggestion.set_score(snippet->score()); |
| 797 result.emplace_back(std::move(suggestion)); | 815 result.emplace_back(std::move(suggestion)); |
| 798 } | 816 } |
| 799 observer_->OnNewSuggestions(ContentSuggestionsCategory::ARTICLES, | 817 observer_->OnNewSuggestions(ContentSuggestionsCategory::ARTICLES, |
| 800 std::move(result)); | 818 std::move(result)); |
| 801 } | 819 } |
| 802 | 820 |
| 803 void NTPSnippetsService::NotifyCategoryStatusChanged() { | 821 void NTPSnippetsService::UpdateCategoryStatus( |
| 822 ContentSuggestionsCategoryStatus status) { | |
| 823 if (status == category_status_) | |
| 824 return; | |
| 825 | |
| 826 category_status_ = status; | |
| 804 if (observer_) { | 827 if (observer_) { |
| 805 observer_->OnCategoryStatusChanged(ContentSuggestionsCategory::ARTICLES, | 828 observer_->OnCategoryStatusChanged(ContentSuggestionsCategory::ARTICLES, |
| 806 category_status_); | 829 category_status_); |
| 807 } | 830 } |
| 808 } | 831 } |
| 809 | 832 |
| 810 } // namespace ntp_snippets | 833 } // namespace ntp_snippets |
| OLD | NEW |