Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/ntp_snippets_service.h" | 5 #include "components/ntp_snippets/remote/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 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 150 } | 150 } |
| 151 | 151 |
| 152 void RemoveNullPointers(NTPSnippet::PtrVector* snippets) { | 152 void RemoveNullPointers(NTPSnippet::PtrVector* snippets) { |
| 153 snippets->erase( | 153 snippets->erase( |
| 154 std::remove_if( | 154 std::remove_if( |
| 155 snippets->begin(), snippets->end(), | 155 snippets->begin(), snippets->end(), |
| 156 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), | 156 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), |
| 157 snippets->end()); | 157 snippets->end()); |
| 158 } | 158 } |
| 159 | 159 |
| 160 void AssignExpiryAndPublishDates(NTPSnippet::PtrVector* snippets) { | |
| 161 for (std::unique_ptr<NTPSnippet>& snippet : *snippets) { | |
| 162 if (snippet->publish_date().is_null()) | |
| 163 snippet->set_publish_date(base::Time::Now()); | |
| 164 if (snippet->expiry_date().is_null()) { | |
| 165 snippet->set_expiry_date( | |
| 166 snippet->publish_date() + | |
| 167 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins)); | |
| 168 } | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 void RemoveIncompleteSnippets(NTPSnippet::PtrVector* snippets) { | |
| 173 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 174 switches::kAddIncompleteSnippets)) { | |
| 175 return; | |
| 176 } | |
| 177 int num_snippets = snippets->size(); | |
| 178 // Remove snippets that do not have all the info we need to display it to | |
| 179 // the user. | |
| 180 snippets->erase( | |
| 181 std::remove_if(snippets->begin(), snippets->end(), | |
| 182 [](const std::unique_ptr<NTPSnippet>& snippet) { | |
| 183 return !snippet->is_complete(); | |
| 184 }), | |
| 185 snippets->end()); | |
| 186 int num_snippets_dismissed = num_snippets - snippets->size(); | |
| 187 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", | |
| 188 num_snippets_dismissed > 0); | |
| 189 if (num_snippets_dismissed > 0) { | |
| 190 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", | |
| 191 num_snippets_dismissed); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 NTPSnippetsService::FetchedMoreCallback NullCallback() { | |
| 196 return NTPSnippetsService::FetchedMoreCallback(); | |
|
Marc Treib
2016/10/20 16:51:40
Just inline this below. Also "NTPSnippetsService::
Marc Treib
2016/10/28 14:49:49
Done.
| |
| 197 } | |
| 198 | |
| 160 } // namespace | 199 } // namespace |
| 161 | 200 |
| 162 NTPSnippetsService::NTPSnippetsService( | 201 NTPSnippetsService::NTPSnippetsService( |
| 163 Observer* observer, | 202 Observer* observer, |
| 164 CategoryFactory* category_factory, | 203 CategoryFactory* category_factory, |
| 165 PrefService* pref_service, | 204 PrefService* pref_service, |
| 166 const std::string& application_language_code, | 205 const std::string& application_language_code, |
| 167 const UserClassifier* user_classifier, | 206 const UserClassifier* user_classifier, |
| 168 NTPSnippetsScheduler* scheduler, | 207 NTPSnippetsScheduler* scheduler, |
| 169 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, | 208 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 232 NTPSnippetsStatusService::RegisterProfilePrefs(registry); | 271 NTPSnippetsStatusService::RegisterProfilePrefs(registry); |
| 233 } | 272 } |
| 234 | 273 |
| 235 void NTPSnippetsService::FetchSnippets(bool interactive_request) { | 274 void NTPSnippetsService::FetchSnippets(bool interactive_request) { |
| 236 if (ready()) | 275 if (ready()) |
| 237 FetchSnippetsFromHosts(std::set<std::string>(), interactive_request); | 276 FetchSnippetsFromHosts(std::set<std::string>(), interactive_request); |
| 238 else | 277 else |
| 239 fetch_when_ready_ = true; | 278 fetch_when_ready_ = true; |
| 240 } | 279 } |
| 241 | 280 |
| 242 void NTPSnippetsService::FetchSnippetsFromHosts( | 281 void NTPSnippetsService::FetchSnippetsFromHosts( |
|
Marc Treib
2016/10/20 16:51:40
This doesn't do anything now - might as well remov
Marc Treib
2016/10/28 14:49:49
...buut it's public, and used by the internals pag
| |
| 243 const std::set<std::string>& hosts, | 282 const std::set<std::string>& hosts, |
| 244 bool interactive_request) { | 283 bool interactive_request) { |
| 284 FetchSnippetsFromHostsImpl(hosts, interactive_request, | |
| 285 /*fetch_more=*/false, NullCallback(), | |
| 286 base::Optional<Category>()); | |
| 287 } | |
| 288 | |
| 289 void NTPSnippetsService::FetchMore(const Category& category, | |
| 290 FetchedMoreCallback callback) { | |
| 291 FetchSnippetsFromHostsImpl(std::set<std::string>(), | |
| 292 /*interactive_request=*/true, | |
| 293 /*fetch_more=*/true, callback, | |
| 294 base::Optional<Category>(category)); | |
| 295 } | |
| 296 | |
| 297 void NTPSnippetsService::FetchSnippetsFromHostsImpl( | |
| 298 const std::set<std::string>& hosts, | |
| 299 bool interactive_request, | |
| 300 bool fetch_more, | |
| 301 FetchedMoreCallback callback, | |
| 302 base::Optional<Category> exclusive_category) { | |
| 245 if (!ready()) | 303 if (!ready()) |
| 246 return; | 304 return; |
| 247 | 305 |
| 248 // Empty categories are marked as loading; others are unchanged. | 306 MarkEmptyCategoriesAsLoading(); |
| 307 | |
| 308 // TODO(fhorschig): Refactor parameter to data object or management class. | |
| 309 snippets_fetcher_->FetchSnippetsFromHosts( | |
| 310 hosts, application_language_code_, | |
| 311 std::move(CollectIdsToExclude(fetch_more)), kMaxSnippetCount, | |
|
Marc Treib
2016/10/20 16:51:40
std::move isn't needed here (it's movable anyway)
Marc Treib
2016/10/28 14:49:49
Done.
| |
| 312 interactive_request, | |
| 313 base::BindOnce(&NTPSnippetsService::OnFetchFinished, | |
| 314 base::Unretained(this), fetch_more, callback), | |
| 315 std::move(exclusive_category)); | |
| 316 } | |
| 317 | |
| 318 std::set<std::string> NTPSnippetsService::CollectIdsToExclude(bool fetch_more) { | |
| 319 std::set<std::string> ids; | |
| 320 for (const auto& item : categories_) { | |
| 321 const CategoryContent& content = item.second; | |
| 322 for (const auto& snippet : content.dismissed) | |
| 323 ids.insert(snippet->id()); | |
| 324 if (!fetch_more) | |
| 325 continue; | |
| 326 for (const auto& snippet : content.archived) | |
| 327 ids.insert(snippet->id()); | |
| 328 for (const auto& snippet : content.snippets) | |
| 329 ids.insert(snippet->id()); | |
| 330 } | |
| 331 return ids; | |
| 332 } | |
| 333 | |
| 334 void NTPSnippetsService::MarkEmptyCategoriesAsLoading() { | |
| 249 for (const auto& item : categories_) { | 335 for (const auto& item : categories_) { |
| 250 Category category = item.first; | 336 Category category = item.first; |
| 251 const CategoryContent& content = item.second; | 337 const CategoryContent& content = item.second; |
| 252 if (content.snippets.empty()) | 338 if (content.snippets.empty()) |
| 253 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); | 339 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING); |
| 254 } | 340 } |
| 255 | |
| 256 std::set<std::string> excluded_ids; | |
| 257 for (const auto& item : categories_) { | |
| 258 const CategoryContent& content = item.second; | |
| 259 for (const auto& snippet : content.dismissed) | |
| 260 excluded_ids.insert(snippet->id()); | |
| 261 } | |
| 262 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, | |
| 263 excluded_ids, kMaxSnippetCount, | |
| 264 interactive_request); | |
| 265 } | 341 } |
| 266 | 342 |
| 267 void NTPSnippetsService::RescheduleFetching(bool force) { | 343 void NTPSnippetsService::RescheduleFetching(bool force) { |
| 268 // The scheduler only exists on Android so far, it's null on other platforms. | 344 // The scheduler only exists on Android so far, it's null on other platforms. |
| 269 if (!scheduler_) | 345 if (!scheduler_) |
| 270 return; | 346 return; |
| 271 | 347 |
| 272 if (ready()) { | 348 if (ready()) { |
| 273 base::TimeDelta old_interval_wifi = | 349 base::TimeDelta old_interval_wifi = |
| 274 base::TimeDelta::FromInternalValue(pref_service_->GetInt64( | 350 base::TimeDelta::FromInternalValue(pref_service_->GetInt64( |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 372 if (!base::ContainsKey(categories_, category)) | 448 if (!base::ContainsKey(categories_, category)) |
| 373 return; | 449 return; |
| 374 CategoryContent* content = &categories_[category]; | 450 CategoryContent* content = &categories_[category]; |
| 375 if (content->snippets.empty()) | 451 if (content->snippets.empty()) |
| 376 return; | 452 return; |
| 377 | 453 |
| 378 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); | 454 database_->DeleteSnippets(GetSnippetIDVector(content->snippets)); |
| 379 database_->DeleteImages(GetSnippetIDVector(content->snippets)); | 455 database_->DeleteImages(GetSnippetIDVector(content->snippets)); |
| 380 content->snippets.clear(); | 456 content->snippets.clear(); |
| 381 | 457 |
| 382 NotifyNewSuggestions(); | 458 NotifyNewSuggestions(/*fetched_more=*/false, NullCallback()); |
| 383 } | 459 } |
| 384 | 460 |
| 385 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( | 461 void NTPSnippetsService::GetDismissedSuggestionsForDebugging( |
| 386 Category category, | 462 Category category, |
| 387 const DismissedSuggestionsCallback& callback) { | 463 const DismissedSuggestionsCallback& callback) { |
| 388 DCHECK(base::ContainsKey(categories_, category)); | 464 DCHECK(base::ContainsKey(categories_, category)); |
| 389 | 465 |
| 390 std::vector<ContentSuggestion> result; | 466 std::vector<ContentSuggestion> result; |
| 391 const CategoryContent& content = categories_[category]; | 467 const CategoryContent& content = categories_[category]; |
| 392 for (const std::unique_ptr<NTPSnippet>& snippet : content.dismissed) { | 468 for (const std::unique_ptr<NTPSnippet>& snippet : content.dismissed) { |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 513 ClearOrphanedImages(); | 589 ClearOrphanedImages(); |
| 514 FinishInitialization(); | 590 FinishInitialization(); |
| 515 } | 591 } |
| 516 | 592 |
| 517 void NTPSnippetsService::OnDatabaseError() { | 593 void NTPSnippetsService::OnDatabaseError() { |
| 518 EnterState(State::ERROR_OCCURRED); | 594 EnterState(State::ERROR_OCCURRED); |
| 519 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | 595 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); |
| 520 } | 596 } |
| 521 | 597 |
| 522 void NTPSnippetsService::OnFetchFinished( | 598 void NTPSnippetsService::OnFetchFinished( |
| 599 bool fetched_more, | |
| 600 FetchedMoreCallback fetched_more_callback, | |
| 523 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { | 601 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { |
| 524 if (!ready()) | 602 if (!ready()) |
| 525 return; | 603 return; |
| 526 | 604 |
| 527 // Mark all categories as not provided by the server in the latest fetch. The | 605 // Mark all categories as not provided by the server in the latest fetch. The |
| 528 // ones we got will be marked again below. | 606 // ones we got will be marked again below. |
| 529 for (auto& item : categories_) { | 607 for (auto& item : categories_) { |
| 530 CategoryContent* content = &item.second; | 608 CategoryContent* content = &item.second; |
| 531 content->provided_by_server = false; | 609 content->provided_by_server = false; |
| 532 } | 610 } |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 557 categories_[category].provided_by_server = true; | 635 categories_[category].provided_by_server = true; |
| 558 | 636 |
| 559 // TODO(tschumann): Remove this histogram once we only talk to the content | 637 // TODO(tschumann): Remove this histogram once we only talk to the content |
| 560 // suggestions cloud backend. | 638 // suggestions cloud backend. |
| 561 if (category == articles_category_) { | 639 if (category == articles_category_) { |
| 562 UMA_HISTOGRAM_SPARSE_SLOWLY( | 640 UMA_HISTOGRAM_SPARSE_SLOWLY( |
| 563 "NewTabPage.Snippets.NumArticlesFetched", | 641 "NewTabPage.Snippets.NumArticlesFetched", |
| 564 std::min(fetched_category.snippets.size(), | 642 std::min(fetched_category.snippets.size(), |
| 565 static_cast<size_t>(kMaxSnippetCount + 1))); | 643 static_cast<size_t>(kMaxSnippetCount + 1))); |
| 566 } | 644 } |
| 567 ReplaceSnippets(category, std::move(fetched_category.snippets)); | 645 IncludeSnippets(category, std::move(fetched_category.snippets), |
| 646 /*replace_snippets=*/!fetched_more); | |
| 568 } | 647 } |
| 569 } | 648 } |
| 570 | 649 |
| 571 // We might have gotten new categories (or updated the titles of existing | 650 // We might have gotten new categories (or updated the titles of existing |
| 572 // ones), so update the pref. | 651 // ones), so update the pref. |
| 573 StoreCategoriesToPrefs(); | 652 StoreCategoriesToPrefs(); |
| 574 | 653 |
| 575 for (const auto& item : categories_) { | 654 for (const auto& item : categories_) { |
| 576 Category category = item.first; | 655 Category category = item.first; |
| 577 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); | 656 UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); |
| 578 } | 657 } |
| 579 | 658 |
| 580 // TODO(sfiera): equivalent metrics for non-articles. | 659 // TODO(sfiera): equivalent metrics for non-articles. |
| 581 const CategoryContent& content = categories_[articles_category_]; | 660 const CategoryContent& content = categories_[articles_category_]; |
| 582 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | 661 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", |
| 583 content.snippets.size()); | 662 content.snippets.size()); |
| 584 if (content.snippets.empty() && !content.dismissed.empty()) { | 663 if (content.snippets.empty() && !content.dismissed.empty()) { |
| 585 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 664 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", |
| 586 content.dismissed.size()); | 665 content.dismissed.size()); |
| 587 } | 666 } |
| 588 | 667 |
| 589 // TODO(sfiera): notify only when a category changed above. | 668 // TODO(sfiera): notify only when a category changed above. |
| 590 NotifyNewSuggestions(); | 669 NotifyNewSuggestions(fetched_more, fetched_more_callback); |
| 591 | 670 |
| 592 // Reschedule after a successful fetch. This resets all currently scheduled | 671 // Reschedule after a successful fetch. This resets all currently scheduled |
| 593 // fetches, to make sure the fallback interval triggers only if no wifi fetch | 672 // fetches, to make sure the fallback interval triggers only if no wifi fetch |
| 594 // succeeded, and also that we don't do a background fetch immediately after | 673 // succeeded, and also that we don't do a background fetch immediately after |
| 595 // a user-initiated one. | 674 // a user-initiated one. |
| 596 if (fetched_categories) | 675 if (fetched_categories) |
| 597 RescheduleFetching(true); | 676 RescheduleFetching(true); |
| 598 } | 677 } |
| 599 | 678 |
| 600 void NTPSnippetsService::ArchiveSnippets(Category category, | 679 void NTPSnippetsService::IncludeSnippets(const Category& category, |
| 601 NTPSnippet::PtrVector* to_archive) { | 680 NTPSnippet::PtrVector new_snippets, |
| 602 CategoryContent* content = &categories_[category]; | 681 bool replace_snippets) { |
| 603 database_->DeleteSnippets(GetSnippetIDVector(*to_archive)); | |
| 604 // Do not delete the thumbnail images as they are still handy on open NTPs. | |
| 605 | |
| 606 // Archive previous snippets - move them at the beginning of the list. | |
| 607 content->archived.insert(content->archived.begin(), | |
| 608 std::make_move_iterator(to_archive->begin()), | |
| 609 std::make_move_iterator(to_archive->end())); | |
| 610 RemoveNullPointers(to_archive); | |
| 611 | |
| 612 // If there are more archived snippets than we want to keep, delete the | |
| 613 // oldest ones by their fetch time (which are always in the back). | |
| 614 if (content->archived.size() > kMaxArchivedSnippetCount) { | |
| 615 NTPSnippet::PtrVector to_delete( | |
| 616 std::make_move_iterator(content->archived.begin() + | |
| 617 kMaxArchivedSnippetCount), | |
| 618 std::make_move_iterator(content->archived.end())); | |
| 619 content->archived.resize(kMaxArchivedSnippetCount); | |
| 620 database_->DeleteImages(GetSnippetIDVector(to_delete)); | |
| 621 } | |
| 622 } | |
| 623 | |
| 624 void NTPSnippetsService::ReplaceSnippets(Category category, | |
| 625 NTPSnippet::PtrVector new_snippets) { | |
| 626 DCHECK(ready()); | 682 DCHECK(ready()); |
| 627 CategoryContent* content = &categories_[category]; | 683 CategoryContent* content = &categories_[category]; |
| 628 | 684 |
| 629 // Remove new snippets that have been dismissed. | 685 // Remove new snippets that have been dismissed. |
| 630 EraseMatchingSnippets(&new_snippets, content->dismissed); | 686 EraseMatchingSnippets(&new_snippets, content->dismissed); |
| 631 | 687 |
| 632 // Fill in default publish/expiry dates where required. | 688 AssignExpiryAndPublishDates(&new_snippets); |
| 633 for (std::unique_ptr<NTPSnippet>& snippet : new_snippets) { | 689 RemoveIncompleteSnippets(&new_snippets); |
| 634 if (snippet->publish_date().is_null()) | |
| 635 snippet->set_publish_date(base::Time::Now()); | |
| 636 if (snippet->expiry_date().is_null()) { | |
| 637 snippet->set_expiry_date( | |
| 638 snippet->publish_date() + | |
| 639 base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins)); | |
| 640 } | |
| 641 } | |
| 642 | |
| 643 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 644 switches::kAddIncompleteSnippets)) { | |
| 645 int num_new_snippets = new_snippets.size(); | |
| 646 // Remove snippets that do not have all the info we need to display it to | |
| 647 // the user. | |
| 648 new_snippets.erase( | |
| 649 std::remove_if(new_snippets.begin(), new_snippets.end(), | |
| 650 [](const std::unique_ptr<NTPSnippet>& snippet) { | |
| 651 return !snippet->is_complete(); | |
| 652 }), | |
| 653 new_snippets.end()); | |
| 654 int num_snippets_dismissed = num_new_snippets - new_snippets.size(); | |
| 655 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", | |
| 656 num_snippets_dismissed > 0); | |
| 657 if (num_snippets_dismissed > 0) { | |
| 658 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", | |
| 659 num_snippets_dismissed); | |
| 660 } | |
| 661 } | |
| 662 | 690 |
| 663 // Do not touch the current set of snippets if the newly fetched one is empty. | 691 // Do not touch the current set of snippets if the newly fetched one is empty. |
| 664 if (new_snippets.empty()) | 692 if (new_snippets.empty()) |
| 665 return; | 693 return; |
| 666 | 694 |
| 667 // It's entirely possible that the newly fetched snippets contain articles | 695 // It's entirely possible that the newly fetched snippets contain articles |
| 668 // that have been present before. | 696 // that have been present before. |
| 669 // Since archival removes snippets from the database (indexed by | 697 // Since archival removes snippets from the database (indexed by |
| 670 // snippet->id()), we need to make sure to only archive snippets that don't | 698 // snippet->id()), we need to make sure to only archive snippets that don't |
| 671 // appear with the same ID in the new suggestions (it's fine for additional | 699 // appear with the same ID in the new suggestions (it's fine for additional |
| 672 // IDs though). | 700 // IDs though). |
| 673 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); | 701 EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets)); |
| 674 ArchiveSnippets(category, &content->snippets); | |
| 675 | 702 |
| 676 // Save new articles to the DB. | 703 SaveSnippets(category, std::move(new_snippets), replace_snippets); |
| 704 } | |
| 705 | |
| 706 void NTPSnippetsService::SaveSnippets(const Category& category, | |
|
Marc Treib
2016/10/20 16:51:40
So this is only called once, just above? Then I wo
Marc Treib
2016/10/28 14:49:49
Done.
| |
| 707 NTPSnippet::PtrVector new_snippets, | |
| 708 bool replace_snippets) { | |
| 677 database_->SaveSnippets(new_snippets); | 709 database_->SaveSnippets(new_snippets); |
| 678 | 710 |
| 679 content->snippets = std::move(new_snippets); | 711 CategoryContent* content = &categories_[category]; |
| 712 | |
| 713 if (replace_snippets) { | |
| 714 ArchiveSnippets(category, &content->snippets); | |
| 715 content->snippets = std::move(new_snippets); | |
| 716 return; | |
| 717 } | |
| 718 | |
| 719 content->snippets.insert(content->snippets.end(), | |
| 720 std::make_move_iterator(new_snippets.begin()), | |
| 721 std::make_move_iterator(new_snippets.end())); | |
| 680 } | 722 } |
| 681 | 723 |
| 724 void NTPSnippetsService::ArchiveSnippets(Category category, | |
|
Marc Treib
2016/10/20 16:51:40
Why did you move this? It makes the diff harder to
Marc Treib
2016/10/28 14:49:49
Done. (Moved back)
| |
| 725 NTPSnippet::PtrVector* to_archive) { | |
| 726 CategoryContent* content = &categories_[category]; | |
| 727 database_->DeleteSnippets(GetSnippetIDVector(*to_archive)); | |
| 728 // Do not delete the thumbnail images as they are still handy on open NTPs. | |
| 729 | |
| 730 // Archive previous snippets - move them at the beginning of the list. | |
| 731 content->archived.insert(content->archived.begin(), | |
| 732 std::make_move_iterator(to_archive->begin()), | |
| 733 std::make_move_iterator(to_archive->end())); | |
| 734 RemoveNullPointers(to_archive); | |
| 735 | |
| 736 // If there are more archived snippets than we want to keep, delete the | |
| 737 // oldest ones by their fetch time (which are always in the back). | |
| 738 if (content->archived.size() > kMaxArchivedSnippetCount) { | |
| 739 NTPSnippet::PtrVector to_delete( | |
| 740 std::make_move_iterator(content->archived.begin() + | |
| 741 kMaxArchivedSnippetCount), | |
| 742 std::make_move_iterator(content->archived.end())); | |
| 743 content->archived.resize(kMaxArchivedSnippetCount); | |
| 744 database_->DeleteImages(GetSnippetIDVector(to_delete)); | |
| 745 } | |
| 746 } | |
| 682 void NTPSnippetsService::ClearExpiredDismissedSnippets() { | 747 void NTPSnippetsService::ClearExpiredDismissedSnippets() { |
| 683 std::vector<Category> categories_to_erase; | 748 std::vector<Category> categories_to_erase; |
| 684 | 749 |
| 685 const base::Time now = base::Time::Now(); | 750 const base::Time now = base::Time::Now(); |
| 686 | 751 |
| 687 for (auto& item : categories_) { | 752 for (auto& item : categories_) { |
| 688 Category category = item.first; | 753 Category category = item.first; |
| 689 CategoryContent* content = &item.second; | 754 CategoryContent* content = &item.second; |
| 690 | 755 |
| 691 NTPSnippet::PtrVector to_delete; | 756 NTPSnippet::PtrVector to_delete; |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 855 } | 920 } |
| 856 | 921 |
| 857 void NTPSnippetsService::FinishInitialization() { | 922 void NTPSnippetsService::FinishInitialization() { |
| 858 if (nuke_when_initialized_) { | 923 if (nuke_when_initialized_) { |
| 859 // We nuke here in addition to EnterStateReady, so that it happens even if | 924 // We nuke here in addition to EnterStateReady, so that it happens even if |
| 860 // we enter the DISABLED state below. | 925 // we enter the DISABLED state below. |
| 861 NukeAllSnippets(); | 926 NukeAllSnippets(); |
| 862 nuke_when_initialized_ = false; | 927 nuke_when_initialized_ = false; |
| 863 } | 928 } |
| 864 | 929 |
| 865 snippets_fetcher_->SetCallback( | |
| 866 base::Bind(&NTPSnippetsService::OnFetchFinished, base::Unretained(this))); | |
| 867 | |
| 868 // |image_fetcher_| can be null in tests. | 930 // |image_fetcher_| can be null in tests. |
| 869 if (image_fetcher_) { | 931 if (image_fetcher_) { |
| 870 image_fetcher_->SetImageFetcherDelegate(this); | 932 image_fetcher_->SetImageFetcherDelegate(this); |
| 871 image_fetcher_->SetDataUseServiceName( | 933 image_fetcher_->SetDataUseServiceName( |
| 872 data_use_measurement::DataUseUserData::NTP_SNIPPETS); | 934 data_use_measurement::DataUseUserData::NTP_SNIPPETS); |
| 873 } | 935 } |
| 874 | 936 |
| 875 // Note: Initializing the status service will run the callback right away with | 937 // Note: Initializing the status service will run the callback right away with |
| 876 // the current state. | 938 // the current state. |
| 877 snippets_status_service_->Init(base::Bind( | 939 snippets_status_service_->Init(base::Bind( |
| 878 &NTPSnippetsService::OnSnippetsStatusChanged, base::Unretained(this))); | 940 &NTPSnippetsService::OnSnippetsStatusChanged, base::Unretained(this))); |
| 879 | 941 |
| 880 // Always notify here even if we got nothing from the database, because we | 942 // Always notify here even if we got nothing from the database, because we |
| 881 // don't know how long the fetch will take or if it will even complete. | 943 // don't know how long the fetch will take or if it will even complete. |
| 882 NotifyNewSuggestions(); | 944 NotifyNewSuggestions(/*fetched_more=*/false, NullCallback()); |
| 883 } | 945 } |
| 884 | 946 |
| 885 void NTPSnippetsService::OnSnippetsStatusChanged( | 947 void NTPSnippetsService::OnSnippetsStatusChanged( |
| 886 SnippetsStatus old_snippets_status, | 948 SnippetsStatus old_snippets_status, |
| 887 SnippetsStatus new_snippets_status) { | 949 SnippetsStatus new_snippets_status) { |
| 888 switch (new_snippets_status) { | 950 switch (new_snippets_status) { |
| 889 case SnippetsStatus::ENABLED_AND_SIGNED_IN: | 951 case SnippetsStatus::ENABLED_AND_SIGNED_IN: |
| 890 if (old_snippets_status == SnippetsStatus::ENABLED_AND_SIGNED_OUT) { | 952 if (old_snippets_status == SnippetsStatus::ENABLED_AND_SIGNED_OUT) { |
| 891 DCHECK(state_ == State::READY); | 953 DCHECK(state_ == State::READY); |
| 892 // Clear nonpersonalized suggestions. | 954 // Clear nonpersonalized suggestions. |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 954 DVLOG(1) << "Entering state: ERROR_OCCURRED"; | 1016 DVLOG(1) << "Entering state: ERROR_OCCURRED"; |
| 955 state_ = State::ERROR_OCCURRED; | 1017 state_ = State::ERROR_OCCURRED; |
| 956 EnterStateError(); | 1018 EnterStateError(); |
| 957 break; | 1019 break; |
| 958 } | 1020 } |
| 959 | 1021 |
| 960 // Schedule or un-schedule background fetching after each state change. | 1022 // Schedule or un-schedule background fetching after each state change. |
| 961 RescheduleFetching(false); | 1023 RescheduleFetching(false); |
| 962 } | 1024 } |
| 963 | 1025 |
| 964 void NTPSnippetsService::NotifyNewSuggestions() { | 1026 void NTPSnippetsService::NotifyNewSuggestions( |
|
Marc Treib
2016/10/20 16:51:40
Hm, this now kinda does two totally different thin
Marc Treib
2016/10/28 14:49:49
Done.
| |
| 1027 bool fetched_more, | |
| 1028 FetchedMoreCallback fetched_more_callback) { | |
| 965 for (const auto& item : categories_) { | 1029 for (const auto& item : categories_) { |
| 966 Category category = item.first; | 1030 Category category = item.first; |
| 967 const CategoryContent& content = item.second; | 1031 const CategoryContent& content = item.second; |
| 968 | 1032 |
| 969 std::vector<ContentSuggestion> result; | 1033 std::vector<ContentSuggestion> result; |
| 970 for (const std::unique_ptr<NTPSnippet>& snippet : content.snippets) { | 1034 for (const std::unique_ptr<NTPSnippet>& snippet : content.snippets) { |
| 971 // TODO(sfiera): if a snippet is not going to be displayed, move it | 1035 // TODO(sfiera): if a snippet is not going to be displayed, move it |
| 972 // directly to content.dismissed on fetch. Otherwise, we might prune | 1036 // directly to content.dismissed on fetch. Otherwise, we might prune |
| 973 // other snippets to get down to kMaxSnippetCount, only to hide one of the | 1037 // other snippets to get down to kMaxSnippetCount, only to hide one of the |
| 974 // incomplete ones we kept. | 1038 // incomplete ones we kept. |
| 975 if (!snippet->is_complete()) | 1039 if (!snippet->is_complete()) |
| 976 continue; | 1040 continue; |
| 977 ContentSuggestion suggestion(category, snippet->id(), | 1041 ContentSuggestion suggestion(category, snippet->id(), |
| 978 snippet->best_source().url); | 1042 snippet->best_source().url); |
| 979 suggestion.set_amp_url(snippet->best_source().amp_url); | 1043 suggestion.set_amp_url(snippet->best_source().amp_url); |
| 980 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); | 1044 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); |
| 981 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); | 1045 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); |
| 982 suggestion.set_publish_date(snippet->publish_date()); | 1046 suggestion.set_publish_date(snippet->publish_date()); |
| 983 suggestion.set_publisher_name( | 1047 suggestion.set_publisher_name( |
| 984 base::UTF8ToUTF16(snippet->best_source().publisher_name)); | 1048 base::UTF8ToUTF16(snippet->best_source().publisher_name)); |
| 985 suggestion.set_score(snippet->score()); | 1049 suggestion.set_score(snippet->score()); |
| 986 result.emplace_back(std::move(suggestion)); | 1050 result.emplace_back(std::move(suggestion)); |
| 987 } | 1051 } |
| 988 | 1052 |
| 989 DVLOG(1) << "NotifyNewSuggestions(): " << result.size() | 1053 DVLOG(1) << "NotifyNewSuggestions(/*fetched_more=*/" << fetched_more |
| 990 << " items in category " << category; | 1054 << "): " << result.size() << " items in category " << category; |
| 991 observer()->OnNewSuggestions(this, category, std::move(result)); | 1055 if (fetched_more) { |
| 1056 DCHECK(!fetched_more_callback.is_null()); | |
| 1057 fetched_more_callback.Run(std::move(result)); | |
| 1058 } else { | |
| 1059 observer()->OnNewSuggestions(this, category, std::move(result)); | |
| 1060 } | |
| 992 } | 1061 } |
| 993 } | 1062 } |
| 994 | 1063 |
| 995 void NTPSnippetsService::UpdateCategoryStatus(Category category, | 1064 void NTPSnippetsService::UpdateCategoryStatus(Category category, |
| 996 CategoryStatus status) { | 1065 CategoryStatus status) { |
| 997 DCHECK(base::ContainsKey(categories_, category)); | 1066 DCHECK(base::ContainsKey(categories_, category)); |
| 998 CategoryContent& content = categories_[category]; | 1067 CategoryContent& content = categories_[category]; |
| 999 if (status == content.status) | 1068 if (status == content.status) |
| 1000 return; | 1069 return; |
| 1001 | 1070 |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1102 } | 1171 } |
| 1103 | 1172 |
| 1104 NTPSnippetsService::CategoryContent::CategoryContent() = default; | 1173 NTPSnippetsService::CategoryContent::CategoryContent() = default; |
| 1105 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = | 1174 NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = |
| 1106 default; | 1175 default; |
| 1107 NTPSnippetsService::CategoryContent::~CategoryContent() = default; | 1176 NTPSnippetsService::CategoryContent::~CategoryContent() = default; |
| 1108 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: | 1177 NTPSnippetsService::CategoryContent& NTPSnippetsService::CategoryContent:: |
| 1109 operator=(CategoryContent&&) = default; | 1178 operator=(CategoryContent&&) = default; |
| 1110 | 1179 |
| 1111 } // namespace ntp_snippets | 1180 } // namespace ntp_snippets |
| OLD | NEW |