Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Side by Side Diff: components/ntp_snippets/remote/remote_suggestions_provider_impl.cc

Issue 2557363002: [NTP Snippets] Refactor background scheduling for remote suggestions (Closed)
Patch Set: Tim's comments and splitting RemoteSuggestionsProvider and RemoteSuggestionsProviderImpl Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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, &param_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
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698