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

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: comments and lint warnings (primarily missing utility include for std::move) 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
11 #include "base/command_line.h" 11 #include "base/command_line.h"
12 #include "base/feature_list.h" 12 #include "base/feature_list.h"
13 #include "base/location.h" 13 #include "base/location.h"
14 #include "base/memory/ptr_util.h" 14 #include "base/memory/ptr_util.h"
15 #include "base/metrics/histogram_macros.h" 15 #include "base/metrics/histogram_macros.h"
16 #include "base/metrics/sparse_histogram.h" 16 #include "base/metrics/sparse_histogram.h"
17 #include "base/path_service.h" 17 #include "base/path_service.h"
18 #include "base/stl_util.h" 18 #include "base/stl_util.h"
19 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/utf_string_conversions.h" 20 #include "base/strings/utf_string_conversions.h"
21 #include "base/task_runner_util.h"
22 #include "base/time/default_tick_clock.h" 21 #include "base/time/default_tick_clock.h"
23 #include "base/time/time.h" 22 #include "base/time/time.h"
24 #include "base/values.h" 23 #include "base/values.h"
25 #include "components/data_use_measurement/core/data_use_user_data.h" 24 #include "components/data_use_measurement/core/data_use_user_data.h"
26 #include "components/history/core/browser/history_service.h" 25 #include "components/history/core/browser/history_service.h"
27 #include "components/image_fetcher/image_decoder.h" 26 #include "components/image_fetcher/image_decoder.h"
28 #include "components/image_fetcher/image_fetcher.h" 27 #include "components/image_fetcher/image_fetcher.h"
29 #include "components/ntp_snippets/features.h" 28 #include "components/ntp_snippets/features.h"
30 #include "components/ntp_snippets/pref_names.h" 29 #include "components/ntp_snippets/pref_names.h"
31 #include "components/ntp_snippets/remote/remote_suggestions_database.h" 30 #include "components/ntp_snippets/remote/remote_suggestions_database.h"
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 void CallWithEmptyResults(const FetchDoneCallback& callback, 213 void CallWithEmptyResults(const FetchDoneCallback& callback,
215 const Status& status) { 214 const Status& status) {
216 if (callback.is_null()) { 215 if (callback.is_null()) {
217 return; 216 return;
218 } 217 }
219 callback.Run(status, std::vector<ContentSuggestion>()); 218 callback.Run(status, std::vector<ContentSuggestion>());
220 } 219 }
221 220
222 } // namespace 221 } // namespace
223 222
223 CachedImageFetcher::CachedImageFetcher(
224 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
225 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
226 PrefService* pref_service,
227 RemoteSuggestionsDatabase* database)
228 : image_fetcher_(std::move(image_fetcher)),
229 image_decoder_(std::move(image_decoder)),
230 database_(database),
231 thumbnail_requests_throttler_(
232 pref_service,
233 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) {
234 // |image_fetcher_| can be null in tests.
235 if (image_fetcher_) {
236 image_fetcher_->SetImageFetcherDelegate(this);
237 image_fetcher_->SetDataUseServiceName(
238 data_use_measurement::DataUseUserData::NTP_SNIPPETS);
239 }
240 }
241
242 CachedImageFetcher::~CachedImageFetcher() {}
243
244 void CachedImageFetcher::FetchSuggestionImage(
245 const ContentSuggestion::ID& suggestion_id,
246 const GURL& url,
247 const ImageFetchedCallback& callback) {
248 database_->LoadImage(
249 suggestion_id.id_within_category(),
250 base::Bind(&CachedImageFetcher::OnSnippetImageFetchedFromDatabase,
251 base::Unretained(this), callback, suggestion_id, url));
252 }
253
254 // This function gets only called for caching the image data received from the
255 // network. The actual decoding is done in OnSnippetImageDecodedFromDatabase().
256 void CachedImageFetcher::OnImageDataFetched(
257 const std::string& id_within_category,
258 const std::string& image_data) {
259 if (image_data.empty()) {
260 return;
261 }
262 database_->SaveImage(id_within_category, image_data);
263 }
264
265 void CachedImageFetcher::OnImageDecodingDone(
266 const ImageFetchedCallback& callback,
267 const std::string& id_within_category,
268 const gfx::Image& image) {
269 callback.Run(image);
270 }
271
272 void CachedImageFetcher::OnSnippetImageFetchedFromDatabase(
273 const ImageFetchedCallback& callback,
274 const ContentSuggestion::ID& suggestion_id,
275 const GURL& url,
276 std::string data) { // SnippetImageCallback requires nonconst reference.
277 // |image_decoder_| is null in tests.
278 if (image_decoder_ && !data.empty()) {
279 image_decoder_->DecodeImage(
280 data, base::Bind(
281 &CachedImageFetcher::OnSnippetImageDecodedFromDatabase,
282 base::Unretained(this), callback, suggestion_id, url));
283 return;
284 }
285 // Fetching from the DB failed; start a network fetch.
286 FetchSnippetImageFromNetwork(suggestion_id, url, callback);
287 }
288
289 void CachedImageFetcher::OnSnippetImageDecodedFromDatabase(
290 const ImageFetchedCallback& callback,
291 const ContentSuggestion::ID& suggestion_id,
292 const GURL& url,
293 const gfx::Image& image) {
294 if (!image.IsEmpty()) {
295 callback.Run(image);
296 return;
297 }
298 // If decoding the image failed, delete the DB entry.
299 database_->DeleteImage(suggestion_id.id_within_category());
300 FetchSnippetImageFromNetwork(suggestion_id, url, callback);
301 }
302
303 void CachedImageFetcher::FetchSnippetImageFromNetwork(
304 const ContentSuggestion::ID& suggestion_id,
305 const GURL& url,
306 const ImageFetchedCallback& callback) {
307 if (url.is_empty() ||
308 !thumbnail_requests_throttler_.DemandQuotaForRequest(
309 /*interactive_request=*/true)) {
310 // Return an empty image. Directly, this is never synchronous with the
311 // original FetchSuggestionImage() call - an asynchronous database query has
312 // happened in the meantime.
313 callback.Run(gfx::Image());
314 return;
315 }
316
317 image_fetcher_->StartOrQueueNetworkRequest(
318 suggestion_id.id_within_category(), url,
319 base::Bind(&CachedImageFetcher::OnImageDecodingDone,
320 base::Unretained(this), callback));
321 }
322
224 RemoteSuggestionsProvider::RemoteSuggestionsProvider( 323 RemoteSuggestionsProvider::RemoteSuggestionsProvider(
225 Observer* observer, 324 Observer* observer,
226 CategoryFactory* category_factory, 325 CategoryFactory* category_factory,
227 PrefService* pref_service, 326 PrefService* pref_service,
228 const std::string& application_language_code, 327 const std::string& application_language_code,
229 const UserClassifier* user_classifier, 328 const UserClassifier* user_classifier,
230 NTPSnippetsScheduler* scheduler, 329 NTPSnippetsScheduler* scheduler,
231 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, 330 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher,
232 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, 331 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
233 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, 332 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
234 std::unique_ptr<RemoteSuggestionsDatabase> database, 333 std::unique_ptr<RemoteSuggestionsDatabase> database,
235 std::unique_ptr<RemoteSuggestionsStatusService> status_service) 334 std::unique_ptr<RemoteSuggestionsStatusService> status_service)
236 : ContentSuggestionsProvider(observer, category_factory), 335 : ContentSuggestionsProvider(observer, category_factory),
237 state_(State::NOT_INITED), 336 state_(State::NOT_INITED),
238 pref_service_(pref_service), 337 pref_service_(pref_service),
239 articles_category_( 338 articles_category_(
240 category_factory->FromKnownCategory(KnownCategories::ARTICLES)), 339 category_factory->FromKnownCategory(KnownCategories::ARTICLES)),
241 application_language_code_(application_language_code), 340 application_language_code_(application_language_code),
242 user_classifier_(user_classifier), 341 user_classifier_(user_classifier),
243 scheduler_(scheduler), 342 scheduler_(scheduler),
244 snippets_fetcher_(std::move(snippets_fetcher)), 343 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)), 344 database_(std::move(database)),
345 image_fetcher_(std::move(image_fetcher),
346 std::move(image_decoder),
347 pref_service,
348 database_.get()),
248 status_service_(std::move(status_service)), 349 status_service_(std::move(status_service)),
249 fetch_when_ready_(false), 350 fetch_when_ready_(false),
250 nuke_when_initialized_(false), 351 nuke_when_initialized_(false),
251 thumbnail_requests_throttler_(
252 pref_service,
253 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL),
254 tick_clock_(base::MakeUnique<base::DefaultTickClock>()) { 352 tick_clock_(base::MakeUnique<base::DefaultTickClock>()) {
255 pref_service_->ClearPref(kDeprecatedSnippetHostsPref); 353 pref_service_->ClearPref(kDeprecatedSnippetHostsPref);
256 354
257 RestoreCategoriesFromPrefs(); 355 RestoreCategoriesFromPrefs();
258 // The articles category always exists. Add it if we didn't get it from prefs. 356 // The articles category always exists. Add it if we didn't get it from prefs.
259 // TODO(treib): Rethink this. 357 // TODO(treib): Rethink this.
260 category_contents_.insert( 358 category_contents_.insert(
261 std::make_pair(articles_category_, 359 std::make_pair(articles_category_,
262 CategoryContent(BuildArticleCategoryInfo(base::nullopt)))); 360 CategoryContent(BuildArticleCategoryInfo(base::nullopt))));
263 // Tell the observer about all the categories. 361 // Tell the observer about all the categories.
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 return; 540 return;
443 } 541 }
444 542
445 auto content_it = category_contents_.find(suggestion_id.category()); 543 auto content_it = category_contents_.find(suggestion_id.category());
446 DCHECK(content_it != category_contents_.end()); 544 DCHECK(content_it != category_contents_.end());
447 CategoryContent* content = &content_it->second; 545 CategoryContent* content = &content_it->second;
448 DismissSuggestionFromCategoryContent(content, 546 DismissSuggestionFromCategoryContent(content,
449 suggestion_id.id_within_category()); 547 suggestion_id.id_within_category());
450 } 548 }
451 549
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( 550 void RemoteSuggestionsProvider::ClearHistory(
462 base::Time begin, 551 base::Time begin,
463 base::Time end, 552 base::Time end,
464 const base::Callback<bool(const GURL& url)>& filter) { 553 const base::Callback<bool(const GURL& url)>& filter) {
465 // Both time range and the filter are ignored and all suggestions are removed, 554 // 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 555 // because it is not known which history entries were used for the suggestions
467 // personalization. 556 // personalization.
468 if (!ready()) { 557 if (!ready()) {
469 nuke_when_initialized_ = true; 558 nuke_when_initialized_ = true;
470 } else { 559 } else {
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 const CategoryContent& content = 638 const CategoryContent& content =
550 category_contents_.at(suggestion_id.category()); 639 category_contents_.at(suggestion_id.category());
551 const NTPSnippet* snippet = 640 const NTPSnippet* snippet =
552 content.FindSnippet(suggestion_id.id_within_category()); 641 content.FindSnippet(suggestion_id.id_within_category());
553 if (!snippet) { 642 if (!snippet) {
554 return GURL(); 643 return GURL();
555 } 644 }
556 return snippet->salient_image_url(); 645 return snippet->salient_image_url();
557 } 646 }
558 647
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( 648 void RemoteSuggestionsProvider::OnDatabaseLoaded(
585 NTPSnippet::PtrVector snippets) { 649 NTPSnippet::PtrVector snippets) {
586 if (state_ == State::ERROR_OCCURRED) { 650 if (state_ == State::ERROR_OCCURRED) {
587 return; 651 return;
588 } 652 }
589 DCHECK(state_ == State::NOT_INITED); 653 DCHECK(state_ == State::NOT_INITED);
590 DCHECK(base::ContainsKey(category_contents_, articles_category_)); 654 DCHECK(base::ContainsKey(category_contents_, articles_category_));
591 655
592 base::TimeDelta database_load_time = 656 base::TimeDelta database_load_time =
593 base::TimeTicks::Now() - database_load_start_; 657 base::TimeTicks::Now() - database_load_start_;
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after
939 } 1003 }
940 } 1004 }
941 1005
942 for (Category category : categories_to_erase) { 1006 for (Category category : categories_to_erase) {
943 category_contents_.erase(category); 1007 category_contents_.erase(category);
944 } 1008 }
945 1009
946 StoreCategoriesToPrefs(); 1010 StoreCategoriesToPrefs();
947 } 1011 }
948 1012
949 void RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase( 1013 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, 1014 const ContentSuggestion::ID& suggestion_id,
983 const ImageFetchedCallback& callback) { 1015 const ImageFetchedCallback& callback) {
984 if (!base::ContainsKey(category_contents_, suggestion_id.category())) { 1016 if (!base::ContainsKey(category_contents_, suggestion_id.category())) {
985 OnSnippetImageDecodedFromNetwork( 1017 base::ThreadTaskRunnerHandle::Get()->PostTask(
986 callback, suggestion_id.id_within_category(), gfx::Image()); 1018 FROM_HERE, base::Bind(callback, gfx::Image()));
Marc Treib 2016/12/14 09:02:15 nit: Wrong indentation.
987 return; 1019 return;
988 } 1020 }
989
990 GURL image_url = FindSnippetImageUrl(suggestion_id); 1021 GURL image_url = FindSnippetImageUrl(suggestion_id);
991 1022 if (image_url.is_empty()) {
992 if (image_url.is_empty() || 1023 // As we don't know the corresponding snippet anymore, we don't expect to
993 !thumbnail_requests_throttler_.DemandQuotaForRequest( 1024 // find it in the database (and also can't fetch it remotely). Cut the
994 /*interactive_request=*/true)) { 1025 // lookup short and return directly.
995 // Return an empty image. Directly, this is never synchronous with the 1026 base::ThreadTaskRunnerHandle::Get()->PostTask(
996 // original FetchSuggestionImage() call - an asynchronous database query has 1027 FROM_HERE, base::Bind(callback, gfx::Image()));
997 // happened in the meantime.
998 OnSnippetImageDecodedFromNetwork(
999 callback, suggestion_id.id_within_category(), gfx::Image());
1000 return; 1028 return;
1001 } 1029 }
1002 1030 image_fetcher_.FetchSuggestionImage(suggestion_id, image_url, callback);
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 } 1031 }
1015 1032
1016 void RemoteSuggestionsProvider::EnterStateReady() { 1033 void RemoteSuggestionsProvider::EnterStateReady() {
1017 if (nuke_when_initialized_) { 1034 if (nuke_when_initialized_) {
1018 NukeAllSnippets(); 1035 NukeAllSnippets();
1019 nuke_when_initialized_ = false; 1036 nuke_when_initialized_ = false;
1020 } 1037 }
1021 1038
1022 auto article_category_it = category_contents_.find(articles_category_); 1039 auto article_category_it = category_contents_.find(articles_category_);
1023 DCHECK(article_category_it != category_contents_.end()); 1040 DCHECK(article_category_it != category_contents_.end());
(...skipping 28 matching lines...) Expand all
1052 } 1069 }
1053 1070
1054 void RemoteSuggestionsProvider::FinishInitialization() { 1071 void RemoteSuggestionsProvider::FinishInitialization() {
1055 if (nuke_when_initialized_) { 1072 if (nuke_when_initialized_) {
1056 // We nuke here in addition to EnterStateReady, so that it happens even if 1073 // We nuke here in addition to EnterStateReady, so that it happens even if
1057 // we enter the DISABLED state below. 1074 // we enter the DISABLED state below.
1058 NukeAllSnippets(); 1075 NukeAllSnippets();
1059 nuke_when_initialized_ = false; 1076 nuke_when_initialized_ = false;
1060 } 1077 }
1061 1078
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 1079 // Note: Initializing the status service will run the callback right away with
1070 // the current state. 1080 // the current state.
1071 status_service_->Init(base::Bind(&RemoteSuggestionsProvider::OnStatusChanged, 1081 status_service_->Init(base::Bind(&RemoteSuggestionsProvider::OnStatusChanged,
1072 base::Unretained(this))); 1082 base::Unretained(this)));
1073 1083
1074 // Always notify here even if we got nothing from the database, because we 1084 // 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. 1085 // don't know how long the fetch will take or if it will even complete.
1076 for (const auto& item : category_contents_) { 1086 for (const auto& item : category_contents_) {
1077 Category category = item.first; 1087 Category category = item.first;
1078 const CategoryContent& content = item.second; 1088 const CategoryContent& content = item.second;
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after
1345 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) = 1355 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) =
1346 default; 1356 default;
1347 1357
1348 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default; 1358 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default;
1349 1359
1350 RemoteSuggestionsProvider::CategoryContent& 1360 RemoteSuggestionsProvider::CategoryContent&
1351 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) = 1361 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) =
1352 default; 1362 default;
1353 1363
1354 } // namespace ntp_snippets 1364 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698