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 | |
343 // Call the observer right away if we've reached any final state. | |
344 switch (state_) { | |
Marc Treib
2016/12/19 12:59:23
Maybe pull this logic into a helper method ("Notif
jkrcal
2016/12/20 16:39:46
Done.
| |
345 case State::NOT_INITED: | |
346 // Initial state, not sure yet whether active or not. | |
347 break; | |
348 case State::READY: | |
349 provider_status_callback_.Run(ProviderStatus::ACTIVE); | |
350 break; | |
351 case State::DISABLED: | |
352 provider_status_callback_.Run(ProviderStatus::INACTIVE); | |
353 break; | |
354 case State::ERROR_OCCURRED: | |
355 provider_status_callback_.Run(ProviderStatus::INACTIVE); | |
356 break; | |
357 case State::COUNT: | |
358 NOTREACHED(); | |
359 break; | |
360 } | |
400 } | 361 } |
401 | 362 |
402 void RemoteSuggestionsProvider::FetchSnippetsForAllCategories() { | 363 void RemoteSuggestionsProviderImpl::ReloadSuggestions() { |
403 // TODO(markusheintz): Investigate whether we can call the Fetch method | 364 FetchSnippets(/*interactive_request=*/true, FetchStatusCallback()); |
404 // instead of the FetchSnippets. | |
405 FetchSnippets(/*interactive_request=*/true); | |
406 } | 365 } |
407 | 366 |
408 void RemoteSuggestionsProvider::FetchSnippets( | 367 void RemoteSuggestionsProviderImpl::RefetchInTheBackground( |
409 bool interactive_request) { | 368 const FetchStatusCallback& callback) { |
369 FetchSnippets(/*interactive_request=*/false, callback); | |
370 } | |
371 | |
372 const NTPSnippetsFetcher* | |
373 RemoteSuggestionsProviderImpl::snippets_fetcher_for_testing_and_debugging() | |
374 const { | |
375 return snippets_fetcher_.get(); | |
376 } | |
377 | |
378 void RemoteSuggestionsProviderImpl::FetchSnippets( | |
379 bool interactive_request, | |
380 const FetchStatusCallback& callback) { | |
410 if (!ready()) { | 381 if (!ready()) { |
411 fetch_when_ready_ = true; | 382 fetch_when_ready_ = true; |
412 return; | 383 fetch_when_ready_interactive_ = interactive_request; |
Marc Treib
2016/12/19 12:59:23
I think the "return" should stay?
jkrcal
2016/12/20 16:39:46
Indeed!
| |
384 fetch_when_ready_callback_ = callback; | |
413 } | 385 } |
414 | 386 |
415 MarkEmptyCategoriesAsLoading(); | 387 MarkEmptyCategoriesAsLoading(); |
416 | 388 |
417 NTPSnippetsFetcher::Params params = BuildFetchParams(); | 389 NTPSnippetsFetcher::Params params = BuildFetchParams(); |
418 params.interactive_request = interactive_request; | 390 params.interactive_request = interactive_request; |
419 snippets_fetcher_->FetchSnippets( | 391 snippets_fetcher_->FetchSnippets( |
420 params, base::BindOnce(&RemoteSuggestionsProvider::OnFetchFinished, | 392 params, |
421 base::Unretained(this), interactive_request)); | 393 base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchFinished, |
394 base::Unretained(this), callback, interactive_request)); | |
422 } | 395 } |
423 | 396 |
424 void RemoteSuggestionsProvider::Fetch( | 397 void RemoteSuggestionsProviderImpl::Fetch( |
425 const Category& category, | 398 const Category& category, |
426 const std::set<std::string>& known_suggestion_ids, | 399 const std::set<std::string>& known_suggestion_ids, |
427 const FetchDoneCallback& callback) { | 400 const FetchDoneCallback& callback) { |
428 if (!ready()) { | 401 if (!ready()) { |
429 CallWithEmptyResults(callback, | 402 CallWithEmptyResults(callback, |
430 Status(StatusCode::TEMPORARY_ERROR, | 403 Status(StatusCode::TEMPORARY_ERROR, |
431 "RemoteSuggestionsProvider is not ready!")); | 404 "RemoteSuggestionsProvider is not ready!")); |
432 return; | 405 return; |
433 } | 406 } |
434 NTPSnippetsFetcher::Params params = BuildFetchParams(); | 407 NTPSnippetsFetcher::Params params = BuildFetchParams(); |
435 params.excluded_ids.insert(known_suggestion_ids.begin(), | 408 params.excluded_ids.insert(known_suggestion_ids.begin(), |
436 known_suggestion_ids.end()); | 409 known_suggestion_ids.end()); |
437 params.interactive_request = true; | 410 params.interactive_request = true; |
438 params.exclusive_category = category; | 411 params.exclusive_category = category; |
439 | 412 |
440 snippets_fetcher_->FetchSnippets( | 413 snippets_fetcher_->FetchSnippets( |
441 params, base::BindOnce(&RemoteSuggestionsProvider::OnFetchMoreFinished, | 414 params, |
442 base::Unretained(this), callback)); | 415 base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchMoreFinished, |
416 base::Unretained(this), callback)); | |
443 } | 417 } |
444 | 418 |
445 // Builds default fetcher params. | 419 // Builds default fetcher params. |
446 NTPSnippetsFetcher::Params RemoteSuggestionsProvider::BuildFetchParams() const { | 420 NTPSnippetsFetcher::Params RemoteSuggestionsProviderImpl::BuildFetchParams() |
421 const { | |
447 NTPSnippetsFetcher::Params result; | 422 NTPSnippetsFetcher::Params result; |
448 result.language_code = application_language_code_; | 423 result.language_code = application_language_code_; |
449 result.count_to_fetch = kMaxSnippetCount; | 424 result.count_to_fetch = kMaxSnippetCount; |
450 for (const auto& map_entry : category_contents_) { | 425 for (const auto& map_entry : category_contents_) { |
451 const CategoryContent& content = map_entry.second; | 426 const CategoryContent& content = map_entry.second; |
452 for (const auto& dismissed_snippet : content.dismissed) { | 427 for (const auto& dismissed_snippet : content.dismissed) { |
453 result.excluded_ids.insert(dismissed_snippet->id()); | 428 result.excluded_ids.insert(dismissed_snippet->id()); |
454 } | 429 } |
455 } | 430 } |
456 return result; | 431 return result; |
457 } | 432 } |
458 | 433 |
459 void RemoteSuggestionsProvider::MarkEmptyCategoriesAsLoading() { | 434 void RemoteSuggestionsProviderImpl::MarkEmptyCategoriesAsLoading() { |
460 for (const auto& item : category_contents_) { | 435 for (const auto& item : category_contents_) { |
461 Category category = item.first; | 436 Category category = item.first; |
462 const CategoryContent& content = item.second; | 437 const CategoryContent& content = item.second; |
463 if (content.snippets.empty()) { | 438 if (content.snippets.empty()) { |
464 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); | 439 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); |
465 } | 440 } |
466 } | 441 } |
467 } | 442 } |
468 | 443 |
469 void RemoteSuggestionsProvider::RescheduleFetching(bool force) { | 444 CategoryStatus RemoteSuggestionsProviderImpl::GetCategoryStatus( |
470 // The scheduler only exists on Android so far, it's null on other platforms. | 445 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); | 446 auto content_it = category_contents_.find(category); |
510 DCHECK(content_it != category_contents_.end()); | 447 DCHECK(content_it != category_contents_.end()); |
511 return content_it->second.status; | 448 return content_it->second.status; |
512 } | 449 } |
513 | 450 |
514 CategoryInfo RemoteSuggestionsProvider::GetCategoryInfo(Category category) { | 451 CategoryInfo RemoteSuggestionsProviderImpl::GetCategoryInfo(Category category) { |
515 auto content_it = category_contents_.find(category); | 452 auto content_it = category_contents_.find(category); |
516 DCHECK(content_it != category_contents_.end()); | 453 DCHECK(content_it != category_contents_.end()); |
517 return content_it->second.info; | 454 return content_it->second.info; |
518 } | 455 } |
519 | 456 |
520 void RemoteSuggestionsProvider::DismissSuggestion( | 457 void RemoteSuggestionsProviderImpl::DismissSuggestion( |
521 const ContentSuggestion::ID& suggestion_id) { | 458 const ContentSuggestion::ID& suggestion_id) { |
522 if (!ready()) { | 459 if (!ready()) { |
523 return; | 460 return; |
524 } | 461 } |
525 | 462 |
526 auto content_it = category_contents_.find(suggestion_id.category()); | 463 auto content_it = category_contents_.find(suggestion_id.category()); |
527 DCHECK(content_it != category_contents_.end()); | 464 DCHECK(content_it != category_contents_.end()); |
528 CategoryContent* content = &content_it->second; | 465 CategoryContent* content = &content_it->second; |
529 DismissSuggestionFromCategoryContent(content, | 466 DismissSuggestionFromCategoryContent(content, |
530 suggestion_id.id_within_category()); | 467 suggestion_id.id_within_category()); |
531 } | 468 } |
532 | 469 |
533 void RemoteSuggestionsProvider::ClearHistory( | 470 void RemoteSuggestionsProviderImpl::ClearHistory( |
534 base::Time begin, | 471 base::Time begin, |
535 base::Time end, | 472 base::Time end, |
536 const base::Callback<bool(const GURL& url)>& filter) { | 473 const base::Callback<bool(const GURL& url)>& filter) { |
537 // Both time range and the filter are ignored and all suggestions are removed, | 474 // 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 | 475 // because it is not known which history entries were used for the suggestions |
539 // personalization. | 476 // personalization. |
540 if (!ready()) { | 477 if (!ready()) { |
541 nuke_when_initialized_ = true; | 478 nuke_when_initialized_ = true; |
542 } else { | 479 } else { |
543 NukeAllSnippets(); | 480 NukeAllSnippets(); |
544 } | 481 } |
545 } | 482 } |
546 | 483 |
547 void RemoteSuggestionsProvider::ClearCachedSuggestions(Category category) { | 484 void RemoteSuggestionsProviderImpl::ClearCachedSuggestions(Category category) { |
548 if (!initialized()) { | 485 if (!initialized()) { |
549 return; | 486 return; |
550 } | 487 } |
551 | 488 |
552 auto content_it = category_contents_.find(category); | 489 auto content_it = category_contents_.find(category); |
553 if (content_it == category_contents_.end()) { | 490 if (content_it == category_contents_.end()) { |
554 return; | 491 return; |
555 } | 492 } |
556 CategoryContent* content = &content_it->second; | 493 CategoryContent* content = &content_it->second; |
557 if (content->snippets.empty()) { | 494 if (content->snippets.empty()) { |
558 return; | 495 return; |
559 } | 496 } |
560 | 497 |
561 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); | 498 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
562 database_->DeleteImages(GetSnippetIDVector(content->snippets)); | 499 database_->DeleteImages(GetSnippetIDVector(content->snippets)); |
563 content->snippets.clear(); | 500 content->snippets.clear(); |
564 | 501 |
565 if (IsCategoryStatusAvailable(content->status)) { | 502 if (IsCategoryStatusAvailable(content->status)) { |
566 NotifyNewSuggestions(category, *content); | 503 NotifyNewSuggestions(category, *content); |
567 } | 504 } |
568 } | 505 } |
569 | 506 |
570 void RemoteSuggestionsProvider::OnSignInStateChanged() { | 507 void RemoteSuggestionsProviderImpl::OnSignInStateChanged() { |
571 // Make sure the status service is registered and we already initialised its | 508 // Make sure the status service is registered and we already initialised its |
572 // start state. | 509 // start state. |
573 if (!initialized()) { | 510 if (!initialized()) { |
574 return; | 511 return; |
575 } | 512 } |
576 | 513 |
577 status_service_->OnSignInStateChanged(); | 514 status_service_->OnSignInStateChanged(); |
578 } | 515 } |
579 | 516 |
580 void RemoteSuggestionsProvider::GetDismissedSuggestionsForDebugging( | 517 void RemoteSuggestionsProviderImpl::GetDismissedSuggestionsForDebugging( |
581 Category category, | 518 Category category, |
582 const DismissedSuggestionsCallback& callback) { | 519 const DismissedSuggestionsCallback& callback) { |
583 auto content_it = category_contents_.find(category); | 520 auto content_it = category_contents_.find(category); |
584 DCHECK(content_it != category_contents_.end()); | 521 DCHECK(content_it != category_contents_.end()); |
585 callback.Run( | 522 callback.Run( |
586 ConvertToContentSuggestions(category, content_it->second.dismissed)); | 523 ConvertToContentSuggestions(category, content_it->second.dismissed)); |
587 } | 524 } |
588 | 525 |
589 void RemoteSuggestionsProvider::ClearDismissedSuggestionsForDebugging( | 526 void RemoteSuggestionsProviderImpl::ClearDismissedSuggestionsForDebugging( |
590 Category category) { | 527 Category category) { |
591 auto content_it = category_contents_.find(category); | 528 auto content_it = category_contents_.find(category); |
592 DCHECK(content_it != category_contents_.end()); | 529 DCHECK(content_it != category_contents_.end()); |
593 CategoryContent* content = &content_it->second; | 530 CategoryContent* content = &content_it->second; |
594 | 531 |
595 if (!initialized()) { | 532 if (!initialized()) { |
596 return; | 533 return; |
597 } | 534 } |
598 | 535 |
599 if (content->dismissed.empty()) { | 536 if (content->dismissed.empty()) { |
600 return; | 537 return; |
601 } | 538 } |
602 | 539 |
603 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); | 540 database_->DeleteSnippets(GetSnippetIDVector(content->dismissed)); |
604 // The image got already deleted when the suggestion was dismissed. | 541 // The image got already deleted when the suggestion was dismissed. |
605 | 542 |
606 content->dismissed.clear(); | 543 content->dismissed.clear(); |
607 } | 544 } |
608 | 545 |
609 // static | 546 // static |
610 int RemoteSuggestionsProvider::GetMaxSnippetCountForTesting() { | 547 int RemoteSuggestionsProviderImpl::GetMaxSnippetCountForTesting() { |
611 return kMaxSnippetCount; | 548 return kMaxSnippetCount; |
612 } | 549 } |
613 | 550 |
614 //////////////////////////////////////////////////////////////////////////////// | 551 //////////////////////////////////////////////////////////////////////////////// |
615 // Private methods | 552 // Private methods |
616 | 553 |
617 GURL RemoteSuggestionsProvider::FindSnippetImageUrl( | 554 GURL RemoteSuggestionsProviderImpl::FindSnippetImageUrl( |
618 const ContentSuggestion::ID& suggestion_id) const { | 555 const ContentSuggestion::ID& suggestion_id) const { |
619 DCHECK(base::ContainsKey(category_contents_, suggestion_id.category())); | 556 DCHECK(base::ContainsKey(category_contents_, suggestion_id.category())); |
620 | 557 |
621 const CategoryContent& content = | 558 const CategoryContent& content = |
622 category_contents_.at(suggestion_id.category()); | 559 category_contents_.at(suggestion_id.category()); |
623 const NTPSnippet* snippet = | 560 const NTPSnippet* snippet = |
624 content.FindSnippet(suggestion_id.id_within_category()); | 561 content.FindSnippet(suggestion_id.id_within_category()); |
625 if (!snippet) { | 562 if (!snippet) { |
626 return GURL(); | 563 return GURL(); |
627 } | 564 } |
628 return snippet->salient_image_url(); | 565 return snippet->salient_image_url(); |
629 } | 566 } |
630 | 567 |
631 void RemoteSuggestionsProvider::OnDatabaseLoaded( | 568 void RemoteSuggestionsProviderImpl::OnDatabaseLoaded( |
632 NTPSnippet::PtrVector snippets) { | 569 NTPSnippet::PtrVector snippets) { |
633 if (state_ == State::ERROR_OCCURRED) { | 570 if (state_ == State::ERROR_OCCURRED) { |
634 return; | 571 return; |
635 } | 572 } |
636 DCHECK(state_ == State::NOT_INITED); | 573 DCHECK(state_ == State::NOT_INITED); |
637 DCHECK(base::ContainsKey(category_contents_, articles_category_)); | 574 DCHECK(base::ContainsKey(category_contents_, articles_category_)); |
638 | 575 |
639 base::TimeDelta database_load_time = | 576 base::TimeDelta database_load_time = |
640 base::TimeTicks::Now() - database_load_start_; | 577 base::TimeTicks::Now() - database_load_start_; |
641 UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Snippets.DatabaseLoadTime", | 578 UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Snippets.DatabaseLoadTime", |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
677 } | 614 } |
678 | 615 |
679 // TODO(tschumann): If I move ClearExpiredDismissedSnippets() to the beginning | 616 // TODO(tschumann): If I move ClearExpiredDismissedSnippets() to the beginning |
680 // of the function, it essentially does nothing but tests are still green. Fix | 617 // of the function, it essentially does nothing but tests are still green. Fix |
681 // this! | 618 // this! |
682 ClearExpiredDismissedSnippets(); | 619 ClearExpiredDismissedSnippets(); |
683 ClearOrphanedImages(); | 620 ClearOrphanedImages(); |
684 FinishInitialization(); | 621 FinishInitialization(); |
685 } | 622 } |
686 | 623 |
687 void RemoteSuggestionsProvider::OnDatabaseError() { | 624 void RemoteSuggestionsProviderImpl::OnDatabaseError() { |
688 EnterState(State::ERROR_OCCURRED); | 625 EnterState(State::ERROR_OCCURRED); |
689 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | 626 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); |
690 } | 627 } |
691 | 628 |
692 void RemoteSuggestionsProvider::OnFetchMoreFinished( | 629 void RemoteSuggestionsProviderImpl::OnFetchMoreFinished( |
693 const FetchDoneCallback& fetching_callback, | 630 const FetchDoneCallback& fetching_callback, |
694 Status status, | 631 Status status, |
695 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { | 632 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { |
696 if (!fetched_categories) { | 633 if (!fetched_categories) { |
697 DCHECK(!status.IsSuccess()); | 634 DCHECK(!status.IsSuccess()); |
698 CallWithEmptyResults(fetching_callback, status); | 635 CallWithEmptyResults(fetching_callback, status); |
699 return; | 636 return; |
700 } | 637 } |
701 if (fetched_categories->size() != 1u) { | 638 if (fetched_categories->size() != 1u) { |
702 LOG(DFATAL) << "Requested one exclusive category but received " | 639 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 | 679 // 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 | 680 // 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 | 681 // Nuke should also cancel outstanding requests or we want to check the |
745 // status. | 682 // status. |
746 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 683 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
747 // Notify callers and observers. | 684 // Notify callers and observers. |
748 fetching_callback.Run(Status::Success(), std::move(result)); | 685 fetching_callback.Run(Status::Success(), std::move(result)); |
749 NotifyNewSuggestions(category, *existing_content); | 686 NotifyNewSuggestions(category, *existing_content); |
750 } | 687 } |
751 | 688 |
752 void RemoteSuggestionsProvider::OnFetchFinished( | 689 void RemoteSuggestionsProviderImpl::OnFetchFinished( |
690 const FetchStatusCallback& callback, | |
753 bool interactive_request, | 691 bool interactive_request, |
754 Status status, | 692 Status status, |
755 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { | 693 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { |
756 if (!ready()) { | 694 if (!ready()) { |
757 // TODO(tschumann): What happens if this was a user-triggered, interactive | 695 // TODO(tschumann): What happens if this was a user-triggered, interactive |
758 // request? Is the UI waiting indefinitely now? | 696 // request? Is the UI waiting indefinitely now? |
759 return; | 697 return; |
760 } | 698 } |
761 | 699 |
762 // Record the fetch time of a successfull background fetch. | 700 // 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_); | 755 auto content_it = category_contents_.find(articles_category_); |
818 DCHECK(content_it != category_contents_.end()); | 756 DCHECK(content_it != category_contents_.end()); |
819 const CategoryContent& content = content_it->second; | 757 const CategoryContent& content = content_it->second; |
820 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | 758 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", |
821 content.snippets.size()); | 759 content.snippets.size()); |
822 if (content.snippets.empty() && !content.dismissed.empty()) { | 760 if (content.snippets.empty() && !content.dismissed.empty()) { |
823 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 761 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", |
824 content.dismissed.size()); | 762 content.dismissed.size()); |
825 } | 763 } |
826 | 764 |
827 // Reschedule after a successful fetch. This resets all currently scheduled | 765 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 } | 766 } |
835 | 767 |
836 void RemoteSuggestionsProvider::ArchiveSnippets( | 768 void RemoteSuggestionsProviderImpl::ArchiveSnippets( |
837 CategoryContent* content, | 769 CategoryContent* content, |
838 NTPSnippet::PtrVector* to_archive) { | 770 NTPSnippet::PtrVector* to_archive) { |
839 // Archive previous snippets - move them at the beginning of the list. | 771 // Archive previous snippets - move them at the beginning of the list. |
840 content->archived.insert(content->archived.begin(), | 772 content->archived.insert(content->archived.begin(), |
841 std::make_move_iterator(to_archive->begin()), | 773 std::make_move_iterator(to_archive->begin()), |
842 std::make_move_iterator(to_archive->end())); | 774 std::make_move_iterator(to_archive->end())); |
843 to_archive->clear(); | 775 to_archive->clear(); |
844 | 776 |
845 // If there are more archived snippets than we want to keep, delete the | 777 // 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). | 778 // oldest ones by their fetch time (which are always in the back). |
847 if (content->archived.size() > kMaxArchivedSnippetCount) { | 779 if (content->archived.size() > kMaxArchivedSnippetCount) { |
848 NTPSnippet::PtrVector to_delete( | 780 NTPSnippet::PtrVector to_delete( |
849 std::make_move_iterator(content->archived.begin() + | 781 std::make_move_iterator(content->archived.begin() + |
850 kMaxArchivedSnippetCount), | 782 kMaxArchivedSnippetCount), |
851 std::make_move_iterator(content->archived.end())); | 783 std::make_move_iterator(content->archived.end())); |
852 content->archived.resize(kMaxArchivedSnippetCount); | 784 content->archived.resize(kMaxArchivedSnippetCount); |
853 database_->DeleteImages(GetSnippetIDVector(to_delete)); | 785 database_->DeleteImages(GetSnippetIDVector(to_delete)); |
854 } | 786 } |
855 } | 787 } |
856 | 788 |
857 void RemoteSuggestionsProvider::SanitizeReceivedSnippets( | 789 void RemoteSuggestionsProviderImpl::SanitizeReceivedSnippets( |
858 const NTPSnippet::PtrVector& dismissed, | 790 const NTPSnippet::PtrVector& dismissed, |
859 NTPSnippet::PtrVector* snippets) { | 791 NTPSnippet::PtrVector* snippets) { |
860 DCHECK(ready()); | 792 DCHECK(ready()); |
861 EraseMatchingSnippets(snippets, dismissed); | 793 EraseMatchingSnippets(snippets, dismissed); |
862 RemoveIncompleteSnippets(snippets); | 794 RemoveIncompleteSnippets(snippets); |
863 } | 795 } |
864 | 796 |
865 void RemoteSuggestionsProvider::IntegrateSnippets( | 797 void RemoteSuggestionsProviderImpl::IntegrateSnippets( |
866 CategoryContent* content, | 798 CategoryContent* content, |
867 NTPSnippet::PtrVector new_snippets) { | 799 NTPSnippet::PtrVector new_snippets) { |
868 DCHECK(ready()); | 800 DCHECK(ready()); |
869 | 801 |
870 // Do not touch the current set of snippets if the newly fetched one is empty. | 802 // 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 | 803 // 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 | 804 // accordingly and remove the old one (only of course if this was not received |
873 // through a fetch-more). | 805 // through a fetch-more). |
874 if (new_snippets.empty()) { | 806 if (new_snippets.empty()) { |
875 return; | 807 return; |
876 } | 808 } |
877 | 809 |
878 // It's entirely possible that the newly fetched snippets contain articles | 810 // It's entirely possible that the newly fetched snippets contain articles |
879 // that have been present before. | 811 // that have been present before. |
880 // We need to make sure to only delete and archive snippets that don't | 812 // 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 | 813 // appear with the same ID in the new suggestions (it's fine for additional |
882 // IDs though). | 814 // IDs though). |
883 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); | 815 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); |
884 // Do not delete the thumbnail images as they are still handy on open NTPs. | 816 // Do not delete the thumbnail images as they are still handy on open NTPs. |
885 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); | 817 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
886 // Note, that ArchiveSnippets will clear |content->snippets|. | 818 // Note, that ArchiveSnippets will clear |content->snippets|. |
887 ArchiveSnippets(content, &content->snippets); | 819 ArchiveSnippets(content, &content->snippets); |
888 | 820 |
889 database_->SaveSnippets(new_snippets); | 821 database_->SaveSnippets(new_snippets); |
890 | 822 |
891 content->snippets = std::move(new_snippets); | 823 content->snippets = std::move(new_snippets); |
892 } | 824 } |
893 | 825 |
894 void RemoteSuggestionsProvider::DismissSuggestionFromCategoryContent( | 826 void RemoteSuggestionsProviderImpl::DismissSuggestionFromCategoryContent( |
895 CategoryContent* content, | 827 CategoryContent* content, |
896 const std::string& id_within_category) { | 828 const std::string& id_within_category) { |
897 auto it = std::find_if( | 829 auto it = std::find_if( |
898 content->snippets.begin(), content->snippets.end(), | 830 content->snippets.begin(), content->snippets.end(), |
899 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { | 831 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { |
900 return snippet->id() == id_within_category; | 832 return snippet->id() == id_within_category; |
901 }); | 833 }); |
902 if (it == content->snippets.end()) { | 834 if (it == content->snippets.end()) { |
903 return; | 835 return; |
904 } | 836 } |
905 | 837 |
906 (*it)->set_dismissed(true); | 838 (*it)->set_dismissed(true); |
907 | 839 |
908 database_->SaveSnippet(**it); | 840 database_->SaveSnippet(**it); |
909 | 841 |
910 content->dismissed.push_back(std::move(*it)); | 842 content->dismissed.push_back(std::move(*it)); |
911 content->snippets.erase(it); | 843 content->snippets.erase(it); |
912 } | 844 } |
913 | 845 |
914 void RemoteSuggestionsProvider::ClearExpiredDismissedSnippets() { | 846 void RemoteSuggestionsProviderImpl::ClearExpiredDismissedSnippets() { |
915 std::vector<Category> categories_to_erase; | 847 std::vector<Category> categories_to_erase; |
916 | 848 |
917 const base::Time now = base::Time::Now(); | 849 const base::Time now = base::Time::Now(); |
918 | 850 |
919 for (auto& item : category_contents_) { | 851 for (auto& item : category_contents_) { |
920 Category category = item.first; | 852 Category category = item.first; |
921 CategoryContent* content = &item.second; | 853 CategoryContent* content = &item.second; |
922 | 854 |
923 NTPSnippet::PtrVector to_delete; | 855 NTPSnippet::PtrVector to_delete; |
924 // Move expired dismissed snippets over into |to_delete|. | 856 // Move expired dismissed snippets over into |to_delete|. |
(...skipping 17 matching lines...) Expand all Loading... | |
942 } | 874 } |
943 | 875 |
944 for (Category category : categories_to_erase) { | 876 for (Category category : categories_to_erase) { |
945 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 877 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
946 category_contents_.erase(category); | 878 category_contents_.erase(category); |
947 } | 879 } |
948 | 880 |
949 StoreCategoriesToPrefs(); | 881 StoreCategoriesToPrefs(); |
950 } | 882 } |
951 | 883 |
952 void RemoteSuggestionsProvider::ClearOrphanedImages() { | 884 void RemoteSuggestionsProviderImpl::ClearOrphanedImages() { |
953 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); | 885 auto alive_snippets = base::MakeUnique<std::set<std::string>>(); |
954 for (const auto& entry : category_contents_) { | 886 for (const auto& entry : category_contents_) { |
955 const CategoryContent& content = entry.second; | 887 const CategoryContent& content = entry.second; |
956 for (const auto& snippet_ptr : content.snippets) { | 888 for (const auto& snippet_ptr : content.snippets) { |
957 alive_snippets->insert(snippet_ptr->id()); | 889 alive_snippets->insert(snippet_ptr->id()); |
958 } | 890 } |
959 for (const auto& snippet_ptr : content.dismissed) { | 891 for (const auto& snippet_ptr : content.dismissed) { |
960 alive_snippets->insert(snippet_ptr->id()); | 892 alive_snippets->insert(snippet_ptr->id()); |
961 } | 893 } |
962 } | 894 } |
963 database_->GarbageCollectImages(std::move(alive_snippets)); | 895 database_->GarbageCollectImages(std::move(alive_snippets)); |
964 } | 896 } |
965 | 897 |
966 void RemoteSuggestionsProvider::NukeAllSnippets() { | 898 void RemoteSuggestionsProviderImpl::NukeAllSnippets() { |
967 std::vector<Category> categories_to_erase; | 899 std::vector<Category> categories_to_erase; |
968 | 900 |
969 // Empty the ARTICLES category and remove all others, since they may or may | 901 // Empty the ARTICLES category and remove all others, since they may or may |
970 // not be personalized. | 902 // not be personalized. |
971 for (const auto& item : category_contents_) { | 903 for (const auto& item : category_contents_) { |
972 Category category = item.first; | 904 Category category = item.first; |
973 | 905 |
974 ClearCachedSuggestions(category); | 906 ClearCachedSuggestions(category); |
975 ClearDismissedSuggestionsForDebugging(category); | 907 ClearDismissedSuggestionsForDebugging(category); |
976 | 908 |
977 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | 909 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); |
978 | 910 |
979 // Remove the category entirely; it may or may not reappear. | 911 // Remove the category entirely; it may or may not reappear. |
980 if (category != articles_category_) { | 912 if (category != articles_category_) { |
981 categories_to_erase.push_back(category); | 913 categories_to_erase.push_back(category); |
982 } | 914 } |
983 } | 915 } |
984 | 916 |
985 for (Category category : categories_to_erase) { | 917 for (Category category : categories_to_erase) { |
986 category_contents_.erase(category); | 918 category_contents_.erase(category); |
987 } | 919 } |
988 | 920 |
989 StoreCategoriesToPrefs(); | 921 StoreCategoriesToPrefs(); |
990 } | 922 } |
991 | 923 |
992 void RemoteSuggestionsProvider::FetchSuggestionImage( | 924 void RemoteSuggestionsProviderImpl::FetchSuggestionImage( |
993 const ContentSuggestion::ID& suggestion_id, | 925 const ContentSuggestion::ID& suggestion_id, |
994 const ImageFetchedCallback& callback) { | 926 const ImageFetchedCallback& callback) { |
995 if (!base::ContainsKey(category_contents_, suggestion_id.category())) { | 927 if (!base::ContainsKey(category_contents_, suggestion_id.category())) { |
996 base::ThreadTaskRunnerHandle::Get()->PostTask( | 928 base::ThreadTaskRunnerHandle::Get()->PostTask( |
997 FROM_HERE, base::Bind(callback, gfx::Image())); | 929 FROM_HERE, base::Bind(callback, gfx::Image())); |
998 return; | 930 return; |
999 } | 931 } |
1000 GURL image_url = FindSnippetImageUrl(suggestion_id); | 932 GURL image_url = FindSnippetImageUrl(suggestion_id); |
1001 if (image_url.is_empty()) { | 933 if (image_url.is_empty()) { |
1002 // As we don't know the corresponding snippet anymore, we don't expect to | 934 // 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 | 935 // find it in the database (and also can't fetch it remotely). Cut the |
1004 // lookup short and return directly. | 936 // lookup short and return directly. |
1005 base::ThreadTaskRunnerHandle::Get()->PostTask( | 937 base::ThreadTaskRunnerHandle::Get()->PostTask( |
1006 FROM_HERE, base::Bind(callback, gfx::Image())); | 938 FROM_HERE, base::Bind(callback, gfx::Image())); |
1007 return; | 939 return; |
1008 } | 940 } |
1009 image_fetcher_.FetchSuggestionImage(suggestion_id, image_url, callback); | 941 image_fetcher_.FetchSuggestionImage(suggestion_id, image_url, callback); |
1010 } | 942 } |
1011 | 943 |
1012 void RemoteSuggestionsProvider::EnterStateReady() { | 944 void RemoteSuggestionsProviderImpl::EnterStateReady() { |
1013 if (nuke_when_initialized_) { | 945 if (nuke_when_initialized_) { |
1014 NukeAllSnippets(); | 946 NukeAllSnippets(); |
1015 nuke_when_initialized_ = false; | 947 nuke_when_initialized_ = false; |
1016 } | 948 } |
1017 | 949 |
1018 auto article_category_it = category_contents_.find(articles_category_); | 950 auto article_category_it = category_contents_.find(articles_category_); |
1019 DCHECK(article_category_it != category_contents_.end()); | 951 DCHECK(article_category_it != category_contents_.end()); |
1020 if (article_category_it->second.snippets.empty() || fetch_when_ready_) { | 952 if (article_category_it->second.snippets.empty() || fetch_when_ready_) { |
1021 // TODO(jkrcal): Fetching snippets automatically upon creation of this | 953 // TODO(jkrcal): Fetching snippets automatically upon creation of this |
1022 // lazily created service can cause troubles, e.g. in unit tests where | 954 // lazily created service can cause troubles, e.g. in unit tests where |
1023 // network I/O is not allowed. | 955 // network I/O is not allowed. |
1024 // Either add a DCHECK here that we actually are allowed to do network I/O | 956 // 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 | 957 // or change the logic so that some explicit call is always needed for the |
1026 // network request. | 958 // network request. |
1027 FetchSnippets(/*interactive_request=*/false); | 959 FetchSnippets(fetch_when_ready_interactive_, fetch_when_ready_callback_); |
Marc Treib
2016/12/19 12:59:23
If we get here because our article suggestions are
jkrcal
2016/12/20 16:39:46
I believe so. Callbacks keep internally a pointer
| |
1028 fetch_when_ready_ = false; | 960 fetch_when_ready_ = false; |
1029 } | 961 } |
1030 | 962 |
1031 for (const auto& item : category_contents_) { | 963 for (const auto& item : category_contents_) { |
1032 Category category = item.first; | 964 Category category = item.first; |
1033 const CategoryContent& content = item.second; | 965 const CategoryContent& content = item.second; |
1034 // FetchSnippets has set the status to |AVAILABLE_LOADING| if relevant, | 966 // FetchSnippets has set the status to |AVAILABLE_LOADING| if relevant, |
1035 // otherwise we transition to |AVAILABLE| here. | 967 // otherwise we transition to |AVAILABLE| here. |
1036 if (content.status != CategoryStatus::AVAILABLE_LOADING) { | 968 if (content.status != CategoryStatus::AVAILABLE_LOADING) { |
1037 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 969 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
1038 } | 970 } |
1039 } | 971 } |
1040 } | 972 } |
1041 | 973 |
1042 void RemoteSuggestionsProvider::EnterStateDisabled() { | 974 void RemoteSuggestionsProviderImpl::EnterStateDisabled() { |
1043 NukeAllSnippets(); | 975 NukeAllSnippets(); |
1044 } | 976 } |
1045 | 977 |
1046 void RemoteSuggestionsProvider::EnterStateError() { | 978 void RemoteSuggestionsProviderImpl::EnterStateError() { |
1047 status_service_.reset(); | 979 status_service_.reset(); |
1048 } | 980 } |
1049 | 981 |
1050 void RemoteSuggestionsProvider::FinishInitialization() { | 982 void RemoteSuggestionsProviderImpl::FinishInitialization() { |
1051 if (nuke_when_initialized_) { | 983 if (nuke_when_initialized_) { |
1052 // We nuke here in addition to EnterStateReady, so that it happens even if | 984 // We nuke here in addition to EnterStateReady, so that it happens even if |
1053 // we enter the DISABLED state below. | 985 // we enter the DISABLED state below. |
1054 NukeAllSnippets(); | 986 NukeAllSnippets(); |
1055 nuke_when_initialized_ = false; | 987 nuke_when_initialized_ = false; |
1056 } | 988 } |
1057 | 989 |
1058 // Note: Initializing the status service will run the callback right away with | 990 // Note: Initializing the status service will run the callback right away with |
1059 // the current state. | 991 // the current state. |
1060 status_service_->Init(base::Bind(&RemoteSuggestionsProvider::OnStatusChanged, | 992 status_service_->Init(base::Bind( |
1061 base::Unretained(this))); | 993 &RemoteSuggestionsProviderImpl::OnStatusChanged, base::Unretained(this))); |
1062 | 994 |
1063 // Always notify here even if we got nothing from the database, because we | 995 // 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. | 996 // don't know how long the fetch will take or if it will even complete. |
1065 for (const auto& item : category_contents_) { | 997 for (const auto& item : category_contents_) { |
1066 Category category = item.first; | 998 Category category = item.first; |
1067 const CategoryContent& content = item.second; | 999 const CategoryContent& content = item.second; |
1068 // Note: We might be in a non-available status here, e.g. DISABLED due to | 1000 // Note: We might be in a non-available status here, e.g. DISABLED due to |
1069 // enterprise policy. | 1001 // enterprise policy. |
1070 if (IsCategoryStatusAvailable(content.status)) { | 1002 if (IsCategoryStatusAvailable(content.status)) { |
1071 NotifyNewSuggestions(category, content); | 1003 NotifyNewSuggestions(category, content); |
1072 } | 1004 } |
1073 } | 1005 } |
1074 } | 1006 } |
1075 | 1007 |
1076 void RemoteSuggestionsProvider::OnStatusChanged( | 1008 void RemoteSuggestionsProviderImpl::OnStatusChanged( |
1077 RemoteSuggestionsStatus old_status, | 1009 RemoteSuggestionsStatus old_status, |
1078 RemoteSuggestionsStatus new_status) { | 1010 RemoteSuggestionsStatus new_status) { |
1079 switch (new_status) { | 1011 switch (new_status) { |
1080 case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN: | 1012 case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN: |
1081 if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT) { | 1013 if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT) { |
1082 DCHECK(state_ == State::READY); | 1014 DCHECK(state_ == State::READY); |
1083 // Clear nonpersonalized suggestions. | 1015 // Clear nonpersonalized suggestions. |
1084 NukeAllSnippets(); | 1016 NukeAllSnippets(); |
1085 // Fetch personalized ones. | 1017 // Fetch personalized ones. |
1086 FetchSnippets(/*interactive_request=*/true); | 1018 // TODO(jkrcal): Loop in SchedulingRemoteSuggestionsProvider somehow. |
1019 FetchSnippets(/*interactive_request=*/true, FetchStatusCallback()); | |
1087 } else { | 1020 } else { |
1088 // Do not change the status. That will be done in EnterStateReady(). | 1021 // Do not change the status. That will be done in EnterStateReady(). |
1089 EnterState(State::READY); | 1022 EnterState(State::READY); |
1090 } | 1023 } |
1091 break; | 1024 break; |
1092 | 1025 |
1093 case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT: | 1026 case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT: |
1094 if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN) { | 1027 if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN) { |
1095 DCHECK(state_ == State::READY); | 1028 DCHECK(state_ == State::READY); |
1096 // Clear personalized suggestions. | 1029 // Clear personalized suggestions. |
1097 NukeAllSnippets(); | 1030 NukeAllSnippets(); |
1098 // Fetch nonpersonalized ones. | 1031 // Fetch nonpersonalized ones. |
1099 FetchSnippets(/*interactive_request=*/true); | 1032 // TODO(jkrcal): Loop in SchedulingRemoteSuggestionsProvider somehow. |
1033 FetchSnippets(/*interactive_request=*/true, FetchStatusCallback()); | |
1100 } else { | 1034 } else { |
1101 // Do not change the status. That will be done in EnterStateReady(). | 1035 // Do not change the status. That will be done in EnterStateReady(). |
1102 EnterState(State::READY); | 1036 EnterState(State::READY); |
1103 } | 1037 } |
1104 break; | 1038 break; |
1105 | 1039 |
1106 case RemoteSuggestionsStatus::EXPLICITLY_DISABLED: | 1040 case RemoteSuggestionsStatus::EXPLICITLY_DISABLED: |
1107 EnterState(State::DISABLED); | 1041 EnterState(State::DISABLED); |
1108 UpdateAllCategoryStatus(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED); | 1042 UpdateAllCategoryStatus(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED); |
1109 break; | 1043 break; |
1110 | 1044 |
1111 case RemoteSuggestionsStatus::SIGNED_OUT_AND_DISABLED: | 1045 case RemoteSuggestionsStatus::SIGNED_OUT_AND_DISABLED: |
1112 EnterState(State::DISABLED); | 1046 EnterState(State::DISABLED); |
1113 UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT); | 1047 UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT); |
1114 break; | 1048 break; |
1115 } | 1049 } |
1116 } | 1050 } |
1117 | 1051 |
1118 void RemoteSuggestionsProvider::EnterState(State state) { | 1052 void RemoteSuggestionsProviderImpl::EnterState(State state) { |
1119 if (state == state_) { | 1053 if (state == state_) { |
1120 return; | 1054 return; |
1121 } | 1055 } |
1122 | 1056 |
1123 UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.EnteredState", | 1057 UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.EnteredState", |
1124 static_cast<int>(state), | 1058 static_cast<int>(state), |
1125 static_cast<int>(State::COUNT)); | 1059 static_cast<int>(State::COUNT)); |
1126 | 1060 |
1127 switch (state) { | 1061 switch (state) { |
1128 case State::NOT_INITED: | 1062 case State::NOT_INITED: |
1129 // Initial state, it should not be possible to get back there. | 1063 // Initial state, it should not be possible to get back there. |
1130 NOTREACHED(); | 1064 NOTREACHED(); |
1131 break; | 1065 break; |
1132 | 1066 |
1133 case State::READY: | 1067 case State::READY: |
1134 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED); | 1068 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED); |
1135 | 1069 |
1136 DVLOG(1) << "Entering state: READY"; | 1070 DVLOG(1) << "Entering state: READY"; |
1137 state_ = State::READY; | 1071 state_ = State::READY; |
1138 EnterStateReady(); | 1072 EnterStateReady(); |
1073 provider_status_callback_.Run(ProviderStatus::ACTIVE); | |
1139 break; | 1074 break; |
1140 | 1075 |
1141 case State::DISABLED: | 1076 case State::DISABLED: |
1142 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); | 1077 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); |
1143 | 1078 |
1144 DVLOG(1) << "Entering state: DISABLED"; | 1079 DVLOG(1) << "Entering state: DISABLED"; |
1145 state_ = State::DISABLED; | 1080 state_ = State::DISABLED; |
1146 EnterStateDisabled(); | 1081 EnterStateDisabled(); |
1082 provider_status_callback_.Run(ProviderStatus::INACTIVE); | |
1147 break; | 1083 break; |
1148 | 1084 |
1149 case State::ERROR_OCCURRED: | 1085 case State::ERROR_OCCURRED: |
1150 DVLOG(1) << "Entering state: ERROR_OCCURRED"; | 1086 DVLOG(1) << "Entering state: ERROR_OCCURRED"; |
1151 state_ = State::ERROR_OCCURRED; | 1087 state_ = State::ERROR_OCCURRED; |
1152 EnterStateError(); | 1088 EnterStateError(); |
1089 provider_status_callback_.Run(ProviderStatus::INACTIVE); | |
1153 break; | 1090 break; |
1154 | 1091 |
1155 case State::COUNT: | 1092 case State::COUNT: |
1156 NOTREACHED(); | 1093 NOTREACHED(); |
1157 break; | 1094 break; |
1158 } | 1095 } |
1159 | |
1160 // Schedule or un-schedule background fetching after each state change. | |
1161 RescheduleFetching(false); | |
1162 } | 1096 } |
1163 | 1097 |
1164 void RemoteSuggestionsProvider::NotifyNewSuggestions( | 1098 void RemoteSuggestionsProviderImpl::NotifyNewSuggestions( |
1165 Category category, | 1099 Category category, |
1166 const CategoryContent& content) { | 1100 const CategoryContent& content) { |
1167 DCHECK(IsCategoryStatusAvailable(content.status)); | 1101 DCHECK(IsCategoryStatusAvailable(content.status)); |
1168 | 1102 |
1169 std::vector<ContentSuggestion> result = | 1103 std::vector<ContentSuggestion> result = |
1170 ConvertToContentSuggestions(category, content.snippets); | 1104 ConvertToContentSuggestions(category, content.snippets); |
1171 | 1105 |
1172 DVLOG(1) << "NotifyNewSuggestions(): " << result.size() | 1106 DVLOG(1) << "NotifyNewSuggestions(): " << result.size() |
1173 << " items in category " << category; | 1107 << " items in category " << category; |
1174 observer()->OnNewSuggestions(this, category, std::move(result)); | 1108 observer()->OnNewSuggestions(this, category, std::move(result)); |
1175 } | 1109 } |
1176 | 1110 |
1177 void RemoteSuggestionsProvider::UpdateCategoryStatus(Category category, | 1111 void RemoteSuggestionsProviderImpl::UpdateCategoryStatus( |
1178 CategoryStatus status) { | 1112 Category category, |
1113 CategoryStatus status) { | |
1179 auto content_it = category_contents_.find(category); | 1114 auto content_it = category_contents_.find(category); |
1180 DCHECK(content_it != category_contents_.end()); | 1115 DCHECK(content_it != category_contents_.end()); |
1181 CategoryContent& content = content_it->second; | 1116 CategoryContent& content = content_it->second; |
1182 | 1117 |
1183 if (status == content.status) { | 1118 if (status == content.status) { |
1184 return; | 1119 return; |
1185 } | 1120 } |
1186 | 1121 |
1187 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " | 1122 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " |
1188 << static_cast<int>(content.status) << " -> " | 1123 << static_cast<int>(content.status) << " -> " |
1189 << static_cast<int>(status); | 1124 << static_cast<int>(status); |
1190 content.status = status; | 1125 content.status = status; |
1191 observer()->OnCategoryStatusChanged(this, category, content.status); | 1126 observer()->OnCategoryStatusChanged(this, category, content.status); |
1192 } | 1127 } |
1193 | 1128 |
1194 void RemoteSuggestionsProvider::UpdateAllCategoryStatus(CategoryStatus status) { | 1129 void RemoteSuggestionsProviderImpl::UpdateAllCategoryStatus( |
1130 CategoryStatus status) { | |
1195 for (const auto& category : category_contents_) { | 1131 for (const auto& category : category_contents_) { |
1196 UpdateCategoryStatus(category.first, status); | 1132 UpdateCategoryStatus(category.first, status); |
1197 } | 1133 } |
1198 } | 1134 } |
1199 | 1135 |
1200 namespace { | 1136 namespace { |
1201 | 1137 |
1202 template <typename T> | 1138 template <typename T> |
1203 typename T::const_iterator FindSnippetInContainer( | 1139 typename T::const_iterator FindSnippetInContainer( |
1204 const T& container, | 1140 const T& container, |
1205 const std::string& id_within_category) { | 1141 const std::string& id_within_category) { |
1206 return std::find_if( | 1142 return std::find_if( |
1207 container.begin(), container.end(), | 1143 container.begin(), container.end(), |
1208 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { | 1144 [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { |
1209 return snippet->id() == id_within_category; | 1145 return snippet->id() == id_within_category; |
1210 }); | 1146 }); |
1211 } | 1147 } |
1212 | 1148 |
1213 } // namespace | 1149 } // namespace |
1214 | 1150 |
1215 const NTPSnippet* RemoteSuggestionsProvider::CategoryContent::FindSnippet( | 1151 const NTPSnippet* RemoteSuggestionsProviderImpl::CategoryContent::FindSnippet( |
1216 const std::string& id_within_category) const { | 1152 const std::string& id_within_category) const { |
1217 // Search for the snippet in current and archived snippets. | 1153 // Search for the snippet in current and archived snippets. |
1218 auto it = FindSnippetInContainer(snippets, id_within_category); | 1154 auto it = FindSnippetInContainer(snippets, id_within_category); |
1219 if (it != snippets.end()) { | 1155 if (it != snippets.end()) { |
1220 return it->get(); | 1156 return it->get(); |
1221 } | 1157 } |
1222 auto archived_it = FindSnippetInContainer(archived, id_within_category); | 1158 auto archived_it = FindSnippetInContainer(archived, id_within_category); |
1223 if (archived_it != archived.end()) { | 1159 if (archived_it != archived.end()) { |
1224 return archived_it->get(); | 1160 return archived_it->get(); |
1225 } | 1161 } |
1226 auto dismissed_it = FindSnippetInContainer(dismissed, id_within_category); | 1162 auto dismissed_it = FindSnippetInContainer(dismissed, id_within_category); |
1227 if (dismissed_it != dismissed.end()) { | 1163 if (dismissed_it != dismissed.end()) { |
1228 return dismissed_it->get(); | 1164 return dismissed_it->get(); |
1229 } | 1165 } |
1230 return nullptr; | 1166 return nullptr; |
1231 } | 1167 } |
1232 | 1168 |
1233 RemoteSuggestionsProvider::CategoryContent* | 1169 RemoteSuggestionsProviderImpl::CategoryContent* |
1234 RemoteSuggestionsProvider::UpdateCategoryInfo(Category category, | 1170 RemoteSuggestionsProviderImpl::UpdateCategoryInfo(Category category, |
1235 const CategoryInfo& info) { | 1171 const CategoryInfo& info) { |
1236 auto content_it = category_contents_.find(category); | 1172 auto content_it = category_contents_.find(category); |
1237 if (content_it == category_contents_.end()) { | 1173 if (content_it == category_contents_.end()) { |
1238 content_it = category_contents_ | 1174 content_it = category_contents_ |
1239 .insert(std::make_pair(category, CategoryContent(info))) | 1175 .insert(std::make_pair(category, CategoryContent(info))) |
1240 .first; | 1176 .first; |
1241 } else { | 1177 } else { |
1242 content_it->second.info = info; | 1178 content_it->second.info = info; |
1243 } | 1179 } |
1244 return &content_it->second; | 1180 return &content_it->second; |
1245 } | 1181 } |
1246 | 1182 |
1247 void RemoteSuggestionsProvider::RestoreCategoriesFromPrefs() { | 1183 void RemoteSuggestionsProviderImpl::RestoreCategoriesFromPrefs() { |
1248 // This must only be called at startup, before there are any categories. | 1184 // This must only be called at startup, before there are any categories. |
1249 DCHECK(category_contents_.empty()); | 1185 DCHECK(category_contents_.empty()); |
1250 | 1186 |
1251 const base::ListValue* list = | 1187 const base::ListValue* list = |
1252 pref_service_->GetList(prefs::kRemoteSuggestionCategories); | 1188 pref_service_->GetList(prefs::kRemoteSuggestionCategories); |
1253 for (const std::unique_ptr<base::Value>& entry : *list) { | 1189 for (const std::unique_ptr<base::Value>& entry : *list) { |
1254 const base::DictionaryValue* dict = nullptr; | 1190 const base::DictionaryValue* dict = nullptr; |
1255 if (!entry->GetAsDictionary(&dict)) { | 1191 if (!entry->GetAsDictionary(&dict)) { |
1256 DLOG(WARNING) << "Invalid category pref value: " << *entry; | 1192 DLOG(WARNING) << "Invalid category pref value: " << *entry; |
1257 continue; | 1193 continue; |
(...skipping 30 matching lines...) Expand all Loading... | |
1288 CategoryInfo info = | 1224 CategoryInfo info = |
1289 category == articles_category_ | 1225 category == articles_category_ |
1290 ? BuildArticleCategoryInfo(title) | 1226 ? BuildArticleCategoryInfo(title) |
1291 : BuildRemoteCategoryInfo(title, allow_fetching_more_results); | 1227 : BuildRemoteCategoryInfo(title, allow_fetching_more_results); |
1292 CategoryContent* content = UpdateCategoryInfo(category, info); | 1228 CategoryContent* content = UpdateCategoryInfo(category, info); |
1293 content->included_in_last_server_response = | 1229 content->included_in_last_server_response = |
1294 included_in_last_server_response; | 1230 included_in_last_server_response; |
1295 } | 1231 } |
1296 } | 1232 } |
1297 | 1233 |
1298 void RemoteSuggestionsProvider::StoreCategoriesToPrefs() { | 1234 void RemoteSuggestionsProviderImpl::StoreCategoriesToPrefs() { |
1299 // Collect all the CategoryContents. | 1235 // Collect all the CategoryContents. |
1300 std::vector<std::pair<Category, const CategoryContent*>> to_store; | 1236 std::vector<std::pair<Category, const CategoryContent*>> to_store; |
1301 for (const auto& entry : category_contents_) { | 1237 for (const auto& entry : category_contents_) { |
1302 to_store.emplace_back(entry.first, &entry.second); | 1238 to_store.emplace_back(entry.first, &entry.second); |
1303 } | 1239 } |
1304 // Sort them into the proper category order. | 1240 // Sort them into the proper category order. |
1305 std::sort(to_store.begin(), to_store.end(), | 1241 std::sort(to_store.begin(), to_store.end(), |
1306 [this](const std::pair<Category, const CategoryContent*>& left, | 1242 [this](const std::pair<Category, const CategoryContent*>& left, |
1307 const std::pair<Category, const CategoryContent*>& right) { | 1243 const std::pair<Category, const CategoryContent*>& right) { |
1308 return category_factory()->CompareCategories(left.first, | 1244 return category_factory()->CompareCategories(left.first, |
(...skipping 11 matching lines...) Expand all Loading... | |
1320 dict->SetBoolean(kCategoryContentProvidedByServer, | 1256 dict->SetBoolean(kCategoryContentProvidedByServer, |
1321 content.included_in_last_server_response); | 1257 content.included_in_last_server_response); |
1322 dict->SetBoolean(kCategoryContentAllowFetchingMore, | 1258 dict->SetBoolean(kCategoryContentAllowFetchingMore, |
1323 content.info.has_more_action()); | 1259 content.info.has_more_action()); |
1324 list.Append(std::move(dict)); | 1260 list.Append(std::move(dict)); |
1325 } | 1261 } |
1326 // Finally, store the result in the pref service. | 1262 // Finally, store the result in the pref service. |
1327 pref_service_->Set(prefs::kRemoteSuggestionCategories, list); | 1263 pref_service_->Set(prefs::kRemoteSuggestionCategories, list); |
1328 } | 1264 } |
1329 | 1265 |
1330 RemoteSuggestionsProvider::CategoryContent::CategoryContent( | 1266 RemoteSuggestionsProviderImpl::CategoryContent::CategoryContent( |
1331 const CategoryInfo& info) | 1267 const CategoryInfo& info) |
1332 : info(info) {} | 1268 : info(info) {} |
1333 | 1269 |
1334 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) = | 1270 RemoteSuggestionsProviderImpl::CategoryContent::CategoryContent( |
1335 default; | 1271 CategoryContent&&) = default; |
1336 | 1272 |
1337 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default; | 1273 RemoteSuggestionsProviderImpl::CategoryContent::~CategoryContent() = default; |
1338 | 1274 |
1339 RemoteSuggestionsProvider::CategoryContent& | 1275 RemoteSuggestionsProviderImpl::CategoryContent& |
1340 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) = | 1276 RemoteSuggestionsProviderImpl::CategoryContent::operator=(CategoryContent&&) = |
1341 default; | 1277 default; |
1342 | 1278 |
1343 } // namespace ntp_snippets | 1279 } // namespace ntp_snippets |
OLD | NEW |