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