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

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

Powered by Google App Engine
This is Rietveld 408576698