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

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

Powered by Google App Engine
This is Rietveld 408576698