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