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

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

Powered by Google App Engine
This is Rietveld 408576698