| 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/remote/remote_suggestions_provider.h" | 5 #include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <iterator> | 8 #include <iterator> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/feature_list.h" | 12 #include "base/feature_list.h" |
| 13 #include "base/location.h" | 13 #include "base/location.h" |
| 14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 15 #include "base/metrics/histogram_macros.h" | 15 #include "base/metrics/histogram_macros.h" |
| 16 #include "base/metrics/sparse_histogram.h" | 16 #include "base/metrics/sparse_histogram.h" |
| 17 #include "base/path_service.h" | 17 #include "base/path_service.h" |
| 18 #include "base/stl_util.h" | 18 #include "base/stl_util.h" |
| 19 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
| 20 #include "base/strings/utf_string_conversions.h" | 20 #include "base/strings/utf_string_conversions.h" |
| 21 #include "base/time/default_clock.h" | 21 #include "base/time/default_clock.h" |
| 22 #include "base/time/time.h" | 22 #include "base/time/time.h" |
| 23 #include "base/values.h" | 23 #include "base/values.h" |
| 24 #include "components/data_use_measurement/core/data_use_user_data.h" | 24 #include "components/data_use_measurement/core/data_use_user_data.h" |
| 25 #include "components/history/core/browser/history_service.h" | 25 #include "components/history/core/browser/history_service.h" |
| 26 #include "components/image_fetcher/image_decoder.h" | 26 #include "components/image_fetcher/image_decoder.h" |
| 27 #include "components/image_fetcher/image_fetcher.h" | 27 #include "components/image_fetcher/image_fetcher.h" |
| 28 #include "components/ntp_snippets/category_rankers/category_ranker.h" | 28 #include "components/ntp_snippets/category_rankers/category_ranker.h" |
| 29 #include "components/ntp_snippets/features.h" | 29 #include "components/ntp_snippets/features.h" |
| 30 #include "components/ntp_snippets/pref_names.h" | 30 #include "components/ntp_snippets/pref_names.h" |
| 31 #include "components/ntp_snippets/remote/remote_suggestions_database.h" | 31 #include "components/ntp_snippets/remote/remote_suggestions_database.h" |
| 32 #include "components/ntp_snippets/switches.h" | 32 #include "components/ntp_snippets/switches.h" |
| 33 #include "components/ntp_snippets/user_classifier.h" | |
| 34 #include "components/prefs/pref_registry_simple.h" | 33 #include "components/prefs/pref_registry_simple.h" |
| 35 #include "components/prefs/pref_service.h" | 34 #include "components/prefs/pref_service.h" |
| 36 #include "components/variations/variations_associated_data.h" | 35 #include "components/variations/variations_associated_data.h" |
| 37 #include "grit/components_strings.h" | 36 #include "grit/components_strings.h" |
| 38 #include "ui/base/l10n/l10n_util.h" | 37 #include "ui/base/l10n/l10n_util.h" |
| 39 #include "ui/gfx/image/image.h" | 38 #include "ui/gfx/image/image.h" |
| 40 | 39 |
| 41 namespace ntp_snippets { | 40 namespace ntp_snippets { |
| 42 | 41 |
| 43 namespace { | 42 namespace { |
| 44 | 43 |
| 45 // Number of snippets requested to the server. Consider replacing sparse UMA | 44 // Number of snippets requested to the server. Consider replacing sparse UMA |
| 46 // histograms with COUNTS() if this number increases beyond 50. | 45 // histograms with COUNTS() if this number increases beyond 50. |
| 47 const int kMaxSnippetCount = 10; | 46 const int kMaxSnippetCount = 10; |
| 48 | 47 |
| 49 // Number of archived snippets we keep around in memory. | 48 // Number of archived snippets we keep around in memory. |
| 50 const int kMaxArchivedSnippetCount = 200; | 49 const int kMaxArchivedSnippetCount = 200; |
| 51 | 50 |
| 52 // Default values for fetching intervals, fallback and wifi. | |
| 53 const double kDefaultFetchingIntervalRareNtpUser[] = {48.0, 24.0}; | |
| 54 const double kDefaultFetchingIntervalActiveNtpUser[] = {24.0, 6.0}; | |
| 55 const double kDefaultFetchingIntervalActiveSuggestionsConsumer[] = {24.0, 6.0}; | |
| 56 | |
| 57 // Variation parameters than can override the default fetching intervals. | |
| 58 const char* kFetchingIntervalParamNameRareNtpUser[] = { | |
| 59 "fetching_interval_hours-fallback-rare_ntp_user", | |
| 60 "fetching_interval_hours-wifi-rare_ntp_user"}; | |
| 61 const char* kFetchingIntervalParamNameActiveNtpUser[] = { | |
| 62 "fetching_interval_hours-fallback-active_ntp_user", | |
| 63 "fetching_interval_hours-wifi-active_ntp_user"}; | |
| 64 const char* kFetchingIntervalParamNameActiveSuggestionsConsumer[] = { | |
| 65 "fetching_interval_hours-fallback-active_suggestions_consumer", | |
| 66 "fetching_interval_hours-wifi-active_suggestions_consumer"}; | |
| 67 | |
| 68 // Keys for storing CategoryContent info in prefs. | 51 // Keys for storing CategoryContent info in prefs. |
| 69 const char kCategoryContentId[] = "id"; | 52 const char kCategoryContentId[] = "id"; |
| 70 const char kCategoryContentTitle[] = "title"; | 53 const char kCategoryContentTitle[] = "title"; |
| 71 const char kCategoryContentProvidedByServer[] = "provided_by_server"; | 54 const char kCategoryContentProvidedByServer[] = "provided_by_server"; |
| 72 const char kCategoryContentAllowFetchingMore[] = "allow_fetching_more"; | 55 const char kCategoryContentAllowFetchingMore[] = "allow_fetching_more"; |
| 73 | 56 |
| 74 // TODO(treib): Remove after M57. | 57 // TODO(treib): Remove after M57. |
| 75 const char kDeprecatedSnippetHostsPref[] = "ntp_snippets.hosts"; | 58 const char kDeprecatedSnippetHostsPref[] = "ntp_snippets.hosts"; |
| 76 | 59 |
| 77 base::TimeDelta GetFetchingInterval(bool is_wifi, | |
| 78 UserClassifier::UserClass user_class) { | |
| 79 double value_hours = 0.0; | |
| 80 | |
| 81 const int index = is_wifi ? 1 : 0; | |
| 82 const char* param_name = ""; | |
| 83 switch (user_class) { | |
| 84 case UserClassifier::UserClass::RARE_NTP_USER: | |
| 85 value_hours = kDefaultFetchingIntervalRareNtpUser[index]; | |
| 86 param_name = kFetchingIntervalParamNameRareNtpUser[index]; | |
| 87 break; | |
| 88 case UserClassifier::UserClass::ACTIVE_NTP_USER: | |
| 89 value_hours = kDefaultFetchingIntervalActiveNtpUser[index]; | |
| 90 param_name = kFetchingIntervalParamNameActiveNtpUser[index]; | |
| 91 break; | |
| 92 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: | |
| 93 value_hours = kDefaultFetchingIntervalActiveSuggestionsConsumer[index]; | |
| 94 param_name = kFetchingIntervalParamNameActiveSuggestionsConsumer[index]; | |
| 95 break; | |
| 96 } | |
| 97 | |
| 98 // The default value can be overridden by a variation parameter. | |
| 99 std::string param_value_str = variations::GetVariationParamValueByFeature( | |
| 100 ntp_snippets::kArticleSuggestionsFeature, param_name); | |
| 101 if (!param_value_str.empty()) { | |
| 102 double param_value_hours = 0.0; | |
| 103 if (base::StringToDouble(param_value_str, ¶m_value_hours)) { | |
| 104 value_hours = param_value_hours; | |
| 105 } else { | |
| 106 LOG(WARNING) << "Invalid value for variation parameter " << param_name; | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 return base::TimeDelta::FromSecondsD(value_hours * 3600.0); | |
| 111 } | |
| 112 | |
| 113 std::unique_ptr<std::vector<std::string>> GetSnippetIDVector( | 60 std::unique_ptr<std::vector<std::string>> GetSnippetIDVector( |
| 114 const NTPSnippet::PtrVector& snippets) { | 61 const NTPSnippet::PtrVector& snippets) { |
| 115 auto result = base::MakeUnique<std::vector<std::string>>(); | 62 auto result = base::MakeUnique<std::vector<std::string>>(); |
| 116 for (const auto& snippet : snippets) { | 63 for (const auto& snippet : snippets) { |
| 117 result->push_back(snippet->id()); | 64 result->push_back(snippet->id()); |
| 118 } | 65 } |
| 119 return result; | 66 return result; |
| 120 } | 67 } |
| 121 | 68 |
| 122 bool HasIntersection(const std::vector<std::string>& a, | 69 bool HasIntersection(const std::vector<std::string>& a, |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 } | 218 } |
| 272 | 219 |
| 273 void CachedImageFetcher::OnSnippetImageFetchedFromDatabase( | 220 void CachedImageFetcher::OnSnippetImageFetchedFromDatabase( |
| 274 const ImageFetchedCallback& callback, | 221 const ImageFetchedCallback& callback, |
| 275 const ContentSuggestion::ID& suggestion_id, | 222 const ContentSuggestion::ID& suggestion_id, |
| 276 const GURL& url, | 223 const GURL& url, |
| 277 std::string data) { // SnippetImageCallback requires nonconst reference. | 224 std::string data) { // SnippetImageCallback requires nonconst reference. |
| 278 // |image_decoder_| is null in tests. | 225 // |image_decoder_| is null in tests. |
| 279 if (image_decoder_ && !data.empty()) { | 226 if (image_decoder_ && !data.empty()) { |
| 280 image_decoder_->DecodeImage( | 227 image_decoder_->DecodeImage( |
| 281 data, base::Bind( | 228 data, base::Bind(&CachedImageFetcher::OnSnippetImageDecodedFromDatabase, |
| 282 &CachedImageFetcher::OnSnippetImageDecodedFromDatabase, | 229 base::Unretained(this), callback, suggestion_id, url)); |
| 283 base::Unretained(this), callback, suggestion_id, url)); | |
| 284 return; | 230 return; |
| 285 } | 231 } |
| 286 // Fetching from the DB failed; start a network fetch. | 232 // Fetching from the DB failed; start a network fetch. |
| 287 FetchSnippetImageFromNetwork(suggestion_id, url, callback); | 233 FetchSnippetImageFromNetwork(suggestion_id, url, callback); |
| 288 } | 234 } |
| 289 | 235 |
| 290 void CachedImageFetcher::OnSnippetImageDecodedFromDatabase( | 236 void CachedImageFetcher::OnSnippetImageDecodedFromDatabase( |
| 291 const ImageFetchedCallback& callback, | 237 const ImageFetchedCallback& callback, |
| 292 const ContentSuggestion::ID& suggestion_id, | 238 const ContentSuggestion::ID& suggestion_id, |
| 293 const GURL& url, | 239 const GURL& url, |
| (...skipping 20 matching lines...) Expand all Loading... |
| 314 callback.Run(gfx::Image()); | 260 callback.Run(gfx::Image()); |
| 315 return; | 261 return; |
| 316 } | 262 } |
| 317 | 263 |
| 318 image_fetcher_->StartOrQueueNetworkRequest( | 264 image_fetcher_->StartOrQueueNetworkRequest( |
| 319 suggestion_id.id_within_category(), url, | 265 suggestion_id.id_within_category(), url, |
| 320 base::Bind(&CachedImageFetcher::OnImageDecodingDone, | 266 base::Bind(&CachedImageFetcher::OnImageDecodingDone, |
| 321 base::Unretained(this), callback)); | 267 base::Unretained(this), callback)); |
| 322 } | 268 } |
| 323 | 269 |
| 324 RemoteSuggestionsProvider::RemoteSuggestionsProvider( | 270 RemoteSuggestionsProviderImpl::RemoteSuggestionsProviderImpl( |
| 325 Observer* observer, | 271 Observer* observer, |
| 326 PrefService* pref_service, | 272 PrefService* pref_service, |
| 327 const std::string& application_language_code, | 273 const std::string& application_language_code, |
| 328 CategoryRanker* category_ranker, | 274 CategoryRanker* category_ranker, |
| 329 const UserClassifier* user_classifier, | |
| 330 NTPSnippetsScheduler* scheduler, | |
| 331 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, | 275 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, |
| 332 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, | 276 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, |
| 333 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, | 277 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, |
| 334 std::unique_ptr<RemoteSuggestionsDatabase> database, | 278 std::unique_ptr<RemoteSuggestionsDatabase> database, |
| 335 std::unique_ptr<RemoteSuggestionsStatusService> status_service) | 279 std::unique_ptr<RemoteSuggestionsStatusService> status_service) |
| 336 : ContentSuggestionsProvider(observer), | 280 : RemoteSuggestionsProvider(observer), |
| 337 state_(State::NOT_INITED), | 281 state_(State::NOT_INITED), |
| 338 pref_service_(pref_service), | 282 pref_service_(pref_service), |
| 339 articles_category_( | 283 articles_category_( |
| 340 Category::FromKnownCategory(KnownCategories::ARTICLES)), | 284 Category::FromKnownCategory(KnownCategories::ARTICLES)), |
| 341 application_language_code_(application_language_code), | 285 application_language_code_(application_language_code), |
| 342 category_ranker_(category_ranker), | 286 category_ranker_(category_ranker), |
| 343 user_classifier_(user_classifier), | |
| 344 scheduler_(scheduler), | |
| 345 snippets_fetcher_(std::move(snippets_fetcher)), | 287 snippets_fetcher_(std::move(snippets_fetcher)), |
| 346 database_(std::move(database)), | 288 database_(std::move(database)), |
| 347 image_fetcher_(std::move(image_fetcher), | 289 image_fetcher_(std::move(image_fetcher), |
| 348 std::move(image_decoder), | 290 std::move(image_decoder), |
| 349 pref_service, | 291 pref_service, |
| 350 database_.get()), | 292 database_.get()), |
| 351 status_service_(std::move(status_service)), | 293 status_service_(std::move(status_service)), |
| 352 fetch_when_ready_(false), | 294 fetch_when_ready_(false), |
| 295 fetch_when_ready_interactive_(false), |
| 296 fetch_when_ready_callback_(nullptr), |
| 297 provider_status_callback_(nullptr), |
| 353 nuke_when_initialized_(false), | 298 nuke_when_initialized_(false), |
| 354 clock_(base::MakeUnique<base::DefaultClock>()) { | 299 clock_(base::MakeUnique<base::DefaultClock>()) { |
| 355 pref_service_->ClearPref(kDeprecatedSnippetHostsPref); | 300 pref_service_->ClearPref(kDeprecatedSnippetHostsPref); |
| 356 | 301 |
| 357 RestoreCategoriesFromPrefs(); | 302 RestoreCategoriesFromPrefs(); |
| 358 // The articles category always exists. Add it if we didn't get it from prefs. | 303 // The articles category always exists. Add it if we didn't get it from prefs. |
| 359 // TODO(treib): Rethink this. | 304 // TODO(treib): Rethink this. |
| 360 category_contents_.insert( | 305 category_contents_.insert( |
| 361 std::make_pair(articles_category_, | 306 std::make_pair(articles_category_, |
| 362 CategoryContent(BuildArticleCategoryInfo(base::nullopt)))); | 307 CategoryContent(BuildArticleCategoryInfo(base::nullopt)))); |
| 363 // Tell the observer about all the categories. | 308 // Tell the observer about all the categories. |
| 364 for (const auto& entry : category_contents_) { | 309 for (const auto& entry : category_contents_) { |
| 365 observer->OnCategoryStatusChanged(this, entry.first, entry.second.status); | 310 observer->OnCategoryStatusChanged(this, entry.first, entry.second.status); |
| 366 } | 311 } |
| 367 | 312 |
| 368 if (database_->IsErrorState()) { | 313 if (database_->IsErrorState()) { |
| 369 EnterState(State::ERROR_OCCURRED); | 314 EnterState(State::ERROR_OCCURRED); |
| 370 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | 315 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); |
| 371 return; | 316 return; |
| 372 } | 317 } |
| 373 | 318 |
| 374 database_->SetErrorCallback(base::Bind( | 319 database_->SetErrorCallback(base::Bind( |
| 375 &RemoteSuggestionsProvider::OnDatabaseError, base::Unretained(this))); | 320 &RemoteSuggestionsProviderImpl::OnDatabaseError, base::Unretained(this))); |
| 376 | 321 |
| 377 // We transition to other states while finalizing the initialization, when the | 322 // We transition to other states while finalizing the initialization, when the |
| 378 // database is done loading. | 323 // database is done loading. |
| 379 database_load_start_ = base::TimeTicks::Now(); | 324 database_load_start_ = base::TimeTicks::Now(); |
| 380 database_->LoadSnippets(base::Bind( | 325 database_->LoadSnippets( |
| 381 &RemoteSuggestionsProvider::OnDatabaseLoaded, base::Unretained(this))); | 326 base::Bind(&RemoteSuggestionsProviderImpl::OnDatabaseLoaded, |
| 327 base::Unretained(this))); |
| 382 } | 328 } |
| 383 | 329 |
| 384 RemoteSuggestionsProvider::~RemoteSuggestionsProvider() = default; | 330 RemoteSuggestionsProviderImpl::~RemoteSuggestionsProviderImpl() = default; |
| 385 | 331 |
| 386 // static | 332 // static |
| 387 void RemoteSuggestionsProvider::RegisterProfilePrefs( | 333 void RemoteSuggestionsProviderImpl::RegisterProfilePrefs( |
| 388 PrefRegistrySimple* registry) { | 334 PrefRegistrySimple* registry) { |
| 389 // TODO(treib): Remove after M57. | 335 // TODO(treib): Remove after M57. |
| 390 registry->RegisterListPref(kDeprecatedSnippetHostsPref); | 336 registry->RegisterListPref(kDeprecatedSnippetHostsPref); |
| 391 registry->RegisterListPref(prefs::kRemoteSuggestionCategories); | 337 registry->RegisterListPref(prefs::kRemoteSuggestionCategories); |
| 392 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalWifi, 0); | |
| 393 registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalFallback, | |
| 394 0); | |
| 395 registry->RegisterInt64Pref(prefs::kLastSuccessfulBackgroundFetchTime, 0); | 338 registry->RegisterInt64Pref(prefs::kLastSuccessfulBackgroundFetchTime, 0); |
| 396 | 339 |
| 397 RemoteSuggestionsStatusService::RegisterProfilePrefs(registry); | 340 RemoteSuggestionsStatusService::RegisterProfilePrefs(registry); |
| 398 } | 341 } |
| 399 | 342 |
| 400 void RemoteSuggestionsProvider::FetchSnippetsInTheBackground() { | 343 void RemoteSuggestionsProviderImpl::SetProviderStatusCallback( |
| 401 FetchSnippets(/*interactive_request=*/false); | 344 std::unique_ptr<ProviderStatusCallback> callback) { |
| 345 provider_status_callback_ = std::move(callback); |
| 346 // Call the observer right away if we've reached any final state. |
| 347 NotifyStateChanged(); |
| 402 } | 348 } |
| 403 | 349 |
| 404 void RemoteSuggestionsProvider::FetchSnippetsForAllCategories() { | 350 void RemoteSuggestionsProviderImpl::ReloadSuggestions() { |
| 405 // TODO(markusheintz): Investigate whether we can call the Fetch method | 351 FetchSnippets(/*interactive_request=*/true, |
| 406 // instead of the FetchSnippets. | 352 std::unique_ptr<FetchStatusCallback>(nullptr)); |
| 407 FetchSnippets(/*interactive_request=*/true); | |
| 408 } | 353 } |
| 409 | 354 |
| 410 void RemoteSuggestionsProvider::FetchSnippets( | 355 void RemoteSuggestionsProviderImpl::RefetchInTheBackground( |
| 411 bool interactive_request) { | 356 std::unique_ptr<FetchStatusCallback> callback) { |
| 357 FetchSnippets(/*interactive_request=*/false, std::move(callback)); |
| 358 } |
| 359 |
| 360 const NTPSnippetsFetcher* |
| 361 RemoteSuggestionsProviderImpl::snippets_fetcher_for_testing_and_debugging() |
| 362 const { |
| 363 return snippets_fetcher_.get(); |
| 364 } |
| 365 |
| 366 void RemoteSuggestionsProviderImpl::FetchSnippets( |
| 367 bool interactive_request, |
| 368 std::unique_ptr<FetchStatusCallback> callback) { |
| 412 if (!ready()) { | 369 if (!ready()) { |
| 413 fetch_when_ready_ = true; | 370 fetch_when_ready_ = true; |
| 371 fetch_when_ready_interactive_ = interactive_request; |
| 372 fetch_when_ready_callback_ = std::move(callback); |
| 414 return; | 373 return; |
| 415 } | 374 } |
| 416 | 375 |
| 417 MarkEmptyCategoriesAsLoading(); | 376 MarkEmptyCategoriesAsLoading(); |
| 418 | 377 |
| 419 NTPSnippetsFetcher::Params params = BuildFetchParams(); | 378 NTPSnippetsFetcher::Params params = BuildFetchParams(); |
| 420 params.interactive_request = interactive_request; | 379 params.interactive_request = interactive_request; |
| 421 snippets_fetcher_->FetchSnippets( | 380 snippets_fetcher_->FetchSnippets( |
| 422 params, base::BindOnce(&RemoteSuggestionsProvider::OnFetchFinished, | 381 params, base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchFinished, |
| 423 base::Unretained(this), interactive_request)); | 382 base::Unretained(this), std::move(callback), |
| 383 interactive_request)); |
| 424 } | 384 } |
| 425 | 385 |
| 426 void RemoteSuggestionsProvider::Fetch( | 386 void RemoteSuggestionsProviderImpl::Fetch( |
| 427 const Category& category, | 387 const Category& category, |
| 428 const std::set<std::string>& known_suggestion_ids, | 388 const std::set<std::string>& known_suggestion_ids, |
| 429 const FetchDoneCallback& callback) { | 389 const FetchDoneCallback& callback) { |
| 430 if (!ready()) { | 390 if (!ready()) { |
| 431 CallWithEmptyResults(callback, | 391 CallWithEmptyResults(callback, |
| 432 Status(StatusCode::TEMPORARY_ERROR, | 392 Status(StatusCode::TEMPORARY_ERROR, |
| 433 "RemoteSuggestionsProvider is not ready!")); | 393 "RemoteSuggestionsProvider is not ready!")); |
| 434 return; | 394 return; |
| 435 } | 395 } |
| 436 NTPSnippetsFetcher::Params params = BuildFetchParams(); | 396 NTPSnippetsFetcher::Params params = BuildFetchParams(); |
| 437 params.excluded_ids.insert(known_suggestion_ids.begin(), | 397 params.excluded_ids.insert(known_suggestion_ids.begin(), |
| 438 known_suggestion_ids.end()); | 398 known_suggestion_ids.end()); |
| 439 params.interactive_request = true; | 399 params.interactive_request = true; |
| 440 params.exclusive_category = category; | 400 params.exclusive_category = category; |
| 441 | 401 |
| 442 snippets_fetcher_->FetchSnippets( | 402 snippets_fetcher_->FetchSnippets( |
| 443 params, base::BindOnce(&RemoteSuggestionsProvider::OnFetchMoreFinished, | 403 params, |
| 444 base::Unretained(this), callback)); | 404 base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchMoreFinished, |
| 405 base::Unretained(this), callback)); |
| 445 } | 406 } |
| 446 | 407 |
| 447 // Builds default fetcher params. | 408 // Builds default fetcher params. |
| 448 NTPSnippetsFetcher::Params RemoteSuggestionsProvider::BuildFetchParams() const { | 409 NTPSnippetsFetcher::Params RemoteSuggestionsProviderImpl::BuildFetchParams() |
| 410 const { |
| 449 NTPSnippetsFetcher::Params result; | 411 NTPSnippetsFetcher::Params result; |
| 450 result.language_code = application_language_code_; | 412 result.language_code = application_language_code_; |
| 451 result.count_to_fetch = kMaxSnippetCount; | 413 result.count_to_fetch = kMaxSnippetCount; |
| 452 for (const auto& map_entry : category_contents_) { | 414 for (const auto& map_entry : category_contents_) { |
| 453 const CategoryContent& content = map_entry.second; | 415 const CategoryContent& content = map_entry.second; |
| 454 for (const auto& dismissed_snippet : content.dismissed) { | 416 for (const auto& dismissed_snippet : content.dismissed) { |
| 455 result.excluded_ids.insert(dismissed_snippet->id()); | 417 result.excluded_ids.insert(dismissed_snippet->id()); |
| 456 } | 418 } |
| 457 } | 419 } |
| 458 return result; | 420 return result; |
| 459 } | 421 } |
| 460 | 422 |
| 461 void RemoteSuggestionsProvider::MarkEmptyCategoriesAsLoading() { | 423 void RemoteSuggestionsProviderImpl::MarkEmptyCategoriesAsLoading() { |
| 462 for (const auto& item : category_contents_) { | 424 for (const auto& item : category_contents_) { |
| 463 Category category = item.first; | 425 Category category = item.first; |
| 464 const CategoryContent& content = item.second; | 426 const CategoryContent& content = item.second; |
| 465 if (content.snippets.empty()) { | 427 if (content.snippets.empty()) { |
| 466 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); | 428 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); |
| 467 } | 429 } |
| 468 } | 430 } |
| 469 } | 431 } |
| 470 | 432 |
| 471 void RemoteSuggestionsProvider::RescheduleFetching(bool force) { | 433 CategoryStatus RemoteSuggestionsProviderImpl::GetCategoryStatus( |
| 472 // The scheduler only exists on Android so far, it's null on other platforms. | 434 Category category) { |
| 473 if (!scheduler_) { | |
| 474 return; | |
| 475 } | |
| 476 | |
| 477 if (ready()) { | |
| 478 base::TimeDelta old_interval_wifi = base::TimeDelta::FromInternalValue( | |
| 479 pref_service_->GetInt64(prefs::kSnippetBackgroundFetchingIntervalWifi)); | |
| 480 base::TimeDelta old_interval_fallback = | |
| 481 base::TimeDelta::FromInternalValue(pref_service_->GetInt64( | |
| 482 prefs::kSnippetBackgroundFetchingIntervalFallback)); | |
| 483 UserClassifier::UserClass user_class = user_classifier_->GetUserClass(); | |
| 484 base::TimeDelta interval_wifi = | |
| 485 GetFetchingInterval(/*is_wifi=*/true, user_class); | |
| 486 base::TimeDelta interval_fallback = | |
| 487 GetFetchingInterval(/*is_wifi=*/false, user_class); | |
| 488 if (force || interval_wifi != old_interval_wifi || | |
| 489 interval_fallback != old_interval_fallback) { | |
| 490 scheduler_->Schedule(interval_wifi, interval_fallback); | |
| 491 pref_service_->SetInt64(prefs::kSnippetBackgroundFetchingIntervalWifi, | |
| 492 interval_wifi.ToInternalValue()); | |
| 493 pref_service_->SetInt64(prefs::kSnippetBackgroundFetchingIntervalFallback, | |
| 494 interval_fallback.ToInternalValue()); | |
| 495 } | |
| 496 } else { | |
| 497 // If we're NOT_INITED, we don't know whether to schedule or unschedule. | |
| 498 // If |force| is false, all is well: We'll reschedule on the next state | |
| 499 // change anyway. If it's true, then unschedule here, to make sure that the | |
| 500 // next reschedule actually happens. | |
| 501 if (state_ != State::NOT_INITED || force) { | |
| 502 scheduler_->Unschedule(); | |
| 503 pref_service_->ClearPref(prefs::kSnippetBackgroundFetchingIntervalWifi); | |
| 504 pref_service_->ClearPref( | |
| 505 prefs::kSnippetBackgroundFetchingIntervalFallback); | |
| 506 } | |
| 507 } | |
| 508 } | |
| 509 | |
| 510 CategoryStatus RemoteSuggestionsProvider::GetCategoryStatus(Category category) { | |
| 511 auto content_it = category_contents_.find(category); | 435 auto content_it = category_contents_.find(category); |
| 512 DCHECK(content_it != category_contents_.end()); | 436 DCHECK(content_it != category_contents_.end()); |
| 513 return content_it->second.status; | 437 return content_it->second.status; |
| 514 } | 438 } |
| 515 | 439 |
| 516 CategoryInfo RemoteSuggestionsProvider::GetCategoryInfo(Category category) { | 440 CategoryInfo RemoteSuggestionsProviderImpl::GetCategoryInfo(Category category) { |
| 517 auto content_it = category_contents_.find(category); | 441 auto content_it = category_contents_.find(category); |
| 518 DCHECK(content_it != category_contents_.end()); | 442 DCHECK(content_it != category_contents_.end()); |
| 519 return content_it->second.info; | 443 return content_it->second.info; |
| 520 } | 444 } |
| 521 | 445 |
| 522 void RemoteSuggestionsProvider::DismissSuggestion( | 446 void RemoteSuggestionsProviderImpl::DismissSuggestion( |
| 523 const ContentSuggestion::ID& suggestion_id) { | 447 const ContentSuggestion::ID& suggestion_id) { |
| 524 if (!ready()) { | 448 if (!ready()) { |
| 525 return; | 449 return; |
| 526 } | 450 } |
| 527 | 451 |
| 528 auto content_it = category_contents_.find(suggestion_id.category()); | 452 auto content_it = category_contents_.find(suggestion_id.category()); |
| 529 DCHECK(content_it != category_contents_.end()); | 453 DCHECK(content_it != category_contents_.end()); |
| 530 CategoryContent* content = &content_it->second; | 454 CategoryContent* content = &content_it->second; |
| 531 DismissSuggestionFromCategoryContent(content, | 455 DismissSuggestionFromCategoryContent(content, |
| 532 suggestion_id.id_within_category()); | 456 suggestion_id.id_within_category()); |
| 533 } | 457 } |
| 534 | 458 |
| 535 void RemoteSuggestionsProvider::ClearHistory( | 459 void RemoteSuggestionsProviderImpl::ClearHistory( |
| 536 base::Time begin, | 460 base::Time begin, |
| 537 base::Time end, | 461 base::Time end, |
| 538 const base::Callback<bool(const GURL& url)>& filter) { | 462 const base::Callback<bool(const GURL& url)>& filter) { |
| 539 // Both time range and the filter are ignored and all suggestions are removed, | 463 // Both time range and the filter are ignored and all suggestions are removed, |
| 540 // because it is not known which history entries were used for the suggestions | 464 // because it is not known which history entries were used for the suggestions |
| 541 // personalization. | 465 // personalization. |
| 542 if (!ready()) { | 466 if (!ready()) { |
| 543 nuke_when_initialized_ = true; | 467 nuke_when_initialized_ = true; |
| 544 } else { | 468 } else { |
| 545 NukeAllSnippets(); | 469 NukeAllSnippets(); |
| 546 } | 470 } |
| 547 } | 471 } |
| 548 | 472 |
| 549 void RemoteSuggestionsProvider::ClearCachedSuggestions(Category category) { | 473 void RemoteSuggestionsProviderImpl::ClearCachedSuggestions(Category category) { |
| 550 if (!initialized()) { | 474 if (!initialized()) { |
| 551 return; | 475 return; |
| 552 } | 476 } |
| 553 | 477 |
| 554 auto content_it = category_contents_.find(category); | 478 auto content_it = category_contents_.find(category); |
| 555 if (content_it == category_contents_.end()) { | 479 if (content_it == category_contents_.end()) { |
| 556 return; | 480 return; |
| 557 } | 481 } |
| 558 CategoryContent* content = &content_it->second; | 482 CategoryContent* content = &content_it->second; |
| 559 if (content->snippets.empty()) { | 483 if (content->snippets.empty()) { |
| 560 return; | 484 return; |
| 561 } | 485 } |
| 562 | 486 |
| 563 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); | 487 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
| 564 database_->DeleteImages(GetSnippetIDVector(content->snippets)); | 488 database_->DeleteImages(GetSnippetIDVector(content->snippets)); |
| 565 content->snippets.clear(); | 489 content->snippets.clear(); |
| 566 | 490 |
| 567 if (IsCategoryStatusAvailable(content->status)) { | 491 if (IsCategoryStatusAvailable(content->status)) { |
| 568 NotifyNewSuggestions(category, *content); | 492 NotifyNewSuggestions(category, *content); |
| 569 } | 493 } |
| 570 } | 494 } |
| 571 | 495 |
| 572 void RemoteSuggestionsProvider::OnSignInStateChanged() { | 496 void RemoteSuggestionsProviderImpl::OnSignInStateChanged() { |
| 573 // Make sure the status service is registered and we already initialised its | 497 // Make sure the status service is registered and we already initialised its |
| 574 // start state. | 498 // start state. |
| 575 if (!initialized()) { | 499 if (!initialized()) { |
| 576 return; | 500 return; |
| 577 } | 501 } |
| 578 | 502 |
| 579 status_service_->OnSignInStateChanged(); | 503 status_service_->OnSignInStateChanged(); |
| 580 } | 504 } |
| 581 | 505 |
| 582 void RemoteSuggestionsProvider::GetDismissedSuggestionsForDebugging( | 506 void RemoteSuggestionsProviderImpl::GetDismissedSuggestionsForDebugging( |
| 583 Category category, | 507 Category category, |
| 584 const DismissedSuggestionsCallback& callback) { | 508 const DismissedSuggestionsCallback& callback) { |
| 585 auto content_it = category_contents_.find(category); | 509 auto content_it = category_contents_.find(category); |
| 586 DCHECK(content_it != category_contents_.end()); | 510 DCHECK(content_it != category_contents_.end()); |
| 587 callback.Run( | 511 callback.Run( |
| 588 ConvertToContentSuggestions(category, content_it->second.dismissed)); | 512 ConvertToContentSuggestions(category, content_it->second.dismissed)); |
| 589 } | 513 } |
| 590 | 514 |
| 591 void RemoteSuggestionsProvider::ClearDismissedSuggestionsForDebugging( | 515 void RemoteSuggestionsProviderImpl::ClearDismissedSuggestionsForDebugging( |
| 592 Category category) { | 516 Category category) { |
| 593 auto content_it = category_contents_.find(category); | 517 auto content_it = category_contents_.find(category); |
| 594 DCHECK(content_it != category_contents_.end()); | 518 DCHECK(content_it != category_contents_.end()); |
| 595 CategoryContent* content = &content_it->second; | 519 CategoryContent* content = &content_it->second; |
| 596 | 520 |
| 597 if (!initialized()) { | 521 if (!initialized()) { |
| 598 return; | 522 return; |
| 599 } | 523 } |
| 600 | 524 |
| 601 if (content->dismissed.empty()) { | 525 if (content->dismissed.empty()) { |
| 602 return; | 526 return; |
| 603 } | 527 } |
| 604 | 528 |
| 605 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); | 529 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); |
| 606 // The image got already deleted when the suggestion was dismissed. | 530 // The image got already deleted when the suggestion was dismissed. |
| 607 | 531 |
| 608 content->dismissed.clear(); | 532 content->dismissed.clear(); |
| 609 } | 533 } |
| 610 | 534 |
| 611 // static | 535 // static |
| 612 int RemoteSuggestionsProvider::GetMaxSnippetCountForTesting() { | 536 int RemoteSuggestionsProviderImpl::GetMaxSnippetCountForTesting() { |
| 613 return kMaxSnippetCount; | 537 return kMaxSnippetCount; |
| 614 } | 538 } |
| 615 | 539 |
| 616 //////////////////////////////////////////////////////////////////////////////// | 540 //////////////////////////////////////////////////////////////////////////////// |
| 617 // Private methods | 541 // Private methods |
| 618 | 542 |
| 619 GURL RemoteSuggestionsProvider::FindSnippetImageUrl( | 543 GURL RemoteSuggestionsProviderImpl::FindSnippetImageUrl( |
| 620 const ContentSuggestion::ID& suggestion_id) const { | 544 const ContentSuggestion::ID& suggestion_id) const { |
| 621 DCHECK(base::ContainsKey(category_contents_, suggestion_id.category())); | 545 DCHECK(base::ContainsKey(category_contents_, suggestion_id.category())); |
| 622 | 546 |
| 623 const CategoryContent& content = | 547 const CategoryContent& content = |
| 624 category_contents_.at(suggestion_id.category()); | 548 category_contents_.at(suggestion_id.category()); |
| 625 const NTPSnippet* snippet = | 549 const NTPSnippet* snippet = |
| 626 content.FindSnippet(suggestion_id.id_within_category()); | 550 content.FindSnippet(suggestion_id.id_within_category()); |
| 627 if (!snippet) { | 551 if (!snippet) { |
| 628 return GURL(); | 552 return GURL(); |
| 629 } | 553 } |
| 630 return snippet->salient_image_url(); | 554 return snippet->salient_image_url(); |
| 631 } | 555 } |
| 632 | 556 |
| 633 void RemoteSuggestionsProvider::OnDatabaseLoaded( | 557 void RemoteSuggestionsProviderImpl::OnDatabaseLoaded( |
| 634 NTPSnippet::PtrVector snippets) { | 558 NTPSnippet::PtrVector snippets) { |
| 635 if (state_ == State::ERROR_OCCURRED) { | 559 if (state_ == State::ERROR_OCCURRED) { |
| 636 return; | 560 return; |
| 637 } | 561 } |
| 638 DCHECK(state_ == State::NOT_INITED); | 562 DCHECK(state_ == State::NOT_INITED); |
| 639 DCHECK(base::ContainsKey(category_contents_, articles_category_)); | 563 DCHECK(base::ContainsKey(category_contents_, articles_category_)); |
| 640 | 564 |
| 641 base::TimeDelta database_load_time = | 565 base::TimeDelta database_load_time = |
| 642 base::TimeTicks::Now() - database_load_start_; | 566 base::TimeTicks::Now() - database_load_start_; |
| 643 UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Snippets.DatabaseLoadTime", | 567 UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Snippets.DatabaseLoadTime", |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 679 } | 603 } |
| 680 | 604 |
| 681 // TODO(tschumann): If I move ClearExpiredDismissedSnippets() to the beginning | 605 // TODO(tschumann): If I move ClearExpiredDismissedSnippets() to the beginning |
| 682 // of the function, it essentially does nothing but tests are still green. Fix | 606 // of the function, it essentially does nothing but tests are still green. Fix |
| 683 // this! | 607 // this! |
| 684 ClearExpiredDismissedSnippets(); | 608 ClearExpiredDismissedSnippets(); |
| 685 ClearOrphanedImages(); | 609 ClearOrphanedImages(); |
| 686 FinishInitialization(); | 610 FinishInitialization(); |
| 687 } | 611 } |
| 688 | 612 |
| 689 void RemoteSuggestionsProvider::OnDatabaseError() { | 613 void RemoteSuggestionsProviderImpl::OnDatabaseError() { |
| 690 EnterState(State::ERROR_OCCURRED); | 614 EnterState(State::ERROR_OCCURRED); |
| 691 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | 615 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); |
| 692 } | 616 } |
| 693 | 617 |
| 694 void RemoteSuggestionsProvider::OnFetchMoreFinished( | 618 void RemoteSuggestionsProviderImpl::OnFetchMoreFinished( |
| 695 const FetchDoneCallback& fetching_callback, | 619 const FetchDoneCallback& fetching_callback, |
| 696 Status status, | 620 Status status, |
| 697 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { | 621 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { |
| 698 if (!fetched_categories) { | 622 if (!fetched_categories) { |
| 699 DCHECK(!status.IsSuccess()); | 623 DCHECK(!status.IsSuccess()); |
| 700 CallWithEmptyResults(fetching_callback, status); | 624 CallWithEmptyResults(fetching_callback, status); |
| 701 return; | 625 return; |
| 702 } | 626 } |
| 703 if (fetched_categories->size() != 1u) { | 627 if (fetched_categories->size() != 1u) { |
| 704 LOG(DFATAL) << "Requested one exclusive category but received " | 628 LOG(DFATAL) << "Requested one exclusive category but received " |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 744 // e.g. to make sure we don't serve results after the sign-out. Revisit this | 668 // e.g. to make sure we don't serve results after the sign-out. Revisit this |
| 745 // once the snippets fetcher supports concurrent requests. We can then see if | 669 // once the snippets fetcher supports concurrent requests. We can then see if |
| 746 // Nuke should also cancel outstanding requests or we want to check the | 670 // Nuke should also cancel outstanding requests or we want to check the |
| 747 // status. | 671 // status. |
| 748 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 672 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
| 749 // Notify callers and observers. | 673 // Notify callers and observers. |
| 750 fetching_callback.Run(Status::Success(), std::move(result)); | 674 fetching_callback.Run(Status::Success(), std::move(result)); |
| 751 NotifyNewSuggestions(category, *existing_content); | 675 NotifyNewSuggestions(category, *existing_content); |
| 752 } | 676 } |
| 753 | 677 |
| 754 void RemoteSuggestionsProvider::OnFetchFinished( | 678 void RemoteSuggestionsProviderImpl::OnFetchFinished( |
| 679 std::unique_ptr<FetchStatusCallback> callback, |
| 755 bool interactive_request, | 680 bool interactive_request, |
| 756 Status status, | 681 Status status, |
| 757 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { | 682 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { |
| 758 if (!ready()) { | 683 if (!ready()) { |
| 759 // TODO(tschumann): What happens if this was a user-triggered, interactive | 684 // TODO(tschumann): What happens if this was a user-triggered, interactive |
| 760 // request? Is the UI waiting indefinitely now? | 685 // request? Is the UI waiting indefinitely now? |
| 761 return; | 686 return; |
| 762 } | 687 } |
| 763 | 688 |
| 764 // Record the fetch time of a successfull background fetch. | 689 // Record the fetch time of a successfull background fetch. |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 820 auto content_it = category_contents_.find(articles_category_); | 745 auto content_it = category_contents_.find(articles_category_); |
| 821 DCHECK(content_it != category_contents_.end()); | 746 DCHECK(content_it != category_contents_.end()); |
| 822 const CategoryContent& content = content_it->second; | 747 const CategoryContent& content = content_it->second; |
| 823 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | 748 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", |
| 824 content.snippets.size()); | 749 content.snippets.size()); |
| 825 if (content.snippets.empty() && !content.dismissed.empty()) { | 750 if (content.snippets.empty() && !content.dismissed.empty()) { |
| 826 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 751 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", |
| 827 content.dismissed.size()); | 752 content.dismissed.size()); |
| 828 } | 753 } |
| 829 | 754 |
| 830 // Reschedule after a successful fetch. This resets all currently scheduled | 755 if (callback) { |
| 831 // fetches, to make sure the fallback interval triggers only if no wifi fetch | 756 callback->Run(status); |
| 832 // succeeded, and also that we don't do a background fetch immediately after | |
| 833 // a user-initiated one. | |
| 834 if (fetched_categories) { | |
| 835 RescheduleFetching(true); | |
| 836 } | 757 } |
| 837 } | 758 } |
| 838 | 759 |
| 839 void RemoteSuggestionsProvider::ArchiveSnippets( | 760 void RemoteSuggestionsProviderImpl::ArchiveSnippets( |
| 840 CategoryContent* content, | 761 CategoryContent* content, |
| 841 NTPSnippet::PtrVector* to_archive) { | 762 NTPSnippet::PtrVector* to_archive) { |
| 842 // Archive previous snippets - move them at the beginning of the list. | 763 // Archive previous snippets - move them at the beginning of the list. |
| 843 content->archived.insert(content->archived.begin(), | 764 content->archived.insert(content->archived.begin(), |
| 844 std::make_move_iterator(to_archive->begin()), | 765 std::make_move_iterator(to_archive->begin()), |
| 845 std::make_move_iterator(to_archive->end())); | 766 std::make_move_iterator(to_archive->end())); |
| 846 to_archive->clear(); | 767 to_archive->clear(); |
| 847 | 768 |
| 848 // If there are more archived snippets than we want to keep, delete the | 769 // If there are more archived snippets than we want to keep, delete the |
| 849 // oldest ones by their fetch time (which are always in the back). | 770 // oldest ones by their fetch time (which are always in the back). |
| 850 if (content->archived.size() > kMaxArchivedSnippetCount) { | 771 if (content->archived.size() > kMaxArchivedSnippetCount) { |
| 851 NTPSnippet::PtrVector to_delete( | 772 NTPSnippet::PtrVector to_delete( |
| 852 std::make_move_iterator(content->archived.begin() + | 773 std::make_move_iterator(content->archived.begin() + |
| 853 kMaxArchivedSnippetCount), | 774 kMaxArchivedSnippetCount), |
| 854 std::make_move_iterator(content->archived.end())); | 775 std::make_move_iterator(content->archived.end())); |
| 855 content->archived.resize(kMaxArchivedSnippetCount); | 776 content->archived.resize(kMaxArchivedSnippetCount); |
| 856 database_->DeleteImages(GetSnippetIDVector(to_delete)); | 777 database_->DeleteImages(GetSnippetIDVector(to_delete)); |
| 857 } | 778 } |
| 858 } | 779 } |
| 859 | 780 |
| 860 void RemoteSuggestionsProvider::SanitizeReceivedSnippets( | 781 void RemoteSuggestionsProviderImpl::SanitizeReceivedSnippets( |
| 861 const NTPSnippet::PtrVector& dismissed, | 782 const NTPSnippet::PtrVector& dismissed, |
| 862 NTPSnippet::PtrVector* snippets) { | 783 NTPSnippet::PtrVector* snippets) { |
| 863 DCHECK(ready()); | 784 DCHECK(ready()); |
| 864 EraseMatchingSnippets(snippets, dismissed); | 785 EraseMatchingSnippets(snippets, dismissed); |
| 865 RemoveIncompleteSnippets(snippets); | 786 RemoveIncompleteSnippets(snippets); |
| 866 } | 787 } |
| 867 | 788 |
| 868 void RemoteSuggestionsProvider::IntegrateSnippets( | 789 void RemoteSuggestionsProviderImpl::IntegrateSnippets( |
| 869 CategoryContent* content, | 790 CategoryContent* content, |
| 870 NTPSnippet::PtrVector new_snippets) { | 791 NTPSnippet::PtrVector new_snippets) { |
| 871 DCHECK(ready()); | 792 DCHECK(ready()); |
| 872 | 793 |
| 873 // Do not touch the current set of snippets if the newly fetched one is empty. | 794 // Do not touch the current set of snippets if the newly fetched one is empty. |
| 874 // TODO(tschumann): This should go. If we get empty results we should update | 795 // TODO(tschumann): This should go. If we get empty results we should update |
| 875 // accordingly and remove the old one (only of course if this was not received | 796 // accordingly and remove the old one (only of course if this was not received |
| 876 // through a fetch-more). | 797 // through a fetch-more). |
| 877 if (new_snippets.empty()) { | 798 if (new_snippets.empty()) { |
| 878 return; | 799 return; |
| 879 } | 800 } |
| 880 | 801 |
| 881 // It's entirely possible that the newly fetched snippets contain articles | 802 // It's entirely possible that the newly fetched snippets contain articles |
| 882 // that have been present before. | 803 // that have been present before. |
| 883 // We need to make sure to only delete and archive snippets that don't | 804 // We need to make sure to only delete and archive snippets that don't |
| 884 // appear with the same ID in the new suggestions (it's fine for additional | 805 // appear with the same ID in the new suggestions (it's fine for additional |
| 885 // IDs though). | 806 // IDs though). |
| 886 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); | 807 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); |
| 887 // Do not delete the thumbnail images as they are still handy on open NTPs. | 808 // Do not delete the thumbnail images as they are still handy on open NTPs. |
| 888 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); | 809 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
| 889 // Note, that ArchiveSnippets will clear |content->snippets|. | 810 // Note, that ArchiveSnippets will clear |content->snippets|. |
| 890 ArchiveSnippets(content, &content->snippets); | 811 ArchiveSnippets(content, &content->snippets); |
| 891 | 812 |
| 892 database_->SaveSnippets(new_snippets); | 813 database_->SaveSnippets(new_snippets); |
| 893 | 814 |
| 894 content->snippets = std::move(new_snippets); | 815 content->snippets = std::move(new_snippets); |
| 895 } | 816 } |
| 896 | 817 |
| 897 void RemoteSuggestionsProvider::DismissSuggestionFromCategoryContent( | 818 void RemoteSuggestionsProviderImpl::DismissSuggestionFromCategoryContent( |
| 898 CategoryContent* content, | 819 CategoryContent* content, |
| 899 const std::string& id_within_category) { | 820 const std::string& id_within_category) { |
| 900 auto it = std::find_if( | 821 auto it = std::find_if( |
| 901 content->snippets.begin(), content->snippets.end(), | 822 content->snippets.begin(), content->snippets.end(), |
| 902 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { | 823 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { |
| 903 return snippet->id() == id_within_category; | 824 return snippet->id() == id_within_category; |
| 904 }); | 825 }); |
| 905 if (it == content->snippets.end()) { | 826 if (it == content->snippets.end()) { |
| 906 return; | 827 return; |
| 907 } | 828 } |
| 908 | 829 |
| 909 (*it)->set_dismissed(true); | 830 (*it)->set_dismissed(true); |
| 910 | 831 |
| 911 database_->SaveSnippet(**it); | 832 database_->SaveSnippet(**it); |
| 912 | 833 |
| 913 content->dismissed.push_back(std::move(*it)); | 834 content->dismissed.push_back(std::move(*it)); |
| 914 content->snippets.erase(it); | 835 content->snippets.erase(it); |
| 915 } | 836 } |
| 916 | 837 |
| 917 void RemoteSuggestionsProvider::ClearExpiredDismissedSnippets() { | 838 void RemoteSuggestionsProviderImpl::ClearExpiredDismissedSnippets() { |
| 918 std::vector<Category> categories_to_erase; | 839 std::vector<Category> categories_to_erase; |
| 919 | 840 |
| 920 const base::Time now = base::Time::Now(); | 841 const base::Time now = base::Time::Now(); |
| 921 | 842 |
| 922 for (auto& item : category_contents_) { | 843 for (auto& item : category_contents_) { |
| 923 Category category = item.first; | 844 Category category = item.first; |
| 924 CategoryContent* content = &item.second; | 845 CategoryContent* content = &item.second; |
| 925 | 846 |
| 926 NTPSnippet::PtrVector to_delete; | 847 NTPSnippet::PtrVector to_delete; |
| 927 // Move expired dismissed snippets over into |to_delete|. | 848 // Move expired dismissed snippets over into |to_delete|. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 945 } | 866 } |
| 946 | 867 |
| 947 for (Category category : categories_to_erase) { | 868 for (Category category : categories_to_erase) { |
| 948 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 869 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
| 949 category_contents_.erase(category); | 870 category_contents_.erase(category); |
| 950 } | 871 } |
| 951 | 872 |
| 952 StoreCategoriesToPrefs(); | 873 StoreCategoriesToPrefs(); |
| 953 } | 874 } |
| 954 | 875 |
| 955 void RemoteSuggestionsProvider::ClearOrphanedImages() { | 876 void RemoteSuggestionsProviderImpl::ClearOrphanedImages() { |
| 956 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); | 877 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); |
| 957 for (const auto& entry : category_contents_) { | 878 for (const auto& entry : category_contents_) { |
| 958 const CategoryContent& content = entry.second; | 879 const CategoryContent& content = entry.second; |
| 959 for (const auto& snippet_ptr : content.snippets) { | 880 for (const auto& snippet_ptr : content.snippets) { |
| 960 alive_snippets->insert(snippet_ptr->id()); | 881 alive_snippets->insert(snippet_ptr->id()); |
| 961 } | 882 } |
| 962 for (const auto& snippet_ptr : content.dismissed) { | 883 for (const auto& snippet_ptr : content.dismissed) { |
| 963 alive_snippets->insert(snippet_ptr->id()); | 884 alive_snippets->insert(snippet_ptr->id()); |
| 964 } | 885 } |
| 965 } | 886 } |
| 966 database_->GarbageCollectImages(std::move(alive_snippets)); | 887 database_->GarbageCollectImages(std::move(alive_snippets)); |
| 967 } | 888 } |
| 968 | 889 |
| 969 void RemoteSuggestionsProvider::NukeAllSnippets() { | 890 void RemoteSuggestionsProviderImpl::NukeAllSnippets() { |
| 970 std::vector<Category> categories_to_erase; | 891 std::vector<Category> categories_to_erase; |
| 971 | 892 |
| 972 // Empty the ARTICLES category and remove all others, since they may or may | 893 // Empty the ARTICLES category and remove all others, since they may or may |
| 973 // not be personalized. | 894 // not be personalized. |
| 974 for (const auto& item : category_contents_) { | 895 for (const auto& item : category_contents_) { |
| 975 Category category = item.first; | 896 Category category = item.first; |
| 976 | 897 |
| 977 ClearCachedSuggestions(category); | 898 ClearCachedSuggestions(category); |
| 978 ClearDismissedSuggestionsForDebugging(category); | 899 ClearDismissedSuggestionsForDebugging(category); |
| 979 | 900 |
| 980 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 901 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
| 981 | 902 |
| 982 // Remove the category entirely; it may or may not reappear. | 903 // Remove the category entirely; it may or may not reappear. |
| 983 if (category != articles_category_) { | 904 if (category != articles_category_) { |
| 984 categories_to_erase.push_back(category); | 905 categories_to_erase.push_back(category); |
| 985 } | 906 } |
| 986 } | 907 } |
| 987 | 908 |
| 988 for (Category category : categories_to_erase) { | 909 for (Category category : categories_to_erase) { |
| 989 category_contents_.erase(category); | 910 category_contents_.erase(category); |
| 990 } | 911 } |
| 991 | 912 |
| 992 StoreCategoriesToPrefs(); | 913 StoreCategoriesToPrefs(); |
| 993 } | 914 } |
| 994 | 915 |
| 995 void RemoteSuggestionsProvider::FetchSuggestionImage( | 916 void RemoteSuggestionsProviderImpl::FetchSuggestionImage( |
| 996 const ContentSuggestion::ID& suggestion_id, | 917 const ContentSuggestion::ID& suggestion_id, |
| 997 const ImageFetchedCallback& callback) { | 918 const ImageFetchedCallback& callback) { |
| 998 if (!base::ContainsKey(category_contents_, suggestion_id.category())) { | 919 if (!base::ContainsKey(category_contents_, suggestion_id.category())) { |
| 999 base::ThreadTaskRunnerHandle::Get()->PostTask( | 920 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 1000 FROM_HERE, base::Bind(callback, gfx::Image())); | 921 FROM_HERE, base::Bind(callback, gfx::Image())); |
| 1001 return; | 922 return; |
| 1002 } | 923 } |
| 1003 GURL image_url = FindSnippetImageUrl(suggestion_id); | 924 GURL image_url = FindSnippetImageUrl(suggestion_id); |
| 1004 if (image_url.is_empty()) { | 925 if (image_url.is_empty()) { |
| 1005 // As we don't know the corresponding snippet anymore, we don't expect to | 926 // As we don't know the corresponding snippet anymore, we don't expect to |
| 1006 // find it in the database (and also can't fetch it remotely). Cut the | 927 // find it in the database (and also can't fetch it remotely). Cut the |
| 1007 // lookup short and return directly. | 928 // lookup short and return directly. |
| 1008 base::ThreadTaskRunnerHandle::Get()->PostTask( | 929 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 1009 FROM_HERE, base::Bind(callback, gfx::Image())); | 930 FROM_HERE, base::Bind(callback, gfx::Image())); |
| 1010 return; | 931 return; |
| 1011 } | 932 } |
| 1012 image_fetcher_.FetchSuggestionImage(suggestion_id, image_url, callback); | 933 image_fetcher_.FetchSuggestionImage(suggestion_id, image_url, callback); |
| 1013 } | 934 } |
| 1014 | 935 |
| 1015 void RemoteSuggestionsProvider::EnterStateReady() { | 936 void RemoteSuggestionsProviderImpl::EnterStateReady() { |
| 1016 if (nuke_when_initialized_) { | 937 if (nuke_when_initialized_) { |
| 1017 NukeAllSnippets(); | 938 NukeAllSnippets(); |
| 1018 nuke_when_initialized_ = false; | 939 nuke_when_initialized_ = false; |
| 1019 } | 940 } |
| 1020 | 941 |
| 1021 auto article_category_it = category_contents_.find(articles_category_); | 942 auto article_category_it = category_contents_.find(articles_category_); |
| 1022 DCHECK(article_category_it != category_contents_.end()); | 943 DCHECK(article_category_it != category_contents_.end()); |
| 1023 if (article_category_it->second.snippets.empty() || fetch_when_ready_) { | 944 if (article_category_it->second.snippets.empty() || fetch_when_ready_) { |
| 1024 // TODO(jkrcal): Fetching snippets automatically upon creation of this | 945 // TODO(jkrcal): Fetching snippets automatically upon creation of this |
| 1025 // lazily created service can cause troubles, e.g. in unit tests where | 946 // lazily created service can cause troubles, e.g. in unit tests where |
| 1026 // network I/O is not allowed. | 947 // network I/O is not allowed. |
| 1027 // Either add a DCHECK here that we actually are allowed to do network I/O | 948 // Either add a DCHECK here that we actually are allowed to do network I/O |
| 1028 // or change the logic so that some explicit call is always needed for the | 949 // or change the logic so that some explicit call is always needed for the |
| 1029 // network request. | 950 // network request. |
| 1030 FetchSnippets(/*interactive_request=*/false); | 951 FetchSnippets(fetch_when_ready_interactive_, |
| 952 std::move(fetch_when_ready_callback_)); |
| 1031 fetch_when_ready_ = false; | 953 fetch_when_ready_ = false; |
| 954 fetch_when_ready_callback_.reset(); |
| 1032 } | 955 } |
| 1033 | 956 |
| 1034 for (const auto& item : category_contents_) { | 957 for (const auto& item : category_contents_) { |
| 1035 Category category = item.first; | 958 Category category = item.first; |
| 1036 const CategoryContent& content = item.second; | 959 const CategoryContent& content = item.second; |
| 1037 // FetchSnippets has set the status to |AVAILABLE_LOADING| if relevant, | 960 // FetchSnippets has set the status to |AVAILABLE_LOADING| if relevant, |
| 1038 // otherwise we transition to |AVAILABLE| here. | 961 // otherwise we transition to |AVAILABLE| here. |
| 1039 if (content.status != CategoryStatus::AVAILABLE_LOADING) { | 962 if (content.status != CategoryStatus::AVAILABLE_LOADING) { |
| 1040 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 963 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
| 1041 } | 964 } |
| 1042 } | 965 } |
| 1043 } | 966 } |
| 1044 | 967 |
| 1045 void RemoteSuggestionsProvider::EnterStateDisabled() { | 968 void RemoteSuggestionsProviderImpl::EnterStateDisabled() { |
| 1046 NukeAllSnippets(); | 969 NukeAllSnippets(); |
| 1047 } | 970 } |
| 1048 | 971 |
| 1049 void RemoteSuggestionsProvider::EnterStateError() { | 972 void RemoteSuggestionsProviderImpl::EnterStateError() { |
| 1050 status_service_.reset(); | 973 status_service_.reset(); |
| 1051 } | 974 } |
| 1052 | 975 |
| 1053 void RemoteSuggestionsProvider::FinishInitialization() { | 976 void RemoteSuggestionsProviderImpl::FinishInitialization() { |
| 1054 if (nuke_when_initialized_) { | 977 if (nuke_when_initialized_) { |
| 1055 // We nuke here in addition to EnterStateReady, so that it happens even if | 978 // We nuke here in addition to EnterStateReady, so that it happens even if |
| 1056 // we enter the DISABLED state below. | 979 // we enter the DISABLED state below. |
| 1057 NukeAllSnippets(); | 980 NukeAllSnippets(); |
| 1058 nuke_when_initialized_ = false; | 981 nuke_when_initialized_ = false; |
| 1059 } | 982 } |
| 1060 | 983 |
| 1061 // Note: Initializing the status service will run the callback right away with | 984 // Note: Initializing the status service will run the callback right away with |
| 1062 // the current state. | 985 // the current state. |
| 1063 status_service_->Init(base::Bind(&RemoteSuggestionsProvider::OnStatusChanged, | 986 status_service_->Init(base::Bind( |
| 1064 base::Unretained(this))); | 987 &RemoteSuggestionsProviderImpl::OnStatusChanged, base::Unretained(this))); |
| 1065 | 988 |
| 1066 // Always notify here even if we got nothing from the database, because we | 989 // Always notify here even if we got nothing from the database, because we |
| 1067 // don't know how long the fetch will take or if it will even complete. | 990 // don't know how long the fetch will take or if it will even complete. |
| 1068 for (const auto& item : category_contents_) { | 991 for (const auto& item : category_contents_) { |
| 1069 Category category = item.first; | 992 Category category = item.first; |
| 1070 const CategoryContent& content = item.second; | 993 const CategoryContent& content = item.second; |
| 1071 // Note: We might be in a non-available status here, e.g. DISABLED due to | 994 // Note: We might be in a non-available status here, e.g. DISABLED due to |
| 1072 // enterprise policy. | 995 // enterprise policy. |
| 1073 if (IsCategoryStatusAvailable(content.status)) { | 996 if (IsCategoryStatusAvailable(content.status)) { |
| 1074 NotifyNewSuggestions(category, content); | 997 NotifyNewSuggestions(category, content); |
| 1075 } | 998 } |
| 1076 } | 999 } |
| 1077 } | 1000 } |
| 1078 | 1001 |
| 1079 void RemoteSuggestionsProvider::OnStatusChanged( | 1002 void RemoteSuggestionsProviderImpl::OnStatusChanged( |
| 1080 RemoteSuggestionsStatus old_status, | 1003 RemoteSuggestionsStatus old_status, |
| 1081 RemoteSuggestionsStatus new_status) { | 1004 RemoteSuggestionsStatus new_status) { |
| 1082 switch (new_status) { | 1005 switch (new_status) { |
| 1083 case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN: | 1006 case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN: |
| 1084 if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT) { | 1007 if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT) { |
| 1085 DCHECK(state_ == State::READY); | 1008 DCHECK(state_ == State::READY); |
| 1086 // Clear nonpersonalized suggestions. | 1009 // Clear nonpersonalized suggestions. |
| 1087 NukeAllSnippets(); | 1010 NukeAllSnippets(); |
| 1088 // Fetch personalized ones. | 1011 // Fetch personalized ones. |
| 1089 FetchSnippets(/*interactive_request=*/true); | 1012 // TODO(jkrcal): Loop in SchedulingRemoteSuggestionsProvider somehow. |
| 1013 FetchSnippets(/*interactive_request=*/true, |
| 1014 std::unique_ptr<FetchStatusCallback>(nullptr)); |
| 1090 } else { | 1015 } else { |
| 1091 // Do not change the status. That will be done in EnterStateReady(). | 1016 // Do not change the status. That will be done in EnterStateReady(). |
| 1092 EnterState(State::READY); | 1017 EnterState(State::READY); |
| 1093 } | 1018 } |
| 1094 break; | 1019 break; |
| 1095 | 1020 |
| 1096 case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT: | 1021 case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT: |
| 1097 if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN) { | 1022 if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN) { |
| 1098 DCHECK(state_ == State::READY); | 1023 DCHECK(state_ == State::READY); |
| 1099 // Clear personalized suggestions. | 1024 // Clear personalized suggestions. |
| 1100 NukeAllSnippets(); | 1025 NukeAllSnippets(); |
| 1101 // Fetch nonpersonalized ones. | 1026 // Fetch nonpersonalized ones. |
| 1102 FetchSnippets(/*interactive_request=*/true); | 1027 // TODO(jkrcal): Loop in SchedulingRemoteSuggestionsProvider somehow. |
| 1028 FetchSnippets(/*interactive_request=*/true, |
| 1029 std::unique_ptr<FetchStatusCallback>(nullptr)); |
| 1103 } else { | 1030 } else { |
| 1104 // Do not change the status. That will be done in EnterStateReady(). | 1031 // Do not change the status. That will be done in EnterStateReady(). |
| 1105 EnterState(State::READY); | 1032 EnterState(State::READY); |
| 1106 } | 1033 } |
| 1107 break; | 1034 break; |
| 1108 | 1035 |
| 1109 case RemoteSuggestionsStatus::EXPLICITLY_DISABLED: | 1036 case RemoteSuggestionsStatus::EXPLICITLY_DISABLED: |
| 1110 EnterState(State::DISABLED); | 1037 EnterState(State::DISABLED); |
| 1111 UpdateAllCategoryStatus(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED); | 1038 UpdateAllCategoryStatus(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED); |
| 1112 break; | 1039 break; |
| 1113 | 1040 |
| 1114 case RemoteSuggestionsStatus::SIGNED_OUT_AND_DISABLED: | 1041 case RemoteSuggestionsStatus::SIGNED_OUT_AND_DISABLED: |
| 1115 EnterState(State::DISABLED); | 1042 EnterState(State::DISABLED); |
| 1116 UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT); | 1043 UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT); |
| 1117 break; | 1044 break; |
| 1118 } | 1045 } |
| 1119 } | 1046 } |
| 1120 | 1047 |
| 1121 void RemoteSuggestionsProvider::EnterState(State state) { | 1048 void RemoteSuggestionsProviderImpl::EnterState(State state) { |
| 1122 if (state == state_) { | 1049 if (state == state_) { |
| 1123 return; | 1050 return; |
| 1124 } | 1051 } |
| 1125 | 1052 |
| 1126 UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.EnteredState", | 1053 UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.EnteredState", |
| 1127 static_cast<int>(state), | 1054 static_cast<int>(state), |
| 1128 static_cast<int>(State::COUNT)); | 1055 static_cast<int>(State::COUNT)); |
| 1129 | 1056 |
| 1130 switch (state) { | 1057 switch (state) { |
| 1131 case State::NOT_INITED: | 1058 case State::NOT_INITED: |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1153 DVLOG(1) << "Entering state: ERROR_OCCURRED"; | 1080 DVLOG(1) << "Entering state: ERROR_OCCURRED"; |
| 1154 state_ = State::ERROR_OCCURRED; | 1081 state_ = State::ERROR_OCCURRED; |
| 1155 EnterStateError(); | 1082 EnterStateError(); |
| 1156 break; | 1083 break; |
| 1157 | 1084 |
| 1158 case State::COUNT: | 1085 case State::COUNT: |
| 1159 NOTREACHED(); | 1086 NOTREACHED(); |
| 1160 break; | 1087 break; |
| 1161 } | 1088 } |
| 1162 | 1089 |
| 1163 // Schedule or un-schedule background fetching after each state change. | 1090 NotifyStateChanged(); |
| 1164 RescheduleFetching(false); | |
| 1165 } | 1091 } |
| 1166 | 1092 |
| 1167 void RemoteSuggestionsProvider::NotifyNewSuggestions( | 1093 void RemoteSuggestionsProviderImpl::NotifyStateChanged() { |
| 1094 if (!provider_status_callback_) { |
| 1095 return; |
| 1096 } |
| 1097 |
| 1098 switch (state_) { |
| 1099 case State::NOT_INITED: |
| 1100 // Initial state, not sure yet whether active or not. |
| 1101 break; |
| 1102 case State::READY: |
| 1103 provider_status_callback_->Run(ProviderStatus::ACTIVE); |
| 1104 break; |
| 1105 case State::DISABLED: |
| 1106 provider_status_callback_->Run(ProviderStatus::INACTIVE); |
| 1107 break; |
| 1108 case State::ERROR_OCCURRED: |
| 1109 provider_status_callback_->Run(ProviderStatus::INACTIVE); |
| 1110 break; |
| 1111 case State::COUNT: |
| 1112 NOTREACHED(); |
| 1113 break; |
| 1114 } |
| 1115 } |
| 1116 |
| 1117 void RemoteSuggestionsProviderImpl::NotifyNewSuggestions( |
| 1168 Category category, | 1118 Category category, |
| 1169 const CategoryContent& content) { | 1119 const CategoryContent& content) { |
| 1170 DCHECK(IsCategoryStatusAvailable(content.status)); | 1120 DCHECK(IsCategoryStatusAvailable(content.status)); |
| 1171 | 1121 |
| 1172 std::vector<ContentSuggestion> result = | 1122 std::vector<ContentSuggestion> result = |
| 1173 ConvertToContentSuggestions(category, content.snippets); | 1123 ConvertToContentSuggestions(category, content.snippets); |
| 1174 | 1124 |
| 1175 DVLOG(1) << "NotifyNewSuggestions(): " << result.size() | 1125 DVLOG(1) << "NotifyNewSuggestions(): " << result.size() |
| 1176 << " items in category " << category; | 1126 << " items in category " << category; |
| 1177 observer()->OnNewSuggestions(this, category, std::move(result)); | 1127 observer()->OnNewSuggestions(this, category, std::move(result)); |
| 1178 } | 1128 } |
| 1179 | 1129 |
| 1180 void RemoteSuggestionsProvider::UpdateCategoryStatus(Category category, | 1130 void RemoteSuggestionsProviderImpl::UpdateCategoryStatus( |
| 1181 CategoryStatus status) { | 1131 Category category, |
| 1132 CategoryStatus status) { |
| 1182 auto content_it = category_contents_.find(category); | 1133 auto content_it = category_contents_.find(category); |
| 1183 DCHECK(content_it != category_contents_.end()); | 1134 DCHECK(content_it != category_contents_.end()); |
| 1184 CategoryContent& content = content_it->second; | 1135 CategoryContent& content = content_it->second; |
| 1185 | 1136 |
| 1186 if (status == content.status) { | 1137 if (status == content.status) { |
| 1187 return; | 1138 return; |
| 1188 } | 1139 } |
| 1189 | 1140 |
| 1190 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " | 1141 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " |
| 1191 << static_cast<int>(content.status) << " -> " | 1142 << static_cast<int>(content.status) << " -> " |
| 1192 << static_cast<int>(status); | 1143 << static_cast<int>(status); |
| 1193 content.status = status; | 1144 content.status = status; |
| 1194 observer()->OnCategoryStatusChanged(this, category, content.status); | 1145 observer()->OnCategoryStatusChanged(this, category, content.status); |
| 1195 } | 1146 } |
| 1196 | 1147 |
| 1197 void RemoteSuggestionsProvider::UpdateAllCategoryStatus(CategoryStatus status) { | 1148 void RemoteSuggestionsProviderImpl::UpdateAllCategoryStatus( |
| 1149 CategoryStatus status) { |
| 1198 for (const auto& category : category_contents_) { | 1150 for (const auto& category : category_contents_) { |
| 1199 UpdateCategoryStatus(category.first, status); | 1151 UpdateCategoryStatus(category.first, status); |
| 1200 } | 1152 } |
| 1201 } | 1153 } |
| 1202 | 1154 |
| 1203 namespace { | 1155 namespace { |
| 1204 | 1156 |
| 1205 template <typename T> | 1157 template <typename T> |
| 1206 typename T::const_iterator FindSnippetInContainer( | 1158 typename T::const_iterator FindSnippetInContainer( |
| 1207 const T& container, | 1159 const T& container, |
| 1208 const std::string& id_within_category) { | 1160 const std::string& id_within_category) { |
| 1209 return std::find_if( | 1161 return std::find_if( |
| 1210 container.begin(), container.end(), | 1162 container.begin(), container.end(), |
| 1211 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { | 1163 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { |
| 1212 return snippet->id() == id_within_category; | 1164 return snippet->id() == id_within_category; |
| 1213 }); | 1165 }); |
| 1214 } | 1166 } |
| 1215 | 1167 |
| 1216 } // namespace | 1168 } // namespace |
| 1217 | 1169 |
| 1218 const NTPSnippet* RemoteSuggestionsProvider::CategoryContent::FindSnippet( | 1170 const NTPSnippet* RemoteSuggestionsProviderImpl::CategoryContent::FindSnippet( |
| 1219 const std::string& id_within_category) const { | 1171 const std::string& id_within_category) const { |
| 1220 // Search for the snippet in current and archived snippets. | 1172 // Search for the snippet in current and archived snippets. |
| 1221 auto it = FindSnippetInContainer(snippets, id_within_category); | 1173 auto it = FindSnippetInContainer(snippets, id_within_category); |
| 1222 if (it != snippets.end()) { | 1174 if (it != snippets.end()) { |
| 1223 return it->get(); | 1175 return it->get(); |
| 1224 } | 1176 } |
| 1225 auto archived_it = FindSnippetInContainer(archived, id_within_category); | 1177 auto archived_it = FindSnippetInContainer(archived, id_within_category); |
| 1226 if (archived_it != archived.end()) { | 1178 if (archived_it != archived.end()) { |
| 1227 return archived_it->get(); | 1179 return archived_it->get(); |
| 1228 } | 1180 } |
| 1229 auto dismissed_it = FindSnippetInContainer(dismissed, id_within_category); | 1181 auto dismissed_it = FindSnippetInContainer(dismissed, id_within_category); |
| 1230 if (dismissed_it != dismissed.end()) { | 1182 if (dismissed_it != dismissed.end()) { |
| 1231 return dismissed_it->get(); | 1183 return dismissed_it->get(); |
| 1232 } | 1184 } |
| 1233 return nullptr; | 1185 return nullptr; |
| 1234 } | 1186 } |
| 1235 | 1187 |
| 1236 RemoteSuggestionsProvider::CategoryContent* | 1188 RemoteSuggestionsProviderImpl::CategoryContent* |
| 1237 RemoteSuggestionsProvider::UpdateCategoryInfo(Category category, | 1189 RemoteSuggestionsProviderImpl::UpdateCategoryInfo(Category category, |
| 1238 const CategoryInfo& info) { | 1190 const CategoryInfo& info) { |
| 1239 auto content_it = category_contents_.find(category); | 1191 auto content_it = category_contents_.find(category); |
| 1240 if (content_it == category_contents_.end()) { | 1192 if (content_it == category_contents_.end()) { |
| 1241 content_it = category_contents_ | 1193 content_it = category_contents_ |
| 1242 .insert(std::make_pair(category, CategoryContent(info))) | 1194 .insert(std::make_pair(category, CategoryContent(info))) |
| 1243 .first; | 1195 .first; |
| 1244 } else { | 1196 } else { |
| 1245 content_it->second.info = info; | 1197 content_it->second.info = info; |
| 1246 } | 1198 } |
| 1247 return &content_it->second; | 1199 return &content_it->second; |
| 1248 } | 1200 } |
| 1249 | 1201 |
| 1250 void RemoteSuggestionsProvider::RestoreCategoriesFromPrefs() { | 1202 void RemoteSuggestionsProviderImpl::RestoreCategoriesFromPrefs() { |
| 1251 // This must only be called at startup, before there are any categories. | 1203 // This must only be called at startup, before there are any categories. |
| 1252 DCHECK(category_contents_.empty()); | 1204 DCHECK(category_contents_.empty()); |
| 1253 | 1205 |
| 1254 const base::ListValue* list = | 1206 const base::ListValue* list = |
| 1255 pref_service_->GetList(prefs::kRemoteSuggestionCategories); | 1207 pref_service_->GetList(prefs::kRemoteSuggestionCategories); |
| 1256 for (const std::unique_ptr<base::Value>& entry : *list) { | 1208 for (const std::unique_ptr<base::Value>& entry : *list) { |
| 1257 const base::DictionaryValue* dict = nullptr; | 1209 const base::DictionaryValue* dict = nullptr; |
| 1258 if (!entry->GetAsDictionary(&dict)) { | 1210 if (!entry->GetAsDictionary(&dict)) { |
| 1259 DLOG(WARNING) << "Invalid category pref value: " << *entry; | 1211 DLOG(WARNING) << "Invalid category pref value: " << *entry; |
| 1260 continue; | 1212 continue; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1293 CategoryInfo info = | 1245 CategoryInfo info = |
| 1294 category == articles_category_ | 1246 category == articles_category_ |
| 1295 ? BuildArticleCategoryInfo(title) | 1247 ? BuildArticleCategoryInfo(title) |
| 1296 : BuildRemoteCategoryInfo(title, allow_fetching_more_results); | 1248 : BuildRemoteCategoryInfo(title, allow_fetching_more_results); |
| 1297 CategoryContent* content = UpdateCategoryInfo(category, info); | 1249 CategoryContent* content = UpdateCategoryInfo(category, info); |
| 1298 content->included_in_last_server_response = | 1250 content->included_in_last_server_response = |
| 1299 included_in_last_server_response; | 1251 included_in_last_server_response; |
| 1300 } | 1252 } |
| 1301 } | 1253 } |
| 1302 | 1254 |
| 1303 void RemoteSuggestionsProvider::StoreCategoriesToPrefs() { | 1255 void RemoteSuggestionsProviderImpl::StoreCategoriesToPrefs() { |
| 1304 // Collect all the CategoryContents. | 1256 // Collect all the CategoryContents. |
| 1305 std::vector<std::pair<Category, const CategoryContent*>> to_store; | 1257 std::vector<std::pair<Category, const CategoryContent*>> to_store; |
| 1306 for (const auto& entry : category_contents_) { | 1258 for (const auto& entry : category_contents_) { |
| 1307 to_store.emplace_back(entry.first, &entry.second); | 1259 to_store.emplace_back(entry.first, &entry.second); |
| 1308 } | 1260 } |
| 1309 // The ranker may not persist the order, thus, it is stored by the provider. | 1261 // The ranker may not persist the order, thus, it is stored by the provider. |
| 1310 std::sort(to_store.begin(), to_store.end(), | 1262 std::sort(to_store.begin(), to_store.end(), |
| 1311 [this](const std::pair<Category, const CategoryContent*>& left, | 1263 [this](const std::pair<Category, const CategoryContent*>& left, |
| 1312 const std::pair<Category, const CategoryContent*>& right) { | 1264 const std::pair<Category, const CategoryContent*>& right) { |
| 1313 return category_ranker_->Compare(left.first, right.first); | 1265 return category_ranker_->Compare(left.first, right.first); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1324 dict->SetBoolean(kCategoryContentProvidedByServer, | 1276 dict->SetBoolean(kCategoryContentProvidedByServer, |
| 1325 content.included_in_last_server_response); | 1277 content.included_in_last_server_response); |
| 1326 dict->SetBoolean(kCategoryContentAllowFetchingMore, | 1278 dict->SetBoolean(kCategoryContentAllowFetchingMore, |
| 1327 content.info.has_more_action()); | 1279 content.info.has_more_action()); |
| 1328 list.Append(std::move(dict)); | 1280 list.Append(std::move(dict)); |
| 1329 } | 1281 } |
| 1330 // Finally, store the result in the pref service. | 1282 // Finally, store the result in the pref service. |
| 1331 pref_service_->Set(prefs::kRemoteSuggestionCategories, list); | 1283 pref_service_->Set(prefs::kRemoteSuggestionCategories, list); |
| 1332 } | 1284 } |
| 1333 | 1285 |
| 1334 RemoteSuggestionsProvider::CategoryContent::CategoryContent( | 1286 RemoteSuggestionsProviderImpl::CategoryContent::CategoryContent( |
| 1335 const CategoryInfo& info) | 1287 const CategoryInfo& info) |
| 1336 : info(info) {} | 1288 : info(info) {} |
| 1337 | 1289 |
| 1338 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) = | 1290 RemoteSuggestionsProviderImpl::CategoryContent::CategoryContent( |
| 1339 default; | 1291 CategoryContent&&) = default; |
| 1340 | 1292 |
| 1341 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default; | 1293 RemoteSuggestionsProviderImpl::CategoryContent::~CategoryContent() = default; |
| 1342 | 1294 |
| 1343 RemoteSuggestionsProvider::CategoryContent& | 1295 RemoteSuggestionsProviderImpl::CategoryContent& |
| 1344 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) = | 1296 RemoteSuggestionsProviderImpl::CategoryContent::operator=(CategoryContent&&) = |
| 1345 default; | 1297 default; |
| 1346 | 1298 |
| 1347 } // namespace ntp_snippets | 1299 } // namespace ntp_snippets |
| OLD | NEW |