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