| 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 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 snippets->erase( | 178 snippets->erase( |
| 179 std::remove_if( | 179 std::remove_if( |
| 180 snippets->begin(), snippets->end(), | 180 snippets->begin(), snippets->end(), |
| 181 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), | 181 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), |
| 182 snippets->end()); | 182 snippets->end()); |
| 183 } | 183 } |
| 184 | 184 |
| 185 } // namespace | 185 } // namespace |
| 186 | 186 |
| 187 NTPSnippetsService::NTPSnippetsService( | 187 NTPSnippetsService::NTPSnippetsService( |
| 188 bool enabled, | 188 Observer* observer, |
| 189 CategoryFactory* category_factory, |
| 189 PrefService* pref_service, | 190 PrefService* pref_service, |
| 190 SuggestionsService* suggestions_service, | 191 SuggestionsService* suggestions_service, |
| 191 CategoryFactory* category_factory, | |
| 192 const std::string& application_language_code, | 192 const std::string& application_language_code, |
| 193 NTPSnippetsScheduler* scheduler, | 193 NTPSnippetsScheduler* scheduler, |
| 194 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, | 194 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, |
| 195 std::unique_ptr<ImageFetcher> image_fetcher, | 195 std::unique_ptr<ImageFetcher> image_fetcher, |
| 196 std::unique_ptr<ImageDecoder> image_decoder, | 196 std::unique_ptr<ImageDecoder> image_decoder, |
| 197 std::unique_ptr<NTPSnippetsDatabase> database, | 197 std::unique_ptr<NTPSnippetsDatabase> database, |
| 198 std::unique_ptr<NTPSnippetsStatusService> status_service) | 198 std::unique_ptr<NTPSnippetsStatusService> status_service) |
| 199 : ContentSuggestionsProvider(category_factory), | 199 : ContentSuggestionsProvider(observer, category_factory), |
| 200 state_(State::NOT_INITED), | 200 state_(State::NOT_INITED), |
| 201 category_status_(CategoryStatus::INITIALIZING), | 201 category_status_(CategoryStatus::INITIALIZING), |
| 202 pref_service_(pref_service), | 202 pref_service_(pref_service), |
| 203 suggestions_service_(suggestions_service), | 203 suggestions_service_(suggestions_service), |
| 204 application_language_code_(application_language_code), | 204 application_language_code_(application_language_code), |
| 205 observer_(nullptr), | |
| 206 scheduler_(scheduler), | 205 scheduler_(scheduler), |
| 207 snippets_fetcher_(std::move(snippets_fetcher)), | 206 snippets_fetcher_(std::move(snippets_fetcher)), |
| 208 image_fetcher_(std::move(image_fetcher)), | 207 image_fetcher_(std::move(image_fetcher)), |
| 209 image_decoder_(std::move(image_decoder)), | 208 image_decoder_(std::move(image_decoder)), |
| 210 database_(std::move(database)), | 209 database_(std::move(database)), |
| 211 snippets_status_service_(std::move(status_service)), | 210 snippets_status_service_(std::move(status_service)), |
| 212 fetch_after_load_(false), | 211 fetch_after_load_(false), |
| 213 provided_category_( | 212 provided_category_( |
| 214 category_factory->FromKnownCategory(KnownCategories::ARTICLES)) { | 213 category_factory->FromKnownCategory(KnownCategories::ARTICLES)) { |
| 215 // In some cases, don't even bother loading the database. | |
| 216 if (!enabled) { | |
| 217 EnterState(State::SHUT_DOWN, CategoryStatus::CATEGORY_EXPLICITLY_DISABLED); | |
| 218 return; | |
| 219 } | |
| 220 if (database_->IsErrorState()) { | 214 if (database_->IsErrorState()) { |
| 221 EnterState(State::SHUT_DOWN, CategoryStatus::LOADING_ERROR); | 215 EnterState(State::ERROR_OCCURRED, CategoryStatus::LOADING_ERROR); |
| 222 return; | 216 return; |
| 223 } | 217 } |
| 224 | 218 |
| 225 database_->SetErrorCallback(base::Bind(&NTPSnippetsService::OnDatabaseError, | 219 database_->SetErrorCallback(base::Bind(&NTPSnippetsService::OnDatabaseError, |
| 226 base::Unretained(this))); | 220 base::Unretained(this))); |
| 227 | 221 |
| 228 // TODO(pke): Move this to SetObserver as soon as the UI reads from the | |
| 229 // ContentSuggestionsService directly. | |
| 230 // We transition to other states while finalizing the initialization, when the | 222 // We transition to other states while finalizing the initialization, when the |
| 231 // database is done loading. | 223 // database is done loading. |
| 232 database_->LoadSnippets(base::Bind(&NTPSnippetsService::OnDatabaseLoaded, | 224 database_->LoadSnippets(base::Bind(&NTPSnippetsService::OnDatabaseLoaded, |
| 233 base::Unretained(this))); | 225 base::Unretained(this))); |
| 234 } | 226 } |
| 235 | 227 |
| 236 NTPSnippetsService::~NTPSnippetsService() { | 228 NTPSnippetsService::~NTPSnippetsService() { |
| 237 DCHECK(state_ == State::SHUT_DOWN); | |
| 238 } | 229 } |
| 239 | 230 |
| 240 // static | 231 // static |
| 241 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { | 232 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { |
| 242 registry->RegisterListPref(prefs::kSnippetHosts); | 233 registry->RegisterListPref(prefs::kSnippetHosts); |
| 243 | 234 |
| 244 NTPSnippetsStatusService::RegisterProfilePrefs(registry); | 235 NTPSnippetsStatusService::RegisterProfilePrefs(registry); |
| 245 } | 236 } |
| 246 | 237 |
| 247 // Inherited from KeyedService. | |
| 248 void NTPSnippetsService::Shutdown() { | |
| 249 EnterState(State::SHUT_DOWN, CategoryStatus::NOT_PROVIDED); | |
| 250 } | |
| 251 | |
| 252 void NTPSnippetsService::FetchSnippets(bool force_request) { | 238 void NTPSnippetsService::FetchSnippets(bool force_request) { |
| 253 if (ready()) | 239 if (ready()) |
| 254 FetchSnippetsFromHosts(GetSuggestionsHosts(), force_request); | 240 FetchSnippetsFromHosts(GetSuggestionsHosts(), force_request); |
| 255 else | 241 else |
| 256 fetch_after_load_ = true; | 242 fetch_after_load_ = true; |
| 257 } | 243 } |
| 258 | 244 |
| 259 void NTPSnippetsService::FetchSnippetsFromHosts( | 245 void NTPSnippetsService::FetchSnippetsFromHosts( |
| 260 const std::set<std::string>& hosts, | 246 const std::set<std::string>& hosts, |
| 261 bool force_request) { | 247 bool force_request) { |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 348 if (!initialized()) | 334 if (!initialized()) |
| 349 return; | 335 return; |
| 350 | 336 |
| 351 if (dismissed_snippets_.empty()) | 337 if (dismissed_snippets_.empty()) |
| 352 return; | 338 return; |
| 353 | 339 |
| 354 database_->DeleteSnippets(dismissed_snippets_); | 340 database_->DeleteSnippets(dismissed_snippets_); |
| 355 dismissed_snippets_.clear(); | 341 dismissed_snippets_.clear(); |
| 356 } | 342 } |
| 357 | 343 |
| 358 void NTPSnippetsService::SetObserver(Observer* observer) { | |
| 359 observer_ = observer; | |
| 360 } | |
| 361 | |
| 362 CategoryStatus NTPSnippetsService::GetCategoryStatus(Category category) { | 344 CategoryStatus NTPSnippetsService::GetCategoryStatus(Category category) { |
| 363 DCHECK(category.IsKnownCategory(KnownCategories::ARTICLES)); | 345 DCHECK(category.IsKnownCategory(KnownCategories::ARTICLES)); |
| 364 return category_status_; | 346 return category_status_; |
| 365 } | 347 } |
| 366 | 348 |
| 367 void NTPSnippetsService::AddObserver(NTPSnippetsServiceObserver* observer) { | |
| 368 observers_.AddObserver(observer); | |
| 369 } | |
| 370 | |
| 371 void NTPSnippetsService::RemoveObserver(NTPSnippetsServiceObserver* observer) { | |
| 372 observers_.RemoveObserver(observer); | |
| 373 } | |
| 374 | |
| 375 // static | 349 // static |
| 376 int NTPSnippetsService::GetMaxSnippetCountForTesting() { | 350 int NTPSnippetsService::GetMaxSnippetCountForTesting() { |
| 377 return kMaxSnippetCount; | 351 return kMaxSnippetCount; |
| 378 } | 352 } |
| 379 | 353 |
| 380 //////////////////////////////////////////////////////////////////////////////// | 354 //////////////////////////////////////////////////////////////////////////////// |
| 381 // Private methods | 355 // Private methods |
| 382 | 356 |
| 383 // image_fetcher::ImageFetcherDelegate implementation. | 357 // image_fetcher::ImageFetcherDelegate implementation. |
| 384 void NTPSnippetsService::OnImageDataFetched(const std::string& snippet_id, | 358 void NTPSnippetsService::OnImageDataFetched(const std::string& snippet_id, |
| 385 const std::string& image_data) { | 359 const std::string& image_data) { |
| 386 if (image_data.empty()) | 360 if (image_data.empty()) |
| 387 return; | 361 return; |
| 388 | 362 |
| 389 // Only save the image if the corresponding snippet still exists. | 363 // Only save the image if the corresponding snippet still exists. |
| 390 auto it = | 364 auto it = |
| 391 std::find_if(snippets_.begin(), snippets_.end(), | 365 std::find_if(snippets_.begin(), snippets_.end(), |
| 392 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | 366 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { |
| 393 return snippet->id() == snippet_id; | 367 return snippet->id() == snippet_id; |
| 394 }); | 368 }); |
| 395 if (it == snippets_.end()) | 369 if (it == snippets_.end()) |
| 396 return; | 370 return; |
| 397 | 371 |
| 398 database_->SaveImage(snippet_id, image_data); | 372 database_->SaveImage(snippet_id, image_data); |
| 399 } | 373 } |
| 400 | 374 |
| 401 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { | 375 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { |
| 402 DCHECK(state_ == State::NOT_INITED || state_ == State::SHUT_DOWN); | 376 if (state_ == State::ERROR_OCCURRED) |
| 403 if (state_ == State::SHUT_DOWN) | |
| 404 return; | 377 return; |
| 405 | 378 DCHECK(state_ == State::NOT_INITED); |
| 406 DCHECK(snippets_.empty()); | 379 DCHECK(snippets_.empty()); |
| 407 DCHECK(dismissed_snippets_.empty()); | 380 DCHECK(dismissed_snippets_.empty()); |
| 408 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { | 381 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { |
| 409 if (snippet->is_dismissed()) | 382 if (snippet->is_dismissed()) |
| 410 dismissed_snippets_.emplace_back(std::move(snippet)); | 383 dismissed_snippets_.emplace_back(std::move(snippet)); |
| 411 else | 384 else |
| 412 snippets_.emplace_back(std::move(snippet)); | 385 snippets_.emplace_back(std::move(snippet)); |
| 413 } | 386 } |
| 414 std::sort(snippets_.begin(), snippets_.end(), | 387 std::sort(snippets_.begin(), snippets_.end(), |
| 415 [](const std::unique_ptr<NTPSnippet>& lhs, | 388 [](const std::unique_ptr<NTPSnippet>& lhs, |
| 416 const std::unique_ptr<NTPSnippet>& rhs) { | 389 const std::unique_ptr<NTPSnippet>& rhs) { |
| 417 return lhs->score() > rhs->score(); | 390 return lhs->score() > rhs->score(); |
| 418 }); | 391 }); |
| 419 | 392 |
| 420 ClearExpiredSnippets(); | 393 ClearExpiredSnippets(); |
| 421 FinishInitialization(); | 394 FinishInitialization(); |
| 422 } | 395 } |
| 423 | 396 |
| 424 void NTPSnippetsService::OnDatabaseError() { | 397 void NTPSnippetsService::OnDatabaseError() { |
| 425 EnterState(State::SHUT_DOWN, CategoryStatus::LOADING_ERROR); | 398 EnterState(State::ERROR_OCCURRED, CategoryStatus::LOADING_ERROR); |
| 426 } | 399 } |
| 427 | 400 |
| 428 // TODO(dgn): name clash between content suggestions and suggestions hosts. | 401 // TODO(dgn): name clash between content suggestions and suggestions hosts. |
| 429 // method name should be changed. | 402 // method name should be changed. |
| 430 void NTPSnippetsService::OnSuggestionsChanged( | 403 void NTPSnippetsService::OnSuggestionsChanged( |
| 431 const SuggestionsProfile& suggestions) { | 404 const SuggestionsProfile& suggestions) { |
| 432 DCHECK(initialized()); | 405 DCHECK(initialized()); |
| 433 | 406 |
| 434 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); | 407 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); |
| 435 if (hosts == GetSnippetHostsFromPrefs()) | 408 if (hosts == GetSnippetHostsFromPrefs()) |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 692 | 665 |
| 693 void NTPSnippetsService::EnterStateDisabled() { | 666 void NTPSnippetsService::EnterStateDisabled() { |
| 694 ClearCachedSuggestionsForDebugging(); | 667 ClearCachedSuggestionsForDebugging(); |
| 695 ClearDismissedSuggestionsForDebugging(); | 668 ClearDismissedSuggestionsForDebugging(); |
| 696 | 669 |
| 697 expiry_timer_.Stop(); | 670 expiry_timer_.Stop(); |
| 698 suggestions_service_subscription_.reset(); | 671 suggestions_service_subscription_.reset(); |
| 699 RescheduleFetching(); | 672 RescheduleFetching(); |
| 700 } | 673 } |
| 701 | 674 |
| 702 void NTPSnippetsService::EnterStateShutdown() { | 675 void NTPSnippetsService::EnterStateError() { |
| 703 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | |
| 704 NTPSnippetsServiceShutdown()); | |
| 705 | |
| 706 expiry_timer_.Stop(); | 676 expiry_timer_.Stop(); |
| 707 suggestions_service_subscription_.reset(); | 677 suggestions_service_subscription_.reset(); |
| 708 RescheduleFetching(); | 678 RescheduleFetching(); |
| 709 | |
| 710 snippets_status_service_.reset(); | 679 snippets_status_service_.reset(); |
| 711 } | 680 } |
| 712 | 681 |
| 713 void NTPSnippetsService::FinishInitialization() { | 682 void NTPSnippetsService::FinishInitialization() { |
| 714 snippets_fetcher_->SetCallback( | 683 snippets_fetcher_->SetCallback( |
| 715 base::Bind(&NTPSnippetsService::OnFetchFinished, base::Unretained(this))); | 684 base::Bind(&NTPSnippetsService::OnFetchFinished, base::Unretained(this))); |
| 716 | 685 |
| 717 // |image_fetcher_| can be null in tests. | 686 // |image_fetcher_| can be null in tests. |
| 718 if (image_fetcher_) { | 687 if (image_fetcher_) { |
| 719 image_fetcher_->SetImageFetcherDelegate(this); | 688 image_fetcher_->SetImageFetcherDelegate(this); |
| 720 image_fetcher_->SetDataUseServiceName( | 689 image_fetcher_->SetDataUseServiceName( |
| 721 data_use_measurement::DataUseUserData::NTP_SNIPPETS); | 690 data_use_measurement::DataUseUserData::NTP_SNIPPETS); |
| 722 } | 691 } |
| 723 | 692 |
| 724 // Note: Initializing the status service will run the callback right away with | 693 // Note: Initializing the status service will run the callback right away with |
| 725 // the current state. | 694 // the current state. |
| 726 snippets_status_service_->Init(base::Bind( | 695 snippets_status_service_->Init(base::Bind( |
| 727 &NTPSnippetsService::OnDisabledReasonChanged, base::Unretained(this))); | 696 &NTPSnippetsService::OnDisabledReasonChanged, base::Unretained(this))); |
| 728 | 697 |
| 729 // Always notify here even if we got nothing from the database, because we | 698 // 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. | 699 // don't know how long the fetch will take or if it will even complete. |
| 731 NotifyNewSuggestions(); | 700 NotifyNewSuggestions(); |
| 732 } | 701 } |
| 733 | 702 |
| 734 void NTPSnippetsService::OnDisabledReasonChanged( | 703 void NTPSnippetsService::OnDisabledReasonChanged( |
| 735 DisabledReason disabled_reason) { | 704 DisabledReason disabled_reason) { |
| 736 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | |
| 737 NTPSnippetsServiceDisabledReasonChanged(disabled_reason)); | |
| 738 | |
| 739 switch (disabled_reason) { | 705 switch (disabled_reason) { |
| 740 case DisabledReason::NONE: | 706 case DisabledReason::NONE: |
| 741 // Do not change the status. That will be done in EnterStateEnabled() | 707 // Do not change the status. That will be done in EnterStateEnabled() |
| 742 EnterState(State::READY, category_status_); | 708 EnterState(State::READY, category_status_); |
| 743 break; | 709 break; |
| 744 | 710 |
| 745 case DisabledReason::EXPLICITLY_DISABLED: | 711 case DisabledReason::EXPLICITLY_DISABLED: |
| 746 EnterState(State::DISABLED, CategoryStatus::CATEGORY_EXPLICITLY_DISABLED); | 712 EnterState(State::DISABLED, CategoryStatus::CATEGORY_EXPLICITLY_DISABLED); |
| 747 break; | 713 break; |
| 748 | 714 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 776 } | 742 } |
| 777 | 743 |
| 778 case State::DISABLED: | 744 case State::DISABLED: |
| 779 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); | 745 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); |
| 780 | 746 |
| 781 DVLOG(1) << "Entering state: DISABLED"; | 747 DVLOG(1) << "Entering state: DISABLED"; |
| 782 state_ = State::DISABLED; | 748 state_ = State::DISABLED; |
| 783 EnterStateDisabled(); | 749 EnterStateDisabled(); |
| 784 return; | 750 return; |
| 785 | 751 |
| 786 case State::SHUT_DOWN: | 752 case State::ERROR_OCCURRED: |
| 787 DVLOG(1) << "Entering state: SHUT_DOWN"; | 753 DVLOG(1) << "Entering state: ERROR_OCCURRED"; |
| 788 state_ = State::SHUT_DOWN; | 754 state_ = State::ERROR_OCCURRED; |
| 789 EnterStateShutdown(); | 755 EnterStateError(); |
| 790 return; | 756 return; |
| 791 } | 757 } |
| 792 } | 758 } |
| 793 | 759 |
| 794 void NTPSnippetsService::NotifyNewSuggestions() { | 760 void NTPSnippetsService::NotifyNewSuggestions() { |
| 795 // TODO(pke): Remove this as soon as this becomes a pure provider. | |
| 796 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | |
| 797 NTPSnippetsServiceLoaded()); | |
| 798 | |
| 799 if (!observer_) | |
| 800 return; | |
| 801 | |
| 802 std::vector<ContentSuggestion> result; | 761 std::vector<ContentSuggestion> result; |
| 803 for (const std::unique_ptr<NTPSnippet>& snippet : snippets_) { | 762 for (const std::unique_ptr<NTPSnippet>& snippet : snippets_) { |
| 804 if (!snippet->is_complete()) | 763 if (!snippet->is_complete()) |
| 805 continue; | 764 continue; |
| 806 ContentSuggestion suggestion( | 765 ContentSuggestion suggestion( |
| 807 MakeUniqueID(provided_category_, snippet->id()), | 766 MakeUniqueID(provided_category_, snippet->id()), |
| 808 snippet->best_source().url); | 767 snippet->best_source().url); |
| 809 suggestion.set_amp_url(snippet->best_source().amp_url); | 768 suggestion.set_amp_url(snippet->best_source().amp_url); |
| 810 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); | 769 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); |
| 811 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); | 770 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); |
| 812 suggestion.set_publish_date(snippet->publish_date()); | 771 suggestion.set_publish_date(snippet->publish_date()); |
| 813 suggestion.set_publisher_name( | 772 suggestion.set_publisher_name( |
| 814 base::UTF8ToUTF16(snippet->best_source().publisher_name)); | 773 base::UTF8ToUTF16(snippet->best_source().publisher_name)); |
| 815 suggestion.set_score(snippet->score()); | 774 suggestion.set_score(snippet->score()); |
| 816 result.emplace_back(std::move(suggestion)); | 775 result.emplace_back(std::move(suggestion)); |
| 817 } | 776 } |
| 818 observer_->OnNewSuggestions(this, provided_category_, std::move(result)); | 777 observer()->OnNewSuggestions(this, provided_category_, std::move(result)); |
| 819 } | 778 } |
| 820 | 779 |
| 821 void NTPSnippetsService::UpdateCategoryStatus(CategoryStatus status) { | 780 void NTPSnippetsService::UpdateCategoryStatus(CategoryStatus status) { |
| 822 if (status == category_status_) | 781 if (status == category_status_) |
| 823 return; | 782 return; |
| 824 | 783 |
| 825 category_status_ = status; | 784 category_status_ = status; |
| 826 if (observer_) { | 785 observer()->OnCategoryStatusChanged(this, provided_category_, |
| 827 observer_->OnCategoryStatusChanged(this, provided_category_, | 786 category_status_); |
| 828 category_status_); | |
| 829 } | |
| 830 } | 787 } |
| 831 | 788 |
| 832 } // namespace ntp_snippets | 789 } // namespace ntp_snippets |
| OLD | NEW |