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

Side by Side Diff: components/ntp_snippets/ntp_snippets_service.cc

Issue 2283743002: Revert of Support server categories in NTPSnippetsService. (Closed) Base URL: https://chromium.googlesource.com/chromium/src@master
Patch Set: Created 4 years, 3 months 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/ntp_snippets_service.h" 5 #include "components/ntp_snippets/ntp_snippets_service.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 #include <utility> 9 #include <utility>
10 10
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
195 SuggestionsService* suggestions_service, 195 SuggestionsService* suggestions_service,
196 const std::string& application_language_code, 196 const std::string& application_language_code,
197 NTPSnippetsScheduler* scheduler, 197 NTPSnippetsScheduler* scheduler,
198 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, 198 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher,
199 std::unique_ptr<ImageFetcher> image_fetcher, 199 std::unique_ptr<ImageFetcher> image_fetcher,
200 std::unique_ptr<ImageDecoder> image_decoder, 200 std::unique_ptr<ImageDecoder> image_decoder,
201 std::unique_ptr<NTPSnippetsDatabase> database, 201 std::unique_ptr<NTPSnippetsDatabase> database,
202 std::unique_ptr<NTPSnippetsStatusService> status_service) 202 std::unique_ptr<NTPSnippetsStatusService> status_service)
203 : ContentSuggestionsProvider(observer, category_factory), 203 : ContentSuggestionsProvider(observer, category_factory),
204 state_(State::NOT_INITED), 204 state_(State::NOT_INITED),
205 category_status_(CategoryStatus::INITIALIZING),
205 pref_service_(pref_service), 206 pref_service_(pref_service),
206 suggestions_service_(suggestions_service), 207 suggestions_service_(suggestions_service),
207 articles_category_(
208 category_factory->FromKnownCategory(KnownCategories::ARTICLES)),
209 application_language_code_(application_language_code), 208 application_language_code_(application_language_code),
210 scheduler_(scheduler), 209 scheduler_(scheduler),
211 history_service_observer_(this), 210 history_service_observer_(this),
212 snippets_fetcher_(std::move(snippets_fetcher)), 211 snippets_fetcher_(std::move(snippets_fetcher)),
213 image_fetcher_(std::move(image_fetcher)), 212 image_fetcher_(std::move(image_fetcher)),
214 image_decoder_(std::move(image_decoder)), 213 image_decoder_(std::move(image_decoder)),
215 database_(std::move(database)), 214 database_(std::move(database)),
216 snippets_status_service_(std::move(status_service)), 215 snippets_status_service_(std::move(status_service)),
217 fetch_after_load_(false), 216 fetch_after_load_(false),
218 nuke_after_load_(false), 217 nuke_after_load_(false),
218 provided_category_(
219 category_factory->FromKnownCategory(KnownCategories::ARTICLES)),
219 thumbnail_requests_throttler_( 220 thumbnail_requests_throttler_(
220 pref_service, 221 pref_service,
221 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) { 222 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) {
222 // Articles category always exists; others will be added as needed. 223 observer->OnCategoryStatusChanged(this, provided_category_, category_status_);
223 categories_[articles_category_] = CategoryContent();
224 observer->OnCategoryStatusChanged(this, articles_category_,
225 categories_[articles_category_].status);
226 if (database_->IsErrorState()) { 224 if (database_->IsErrorState()) {
227 EnterState(State::ERROR_OCCURRED); 225 EnterState(State::ERROR_OCCURRED, CategoryStatus::LOADING_ERROR);
228 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR);
229 return; 226 return;
230 } 227 }
231 228
232 // Can be null in tests. 229 // Can be null in tests.
233 if (history_service) 230 if (history_service)
234 history_service_observer_.Add(history_service); 231 history_service_observer_.Add(history_service);
235 232
236 database_->SetErrorCallback(base::Bind(&NTPSnippetsService::OnDatabaseError, 233 database_->SetErrorCallback(base::Bind(&NTPSnippetsService::OnDatabaseError,
237 base::Unretained(this))); 234 base::Unretained(this)));
238 235
(...skipping 19 matching lines...) Expand all
258 else 255 else
259 fetch_after_load_ = true; 256 fetch_after_load_ = true;
260 } 257 }
261 258
262 void NTPSnippetsService::FetchSnippetsFromHosts( 259 void NTPSnippetsService::FetchSnippetsFromHosts(
263 const std::set<std::string>& hosts, 260 const std::set<std::string>& hosts,
264 bool interactive_request) { 261 bool interactive_request) {
265 if (!ready()) 262 if (!ready())
266 return; 263 return;
267 264
268 // Empty categories are marked as loading; others are unchanged. 265 if (snippets_.empty())
269 for (const auto& item : categories_) { 266 UpdateCategoryStatus(CategoryStatus::AVAILABLE_LOADING);
270 Category category = item.first;
271 const CategoryContent& content = item.second;
272 if (content.snippets.empty())
273 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING);
274 }
275 267
276 std::set<std::string> excluded_ids; 268 std::set<std::string> excluded_ids;
277 for (const auto& item : categories_) { 269 for (const auto& snippet : dismissed_snippets_) {
278 const CategoryContent& content = item.second; 270 excluded_ids.insert(snippet->id());
279 for (const auto& snippet : content.dismissed)
280 excluded_ids.insert(snippet->id());
281 } 271 }
282 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, 272 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_,
283 excluded_ids, kMaxSnippetCount, 273 excluded_ids, kMaxSnippetCount,
284 interactive_request); 274 interactive_request);
285 } 275 }
286 276
287 void NTPSnippetsService::RescheduleFetching() { 277 void NTPSnippetsService::RescheduleFetching() {
288 // The scheduler only exists on Android so far, it's null on other platforms. 278 // The scheduler only exists on Android so far, it's null on other platforms.
289 if (!scheduler_) 279 if (!scheduler_)
290 return; 280 return;
291 281
292 if (ready()) { 282 if (ready()) {
293 base::Time now = base::Time::Now(); 283 base::Time now = base::Time::Now();
294 scheduler_->Schedule( 284 scheduler_->Schedule(
295 GetFetchingIntervalWifiCharging(), GetFetchingIntervalWifi(now), 285 GetFetchingIntervalWifiCharging(), GetFetchingIntervalWifi(now),
296 GetFetchingIntervalFallback(), GetRescheduleTime(now)); 286 GetFetchingIntervalFallback(), GetRescheduleTime(now));
297 } else { 287 } else {
298 scheduler_->Unschedule(); 288 scheduler_->Unschedule();
299 } 289 }
300 } 290 }
301 291
302 CategoryStatus NTPSnippetsService::GetCategoryStatus(Category category) { 292 CategoryStatus NTPSnippetsService::GetCategoryStatus(Category category) {
303 DCHECK(categories_.find(category) != categories_.end()); 293 DCHECK(category.IsKnownCategory(KnownCategories::ARTICLES));
304 return categories_[category].status; 294 return category_status_;
305 } 295 }
306 296
307 CategoryInfo NTPSnippetsService::GetCategoryInfo(Category category) { 297 CategoryInfo NTPSnippetsService::GetCategoryInfo(Category category) {
308 DCHECK(categories_.find(category) != categories_.end());
309 // TODO(sfiera): pass back titles for server categories.
310 return CategoryInfo( 298 return CategoryInfo(
311 l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER), 299 l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER),
312 ContentSuggestionsCardLayout::FULL_CARD, 300 ContentSuggestionsCardLayout::FULL_CARD,
313 /* has_more_button */ false, 301 /* has_more_button */ false,
314 /* show_if_empty */ true); 302 /* show_if_empty */ true);
315 } 303 }
316 304
317 void NTPSnippetsService::DismissSuggestion(const std::string& suggestion_id) { 305 void NTPSnippetsService::DismissSuggestion(const std::string& suggestion_id) {
318 if (!ready()) 306 if (!ready())
319 return; 307 return;
320 308
321 Category category = GetCategoryFromUniqueID(suggestion_id);
322 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); 309 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
323 310
324 DCHECK(categories_.find(category) != categories_.end());
325
326 CategoryContent* content = &categories_[category];
327 auto it = 311 auto it =
328 std::find_if(content->snippets.begin(), content->snippets.end(), 312 std::find_if(snippets_.begin(), snippets_.end(),
329 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { 313 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) {
330 return snippet->id() == snippet_id; 314 return snippet->id() == snippet_id;
331 }); 315 });
332 if (it == content->snippets.end()) 316 if (it == snippets_.end())
333 return; 317 return;
334 318
335 (*it)->set_dismissed(true); 319 (*it)->set_dismissed(true);
336 320
337 database_->SaveSnippet(**it); 321 database_->SaveSnippet(**it);
338 database_->DeleteImage(snippet_id); 322 database_->DeleteImage((*it)->id());
339 323
340 content->dismissed.push_back(std::move(*it)); 324 dismissed_snippets_.push_back(std::move(*it));
341 content->snippets.erase(it); 325 snippets_.erase(it);
342 } 326 }
343 327
344 void NTPSnippetsService::FetchSuggestionImage( 328 void NTPSnippetsService::FetchSuggestionImage(
345 const std::string& suggestion_id, 329 const std::string& suggestion_id,
346 const ImageFetchedCallback& callback) { 330 const ImageFetchedCallback& callback) {
347 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); 331 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
348 database_->LoadImage( 332 database_->LoadImage(
349 snippet_id, 333 snippet_id,
350 base::Bind(&NTPSnippetsService::OnSnippetImageFetchedFromDatabase, 334 base::Bind(&NTPSnippetsService::OnSnippetImageFetchedFromDatabase,
351 base::Unretained(this), callback, suggestion_id)); 335 base::Unretained(this), callback, snippet_id));
352 } 336 }
353 337
354 void NTPSnippetsService::ClearCachedSuggestions(Category category) { 338 void NTPSnippetsService::ClearCachedSuggestions(Category category) {
339 DCHECK_EQ(category, provided_category_);
355 if (!initialized()) 340 if (!initialized())
356 return; 341 return;
357 342
358 if (categories_.find(category) == categories_.end()) 343 if (snippets_.empty())
359 return;
360 CategoryContent* content = &categories_[category];
361 if (content->snippets.empty())
362 return; 344 return;
363 345
364 if (category == articles_category_) 346 database_->DeleteSnippets(snippets_);
365 database_->DeleteSnippets(content->snippets); 347 snippets_.clear();
366 content->snippets.clear();
367 348
368 NotifyNewSuggestions(); 349 NotifyNewSuggestions();
369 } 350 }
370 351
371 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( 352 void NTPSnippetsService::GetDismissedSuggestionsForDebugging(
372 Category category, 353 Category category,
373 const DismissedSuggestionsCallback& callback) { 354 const DismissedSuggestionsCallback& callback) {
374 DCHECK(categories_.find(category) != categories_.end()); 355 DCHECK_EQ(category, provided_category_);
375
376 std::vector<ContentSuggestion> result; 356 std::vector<ContentSuggestion> result;
377 const CategoryContent& content = categories_[category]; 357 for (const std::unique_ptr<NTPSnippet>& snippet : dismissed_snippets_) {
378 for (const std::unique_ptr<NTPSnippet>& snippet : content.dismissed) {
379 if (!snippet->is_complete()) 358 if (!snippet->is_complete())
380 continue; 359 continue;
381 ContentSuggestion suggestion(MakeUniqueID(category, snippet->id()), 360 ContentSuggestion suggestion(
382 snippet->best_source().url); 361 MakeUniqueID(provided_category_, snippet->id()),
362 snippet->best_source().url);
383 suggestion.set_amp_url(snippet->best_source().amp_url); 363 suggestion.set_amp_url(snippet->best_source().amp_url);
384 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); 364 suggestion.set_title(base::UTF8ToUTF16(snippet->title()));
385 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); 365 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet()));
386 suggestion.set_publish_date(snippet->publish_date()); 366 suggestion.set_publish_date(snippet->publish_date());
387 suggestion.set_publisher_name( 367 suggestion.set_publisher_name(
388 base::UTF8ToUTF16(snippet->best_source().publisher_name)); 368 base::UTF8ToUTF16(snippet->best_source().publisher_name));
389 suggestion.set_score(snippet->score()); 369 suggestion.set_score(snippet->score());
390 result.emplace_back(std::move(suggestion)); 370 result.emplace_back(std::move(suggestion));
391 } 371 }
392 callback.Run(std::move(result)); 372 callback.Run(std::move(result));
393 } 373 }
394 374
395 void NTPSnippetsService::ClearDismissedSuggestionsForDebugging( 375 void NTPSnippetsService::ClearDismissedSuggestionsForDebugging(
396 Category category) { 376 Category category) {
397 DCHECK(categories_.find(category) != categories_.end()); 377 DCHECK_EQ(category, provided_category_);
398
399 if (!initialized()) 378 if (!initialized())
400 return; 379 return;
401 380
402 CategoryContent* content = &categories_[category]; 381 if (dismissed_snippets_.empty())
403 if (content->dismissed.empty())
404 return; 382 return;
405 383
406 if (category == articles_category_) 384 database_->DeleteSnippets(dismissed_snippets_);
407 database_->DeleteSnippets(content->dismissed); 385 dismissed_snippets_.clear();
408 content->dismissed.clear();
409 } 386 }
410 387
411 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { 388 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const {
412 // |suggestions_service_| can be null in tests. 389 // |suggestions_service_| can be null in tests.
413 if (!suggestions_service_) 390 if (!suggestions_service_)
414 return std::set<std::string>(); 391 return std::set<std::string>();
415 392
416 // TODO(treib): This should just call GetSnippetHostsFromPrefs. 393 // TODO(treib): This should just call GetSnippetHostsFromPrefs.
417 return GetSuggestionsHostsImpl( 394 return GetSuggestionsHostsImpl(
418 suggestions_service_->GetSuggestionsDataFromCache()); 395 suggestions_service_->GetSuggestionsDataFromCache());
(...skipping 23 matching lines...) Expand all
442 else 419 else
443 NukeAllSnippets(); 420 NukeAllSnippets();
444 } 421 }
445 422
446 void NTPSnippetsService::HistoryServiceBeingDeleted( 423 void NTPSnippetsService::HistoryServiceBeingDeleted(
447 history::HistoryService* history_service) { 424 history::HistoryService* history_service) {
448 history_service_observer_.RemoveAll(); 425 history_service_observer_.RemoveAll();
449 } 426 }
450 427
451 // image_fetcher::ImageFetcherDelegate implementation. 428 // image_fetcher::ImageFetcherDelegate implementation.
452 void NTPSnippetsService::OnImageDataFetched(const std::string& suggestion_id, 429 void NTPSnippetsService::OnImageDataFetched(const std::string& snippet_id,
453 const std::string& image_data) { 430 const std::string& image_data) {
454 if (image_data.empty()) 431 if (image_data.empty())
455 return; 432 return;
456 433
457 Category category = GetCategoryFromUniqueID(suggestion_id);
458 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
459
460 auto category_it = categories_.find(category);
461 if (category_it == categories_.end())
462 return;
463
464 const CategoryContent& content = category_it->second;
465
466 // Only save the image if the corresponding snippet still exists. 434 // Only save the image if the corresponding snippet still exists.
467 auto it = 435 auto it =
468 std::find_if(content.snippets.begin(), content.snippets.end(), 436 std::find_if(snippets_.begin(), snippets_.end(),
469 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { 437 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) {
470 return snippet->id() == snippet_id; 438 return snippet->id() == snippet_id;
471 }); 439 });
472 if (it == content.snippets.end()) 440 if (it == snippets_.end())
473 return; 441 return;
474 442
475 database_->SaveImage(snippet_id, image_data); 443 database_->SaveImage(snippet_id, image_data);
476 } 444 }
477 445
478 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { 446 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) {
479 if (state_ == State::ERROR_OCCURRED) 447 if (state_ == State::ERROR_OCCURRED)
480 return; 448 return;
481 DCHECK(state_ == State::NOT_INITED); 449 DCHECK(state_ == State::NOT_INITED);
482 DCHECK(categories_.size() == 1); // Only articles category, so far. 450 DCHECK(snippets_.empty());
483 DCHECK(categories_.find(articles_category_) != categories_.end()); 451 DCHECK(dismissed_snippets_.empty());
484
485 // TODO(sfiera): support non-article categories in database.
486 CategoryContent* content = &categories_[articles_category_];
487 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { 452 for (std::unique_ptr<NTPSnippet>& snippet : snippets) {
488 if (snippet->is_dismissed()) 453 if (snippet->is_dismissed())
489 content->dismissed.emplace_back(std::move(snippet)); 454 dismissed_snippets_.emplace_back(std::move(snippet));
490 else 455 else
491 content->snippets.emplace_back(std::move(snippet)); 456 snippets_.emplace_back(std::move(snippet));
492 } 457 }
493 458 std::sort(snippets_.begin(), snippets_.end(),
494 std::sort(content->snippets.begin(), content->snippets.end(),
495 [](const std::unique_ptr<NTPSnippet>& lhs, 459 [](const std::unique_ptr<NTPSnippet>& lhs,
496 const std::unique_ptr<NTPSnippet>& rhs) { 460 const std::unique_ptr<NTPSnippet>& rhs) {
497 return lhs->score() > rhs->score(); 461 return lhs->score() > rhs->score();
498 }); 462 });
499 463
500 ClearExpiredSnippets(); 464 ClearExpiredSnippets();
501 FinishInitialization(); 465 FinishInitialization();
502 } 466 }
503 467
504 void NTPSnippetsService::OnDatabaseError() { 468 void NTPSnippetsService::OnDatabaseError() {
505 EnterState(State::ERROR_OCCURRED); 469 EnterState(State::ERROR_OCCURRED, CategoryStatus::LOADING_ERROR);
506 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR);
507 } 470 }
508 471
509 // TODO(dgn): name clash between content suggestions and suggestions hosts. 472 // TODO(dgn): name clash between content suggestions and suggestions hosts.
510 // method name should be changed. 473 // method name should be changed.
511 void NTPSnippetsService::OnSuggestionsChanged( 474 void NTPSnippetsService::OnSuggestionsChanged(
512 const SuggestionsProfile& suggestions) { 475 const SuggestionsProfile& suggestions) {
513 DCHECK(initialized()); 476 DCHECK(initialized());
514 477
515 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); 478 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions);
516 if (hosts == GetSnippetHostsFromPrefs()) 479 if (hosts == GetSnippetHostsFromPrefs())
517 return; 480 return;
518 481
519 // Remove existing snippets that aren't in the suggestions anymore. 482 // Remove existing snippets that aren't in the suggestions anymore.
520 //
521 // TODO(treib,maybelle): If there is another source with an allowed host, 483 // TODO(treib,maybelle): If there is another source with an allowed host,
522 // then we should fall back to that. 484 // then we should fall back to that.
523 //
524 // TODO(sfiera): determine when non-article categories should restrict hosts,
525 // and apply the same logic to them here. Maybe never?
526 //
527 // First, move them over into |to_delete|. 485 // First, move them over into |to_delete|.
528 CategoryContent* content = &categories_[articles_category_];
529 NTPSnippet::PtrVector to_delete; 486 NTPSnippet::PtrVector to_delete;
530 for (std::unique_ptr<NTPSnippet>& snippet : content->snippets) { 487 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) {
531 if (!hosts.count(snippet->best_source().url.host())) 488 if (!hosts.count(snippet->best_source().url.host()))
532 to_delete.emplace_back(std::move(snippet)); 489 to_delete.emplace_back(std::move(snippet));
533 } 490 }
534 Compact(&content->snippets); 491 Compact(&snippets_);
535 // Then delete the removed snippets from the database. 492 // Then delete the removed snippets from the database.
536 database_->DeleteSnippets(to_delete); 493 database_->DeleteSnippets(to_delete);
537 494
538 StoreSnippetHostsToPrefs(hosts); 495 StoreSnippetHostsToPrefs(hosts);
539 496
540 // We removed some suggestions, so we want to let the client know about that. 497 // We removed some suggestions, so we want to let the client know about that.
541 // The fetch might take a long time or not complete so we don't want to wait 498 // The fetch might take a long time or not complete so we don't want to wait
542 // for its callback. 499 // for its callback.
543 NotifyNewSuggestions(); 500 NotifyNewSuggestions();
544 501
545 FetchSnippetsFromHosts(hosts, /*force_request=*/false); 502 FetchSnippetsFromHosts(hosts, /*force_request=*/false);
546 } 503 }
547 504
548 void NTPSnippetsService::OnFetchFinished( 505 void NTPSnippetsService::OnFetchFinished(
549 NTPSnippetsFetcher::OptionalSnippets snippets) { 506 NTPSnippetsFetcher::OptionalSnippets snippets) {
550 if (!ready()) 507 if (!ready())
551 return; 508 return;
552 509
553 for (auto& item : categories_) { 510 DCHECK(category_status_ == CategoryStatus::AVAILABLE ||
554 CategoryContent* content = &item.second; 511 category_status_ == CategoryStatus::AVAILABLE_LOADING);
555 content->provided_by_server = false; 512
513 // TODO(sfiera): support more than just the provided_category_ ARTICLES.
514 if (snippets && (snippets->find(provided_category_) != snippets->end())) {
515 // Sparse histogram used because the number of snippets is small (bound by
516 // kMaxSnippetCount).
517 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount));
518 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched",
519 (*snippets)[provided_category_].size());
520 MergeSnippets(std::move((*snippets)[provided_category_]));
556 } 521 }
557 522
558 // If snippets were fetched successfully, update our |categories_| from each 523 ClearExpiredSnippets();
559 // category provided by the server.
560 if (snippets) {
561 for (std::pair<const Category, NTPSnippet::PtrVector>& item : *snippets) {
562 Category category = item.first;
563 NTPSnippet::PtrVector& new_snippets = item.second;
564 524
565 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); 525 // If there are more snippets than we want to show, delete the extra ones.
566 // TODO(sfiera): histograms for server categories. 526 if (snippets_.size() > kMaxSnippetCount) {
567 // Sparse histogram used because the number of snippets is small (bound by 527 NTPSnippet::PtrVector to_delete(
568 // kMaxSnippetCount). 528 std::make_move_iterator(snippets_.begin() + kMaxSnippetCount),
569 if (category == articles_category_) { 529 std::make_move_iterator(snippets_.end()));
570 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", 530 snippets_.resize(kMaxSnippetCount);
571 new_snippets.size()); 531 database_->DeleteSnippets(to_delete);
572 }
573
574 MergeSnippets(category, std::move(new_snippets));
575
576 // If there are more snippets than we want to show, delete the extra ones.
577 CategoryContent* content = &categories_[category];
578 content->provided_by_server = true;
579 if (content->snippets.size() > kMaxSnippetCount) {
580 NTPSnippet::PtrVector to_delete(
581 std::make_move_iterator(content->snippets.begin() +
582 kMaxSnippetCount),
583 std::make_move_iterator(content->snippets.end()));
584 content->snippets.resize(kMaxSnippetCount);
585 if (category == articles_category_)
586 database_->DeleteSnippets(to_delete);
587 }
588 }
589 } 532 }
590 533
591 // Trigger expiration. This probably won't expire any current snippets (old 534 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
592 // ones should have already been expired by the timer, and new ones shouldn't 535 snippets_.size());
593 // have expired yet), but it will update the timer for the next run. 536 if (snippets_.empty() && !dismissed_snippets_.empty()) {
594 ClearExpiredSnippets(); 537 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
595 538 dismissed_snippets_.size());
596 for (const auto& item : categories_) {
597 Category category = item.first;
598 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
599 } 539 }
600 540
601 // TODO(sfiera): equivalent metrics for non-articles. 541 UpdateCategoryStatus(CategoryStatus::AVAILABLE);
602 const CategoryContent& content = categories_[articles_category_];
603 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
604 content.snippets.size());
605 if (content.snippets.empty() && !content.dismissed.empty()) {
606 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
607 content.dismissed.size());
608 }
609
610 // TODO(sfiera): notify only when a category changed above.
611 NotifyNewSuggestions(); 542 NotifyNewSuggestions();
612 } 543 }
613 544
614 void NTPSnippetsService::MergeSnippets(Category category, 545 void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) {
615 NTPSnippet::PtrVector new_snippets) {
616 DCHECK(ready()); 546 DCHECK(ready());
617 CategoryContent* content = &categories_[category];
618 547
619 // Remove new snippets that we already have, or that have been dismissed. 548 // Remove new snippets that we already have, or that have been dismissed.
620 std::set<std::string> old_snippet_ids; 549 std::set<std::string> old_snippet_ids;
621 InsertAllIDs(content->dismissed, &old_snippet_ids); 550 InsertAllIDs(dismissed_snippets_, &old_snippet_ids);
622 InsertAllIDs(content->snippets, &old_snippet_ids); 551 InsertAllIDs(snippets_, &old_snippet_ids);
623 new_snippets.erase( 552 new_snippets.erase(
624 std::remove_if( 553 std::remove_if(
625 new_snippets.begin(), new_snippets.end(), 554 new_snippets.begin(), new_snippets.end(),
626 [&old_snippet_ids](const std::unique_ptr<NTPSnippet>& snippet) { 555 [&old_snippet_ids](const std::unique_ptr<NTPSnippet>& snippet) {
627 if (old_snippet_ids.count(snippet->id())) 556 if (old_snippet_ids.count(snippet->id()))
628 return true; 557 return true;
629 for (const SnippetSource& source : snippet->sources()) { 558 for (const SnippetSource& source : snippet->sources()) {
630 if (old_snippet_ids.count(source.url.spec())) 559 if (old_snippet_ids.count(source.url.spec()))
631 return true; 560 return true;
632 } 561 }
(...skipping 27 matching lines...) Expand all
660 new_snippets.end()); 589 new_snippets.end());
661 int num_snippets_dismissed = num_new_snippets - new_snippets.size(); 590 int num_snippets_dismissed = num_new_snippets - new_snippets.size();
662 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", 591 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch",
663 num_snippets_dismissed > 0); 592 num_snippets_dismissed > 0);
664 if (num_snippets_dismissed > 0) { 593 if (num_snippets_dismissed > 0) {
665 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", 594 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets",
666 num_snippets_dismissed); 595 num_snippets_dismissed);
667 } 596 }
668 } 597 }
669 598
670 // Save new articles to the DB. 599 // Save the new snippets to the DB.
671 // TODO(sfiera): save non-articles to DB too. 600 database_->SaveSnippets(new_snippets);
672 if (category == articles_category_)
673 database_->SaveSnippets(new_snippets);
674 601
675 // Insert the new snippets at the front. 602 // Insert the new snippets at the front.
676 content->snippets.insert(content->snippets.begin(), 603 snippets_.insert(snippets_.begin(),
677 std::make_move_iterator(new_snippets.begin()), 604 std::make_move_iterator(new_snippets.begin()),
678 std::make_move_iterator(new_snippets.end())); 605 std::make_move_iterator(new_snippets.end()));
679 } 606 }
680 607
681 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const { 608 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const {
682 std::set<std::string> hosts; 609 std::set<std::string> hosts;
683 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts); 610 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts);
684 for (const auto& value : *list) { 611 for (const auto& value : *list) {
685 std::string str; 612 std::string str;
686 bool success = value->GetAsString(&str); 613 bool success = value->GetAsString(&str);
687 DCHECK(success) << "Failed to parse snippet host from prefs"; 614 DCHECK(success) << "Failed to parse snippet host from prefs";
688 hosts.insert(std::move(str)); 615 hosts.insert(std::move(str));
689 } 616 }
690 return hosts; 617 return hosts;
691 } 618 }
692 619
693 void NTPSnippetsService::StoreSnippetHostsToPrefs( 620 void NTPSnippetsService::StoreSnippetHostsToPrefs(
694 const std::set<std::string>& hosts) { 621 const std::set<std::string>& hosts) {
695 base::ListValue list; 622 base::ListValue list;
696 for (const std::string& host : hosts) 623 for (const std::string& host : hosts)
697 list.AppendString(host); 624 list.AppendString(host);
698 pref_service_->Set(prefs::kSnippetHosts, list); 625 pref_service_->Set(prefs::kSnippetHosts, list);
699 } 626 }
700 627
701 void NTPSnippetsService::ClearExpiredSnippets() { 628 void NTPSnippetsService::ClearExpiredSnippets() {
702 std::vector<Category> categories_to_erase; 629 base::Time expiry = base::Time::Now();
703 630
704 const base::Time expiry = base::Time::Now(); 631 // Move expired snippets over into |to_delete|.
632 NTPSnippet::PtrVector to_delete;
633 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) {
634 if (snippet->expiry_date() <= expiry)
635 to_delete.emplace_back(std::move(snippet));
636 }
637 Compact(&snippets_);
638
639 // Move expired dismissed snippets over into |to_delete| as well.
640 for (std::unique_ptr<NTPSnippet>& snippet : dismissed_snippets_) {
641 if (snippet->expiry_date() <= expiry)
642 to_delete.emplace_back(std::move(snippet));
643 }
644 Compact(&dismissed_snippets_);
645
646 // Finally, actually delete the removed snippets from the DB.
647 database_->DeleteSnippets(to_delete);
648
649 // If there are any snippets left, schedule a timer for the next expiry.
650 if (snippets_.empty() && dismissed_snippets_.empty())
651 return;
652
705 base::Time next_expiry = base::Time::Max(); 653 base::Time next_expiry = base::Time::Max();
706 654 for (const auto& snippet : snippets_) {
707 for (auto& item : categories_) { 655 if (snippet->expiry_date() < next_expiry)
708 Category category = item.first; 656 next_expiry = snippet->expiry_date();
709 CategoryContent* content = &item.second;
710
711 // Move expired snippets over into |to_delete|.
712 NTPSnippet::PtrVector to_delete;
713 for (std::unique_ptr<NTPSnippet>& snippet : content->snippets) {
714 if (snippet->expiry_date() <= expiry)
715 to_delete.emplace_back(std::move(snippet));
716 }
717 Compact(&content->snippets);
718
719 // Move expired dismissed snippets over into |to_delete| as well.
720 for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) {
721 if (snippet->expiry_date() <= expiry)
722 to_delete.emplace_back(std::move(snippet));
723 }
724 Compact(&content->dismissed);
725
726 // Finally, actually delete the removed snippets from the DB.
727 if (category == articles_category_)
728 database_->DeleteSnippets(to_delete);
729
730 if (content->snippets.empty() && content->dismissed.empty()) {
731 if ((category != articles_category_) && !content->provided_by_server)
732 categories_to_erase.push_back(category);
733 continue;
734 }
735
736 for (const auto& snippet : content->snippets) {
737 if (snippet->expiry_date() < next_expiry)
738 next_expiry = snippet->expiry_date();
739 }
740 for (const auto& snippet : content->dismissed) {
741 if (snippet->expiry_date() < next_expiry)
742 next_expiry = snippet->expiry_date();
743 }
744 } 657 }
745 658 for (const auto& snippet : dismissed_snippets_) {
746 for (Category category : categories_to_erase) { 659 if (snippet->expiry_date() < next_expiry)
747 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); 660 next_expiry = snippet->expiry_date();
748 categories_.erase(category);
749 } 661 }
750
751 // Unless there are no snippets left, schedule a timer for the next expiry.
752 DCHECK_GT(next_expiry, expiry); 662 DCHECK_GT(next_expiry, expiry);
753 if (next_expiry < base::Time::Max()) { 663 expiry_timer_.Start(FROM_HERE, next_expiry - expiry,
754 expiry_timer_.Start(FROM_HERE, next_expiry - expiry, 664 base::Bind(&NTPSnippetsService::ClearExpiredSnippets,
755 base::Bind(&NTPSnippetsService::ClearExpiredSnippets, 665 base::Unretained(this)));
756 base::Unretained(this)));
757 }
758 } 666 }
759 667
760 void NTPSnippetsService::NukeAllSnippets() { 668 void NTPSnippetsService::NukeAllSnippets() {
761 std::vector<Category> categories_to_erase; 669 ClearCachedSuggestions(provided_category_);
670 ClearDismissedSuggestionsForDebugging(provided_category_);
762 671
763 // Empty the ARTICLES category and remove all others, since they may or may 672 // Temporarily enter an "explicitly disabled" state, so that any open UIs
764 // not be personalized. 673 // will clear the suggestions too.
765 for (const auto& item : categories_) { 674 if (category_status_ != CategoryStatus::CATEGORY_EXPLICITLY_DISABLED) {
766 Category category = item.first; 675 CategoryStatus old_category_status = category_status_;
767 676 UpdateCategoryStatus(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED);
768 ClearCachedSuggestions(category); 677 UpdateCategoryStatus(old_category_status);
769 ClearDismissedSuggestionsForDebugging(category);
770
771 if (category == articles_category_) {
772 // Temporarily enter an "explicitly disabled" state, so that any open UIs
773 // will clear the suggestions too.
774 CategoryContent& content = categories_[category];
775 if (content.status != CategoryStatus::CATEGORY_EXPLICITLY_DISABLED) {
776 CategoryStatus old_category_status = content.status;
777 UpdateCategoryStatus(category,
778 CategoryStatus::CATEGORY_EXPLICITLY_DISABLED);
779 UpdateCategoryStatus(category, old_category_status);
780 }
781 } else {
782 // Remove other categories entirely; they may or may not reappear.
783 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED);
784 categories_to_erase.push_back(category);
785 }
786 }
787
788 for (Category category : categories_to_erase) {
789 categories_.erase(category);
790 } 678 }
791 } 679 }
792 680
793 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase( 681 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase(
794 const ImageFetchedCallback& callback, 682 const ImageFetchedCallback& callback,
795 const std::string& suggestion_id, 683 const std::string& snippet_id,
796 std::string data) { 684 std::string data) {
797 // |image_decoder_| is null in tests. 685 // |image_decoder_| is null in tests.
798 if (image_decoder_ && !data.empty()) { 686 if (image_decoder_ && !data.empty()) {
799 image_decoder_->DecodeImage( 687 image_decoder_->DecodeImage(
800 std::move(data), 688 std::move(data),
801 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase, 689 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase,
802 base::Unretained(this), callback, suggestion_id)); 690 base::Unretained(this), callback, snippet_id));
803 return; 691 return;
804 } 692 }
805 693
806 // Fetching from the DB failed; start a network fetch. 694 // Fetching from the DB failed; start a network fetch.
807 FetchSnippetImageFromNetwork(suggestion_id, callback); 695 FetchSnippetImageFromNetwork(snippet_id, callback);
808 } 696 }
809 697
810 void NTPSnippetsService::OnSnippetImageDecodedFromDatabase( 698 void NTPSnippetsService::OnSnippetImageDecodedFromDatabase(
811 const ImageFetchedCallback& callback, 699 const ImageFetchedCallback& callback,
812 const std::string& suggestion_id, 700 const std::string& snippet_id,
813 const gfx::Image& image) { 701 const gfx::Image& image) {
814 if (!image.IsEmpty()) { 702 if (!image.IsEmpty()) {
815 callback.Run(suggestion_id, image); 703 callback.Run(MakeUniqueID(provided_category_, snippet_id), image);
816 return; 704 return;
817 } 705 }
818 706
819 // If decoding the image failed, delete the DB entry. 707 // If decoding the image failed, delete the DB entry.
820 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
821 database_->DeleteImage(snippet_id); 708 database_->DeleteImage(snippet_id);
822 709
823 FetchSnippetImageFromNetwork(suggestion_id, callback); 710 FetchSnippetImageFromNetwork(snippet_id, callback);
824 } 711 }
825 712
826 void NTPSnippetsService::FetchSnippetImageFromNetwork( 713 void NTPSnippetsService::FetchSnippetImageFromNetwork(
827 const std::string& suggestion_id, 714 const std::string& snippet_id,
828 const ImageFetchedCallback& callback) { 715 const ImageFetchedCallback& callback) {
829 Category category = GetCategoryFromUniqueID(suggestion_id);
830 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
831
832 auto category_it = categories_.find(category);
833 if (category_it == categories_.end()) {
834 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image());
835 return;
836 }
837
838 const CategoryContent& content = category_it->second;
839 auto it = 716 auto it =
840 std::find_if(content.snippets.begin(), content.snippets.end(), 717 std::find_if(snippets_.begin(), snippets_.end(),
841 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { 718 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) {
842 return snippet->id() == snippet_id; 719 return snippet->id() == snippet_id;
843 }); 720 });
844 721
845 if (it == content.snippets.end() || 722 if (it == snippets_.end() ||
846 !thumbnail_requests_throttler_.DemandQuotaForRequest( 723 !thumbnail_requests_throttler_.DemandQuotaForRequest(
847 /*interactive_request=*/true)) { 724 /*interactive_request=*/true)) {
848 // Return an empty image. Directly, this is never synchronous with the 725 // Return an empty image. Directly, this is never synchronous with the
849 // original FetchSuggestionImage() call - an asynchronous database query has 726 // original FetchSuggestionImage() call - an asynchronous database query has
850 // happened in the meantime. 727 // happened in the meantime.
851 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); 728 OnSnippetImageDecodedFromNetwork(callback, snippet_id, gfx::Image());
852 return; 729 return;
853 } 730 }
854 731
855 const NTPSnippet& snippet = *it->get(); 732 const NTPSnippet& snippet = *it->get();
856 733
857 // TODO(jkrcal): We probably should rename OnImageDataFetched() to 734 // TODO(jkrcal): We probably should rename OnImageDataFetched() to
858 // CacheImageData(). This would document that this is actually independent 735 // CacheImageData(). This would document that this is actually independent
859 // from the individual fetch-flow. 736 // from the individual fetch-flow.
860 // The image fetcher calls OnImageDataFetched() with the raw data (this object 737 // The image fetcher calls OnImageDataFetched() with the raw data (this object
861 // is an ImageFetcherDelegate) and then also 738 // is an ImageFetcherDelegate) and then also
862 // OnSnippetImageDecodedFromNetwork() after the raw data gets decoded. 739 // OnSnippetImageDecodedFromNetwork() after the raw data gets decoded.
863 image_fetcher_->StartOrQueueNetworkRequest( 740 image_fetcher_->StartOrQueueNetworkRequest(
864 suggestion_id, snippet.salient_image_url(), 741 snippet.id(), snippet.salient_image_url(),
865 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork, 742 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork,
866 base::Unretained(this), callback)); 743 base::Unretained(this), callback));
867 } 744 }
868 745
869 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork( 746 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork(
870 const ImageFetchedCallback& callback, 747 const ImageFetchedCallback& callback,
871 const std::string& suggestion_id, 748 const std::string& snippet_id,
872 const gfx::Image& image) { 749 const gfx::Image& image) {
873 callback.Run(suggestion_id, image); 750 callback.Run(MakeUniqueID(provided_category_, snippet_id), image);
874 } 751 }
875 752
876 void NTPSnippetsService::EnterStateEnabled(bool fetch_snippets) { 753 void NTPSnippetsService::EnterStateEnabled(bool fetch_snippets) {
877 if (fetch_snippets) 754 if (fetch_snippets)
878 FetchSnippets(/*force_request=*/false); 755 FetchSnippets(/*force_request=*/false);
879 756
880 // FetchSnippets should set the status to |AVAILABLE_LOADING| if relevant, 757 // FetchSnippets should set the status to |AVAILABLE_LOADING| if relevant,
881 // otherwise we transition to |AVAILABLE| here. 758 // otherwise we transition to |AVAILABLE| here.
882 if (categories_[articles_category_].status != 759 if (category_status_ != CategoryStatus::AVAILABLE_LOADING)
883 CategoryStatus::AVAILABLE_LOADING) { 760 UpdateCategoryStatus(CategoryStatus::AVAILABLE);
884 UpdateCategoryStatus(articles_category_, CategoryStatus::AVAILABLE);
885 }
886 761
887 // If host restrictions are enabled, register for host list updates. 762 // If host restrictions are enabled, register for host list updates.
888 // |suggestions_service_| can be null in tests. 763 // |suggestions_service_| can be null in tests.
889 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) { 764 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) {
890 suggestions_service_subscription_ = 765 suggestions_service_subscription_ =
891 suggestions_service_->AddCallback(base::Bind( 766 suggestions_service_->AddCallback(base::Bind(
892 &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this))); 767 &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this)));
893 } 768 }
894 769
895 RescheduleFetching(); 770 RescheduleFetching();
896 } 771 }
897 772
898 void NTPSnippetsService::EnterStateDisabled() { 773 void NTPSnippetsService::EnterStateDisabled() {
899 std::vector<Category> categories_to_erase; 774 ClearCachedSuggestions(provided_category_);
900 775 ClearDismissedSuggestionsForDebugging(provided_category_);
901 // Empty the ARTICLES category and remove all others, since they may or may
902 // not be personalized.
903 for (const auto& item : categories_) {
904 Category category = item.first;
905 ClearCachedSuggestions(category);
906 ClearDismissedSuggestionsForDebugging(category);
907 if (category != articles_category_) {
908 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED);
909 categories_to_erase.push_back(category);
910 }
911 }
912
913 for (Category category : categories_to_erase) {
914 categories_.erase(category);
915 }
916 776
917 expiry_timer_.Stop(); 777 expiry_timer_.Stop();
918 suggestions_service_subscription_.reset(); 778 suggestions_service_subscription_.reset();
919 RescheduleFetching(); 779 RescheduleFetching();
920 } 780 }
921 781
922 void NTPSnippetsService::EnterStateError() { 782 void NTPSnippetsService::EnterStateError() {
923 expiry_timer_.Stop(); 783 expiry_timer_.Stop();
924 suggestions_service_subscription_.reset(); 784 suggestions_service_subscription_.reset();
925 RescheduleFetching(); 785 RescheduleFetching();
(...skipping 24 matching lines...) Expand all
950 // Always notify here even if we got nothing from the database, because we 810 // Always notify here even if we got nothing from the database, because we
951 // don't know how long the fetch will take or if it will even complete. 811 // don't know how long the fetch will take or if it will even complete.
952 NotifyNewSuggestions(); 812 NotifyNewSuggestions();
953 } 813 }
954 814
955 void NTPSnippetsService::OnDisabledReasonChanged( 815 void NTPSnippetsService::OnDisabledReasonChanged(
956 DisabledReason disabled_reason) { 816 DisabledReason disabled_reason) {
957 switch (disabled_reason) { 817 switch (disabled_reason) {
958 case DisabledReason::NONE: 818 case DisabledReason::NONE:
959 // Do not change the status. That will be done in EnterStateEnabled() 819 // Do not change the status. That will be done in EnterStateEnabled()
960 EnterState(State::READY); 820 EnterState(State::READY, category_status_);
961 break; 821 break;
962 822
963 case DisabledReason::EXPLICITLY_DISABLED: 823 case DisabledReason::EXPLICITLY_DISABLED:
964 EnterState(State::DISABLED); 824 EnterState(State::DISABLED, CategoryStatus::CATEGORY_EXPLICITLY_DISABLED);
965 UpdateAllCategoryStatus(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED);
966 break; 825 break;
967 826
968 case DisabledReason::SIGNED_OUT: 827 case DisabledReason::SIGNED_OUT:
969 EnterState(State::DISABLED); 828 EnterState(State::DISABLED, CategoryStatus::SIGNED_OUT);
970 UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT);
971 break; 829 break;
972 } 830 }
973 } 831 }
974 832
975 void NTPSnippetsService::EnterState(State state) { 833 void NTPSnippetsService::EnterState(State state, CategoryStatus status) {
834 UpdateCategoryStatus(status);
835
976 if (state == state_) 836 if (state == state_)
977 return; 837 return;
978 838
979 switch (state) { 839 switch (state) {
980 case State::NOT_INITED: 840 case State::NOT_INITED:
981 // Initial state, it should not be possible to get back there. 841 // Initial state, it should not be possible to get back there.
982 NOTREACHED(); 842 NOTREACHED();
983 return; 843 return;
984 844
985 case State::READY: { 845 case State::READY: {
986 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED); 846 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED);
987 847
988 bool fetch_snippets = 848 bool fetch_snippets = snippets_.empty() || fetch_after_load_;
989 categories_[articles_category_].snippets.empty() || fetch_after_load_;
990 DVLOG(1) << "Entering state: READY"; 849 DVLOG(1) << "Entering state: READY";
991 state_ = State::READY; 850 state_ = State::READY;
992 fetch_after_load_ = false; 851 fetch_after_load_ = false;
993 EnterStateEnabled(fetch_snippets); 852 EnterStateEnabled(fetch_snippets);
994 return; 853 return;
995 } 854 }
996 855
997 case State::DISABLED: 856 case State::DISABLED:
998 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); 857 DCHECK(state_ == State::NOT_INITED || state_ == State::READY);
999 858
1000 DVLOG(1) << "Entering state: DISABLED"; 859 DVLOG(1) << "Entering state: DISABLED";
1001 state_ = State::DISABLED; 860 state_ = State::DISABLED;
1002 EnterStateDisabled(); 861 EnterStateDisabled();
1003 return; 862 return;
1004 863
1005 case State::ERROR_OCCURRED: 864 case State::ERROR_OCCURRED:
1006 DVLOG(1) << "Entering state: ERROR_OCCURRED"; 865 DVLOG(1) << "Entering state: ERROR_OCCURRED";
1007 state_ = State::ERROR_OCCURRED; 866 state_ = State::ERROR_OCCURRED;
1008 EnterStateError(); 867 EnterStateError();
1009 return; 868 return;
1010 } 869 }
1011 } 870 }
1012 871
1013 void NTPSnippetsService::NotifyNewSuggestions() { 872 void NTPSnippetsService::NotifyNewSuggestions() {
1014 for (const auto& item : categories_) { 873 std::vector<ContentSuggestion> result;
1015 Category category = item.first; 874 for (const std::unique_ptr<NTPSnippet>& snippet : snippets_) {
1016 const CategoryContent& content = item.second; 875 if (!snippet->is_complete())
1017 876 continue;
1018 std::vector<ContentSuggestion> result; 877 ContentSuggestion suggestion(
1019 for (const std::unique_ptr<NTPSnippet>& snippet : content.snippets) { 878 MakeUniqueID(provided_category_, snippet->id()),
1020 // TODO(sfiera): if a snippet is not going to be displayed, move it 879 snippet->best_source().url);
1021 // directly to content.dismissed on fetch. Otherwise, we might prune 880 suggestion.set_amp_url(snippet->best_source().amp_url);
1022 // other snippets to get down to kMaxSnippetCount, only to hide one of the 881 suggestion.set_title(base::UTF8ToUTF16(snippet->title()));
1023 // incomplete ones we kept. 882 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet()));
1024 if (!snippet->is_complete()) 883 suggestion.set_publish_date(snippet->publish_date());
1025 continue; 884 suggestion.set_publisher_name(
1026 ContentSuggestion suggestion(MakeUniqueID(category, snippet->id()), 885 base::UTF8ToUTF16(snippet->best_source().publisher_name));
1027 snippet->best_source().url); 886 suggestion.set_score(snippet->score());
1028 suggestion.set_amp_url(snippet->best_source().amp_url); 887 result.emplace_back(std::move(suggestion));
1029 suggestion.set_title(base::UTF8ToUTF16(snippet->title()));
1030 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet()));
1031 suggestion.set_publish_date(snippet->publish_date());
1032 suggestion.set_publisher_name(
1033 base::UTF8ToUTF16(snippet->best_source().publisher_name));
1034 suggestion.set_score(snippet->score());
1035 result.emplace_back(std::move(suggestion));
1036 }
1037
1038 DVLOG(1) << "NotifyNewSuggestions(): " << result.size()
1039 << " items in category " << category;
1040 observer()->OnNewSuggestions(this, category, std::move(result));
1041 } 888 }
889 observer()->OnNewSuggestions(this, provided_category_, std::move(result));
1042 } 890 }
1043 891
1044 void NTPSnippetsService::UpdateCategoryStatus(Category category, 892 void NTPSnippetsService::UpdateCategoryStatus(CategoryStatus status) {
1045 CategoryStatus status) { 893 if (status == category_status_)
1046 DCHECK(categories_.find(category) != categories_.end());
1047 CategoryContent& content = categories_[category];
1048 if (status == content.status)
1049 return; 894 return;
1050 895
1051 DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": " 896 category_status_ = status;
1052 << static_cast<int>(content.status) << " -> " 897 observer()->OnCategoryStatusChanged(this, provided_category_,
1053 << static_cast<int>(status); 898 category_status_);
1054 content.status = status;
1055 observer()->OnCategoryStatusChanged(this, category, content.status);
1056 } 899 }
1057 900
1058 void NTPSnippetsService::UpdateAllCategoryStatus(CategoryStatus status) {
1059 for (const auto& category : categories_) {
1060 UpdateCategoryStatus(category.first, status);
1061 }
1062 }
1063
1064 NTPSnippetsService::CategoryContent::CategoryContent() = default;
1065 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) =
1066 default;
1067 NTPSnippetsService::CategoryContent::~CategoryContent() = default;
1068 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent::
1069 operator=(CategoryContent&&) = default;
1070
1071 } // namespace ntp_snippets 901 } // namespace ntp_snippets
OLDNEW
« no previous file with comments | « components/ntp_snippets/ntp_snippets_service.h ('k') | components/ntp_snippets/ntp_snippets_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698