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

Side by Side Diff: components/ntp_snippets/remote/remote_suggestions_provider.cc

Issue 2558393004: Refactoring: Moves image fetching and caching into CachedImageFetcher (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/ntp_snippets/remote/remote_suggestions_provider.h" 5 #include "components/ntp_snippets/remote/remote_suggestions_provider.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 #include <utility> 9 #include <utility>
10 10
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 void CallWithEmptyResults(const FetchDoneCallback& callback, 214 void CallWithEmptyResults(const FetchDoneCallback& callback,
215 const Status& status) { 215 const Status& status) {
216 if (callback.is_null()) { 216 if (callback.is_null()) {
217 return; 217 return;
218 } 218 }
219 callback.Run(status, std::vector<ContentSuggestion>()); 219 callback.Run(status, std::vector<ContentSuggestion>());
220 } 220 }
221 221
222 } // namespace 222 } // namespace
223 223
224 CachedImageFetcher::CachedImageFetcher(
225 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
226 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
227 PrefService* pref_service,
228 RemoteSuggestionsDatabase* database)
229 : image_fetcher_(std::move(image_fetcher)),
230 image_decoder_(std::move(image_decoder)),
231 database_(database),
232 thumbnail_requests_throttler_(
233 pref_service,
234 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) {
235 // |image_fetcher_| can be null in tests.
236 if (image_fetcher_) {
237 image_fetcher_->SetImageFetcherDelegate(this);
238 image_fetcher_->SetDataUseServiceName(
239 data_use_measurement::DataUseUserData::NTP_SNIPPETS);
240 }
241 }
242
243 CachedImageFetcher::~CachedImageFetcher() {}
244
245 void CachedImageFetcher::FetchSuggestionImage(
246 const ContentSuggestion::ID& suggestion_id,
247 const GURL& url,
248 const ImageFetchedCallback& callback) {
249 database_->LoadImage(
250 suggestion_id.id_within_category(),
251 base::Bind(&CachedImageFetcher::OnSnippetImageFetchedFromDatabase,
252 base::Unretained(this), callback, suggestion_id, url));
253 }
254
255 // This function gets only called for caching the image data received from the
256 // network. The actual decoding is done in OnSnippetImageDecodedFromDatabase().
257 void CachedImageFetcher::OnImageDataFetched(
258 const std::string& id_within_category,
259 const std::string& image_data) {
260 if (image_data.empty()) {
261 return;
262 }
263 database_->SaveImage(id_within_category, image_data);
264 }
265
266 void CachedImageFetcher::OnImageDecodingDone(
267 const ImageFetchedCallback& callback,
268 const std::string& id_within_category,
269 const gfx::Image& image) {
270 callback.Run(image);
271 }
272
273 void CachedImageFetcher::OnSnippetImageFetchedFromDatabase(
274 const ImageFetchedCallback& callback,
275 const ContentSuggestion::ID& suggestion_id,
276 const GURL& url,
277 std::string data) { // SnippetImageCallback requires nonconst reference.
278 // |image_decoder_| is null in tests.
279 if (image_decoder_ && !data.empty()) {
280 image_decoder_->DecodeImage(
281 data, base::Bind(
282 &CachedImageFetcher::OnSnippetImageDecodedFromDatabase,
283 base::Unretained(this), callback, suggestion_id, url));
284 return;
285 }
286 // Fetching from the DB failed; start a network fetch.
287 FetchSnippetImageFromNetwork(suggestion_id, url, callback);
288 }
289
290 void CachedImageFetcher::OnSnippetImageDecodedFromDatabase(
291 const ImageFetchedCallback& callback,
292 const ContentSuggestion::ID& suggestion_id,
293 const GURL& url,
294 const gfx::Image& image) {
295 if (!image.IsEmpty()) {
296 callback.Run(image);
297 return;
298 }
299 // If decoding the image failed, delete the DB entry.
300 database_->DeleteImage(suggestion_id.id_within_category());
301 FetchSnippetImageFromNetwork(suggestion_id, url, callback);
302 };
303
304 void CachedImageFetcher::FetchSnippetImageFromNetwork(
305 const ContentSuggestion::ID& suggestion_id,
306 const GURL& url,
307 const ImageFetchedCallback& callback) {
308 if (url.is_empty() ||
309 !thumbnail_requests_throttler_.DemandQuotaForRequest(
310 /*interactive_request=*/true)) {
311 // Return an empty image. Directly, this is never synchronous with the
312 // original FetchSuggestionImage() call - an asynchronous database query has
313 // happened in the meantime.
314 callback.Run(gfx::Image());
315 return;
316 }
317
318 image_fetcher_->StartOrQueueNetworkRequest(
319 suggestion_id.id_within_category(), url,
320 base::Bind(&CachedImageFetcher::OnImageDecodingDone,
321 base::Unretained(this), callback));
322 }
323
224 RemoteSuggestionsProvider::RemoteSuggestionsProvider( 324 RemoteSuggestionsProvider::RemoteSuggestionsProvider(
225 Observer* observer, 325 Observer* observer,
226 CategoryFactory* category_factory, 326 CategoryFactory* category_factory,
227 PrefService* pref_service, 327 PrefService* pref_service,
228 const std::string& application_language_code, 328 const std::string& application_language_code,
229 const UserClassifier* user_classifier, 329 const UserClassifier* user_classifier,
230 NTPSnippetsScheduler* scheduler, 330 NTPSnippetsScheduler* scheduler,
231 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, 331 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher,
232 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, 332 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
233 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, 333 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
234 std::unique_ptr<RemoteSuggestionsDatabase> database, 334 std::unique_ptr<RemoteSuggestionsDatabase> database,
235 std::unique_ptr<RemoteSuggestionsStatusService> status_service) 335 std::unique_ptr<RemoteSuggestionsStatusService> status_service)
236 : ContentSuggestionsProvider(observer, category_factory), 336 : ContentSuggestionsProvider(observer, category_factory),
237 state_(State::NOT_INITED), 337 state_(State::NOT_INITED),
238 pref_service_(pref_service), 338 pref_service_(pref_service),
239 articles_category_( 339 articles_category_(
240 category_factory->FromKnownCategory(KnownCategories::ARTICLES)), 340 category_factory->FromKnownCategory(KnownCategories::ARTICLES)),
241 application_language_code_(application_language_code), 341 application_language_code_(application_language_code),
242 user_classifier_(user_classifier), 342 user_classifier_(user_classifier),
243 scheduler_(scheduler), 343 scheduler_(scheduler),
244 snippets_fetcher_(std::move(snippets_fetcher)), 344 snippets_fetcher_(std::move(snippets_fetcher)),
245 image_fetcher_(std::move(image_fetcher)),
246 image_decoder_(std::move(image_decoder)),
247 database_(std::move(database)), 345 database_(std::move(database)),
346 image_fetcher_(std::move(image_fetcher),
347 std::move(image_decoder),
348 pref_service,
349 database_.get()),
248 status_service_(std::move(status_service)), 350 status_service_(std::move(status_service)),
249 fetch_when_ready_(false), 351 fetch_when_ready_(false),
250 nuke_when_initialized_(false), 352 nuke_when_initialized_(false),
251 thumbnail_requests_throttler_(
252 pref_service,
253 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL),
254 tick_clock_(base::MakeUnique<base::DefaultTickClock>()) { 353 tick_clock_(base::MakeUnique<base::DefaultTickClock>()) {
255 pref_service_->ClearPref(kDeprecatedSnippetHostsPref); 354 pref_service_->ClearPref(kDeprecatedSnippetHostsPref);
256 355
257 RestoreCategoriesFromPrefs(); 356 RestoreCategoriesFromPrefs();
258 // The articles category always exists. Add it if we didn't get it from prefs. 357 // The articles category always exists. Add it if we didn't get it from prefs.
259 // TODO(treib): Rethink this. 358 // TODO(treib): Rethink this.
260 category_contents_.insert( 359 category_contents_.insert(
261 std::make_pair(articles_category_, 360 std::make_pair(articles_category_,
262 CategoryContent(BuildArticleCategoryInfo(base::nullopt)))); 361 CategoryContent(BuildArticleCategoryInfo(base::nullopt))));
263 // Tell the observer about all the categories. 362 // Tell the observer about all the categories.
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 return; 541 return;
443 } 542 }
444 543
445 auto content_it = category_contents_.find(suggestion_id.category()); 544 auto content_it = category_contents_.find(suggestion_id.category());
446 DCHECK(content_it != category_contents_.end()); 545 DCHECK(content_it != category_contents_.end());
447 CategoryContent* content = &content_it->second; 546 CategoryContent* content = &content_it->second;
448 DismissSuggestionFromCategoryContent(content, 547 DismissSuggestionFromCategoryContent(content,
449 suggestion_id.id_within_category()); 548 suggestion_id.id_within_category());
450 } 549 }
451 550
452 void RemoteSuggestionsProvider::FetchSuggestionImage(
453 const ContentSuggestion::ID& suggestion_id,
454 const ImageFetchedCallback& callback) {
455 database_->LoadImage(
456 suggestion_id.id_within_category(),
457 base::Bind(&RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase,
458 base::Unretained(this), callback, suggestion_id));
459 }
460
461 void RemoteSuggestionsProvider::ClearHistory( 551 void RemoteSuggestionsProvider::ClearHistory(
462 base::Time begin, 552 base::Time begin,
463 base::Time end, 553 base::Time end,
464 const base::Callback<bool(const GURL& url)>& filter) { 554 const base::Callback<bool(const GURL& url)>& filter) {
465 // Both time range and the filter are ignored and all suggestions are removed, 555 // Both time range and the filter are ignored and all suggestions are removed,
466 // because it is not known which history entries were used for the suggestions 556 // because it is not known which history entries were used for the suggestions
467 // personalization. 557 // personalization.
468 if (!ready()) { 558 if (!ready()) {
469 nuke_when_initialized_ = true; 559 nuke_when_initialized_ = true;
470 } else { 560 } else {
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 const CategoryContent& content = 639 const CategoryContent& content =
550 category_contents_.at(suggestion_id.category()); 640 category_contents_.at(suggestion_id.category());
551 const NTPSnippet* snippet = 641 const NTPSnippet* snippet =
552 content.FindSnippet(suggestion_id.id_within_category()); 642 content.FindSnippet(suggestion_id.id_within_category());
553 if (!snippet) { 643 if (!snippet) {
554 return GURL(); 644 return GURL();
555 } 645 }
556 return snippet->salient_image_url(); 646 return snippet->salient_image_url();
557 } 647 }
558 648
559 // image_fetcher::ImageFetcherDelegate implementation.
560 void RemoteSuggestionsProvider::OnImageDataFetched(
561 const std::string& id_within_category,
562 const std::string& image_data) {
563 if (image_data.empty()) {
564 return;
565 }
566
567 // Only save the image if the corresponding snippet still exists.
568 bool found = false;
569 for (const auto& entry : category_contents_) {
570 if (entry.second.FindSnippet(id_within_category)) {
571 found = true;
572 break;
573 }
574 }
575 if (!found) {
576 return;
577 }
578
579 // Only cache the data in the DB, the actual serving is done in the callback
580 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()).
581 database_->SaveImage(id_within_category, image_data);
582 }
583
584 void RemoteSuggestionsProvider::OnDatabaseLoaded( 649 void RemoteSuggestionsProvider::OnDatabaseLoaded(
585 NTPSnippet::PtrVector snippets) { 650 NTPSnippet::PtrVector snippets) {
586 if (state_ == State::ERROR_OCCURRED) { 651 if (state_ == State::ERROR_OCCURRED) {
587 return; 652 return;
588 } 653 }
589 DCHECK(state_ == State::NOT_INITED); 654 DCHECK(state_ == State::NOT_INITED);
590 DCHECK(base::ContainsKey(category_contents_, articles_category_)); 655 DCHECK(base::ContainsKey(category_contents_, articles_category_));
591 656
592 base::TimeDelta database_load_time = 657 base::TimeDelta database_load_time =
593 base::TimeTicks::Now() - database_load_start_; 658 base::TimeTicks::Now() - database_load_start_;
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after
939 } 1004 }
940 } 1005 }
941 1006
942 for (Category category : categories_to_erase) { 1007 for (Category category : categories_to_erase) {
943 category_contents_.erase(category); 1008 category_contents_.erase(category);
944 } 1009 }
945 1010
946 StoreCategoriesToPrefs(); 1011 StoreCategoriesToPrefs();
947 } 1012 }
948 1013
949 void RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase( 1014 void RemoteSuggestionsProvider::FetchSuggestionImage(
950 const ImageFetchedCallback& callback,
951 const ContentSuggestion::ID& suggestion_id,
952 std::string data) { // SnippetImageCallback requires nonconst reference.
953 // |image_decoder_| is null in tests.
954 if (image_decoder_ && !data.empty()) {
955 image_decoder_->DecodeImage(
956 data, base::Bind(
957 &RemoteSuggestionsProvider::OnSnippetImageDecodedFromDatabase,
958 base::Unretained(this), callback, suggestion_id));
959 return;
960 }
961
962 // Fetching from the DB failed; start a network fetch.
963 FetchSnippetImageFromNetwork(suggestion_id, callback);
964 }
965
966 void RemoteSuggestionsProvider::OnSnippetImageDecodedFromDatabase(
967 const ImageFetchedCallback& callback,
968 const ContentSuggestion::ID& suggestion_id,
969 const gfx::Image& image) {
970 if (!image.IsEmpty()) {
971 callback.Run(image);
972 return;
973 }
974
975 // If decoding the image failed, delete the DB entry.
976 database_->DeleteImage(suggestion_id.id_within_category());
977
978 FetchSnippetImageFromNetwork(suggestion_id, callback);
979 }
980
981 void RemoteSuggestionsProvider::FetchSnippetImageFromNetwork(
982 const ContentSuggestion::ID& suggestion_id, 1015 const ContentSuggestion::ID& suggestion_id,
983 const ImageFetchedCallback& callback) { 1016 const ImageFetchedCallback& callback) {
984 if (!base::ContainsKey(category_contents_, suggestion_id.category())) { 1017 if (!base::ContainsKey(category_contents_, suggestion_id.category())) {
985 OnSnippetImageDecodedFromNetwork( 1018 callback.Run(gfx::Image());
986 callback, suggestion_id.id_within_category(), gfx::Image());
987 return; 1019 return;
988 } 1020 }
989
990 GURL image_url = FindSnippetImageUrl(suggestion_id); 1021 GURL image_url = FindSnippetImageUrl(suggestion_id);
jkrcal 2016/12/12 09:01:11 What about empty |image_url| (because the snippet
tschumann 2016/12/13 20:59:09 interesting! The actual behavior is still the same
Marc Treib 2016/12/14 09:02:15 Yup, this SGTM - I don't see what a DB lookup with
991 1022 image_fetcher_.FetchSuggestionImage(suggestion_id, image_url, callback);
992 if (image_url.is_empty() ||
993 !thumbnail_requests_throttler_.DemandQuotaForRequest(
994 /*interactive_request=*/true)) {
995 // Return an empty image. Directly, this is never synchronous with the
996 // original FetchSuggestionImage() call - an asynchronous database query has
997 // happened in the meantime.
998 OnSnippetImageDecodedFromNetwork(
999 callback, suggestion_id.id_within_category(), gfx::Image());
1000 return;
1001 }
1002
1003 image_fetcher_->StartOrQueueNetworkRequest(
1004 suggestion_id.id_within_category(), image_url,
1005 base::Bind(&RemoteSuggestionsProvider::OnSnippetImageDecodedFromNetwork,
1006 base::Unretained(this), callback));
1007 }
1008
1009 void RemoteSuggestionsProvider::OnSnippetImageDecodedFromNetwork(
1010 const ImageFetchedCallback& callback,
1011 const std::string& id_within_category,
1012 const gfx::Image& image) {
1013 callback.Run(image);
1014 } 1023 }
1015 1024
1016 void RemoteSuggestionsProvider::EnterStateReady() { 1025 void RemoteSuggestionsProvider::EnterStateReady() {
1017 if (nuke_when_initialized_) { 1026 if (nuke_when_initialized_) {
1018 NukeAllSnippets(); 1027 NukeAllSnippets();
1019 nuke_when_initialized_ = false; 1028 nuke_when_initialized_ = false;
1020 } 1029 }
1021 1030
1022 auto article_category_it = category_contents_.find(articles_category_); 1031 auto article_category_it = category_contents_.find(articles_category_);
1023 DCHECK(article_category_it != category_contents_.end()); 1032 DCHECK(article_category_it != category_contents_.end());
(...skipping 28 matching lines...) Expand all
1052 } 1061 }
1053 1062
1054 void RemoteSuggestionsProvider::FinishInitialization() { 1063 void RemoteSuggestionsProvider::FinishInitialization() {
1055 if (nuke_when_initialized_) { 1064 if (nuke_when_initialized_) {
1056 // We nuke here in addition to EnterStateReady, so that it happens even if 1065 // We nuke here in addition to EnterStateReady, so that it happens even if
1057 // we enter the DISABLED state below. 1066 // we enter the DISABLED state below.
1058 NukeAllSnippets(); 1067 NukeAllSnippets();
1059 nuke_when_initialized_ = false; 1068 nuke_when_initialized_ = false;
1060 } 1069 }
1061 1070
1062 // |image_fetcher_| can be null in tests.
1063 if (image_fetcher_) {
1064 image_fetcher_->SetImageFetcherDelegate(this);
1065 image_fetcher_->SetDataUseServiceName(
1066 data_use_measurement::DataUseUserData::NTP_SNIPPETS);
1067 }
1068
1069 // Note: Initializing the status service will run the callback right away with 1071 // Note: Initializing the status service will run the callback right away with
1070 // the current state. 1072 // the current state.
1071 status_service_->Init(base::Bind(&RemoteSuggestionsProvider::OnStatusChanged, 1073 status_service_->Init(base::Bind(&RemoteSuggestionsProvider::OnStatusChanged,
1072 base::Unretained(this))); 1074 base::Unretained(this)));
1073 1075
1074 // Always notify here even if we got nothing from the database, because we 1076 // Always notify here even if we got nothing from the database, because we
1075 // don't know how long the fetch will take or if it will even complete. 1077 // don't know how long the fetch will take or if it will even complete.
1076 for (const auto& item : category_contents_) { 1078 for (const auto& item : category_contents_) {
1077 Category category = item.first; 1079 Category category = item.first;
1078 const CategoryContent& content = item.second; 1080 const CategoryContent& content = item.second;
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after
1345 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) = 1347 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) =
1346 default; 1348 default;
1347 1349
1348 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default; 1350 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default;
1349 1351
1350 RemoteSuggestionsProvider::CategoryContent& 1352 RemoteSuggestionsProvider::CategoryContent&
1351 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) = 1353 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) =
1352 default; 1354 default;
1353 1355
1354 } // namespace ntp_snippets 1356 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698