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/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 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 193 SuggestionsService* suggestions_service, | 193 SuggestionsService* suggestions_service, |
| 194 const std::string& application_language_code, | 194 const std::string& application_language_code, |
| 195 NTPSnippetsScheduler* scheduler, | 195 NTPSnippetsScheduler* scheduler, |
| 196 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, | 196 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, |
| 197 std::unique_ptr<ImageFetcher> image_fetcher, | 197 std::unique_ptr<ImageFetcher> image_fetcher, |
| 198 std::unique_ptr<ImageDecoder> image_decoder, | 198 std::unique_ptr<ImageDecoder> image_decoder, |
| 199 std::unique_ptr<NTPSnippetsDatabase> database, | 199 std::unique_ptr<NTPSnippetsDatabase> database, |
| 200 std::unique_ptr<NTPSnippetsStatusService> status_service) | 200 std::unique_ptr<NTPSnippetsStatusService> status_service) |
| 201 : ContentSuggestionsProvider(observer, category_factory), | 201 : ContentSuggestionsProvider(observer, category_factory), |
| 202 state_(State::NOT_INITED), | 202 state_(State::NOT_INITED), |
| 203 category_status_(CategoryStatus::INITIALIZING), | |
| 204 pref_service_(pref_service), | 203 pref_service_(pref_service), |
| 205 suggestions_service_(suggestions_service), | 204 suggestions_service_(suggestions_service), |
| 205 articles_category_( | |
| 206 category_factory->FromKnownCategory(KnownCategories::ARTICLES)), | |
| 206 application_language_code_(application_language_code), | 207 application_language_code_(application_language_code), |
| 207 scheduler_(scheduler), | 208 scheduler_(scheduler), |
| 208 snippets_fetcher_(std::move(snippets_fetcher)), | 209 snippets_fetcher_(std::move(snippets_fetcher)), |
| 209 image_fetcher_(std::move(image_fetcher)), | 210 image_fetcher_(std::move(image_fetcher)), |
| 210 image_decoder_(std::move(image_decoder)), | 211 image_decoder_(std::move(image_decoder)), |
| 211 database_(std::move(database)), | 212 database_(std::move(database)), |
| 212 snippets_status_service_(std::move(status_service)), | 213 snippets_status_service_(std::move(status_service)), |
| 213 fetch_after_load_(false), | 214 fetch_after_load_(false), |
| 214 provided_category_( | |
| 215 category_factory->FromKnownCategory(KnownCategories::ARTICLES)), | |
| 216 thumbnail_requests_throttler_( | 215 thumbnail_requests_throttler_( |
| 217 pref_service, | 216 pref_service, |
| 218 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) { | 217 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) { |
| 219 observer->OnCategoryStatusChanged(this, provided_category_, category_status_); | 218 // Articles category always exists; others will be added as needed. |
| 219 categories_.emplace(articles_category_, CategoryContent()); | |
|
Marc Treib
2016/08/22 15:06:46
map::emplace isn't available everywhere yet, see h
sfiera
2016/08/24 14:35:56
Done.
| |
| 220 observer->OnCategoryStatusChanged(this, articles_category_, | |
| 221 CategoryStatus::INITIALIZING); | |
|
Marc Treib
2016/08/22 15:06:47
Use the actual value from categories_, so the defa
sfiera
2016/08/24 14:35:56
Done.
| |
| 220 if (database_->IsErrorState()) { | 222 if (database_->IsErrorState()) { |
| 221 EnterState(State::ERROR_OCCURRED, CategoryStatus::LOADING_ERROR); | 223 EnterState(State::ERROR_OCCURRED); |
| 224 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | |
| 222 return; | 225 return; |
| 223 } | 226 } |
| 224 | 227 |
| 225 database_->SetErrorCallback(base::Bind(&NTPSnippetsService::OnDatabaseError, | 228 database_->SetErrorCallback(base::Bind(&NTPSnippetsService::OnDatabaseError, |
| 226 base::Unretained(this))); | 229 base::Unretained(this))); |
| 227 | 230 |
| 228 // We transition to other states while finalizing the initialization, when the | 231 // We transition to other states while finalizing the initialization, when the |
| 229 // database is done loading. | 232 // database is done loading. |
| 230 database_->LoadSnippets(base::Bind(&NTPSnippetsService::OnDatabaseLoaded, | 233 database_->LoadSnippets(base::Bind(&NTPSnippetsService::OnDatabaseLoaded, |
| 231 base::Unretained(this))); | 234 base::Unretained(this))); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 247 else | 250 else |
| 248 fetch_after_load_ = true; | 251 fetch_after_load_ = true; |
| 249 } | 252 } |
| 250 | 253 |
| 251 void NTPSnippetsService::FetchSnippetsFromHosts( | 254 void NTPSnippetsService::FetchSnippetsFromHosts( |
| 252 const std::set<std::string>& hosts, | 255 const std::set<std::string>& hosts, |
| 253 bool interactive_request) { | 256 bool interactive_request) { |
| 254 if (!ready()) | 257 if (!ready()) |
| 255 return; | 258 return; |
| 256 | 259 |
| 257 if (snippets_.empty()) | 260 // Empty categories are marked as loading; others are unchanged. |
| 258 UpdateCategoryStatus(CategoryStatus::AVAILABLE_LOADING); | 261 for (const auto& category : categories_) { |
|
Marc Treib
2016/08/22 15:06:47
Use the actual type? auto isn't clear here, since
sfiera
2016/08/24 14:35:56
I've switched this to what I do in most places, wh
| |
| 262 if (category.second.snippets.empty()) | |
| 263 UpdateCategoryStatus(category.first, CategoryStatus::AVAILABLE_LOADING); | |
| 264 } | |
| 259 | 265 |
| 260 snippets_fetcher_->FetchSnippetsFromHosts( | 266 snippets_fetcher_->FetchSnippetsFromHosts( |
| 261 hosts, application_language_code_, kMaxSnippetCount, interactive_request); | 267 hosts, application_language_code_, kMaxSnippetCount, interactive_request); |
| 262 } | 268 } |
| 263 | 269 |
| 264 void NTPSnippetsService::RescheduleFetching() { | 270 void NTPSnippetsService::RescheduleFetching() { |
| 265 // The scheduler only exists on Android so far, it's null on other platforms. | 271 // The scheduler only exists on Android so far, it's null on other platforms. |
| 266 if (!scheduler_) | 272 if (!scheduler_) |
| 267 return; | 273 return; |
| 268 | 274 |
| 269 if (ready()) { | 275 if (ready()) { |
| 270 base::Time now = base::Time::Now(); | 276 base::Time now = base::Time::Now(); |
| 271 scheduler_->Schedule( | 277 scheduler_->Schedule( |
| 272 GetFetchingIntervalWifiCharging(), GetFetchingIntervalWifi(now), | 278 GetFetchingIntervalWifiCharging(), GetFetchingIntervalWifi(now), |
| 273 GetFetchingIntervalFallback(), GetRescheduleTime(now)); | 279 GetFetchingIntervalFallback(), GetRescheduleTime(now)); |
| 274 } else { | 280 } else { |
| 275 scheduler_->Unschedule(); | 281 scheduler_->Unschedule(); |
| 276 } | 282 } |
| 277 } | 283 } |
| 278 | 284 |
| 279 CategoryStatus NTPSnippetsService::GetCategoryStatus(Category category) { | 285 CategoryStatus NTPSnippetsService::GetCategoryStatus(Category category) { |
| 280 DCHECK(category.IsKnownCategory(KnownCategories::ARTICLES)); | 286 DCHECK(categories_.find(category) != categories_.end()); |
| 281 return category_status_; | 287 return categories_[category].status; |
| 282 } | 288 } |
| 283 | 289 |
| 284 CategoryInfo NTPSnippetsService::GetCategoryInfo(Category category) { | 290 CategoryInfo NTPSnippetsService::GetCategoryInfo(Category category) { |
| 291 DCHECK(categories_.find(category) != categories_.end()); | |
| 292 // TODO(sfiera): pass back localized category titles. | |
|
Marc Treib
2016/08/22 15:06:47
nit: mention that this refers to non-articles cate
sfiera
2016/08/24 14:35:56
Changed.
| |
| 285 return CategoryInfo( | 293 return CategoryInfo( |
| 286 l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER), | 294 l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER), |
| 287 ContentSuggestionsCardLayout::FULL_CARD, | 295 ContentSuggestionsCardLayout::FULL_CARD, |
| 288 /* has_more_button */ false, | 296 /* has_more_button */ false, |
| 289 /* show_if_empty */ true); | 297 /* show_if_empty */ true); |
| 290 } | 298 } |
| 291 | 299 |
| 292 void NTPSnippetsService::DismissSuggestion(const std::string& suggestion_id) { | 300 void NTPSnippetsService::DismissSuggestion(const std::string& suggestion_id) { |
| 293 if (!ready()) | 301 if (!ready()) |
| 294 return; | 302 return; |
| 295 | 303 |
| 304 Category category_id = GetCategoryFromUniqueID(suggestion_id); | |
|
Marc Treib
2016/08/22 15:06:47
Just "category" please (that's what it's called ev
sfiera
2016/08/24 14:35:56
Done.
| |
| 296 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | 305 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); |
| 297 | 306 |
| 307 DCHECK(categories_.find(category_id) != categories_.end()); | |
| 308 | |
| 309 CategoryContent* category = &categories_[category_id]; | |
|
Marc Treib
2016/08/22 15:06:46
call this something else, like "content"
sfiera
2016/08/24 14:35:56
Done.
| |
| 298 auto it = | 310 auto it = |
| 299 std::find_if(snippets_.begin(), snippets_.end(), | 311 std::find_if(category->snippets.begin(), category->snippets.end(), |
| 300 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | 312 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { |
| 301 return snippet->id() == snippet_id; | 313 return snippet->id() == snippet_id; |
| 302 }); | 314 }); |
| 303 if (it == snippets_.end()) | 315 if (it == category->snippets.end()) |
| 304 return; | 316 return; |
| 305 | 317 |
| 306 (*it)->set_dismissed(true); | 318 (*it)->set_dismissed(true); |
| 307 | 319 |
| 308 database_->SaveSnippet(**it); | 320 database_->SaveSnippet(**it); |
| 309 database_->DeleteImage((*it)->id()); | 321 database_->DeleteImage((*it)->id()); |
| 310 | 322 |
| 311 dismissed_snippets_.push_back(std::move(*it)); | 323 category->dismissed.push_back(std::move(*it)); |
| 312 snippets_.erase(it); | 324 category->snippets.erase(it); |
| 313 } | 325 } |
| 314 | 326 |
| 315 void NTPSnippetsService::FetchSuggestionImage( | 327 void NTPSnippetsService::FetchSuggestionImage( |
| 316 const std::string& suggestion_id, | 328 const std::string& suggestion_id, |
| 317 const ImageFetchedCallback& callback) { | 329 const ImageFetchedCallback& callback) { |
| 318 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | |
| 319 database_->LoadImage( | 330 database_->LoadImage( |
| 320 snippet_id, | 331 suggestion_id, |
| 321 base::Bind(&NTPSnippetsService::OnSnippetImageFetchedFromDatabase, | 332 base::Bind(&NTPSnippetsService::OnSnippetImageFetchedFromDatabase, |
| 322 base::Unretained(this), callback, snippet_id)); | 333 base::Unretained(this), callback, suggestion_id)); |
| 323 } | 334 } |
| 324 | 335 |
| 325 void NTPSnippetsService::ClearCachedSuggestionsForDebugging(Category category) { | 336 void NTPSnippetsService::ClearCachedSuggestionsForDebugging( |
| 326 DCHECK_EQ(category, provided_category_); | 337 Category category_id) { |
| 327 if (!initialized()) | 338 if (!initialized()) |
| 328 return; | 339 return; |
| 329 | 340 |
| 330 if (snippets_.empty()) | 341 if (categories_.find(category_id) == categories_.end()) |
| 342 return; | |
| 343 CategoryContent* category = &categories_[category_id]; | |
| 344 if (category->snippets.empty()) | |
| 331 return; | 345 return; |
| 332 | 346 |
| 333 database_->DeleteSnippets(snippets_); | 347 if (category_id == articles_category_) |
| 334 snippets_.clear(); | 348 database_->DeleteSnippets(category->snippets); |
| 349 category->snippets.clear(); | |
| 335 | 350 |
| 336 NotifyNewSuggestions(); | 351 NotifyNewSuggestions(); |
| 337 } | 352 } |
| 338 | 353 |
| 339 std::vector<ContentSuggestion> | 354 std::vector<ContentSuggestion> |
| 340 NTPSnippetsService::GetDismissedSuggestionsForDebugging(Category category) { | 355 NTPSnippetsService::GetDismissedSuggestionsForDebugging(Category category_id) { |
| 341 DCHECK_EQ(category, provided_category_); | 356 DCHECK(categories_.find(category_id) != categories_.end()); |
| 357 | |
| 342 std::vector<ContentSuggestion> result; | 358 std::vector<ContentSuggestion> result; |
| 343 for (const std::unique_ptr<NTPSnippet>& snippet : dismissed_snippets_) { | 359 const CategoryContent& category = categories_[category_id]; |
| 360 for (const std::unique_ptr<NTPSnippet>& snippet : category.dismissed) { | |
| 344 if (!snippet->is_complete()) | 361 if (!snippet->is_complete()) |
| 345 continue; | 362 continue; |
| 346 ContentSuggestion suggestion( | 363 ContentSuggestion suggestion(MakeUniqueID(category_id, snippet->id()), |
| 347 MakeUniqueID(provided_category_, snippet->id()), | 364 snippet->best_source().url); |
| 348 snippet->best_source().url); | |
| 349 suggestion.set_amp_url(snippet->best_source().amp_url); | 365 suggestion.set_amp_url(snippet->best_source().amp_url); |
| 350 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); | 366 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); |
| 351 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); | 367 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); |
| 352 suggestion.set_publish_date(snippet->publish_date()); | 368 suggestion.set_publish_date(snippet->publish_date()); |
| 353 suggestion.set_publisher_name( | 369 suggestion.set_publisher_name( |
| 354 base::UTF8ToUTF16(snippet->best_source().publisher_name)); | 370 base::UTF8ToUTF16(snippet->best_source().publisher_name)); |
| 355 suggestion.set_score(snippet->score()); | 371 suggestion.set_score(snippet->score()); |
| 356 result.emplace_back(std::move(suggestion)); | 372 result.emplace_back(std::move(suggestion)); |
| 357 } | 373 } |
| 358 return result; | 374 return result; |
| 359 } | 375 } |
| 360 | 376 |
| 361 void NTPSnippetsService::ClearDismissedSuggestionsForDebugging( | 377 void NTPSnippetsService::ClearDismissedSuggestionsForDebugging( |
| 362 Category category) { | 378 Category category_id) { |
| 363 DCHECK_EQ(category, provided_category_); | 379 DCHECK(categories_.find(category_id) != categories_.end()); |
| 380 | |
| 364 if (!initialized()) | 381 if (!initialized()) |
| 365 return; | 382 return; |
| 366 | 383 |
| 367 if (dismissed_snippets_.empty()) | 384 CategoryContent* category = &categories_[category_id]; |
| 385 if (category->dismissed.empty()) | |
| 368 return; | 386 return; |
| 369 | 387 |
| 370 database_->DeleteSnippets(dismissed_snippets_); | 388 if (category_id == articles_category_) |
| 371 dismissed_snippets_.clear(); | 389 database_->DeleteSnippets(category->dismissed); |
| 390 category->dismissed.clear(); | |
| 372 } | 391 } |
| 373 | 392 |
| 374 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { | 393 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { |
| 375 // |suggestions_service_| can be null in tests. | 394 // |suggestions_service_| can be null in tests. |
| 376 if (!suggestions_service_) | 395 if (!suggestions_service_) |
| 377 return std::set<std::string>(); | 396 return std::set<std::string>(); |
| 378 | 397 |
| 379 // TODO(treib): This should just call GetSnippetHostsFromPrefs. | 398 // TODO(treib): This should just call GetSnippetHostsFromPrefs. |
| 380 return GetSuggestionsHostsImpl( | 399 return GetSuggestionsHostsImpl( |
| 381 suggestions_service_->GetSuggestionsDataFromCache()); | 400 suggestions_service_->GetSuggestionsDataFromCache()); |
| 382 } | 401 } |
| 383 | 402 |
| 384 // static | 403 // static |
| 385 int NTPSnippetsService::GetMaxSnippetCountForTesting() { | 404 int NTPSnippetsService::GetMaxSnippetCountForTesting() { |
| 386 return kMaxSnippetCount; | 405 return kMaxSnippetCount; |
| 387 } | 406 } |
| 388 | 407 |
| 389 //////////////////////////////////////////////////////////////////////////////// | 408 //////////////////////////////////////////////////////////////////////////////// |
| 390 // Private methods | 409 // Private methods |
| 391 | 410 |
| 392 // image_fetcher::ImageFetcherDelegate implementation. | 411 // image_fetcher::ImageFetcherDelegate implementation. |
| 393 void NTPSnippetsService::OnImageDataFetched(const std::string& snippet_id, | 412 void NTPSnippetsService::OnImageDataFetched(const std::string& suggestion_id, |
| 394 const std::string& image_data) { | 413 const std::string& image_data) { |
| 395 if (image_data.empty()) | 414 if (image_data.empty()) |
| 396 return; | 415 return; |
| 397 | 416 |
| 417 Category category_id = GetCategoryFromUniqueID(suggestion_id); | |
| 418 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | |
| 419 | |
| 420 auto category_it = categories_.find(category_id); | |
| 421 if (category_it == categories_.end()) | |
| 422 return; | |
| 423 | |
| 424 const CategoryContent& category = category_it->second; | |
| 425 | |
| 398 // Only save the image if the corresponding snippet still exists. | 426 // Only save the image if the corresponding snippet still exists. |
| 399 auto it = | 427 auto it = |
| 400 std::find_if(snippets_.begin(), snippets_.end(), | 428 std::find_if(category.snippets.begin(), category.snippets.end(), |
| 401 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | 429 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { |
| 402 return snippet->id() == snippet_id; | 430 return snippet->id() == snippet_id; |
| 403 }); | 431 }); |
| 404 if (it == snippets_.end()) | 432 if (it == category.snippets.end()) |
| 405 return; | 433 return; |
| 406 | 434 |
| 407 database_->SaveImage(snippet_id, image_data); | 435 database_->SaveImage(suggestion_id, image_data); |
| 408 } | 436 } |
| 409 | 437 |
| 410 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { | 438 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { |
| 411 if (state_ == State::ERROR_OCCURRED) | 439 if (state_ == State::ERROR_OCCURRED) |
| 412 return; | 440 return; |
| 413 DCHECK(state_ == State::NOT_INITED); | 441 DCHECK(state_ == State::NOT_INITED); |
| 414 DCHECK(snippets_.empty()); | 442 DCHECK(categories_.size() == 1); // Only articles category, so far. |
| 415 DCHECK(dismissed_snippets_.empty()); | 443 DCHECK(categories_.find(articles_category_) != categories_.end()); |
| 444 | |
| 445 // TODO(sfiera): support non-article categories in database. | |
| 446 CategoryContent* category = &categories_[articles_category_]; | |
| 416 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { | 447 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { |
| 417 if (snippet->is_dismissed()) | 448 if (snippet->is_dismissed()) |
| 418 dismissed_snippets_.emplace_back(std::move(snippet)); | 449 category->dismissed.emplace_back(std::move(snippet)); |
| 419 else | 450 else |
| 420 snippets_.emplace_back(std::move(snippet)); | 451 category->snippets.emplace_back(std::move(snippet)); |
| 421 } | 452 } |
| 422 std::sort(snippets_.begin(), snippets_.end(), | 453 |
| 454 std::sort(category->snippets.begin(), category->snippets.end(), | |
| 423 [](const std::unique_ptr<NTPSnippet>& lhs, | 455 [](const std::unique_ptr<NTPSnippet>& lhs, |
| 424 const std::unique_ptr<NTPSnippet>& rhs) { | 456 const std::unique_ptr<NTPSnippet>& rhs) { |
| 425 return lhs->score() > rhs->score(); | 457 return lhs->score() > rhs->score(); |
| 426 }); | 458 }); |
| 427 | 459 |
| 428 ClearExpiredSnippets(); | 460 ClearExpiredSnippets(articles_category_); |
| 429 FinishInitialization(); | 461 FinishInitialization(); |
| 430 } | 462 } |
| 431 | 463 |
| 432 void NTPSnippetsService::OnDatabaseError() { | 464 void NTPSnippetsService::OnDatabaseError() { |
| 433 EnterState(State::ERROR_OCCURRED, CategoryStatus::LOADING_ERROR); | 465 EnterState(State::ERROR_OCCURRED); |
| 466 UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR); | |
| 434 } | 467 } |
| 435 | 468 |
| 436 // TODO(dgn): name clash between content suggestions and suggestions hosts. | 469 // TODO(dgn): name clash between content suggestions and suggestions hosts. |
| 437 // method name should be changed. | 470 // method name should be changed. |
| 438 void NTPSnippetsService::OnSuggestionsChanged( | 471 void NTPSnippetsService::OnSuggestionsChanged( |
| 439 const SuggestionsProfile& suggestions) { | 472 const SuggestionsProfile& suggestions) { |
| 440 DCHECK(initialized()); | 473 DCHECK(initialized()); |
| 441 | 474 |
| 442 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); | 475 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); |
| 443 if (hosts == GetSnippetHostsFromPrefs()) | 476 if (hosts == GetSnippetHostsFromPrefs()) |
| 444 return; | 477 return; |
| 445 | 478 |
| 446 // Remove existing snippets that aren't in the suggestions anymore. | 479 // Remove existing snippets that aren't in the suggestions anymore. |
| 480 // | |
| 447 // TODO(treib,maybelle): If there is another source with an allowed host, | 481 // TODO(treib,maybelle): If there is another source with an allowed host, |
| 448 // then we should fall back to that. | 482 // then we should fall back to that. |
| 449 // First, move them over into |to_delete|. | 483 // First, move them over into |to_delete|. |
|
Marc Treib
2016/08/22 15:06:46
This line isn't part of the TODO; move it to the e
sfiera
2016/08/24 14:35:56
Done.
| |
| 484 // | |
| 485 // TODO(sfiera): determine when non-article categories should restrict hosts, | |
| 486 // and apply the same logic to them here. Maybe never? | |
|
Marc Treib
2016/08/22 15:06:47
Eh, since host restricts are likely going away any
sfiera
2016/08/24 14:35:56
Acknowledged.
| |
| 487 CategoryContent* category = &categories_[articles_category_]; | |
| 450 NTPSnippet::PtrVector to_delete; | 488 NTPSnippet::PtrVector to_delete; |
| 451 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) { | 489 for (std::unique_ptr<NTPSnippet>& snippet : category->snippets) { |
| 452 if (!hosts.count(snippet->best_source().url.host())) | 490 if (!hosts.count(snippet->best_source().url.host())) |
| 453 to_delete.emplace_back(std::move(snippet)); | 491 to_delete.emplace_back(std::move(snippet)); |
| 454 } | 492 } |
| 455 Compact(&snippets_); | 493 Compact(&category->snippets); |
| 456 // Then delete the removed snippets from the database. | 494 // Then delete the removed snippets from the database. |
| 457 database_->DeleteSnippets(to_delete); | 495 database_->DeleteSnippets(to_delete); |
| 458 | 496 |
| 459 StoreSnippetHostsToPrefs(hosts); | 497 StoreSnippetHostsToPrefs(hosts); |
| 460 | 498 |
| 461 // We removed some suggestions, so we want to let the client know about that. | 499 // We removed some suggestions, so we want to let the client know about that. |
| 462 // The fetch might take a long time or not complete so we don't want to wait | 500 // The fetch might take a long time or not complete so we don't want to wait |
| 463 // for its callback. | 501 // for its callback. |
| 464 NotifyNewSuggestions(); | 502 NotifyNewSuggestions(); |
| 465 | 503 |
| 466 FetchSnippetsFromHosts(hosts, /*force_request=*/false); | 504 FetchSnippetsFromHosts(hosts, /*force_request=*/false); |
| 467 } | 505 } |
| 468 | 506 |
| 469 void NTPSnippetsService::OnFetchFinished( | 507 void NTPSnippetsService::OnFetchFinished( |
| 470 NTPSnippetsFetcher::OptionalSnippets snippets) { | 508 NTPSnippetsFetcher::OptionalSnippets snippets) { |
| 471 if (!ready()) | 509 if (!ready()) |
| 472 return; | 510 return; |
| 473 | 511 |
| 474 DCHECK(category_status_ == CategoryStatus::AVAILABLE || | 512 // If snippets were fetched successfully, update our |categories_| from each |
| 475 category_status_ == CategoryStatus::AVAILABLE_LOADING); | 513 // source and report the category as AVAILABLE. |
| 514 if (snippets) { | |
| 515 for (std::pair<const Category, NTPSnippet::PtrVector>& item : *snippets) { | |
| 516 Category category_id = item.first; | |
| 517 NTPSnippet::PtrVector& new_snippets = item.second; | |
| 518 // Sparse histogram used because the number of snippets is small (bound by | |
| 519 // kMaxSnippetCount). | |
| 520 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); | |
| 521 // TODO(sfiera): per-category histograms | |
| 522 if (category_id == articles_category_) | |
|
Marc Treib
2016/08/22 15:06:47
Braces please
sfiera
2016/08/24 14:35:56
Done.
| |
| 523 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", | |
| 524 new_snippets.size()); | |
| 476 | 525 |
| 477 // TODO(sfiera): support more than just the provided_category_ ARTICLES. | 526 MergeSnippets(category_id, std::move(new_snippets)); |
| 478 if (snippets && (snippets->find(provided_category_) != snippets->end())) { | 527 ClearExpiredSnippets(category_id); |
| 479 // Sparse histogram used because the number of snippets is small (bound by | 528 // If there are more snippets than we want to show, delete the extra ones. |
| 480 // kMaxSnippetCount). | 529 CategoryContent* category = &categories_[category_id]; |
| 481 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); | 530 if (category->snippets.size() > kMaxSnippetCount) { |
| 482 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", | 531 NTPSnippet::PtrVector to_delete( |
| 483 (*snippets)[provided_category_].size()); | 532 std::make_move_iterator(category->snippets.begin() + |
| 484 MergeSnippets(std::move((*snippets)[provided_category_])); | 533 kMaxSnippetCount), |
| 534 std::make_move_iterator(category->snippets.end())); | |
| 535 category->snippets.resize(kMaxSnippetCount); | |
| 536 database_->DeleteSnippets(to_delete); | |
| 537 } | |
| 538 | |
| 539 // Report category as available, even if empty. It is provided by the | |
| 540 // service, and is no longer loading. | |
| 541 UpdateCategoryStatus(category_id, CategoryStatus::AVAILABLE); | |
| 542 } | |
| 485 } | 543 } |
| 486 | 544 |
| 487 ClearExpiredSnippets(); | 545 // Go back and expire articles out of categories that weren't updated. If |
| 546 // |snippets| was not provided due to an error, that's all categories. | |
| 547 std::vector<Category> categories_to_remove_; | |
| 548 for (auto& item : categories_) { | |
| 549 Category category_id = item.first; | |
| 550 CategoryContent& category = item.second; | |
| 551 if (!snippets || (snippets->find(category_id) == snippets->end())) | |
| 552 continue; // skip anything we processed in the first loop. | |
|
Marc Treib
2016/08/22 15:06:46
Why?
I think the main purpose of calling ClearExpi
sfiera
2016/08/24 14:35:56
I'd sort of misunderstood ClearExpiredSnippets(),
| |
| 488 | 553 |
| 489 // If there are more snippets than we want to show, delete the extra ones. | 554 ClearExpiredSnippets(category_id); |
| 490 if (snippets_.size() > kMaxSnippetCount) { | 555 if (category.snippets.empty() && (category_id != articles_category_)) { |
| 491 NTPSnippet::PtrVector to_delete( | 556 categories_to_remove_.push_back(category_id); |
| 492 std::make_move_iterator(snippets_.begin() + kMaxSnippetCount), | 557 } |
| 493 std::make_move_iterator(snippets_.end())); | |
| 494 snippets_.resize(kMaxSnippetCount); | |
| 495 database_->DeleteSnippets(to_delete); | |
| 496 } | 558 } |
| 497 | 559 |
| 498 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | 560 // If a category (other than ARTICLES) is now empty, and it was not present in |
| 499 snippets_.size()); | 561 // |snippets|, then report that the snippets service no longer provides that |
| 500 if (snippets_.empty() && !dismissed_snippets_.empty()) { | 562 // category. If the category is now |empty| but it was in |snippets|, then we |
|
Marc Treib
2016/08/22 15:06:46
nit: no pipes around empty
sfiera
2016/08/24 14:35:57
Done.
| |
| 501 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 563 // will continue to report it as AVAILABLE. |
| 502 dismissed_snippets_.size()); | 564 for (Category category : categories_to_remove_) { |
| 565 UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED); | |
| 566 categories_.erase(category); | |
| 503 } | 567 } |
| 504 | 568 |
| 505 UpdateCategoryStatus(CategoryStatus::AVAILABLE); | 569 // TODO(sfiera): equivalent metrics for non-articles. |
| 570 const CategoryContent& category = categories_[articles_category_]; | |
| 571 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | |
| 572 category.snippets.size()); | |
| 573 if (category.snippets.empty() && !category.dismissed.empty()) { | |
| 574 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | |
| 575 category.dismissed.size()); | |
| 576 } | |
| 577 | |
| 506 NotifyNewSuggestions(); | 578 NotifyNewSuggestions(); |
| 507 } | 579 } |
| 508 | 580 |
| 509 void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) { | 581 void NTPSnippetsService::MergeSnippets(Category category_id, |
| 582 NTPSnippet::PtrVector new_snippets) { | |
| 510 DCHECK(ready()); | 583 DCHECK(ready()); |
| 584 CategoryContent* category = &categories_[category_id]; | |
| 511 | 585 |
| 512 // Remove new snippets that we already have, or that have been dismissed. | 586 // Remove new snippets that we already have, or that have been dismissed. |
| 513 std::set<std::string> old_snippet_ids; | 587 std::set<std::string> old_snippet_ids; |
| 514 InsertAllIDs(dismissed_snippets_, &old_snippet_ids); | 588 InsertAllIDs(category->dismissed, &old_snippet_ids); |
| 515 InsertAllIDs(snippets_, &old_snippet_ids); | 589 InsertAllIDs(category->snippets, &old_snippet_ids); |
| 516 new_snippets.erase( | 590 new_snippets.erase( |
| 517 std::remove_if( | 591 std::remove_if( |
| 518 new_snippets.begin(), new_snippets.end(), | 592 new_snippets.begin(), new_snippets.end(), |
| 519 [&old_snippet_ids](const std::unique_ptr<NTPSnippet>& snippet) { | 593 [&old_snippet_ids](const std::unique_ptr<NTPSnippet>& snippet) { |
| 520 if (old_snippet_ids.count(snippet->id())) | 594 if (old_snippet_ids.count(snippet->id())) |
| 521 return true; | 595 return true; |
| 522 for (const SnippetSource& source : snippet->sources()) { | 596 for (const SnippetSource& source : snippet->sources()) { |
| 523 if (old_snippet_ids.count(source.url.spec())) | 597 if (old_snippet_ids.count(source.url.spec())) |
| 524 return true; | 598 return true; |
| 525 } | 599 } |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 553 new_snippets.end()); | 627 new_snippets.end()); |
| 554 int num_snippets_dismissed = num_new_snippets - new_snippets.size(); | 628 int num_snippets_dismissed = num_new_snippets - new_snippets.size(); |
| 555 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", | 629 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", |
| 556 num_snippets_dismissed > 0); | 630 num_snippets_dismissed > 0); |
| 557 if (num_snippets_dismissed > 0) { | 631 if (num_snippets_dismissed > 0) { |
| 558 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", | 632 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", |
| 559 num_snippets_dismissed); | 633 num_snippets_dismissed); |
| 560 } | 634 } |
| 561 } | 635 } |
| 562 | 636 |
| 563 // Save the new snippets to the DB. | 637 // Save new articles to the DB. |
| 564 database_->SaveSnippets(new_snippets); | 638 // TODO(sfiera): save non-articles to DB too. |
| 639 if (category_id == articles_category_) | |
| 640 database_->SaveSnippets(new_snippets); | |
| 565 | 641 |
| 566 // Insert the new snippets at the front. | 642 // Insert the new snippets at the front. |
| 567 snippets_.insert(snippets_.begin(), | 643 category->snippets.insert(category->snippets.begin(), |
| 568 std::make_move_iterator(new_snippets.begin()), | 644 std::make_move_iterator(new_snippets.begin()), |
| 569 std::make_move_iterator(new_snippets.end())); | 645 std::make_move_iterator(new_snippets.end())); |
| 570 } | 646 } |
| 571 | 647 |
| 572 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const { | 648 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const { |
| 573 std::set<std::string> hosts; | 649 std::set<std::string> hosts; |
| 574 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts); | 650 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts); |
| 575 for (const auto& value : *list) { | 651 for (const auto& value : *list) { |
| 576 std::string str; | 652 std::string str; |
| 577 bool success = value->GetAsString(&str); | 653 bool success = value->GetAsString(&str); |
| 578 DCHECK(success) << "Failed to parse snippet host from prefs"; | 654 DCHECK(success) << "Failed to parse snippet host from prefs"; |
| 579 hosts.insert(std::move(str)); | 655 hosts.insert(std::move(str)); |
| 580 } | 656 } |
| 581 return hosts; | 657 return hosts; |
| 582 } | 658 } |
| 583 | 659 |
| 584 void NTPSnippetsService::StoreSnippetHostsToPrefs( | 660 void NTPSnippetsService::StoreSnippetHostsToPrefs( |
| 585 const std::set<std::string>& hosts) { | 661 const std::set<std::string>& hosts) { |
| 586 base::ListValue list; | 662 base::ListValue list; |
| 587 for (const std::string& host : hosts) | 663 for (const std::string& host : hosts) |
| 588 list.AppendString(host); | 664 list.AppendString(host); |
| 589 pref_service_->Set(prefs::kSnippetHosts, list); | 665 pref_service_->Set(prefs::kSnippetHosts, list); |
| 590 } | 666 } |
| 591 | 667 |
| 592 void NTPSnippetsService::ClearExpiredSnippets() { | 668 void NTPSnippetsService::ClearExpiredSnippets(Category category_id) { |
| 669 DCHECK(categories_.find(category_id) != categories_.end()); | |
| 670 CategoryContent* category = &categories_[category_id]; | |
| 671 | |
| 593 base::Time expiry = base::Time::Now(); | 672 base::Time expiry = base::Time::Now(); |
| 594 | 673 |
| 595 // Move expired snippets over into |to_delete|. | 674 // Move expired snippets over into |to_delete|. |
| 596 NTPSnippet::PtrVector to_delete; | 675 NTPSnippet::PtrVector to_delete; |
| 597 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) { | 676 for (std::unique_ptr<NTPSnippet>& snippet : category->snippets) { |
| 598 if (snippet->expiry_date() <= expiry) | 677 if (snippet->expiry_date() <= expiry) |
| 599 to_delete.emplace_back(std::move(snippet)); | 678 to_delete.emplace_back(std::move(snippet)); |
| 600 } | 679 } |
| 601 Compact(&snippets_); | 680 Compact(&category->snippets); |
| 602 | 681 |
| 603 // Move expired dismissed snippets over into |to_delete| as well. | 682 // Move expired dismissed snippets over into |to_delete| as well. |
| 604 for (std::unique_ptr<NTPSnippet>& snippet : dismissed_snippets_) { | 683 for (std::unique_ptr<NTPSnippet>& snippet : category->dismissed) { |
| 605 if (snippet->expiry_date() <= expiry) | 684 if (snippet->expiry_date() <= expiry) |
| 606 to_delete.emplace_back(std::move(snippet)); | 685 to_delete.emplace_back(std::move(snippet)); |
| 607 } | 686 } |
| 608 Compact(&dismissed_snippets_); | 687 Compact(&category->dismissed); |
| 609 | 688 |
| 610 // Finally, actually delete the removed snippets from the DB. | 689 // Finally, actually delete the removed snippets from the DB. |
| 611 database_->DeleteSnippets(to_delete); | 690 if (category_id == articles_category_) |
| 691 database_->DeleteSnippets(to_delete); | |
| 612 | 692 |
| 613 // If there are any snippets left, schedule a timer for the next expiry. | 693 // Unless there are no snippets left, schedule a timer for the next expiry. |
| 614 if (snippets_.empty() && dismissed_snippets_.empty()) | 694 if (category->snippets.empty() && category->dismissed.empty()) |
| 615 return; | 695 return; |
| 616 | 696 |
| 617 base::Time next_expiry = base::Time::Max(); | 697 base::Time next_expiry = base::Time::Max(); |
| 618 for (const auto& snippet : snippets_) { | 698 for (const auto& snippet : category->snippets) { |
| 619 if (snippet->expiry_date() < next_expiry) | 699 if (snippet->expiry_date() < next_expiry) |
| 620 next_expiry = snippet->expiry_date(); | 700 next_expiry = snippet->expiry_date(); |
| 621 } | 701 } |
| 622 for (const auto& snippet : dismissed_snippets_) { | 702 for (const auto& snippet : category->dismissed) { |
| 623 if (snippet->expiry_date() < next_expiry) | 703 if (snippet->expiry_date() < next_expiry) |
| 624 next_expiry = snippet->expiry_date(); | 704 next_expiry = snippet->expiry_date(); |
| 625 } | 705 } |
| 706 | |
| 626 DCHECK_GT(next_expiry, expiry); | 707 DCHECK_GT(next_expiry, expiry); |
| 627 expiry_timer_.Start(FROM_HERE, next_expiry - expiry, | 708 expiry_timer_.Start(FROM_HERE, next_expiry - expiry, |
| 628 base::Bind(&NTPSnippetsService::ClearExpiredSnippets, | 709 base::Bind(&NTPSnippetsService::ClearExpiredSnippets, |
| 629 base::Unretained(this))); | 710 base::Unretained(this), category_id)); |
| 630 } | 711 } |
| 631 | 712 |
| 632 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase( | 713 void NTPSnippetsService::OnSnippetImageFetchedFromDatabase( |
| 633 const ImageFetchedCallback& callback, | 714 const ImageFetchedCallback& callback, |
| 634 const std::string& snippet_id, | 715 const std::string& suggestion_id, |
| 635 std::string data) { | 716 std::string data) { |
| 636 // |image_decoder_| is null in tests. | 717 // |image_decoder_| is null in tests. |
| 637 if (image_decoder_ && !data.empty()) { | 718 if (image_decoder_ && !data.empty()) { |
| 638 image_decoder_->DecodeImage( | 719 image_decoder_->DecodeImage( |
| 639 std::move(data), | 720 std::move(data), |
| 640 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase, | 721 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromDatabase, |
| 641 base::Unretained(this), callback, snippet_id)); | 722 base::Unretained(this), callback, suggestion_id)); |
| 642 return; | 723 return; |
| 643 } | 724 } |
| 644 | 725 |
| 645 // Fetching from the DB failed; start a network fetch. | 726 // Fetching from the DB failed; start a network fetch. |
| 646 FetchSnippetImageFromNetwork(snippet_id, callback); | 727 FetchSnippetImageFromNetwork(suggestion_id, callback); |
| 647 } | 728 } |
| 648 | 729 |
| 649 void NTPSnippetsService::OnSnippetImageDecodedFromDatabase( | 730 void NTPSnippetsService::OnSnippetImageDecodedFromDatabase( |
| 650 const ImageFetchedCallback& callback, | 731 const ImageFetchedCallback& callback, |
| 651 const std::string& snippet_id, | 732 const std::string& suggestion_id, |
| 652 const gfx::Image& image) { | 733 const gfx::Image& image) { |
| 653 if (!image.IsEmpty()) { | 734 if (!image.IsEmpty()) { |
| 654 callback.Run(MakeUniqueID(provided_category_, snippet_id), image); | 735 callback.Run(suggestion_id, image); |
| 655 return; | 736 return; |
| 656 } | 737 } |
| 657 | 738 |
| 658 // If decoding the image failed, delete the DB entry. | 739 // If decoding the image failed, delete the DB entry. |
| 659 database_->DeleteImage(snippet_id); | 740 database_->DeleteImage(suggestion_id); |
| 660 | 741 |
| 661 FetchSnippetImageFromNetwork(snippet_id, callback); | 742 FetchSnippetImageFromNetwork(suggestion_id, callback); |
| 662 } | 743 } |
| 663 | 744 |
| 664 void NTPSnippetsService::FetchSnippetImageFromNetwork( | 745 void NTPSnippetsService::FetchSnippetImageFromNetwork( |
| 665 const std::string& snippet_id, | 746 const std::string& suggestion_id, |
| 666 const ImageFetchedCallback& callback) { | 747 const ImageFetchedCallback& callback) { |
| 748 Category category_id = GetCategoryFromUniqueID(suggestion_id); | |
| 749 std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); | |
| 750 | |
| 751 auto category_it = categories_.find(category_id); | |
| 752 if (category_it == categories_.end()) { | |
| 753 callback.Run(suggestion_id, gfx::Image()); | |
| 754 return; | |
| 755 } | |
| 756 | |
| 757 const CategoryContent& category = category_it->second; | |
| 667 auto it = | 758 auto it = |
| 668 std::find_if(snippets_.begin(), snippets_.end(), | 759 std::find_if(category.snippets.begin(), category.snippets.end(), |
| 669 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | 760 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { |
| 670 return snippet->id() == snippet_id; | 761 return snippet->id() == snippet_id; |
| 671 }); | 762 }); |
| 672 | 763 |
| 673 if (it == snippets_.end() || | 764 if (it == category.snippets.end() || |
| 674 !thumbnail_requests_throttler_.DemandQuotaForRequest( | 765 !thumbnail_requests_throttler_.DemandQuotaForRequest( |
| 675 /*interactive_request=*/true)) { | 766 /*interactive_request=*/true)) { |
| 676 // Return an empty image. Directly, this is never synchronous with the | 767 // Return an empty image. Directly, this is never synchronous with the |
| 677 // original FetchSuggestionImage() call - an asynchronous database query has | 768 // original FetchSuggestionImage() call - an asynchronous database query has |
| 678 // happened in the meantime. | 769 // happened in the meantime. |
| 679 OnSnippetImageDecodedFromNetwork(callback, snippet_id, gfx::Image()); | 770 OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); |
| 680 return; | 771 return; |
| 681 } | 772 } |
| 682 | 773 |
| 683 const NTPSnippet& snippet = *it->get(); | 774 const NTPSnippet& snippet = *it->get(); |
| 684 | 775 |
| 685 // TODO(jkrcal): We probably should rename OnImageDataFetched() to | 776 // TODO(jkrcal): We probably should rename OnImageDataFetched() to |
| 686 // CacheImageData(). This would document that this is actually independent | 777 // CacheImageData(). This would document that this is actually independent |
| 687 // from the individual fetch-flow. | 778 // from the individual fetch-flow. |
| 688 // The image fetcher calls OnImageDataFetched() with the raw data (this object | 779 // The image fetcher calls OnImageDataFetched() with the raw data (this object |
| 689 // is an ImageFetcherDelegate) and then also | 780 // is an ImageFetcherDelegate) and then also |
| 690 // OnSnippetImageDecodedFromNetwork() after the raw data gets decoded. | 781 // OnSnippetImageDecodedFromNetwork() after the raw data gets decoded. |
| 691 image_fetcher_->StartOrQueueNetworkRequest( | 782 image_fetcher_->StartOrQueueNetworkRequest( |
| 692 snippet.id(), snippet.salient_image_url(), | 783 suggestion_id, snippet.salient_image_url(), |
| 693 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork, | 784 base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork, |
| 694 base::Unretained(this), callback)); | 785 base::Unretained(this), callback)); |
| 695 } | 786 } |
| 696 | 787 |
| 697 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork( | 788 void NTPSnippetsService::OnSnippetImageDecodedFromNetwork( |
| 698 const ImageFetchedCallback& callback, | 789 const ImageFetchedCallback& callback, |
| 699 const std::string& snippet_id, | 790 const std::string& suggestion_id, |
| 700 const gfx::Image& image) { | 791 const gfx::Image& image) { |
| 701 callback.Run(MakeUniqueID(provided_category_, snippet_id), image); | 792 callback.Run(suggestion_id, image); |
| 702 } | 793 } |
| 703 | 794 |
| 704 void NTPSnippetsService::EnterStateEnabled(bool fetch_snippets) { | 795 void NTPSnippetsService::EnterStateEnabled(bool fetch_snippets) { |
| 705 if (fetch_snippets) | 796 if (fetch_snippets) |
| 706 FetchSnippets(/*force_request=*/false); | 797 FetchSnippets(/*force_request=*/false); |
| 707 | 798 |
| 708 // FetchSnippets should set the status to |AVAILABLE_LOADING| if relevant, | 799 // FetchSnippets should set the status to |AVAILABLE_LOADING| if relevant, |
| 709 // otherwise we transition to |AVAILABLE| here. | 800 // otherwise we transition to |AVAILABLE| here. |
| 710 if (category_status_ != CategoryStatus::AVAILABLE_LOADING) | 801 if (categories_[articles_category_].status != |
| 711 UpdateCategoryStatus(CategoryStatus::AVAILABLE); | 802 CategoryStatus::AVAILABLE_LOADING) |
|
Marc Treib
2016/08/22 15:06:46
Braces please
sfiera
2016/08/24 14:35:57
Done.
| |
| 803 UpdateCategoryStatus(articles_category_, CategoryStatus::AVAILABLE); | |
| 712 | 804 |
| 713 // If host restrictions are enabled, register for host list updates. | 805 // If host restrictions are enabled, register for host list updates. |
| 714 // |suggestions_service_| can be null in tests. | 806 // |suggestions_service_| can be null in tests. |
| 715 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) { | 807 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) { |
| 716 suggestions_service_subscription_ = | 808 suggestions_service_subscription_ = |
| 717 suggestions_service_->AddCallback(base::Bind( | 809 suggestions_service_->AddCallback(base::Bind( |
| 718 &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this))); | 810 &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this))); |
| 719 } | 811 } |
| 720 | 812 |
| 721 RescheduleFetching(); | 813 RescheduleFetching(); |
| 722 } | 814 } |
| 723 | 815 |
| 724 void NTPSnippetsService::EnterStateDisabled() { | 816 void NTPSnippetsService::EnterStateDisabled() { |
| 725 ClearCachedSuggestionsForDebugging(provided_category_); | 817 std::vector<Category> category_list; |
| 726 ClearDismissedSuggestionsForDebugging(provided_category_); | 818 for (const auto& item : categories_) { |
| 819 Category category_id = item.first; | |
| 820 category_list.push_back(category_id); | |
| 821 } | |
| 822 | |
| 823 // Empty the ARTICLES category and remove all others, since they may or may | |
| 824 // not be personalized. | |
| 825 for (Category category_id : category_list) { | |
| 826 ClearCachedSuggestionsForDebugging(category_id); | |
| 827 ClearDismissedSuggestionsForDebugging(category_id); | |
| 828 if (category_id != articles_category_) { | |
| 829 UpdateCategoryStatus(category_id, CategoryStatus::NOT_PROVIDED); | |
| 830 categories_.erase(category_id); | |
| 831 } | |
| 832 } | |
| 727 | 833 |
| 728 expiry_timer_.Stop(); | 834 expiry_timer_.Stop(); |
| 729 suggestions_service_subscription_.reset(); | 835 suggestions_service_subscription_.reset(); |
| 730 RescheduleFetching(); | 836 RescheduleFetching(); |
| 731 } | 837 } |
| 732 | 838 |
| 733 void NTPSnippetsService::EnterStateError() { | 839 void NTPSnippetsService::EnterStateError() { |
| 734 expiry_timer_.Stop(); | 840 expiry_timer_.Stop(); |
| 735 suggestions_service_subscription_.reset(); | 841 suggestions_service_subscription_.reset(); |
| 736 RescheduleFetching(); | 842 RescheduleFetching(); |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 756 // Always notify here even if we got nothing from the database, because we | 862 // Always notify here even if we got nothing from the database, because we |
| 757 // don't know how long the fetch will take or if it will even complete. | 863 // don't know how long the fetch will take or if it will even complete. |
| 758 NotifyNewSuggestions(); | 864 NotifyNewSuggestions(); |
| 759 } | 865 } |
| 760 | 866 |
| 761 void NTPSnippetsService::OnDisabledReasonChanged( | 867 void NTPSnippetsService::OnDisabledReasonChanged( |
| 762 DisabledReason disabled_reason) { | 868 DisabledReason disabled_reason) { |
| 763 switch (disabled_reason) { | 869 switch (disabled_reason) { |
| 764 case DisabledReason::NONE: | 870 case DisabledReason::NONE: |
| 765 // Do not change the status. That will be done in EnterStateEnabled() | 871 // Do not change the status. That will be done in EnterStateEnabled() |
| 766 EnterState(State::READY, category_status_); | 872 EnterState(State::READY); |
| 767 break; | 873 break; |
| 768 | 874 |
| 769 case DisabledReason::EXPLICITLY_DISABLED: | 875 case DisabledReason::EXPLICITLY_DISABLED: |
| 770 EnterState(State::DISABLED, CategoryStatus::CATEGORY_EXPLICITLY_DISABLED); | 876 EnterState(State::DISABLED); |
| 877 UpdateAllCategoryStatus(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED); | |
| 771 break; | 878 break; |
| 772 | 879 |
| 773 case DisabledReason::SIGNED_OUT: | 880 case DisabledReason::SIGNED_OUT: |
| 774 EnterState(State::DISABLED, CategoryStatus::SIGNED_OUT); | 881 EnterState(State::DISABLED); |
| 882 UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT); | |
| 775 break; | 883 break; |
| 776 } | 884 } |
| 777 } | 885 } |
| 778 | 886 |
| 779 void NTPSnippetsService::EnterState(State state, CategoryStatus status) { | 887 void NTPSnippetsService::EnterState(State state) { |
| 780 UpdateCategoryStatus(status); | |
| 781 | |
| 782 if (state == state_) | 888 if (state == state_) |
| 783 return; | 889 return; |
| 784 | 890 |
| 785 switch (state) { | 891 switch (state) { |
| 786 case State::NOT_INITED: | 892 case State::NOT_INITED: |
| 787 // Initial state, it should not be possible to get back there. | 893 // Initial state, it should not be possible to get back there. |
| 788 NOTREACHED(); | 894 NOTREACHED(); |
| 789 return; | 895 return; |
| 790 | 896 |
| 791 case State::READY: { | 897 case State::READY: { |
| 792 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED); | 898 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED); |
| 793 | 899 |
| 794 bool fetch_snippets = snippets_.empty() || fetch_after_load_; | 900 // TODO(sfiera): always fetch, because there might be server-side |
| 901 // categories we're interested in? | |
| 902 bool fetch_snippets = | |
| 903 categories_[articles_category_].snippets.empty() || fetch_after_load_; | |
| 795 DVLOG(1) << "Entering state: READY"; | 904 DVLOG(1) << "Entering state: READY"; |
| 796 state_ = State::READY; | 905 state_ = State::READY; |
| 797 fetch_after_load_ = false; | 906 fetch_after_load_ = false; |
| 798 EnterStateEnabled(fetch_snippets); | 907 EnterStateEnabled(fetch_snippets); |
| 799 return; | 908 return; |
| 800 } | 909 } |
| 801 | 910 |
| 802 case State::DISABLED: | 911 case State::DISABLED: |
| 803 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); | 912 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); |
| 804 | 913 |
| 805 DVLOG(1) << "Entering state: DISABLED"; | 914 DVLOG(1) << "Entering state: DISABLED"; |
| 806 state_ = State::DISABLED; | 915 state_ = State::DISABLED; |
| 807 EnterStateDisabled(); | 916 EnterStateDisabled(); |
| 808 return; | 917 return; |
| 809 | 918 |
| 810 case State::ERROR_OCCURRED: | 919 case State::ERROR_OCCURRED: |
| 811 DVLOG(1) << "Entering state: ERROR_OCCURRED"; | 920 DVLOG(1) << "Entering state: ERROR_OCCURRED"; |
| 812 state_ = State::ERROR_OCCURRED; | 921 state_ = State::ERROR_OCCURRED; |
| 813 EnterStateError(); | 922 EnterStateError(); |
| 814 return; | 923 return; |
| 815 } | 924 } |
| 816 } | 925 } |
| 817 | 926 |
| 818 void NTPSnippetsService::NotifyNewSuggestions() { | 927 void NTPSnippetsService::NotifyNewSuggestions() { |
| 819 std::vector<ContentSuggestion> result; | 928 for (auto& item : categories_) { |
| 820 for (const std::unique_ptr<NTPSnippet>& snippet : snippets_) { | 929 Category category_id = item.first; |
| 821 if (!snippet->is_complete()) | 930 CategoryContent& category = item.second; |
| 822 continue; | 931 |
| 823 ContentSuggestion suggestion( | 932 std::vector<ContentSuggestion> result; |
| 824 MakeUniqueID(provided_category_, snippet->id()), | 933 for (const std::unique_ptr<NTPSnippet>& snippet : category.snippets) { |
| 825 snippet->best_source().url); | 934 // TODO(sfiera): if a snippet is not going to be displayed, move it |
| 826 suggestion.set_amp_url(snippet->best_source().amp_url); | 935 // directly to category.dismissed on fetch. Otherwise, we might prune |
| 827 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); | 936 // other snippets to get down to kMaxSnippetCount, only to hide one of the |
| 828 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); | 937 // incomplete ones we kept. |
| 829 suggestion.set_publish_date(snippet->publish_date()); | 938 if (!snippet->is_complete()) |
| 830 suggestion.set_publisher_name( | 939 continue; |
| 831 base::UTF8ToUTF16(snippet->best_source().publisher_name)); | 940 ContentSuggestion suggestion(MakeUniqueID(category_id, snippet->id()), |
| 832 suggestion.set_score(snippet->score()); | 941 snippet->best_source().url); |
| 833 result.emplace_back(std::move(suggestion)); | 942 suggestion.set_amp_url(snippet->best_source().amp_url); |
| 943 suggestion.set_title(base::UTF8ToUTF16(snippet->title())); | |
| 944 suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet())); | |
| 945 suggestion.set_publish_date(snippet->publish_date()); | |
| 946 suggestion.set_publisher_name( | |
| 947 base::UTF8ToUTF16(snippet->best_source().publisher_name)); | |
| 948 suggestion.set_score(snippet->score()); | |
| 949 result.emplace_back(std::move(suggestion)); | |
| 950 } | |
| 951 // TODO(sfiera): detect when a category is unchanged and don't notify the | |
| 952 // observer in that case. | |
| 953 observer()->OnNewSuggestions(this, category_id, std::move(result)); | |
| 834 } | 954 } |
| 835 observer()->OnNewSuggestions(this, provided_category_, std::move(result)); | |
| 836 } | 955 } |
| 837 | 956 |
| 838 void NTPSnippetsService::UpdateCategoryStatus(CategoryStatus status) { | 957 void NTPSnippetsService::UpdateCategoryStatus(Category category_id, |
| 839 if (status == category_status_) | 958 CategoryStatus status) { |
| 959 DCHECK(categories_.find(category_id) != categories_.end()); | |
| 960 CategoryContent& category = categories_[category_id]; | |
| 961 if (status == category.status) | |
| 840 return; | 962 return; |
| 841 | 963 |
| 842 category_status_ = status; | 964 category.status = status; |
| 843 observer()->OnCategoryStatusChanged(this, provided_category_, | 965 observer()->OnCategoryStatusChanged(this, category_id, category.status); |
| 844 category_status_); | 966 } |
| 967 | |
| 968 void NTPSnippetsService::UpdateAllCategoryStatus(CategoryStatus status) { | |
| 969 for (const auto& category : categories_) { | |
| 970 UpdateCategoryStatus(category.first, status); | |
| 971 } | |
| 845 } | 972 } |
| 846 | 973 |
| 847 } // namespace ntp_snippets | 974 } // namespace ntp_snippets |
| OLD | NEW |