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

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: extend test error message 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_clock.h" 21 #include "base/time/default_clock.h"
23 #include "base/time/default_tick_clock.h"
24 #include "base/time/time.h" 22 #include "base/time/time.h"
25 #include "base/values.h" 23 #include "base/values.h"
26 #include "components/data_use_measurement/core/data_use_user_data.h" 24 #include "components/data_use_measurement/core/data_use_user_data.h"
27 #include "components/history/core/browser/history_service.h" 25 #include "components/history/core/browser/history_service.h"
28 #include "components/image_fetcher/image_decoder.h" 26 #include "components/image_fetcher/image_decoder.h"
29 #include "components/image_fetcher/image_fetcher.h" 27 #include "components/image_fetcher/image_fetcher.h"
30 #include "components/ntp_snippets/features.h" 28 #include "components/ntp_snippets/features.h"
31 #include "components/ntp_snippets/pref_names.h" 29 #include "components/ntp_snippets/pref_names.h"
32 #include "components/ntp_snippets/remote/remote_suggestions_database.h" 30 #include "components/ntp_snippets/remote/remote_suggestions_database.h"
33 #include "components/ntp_snippets/switches.h" 31 #include "components/ntp_snippets/switches.h"
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
215 void CallWithEmptyResults(const FetchDoneCallback& callback, 213 void CallWithEmptyResults(const FetchDoneCallback& callback,
216 const Status& status) { 214 const Status& status) {
217 if (callback.is_null()) { 215 if (callback.is_null()) {
218 return; 216 return;
219 } 217 }
220 callback.Run(status, std::vector<ContentSuggestion>()); 218 callback.Run(status, std::vector<ContentSuggestion>());
221 } 219 }
222 220
223 } // namespace 221 } // namespace
224 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
225 RemoteSuggestionsProvider::RemoteSuggestionsProvider( 323 RemoteSuggestionsProvider::RemoteSuggestionsProvider(
226 Observer* observer, 324 Observer* observer,
227 CategoryFactory* category_factory, 325 CategoryFactory* category_factory,
228 PrefService* pref_service, 326 PrefService* pref_service,
229 const std::string& application_language_code, 327 const std::string& application_language_code,
230 const UserClassifier* user_classifier, 328 const UserClassifier* user_classifier,
231 NTPSnippetsScheduler* scheduler, 329 NTPSnippetsScheduler* scheduler,
232 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, 330 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher,
233 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, 331 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
234 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, 332 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
235 std::unique_ptr<RemoteSuggestionsDatabase> database, 333 std::unique_ptr<RemoteSuggestionsDatabase> database,
236 std::unique_ptr<RemoteSuggestionsStatusService> status_service) 334 std::unique_ptr<RemoteSuggestionsStatusService> status_service)
237 : ContentSuggestionsProvider(observer, category_factory), 335 : ContentSuggestionsProvider(observer, category_factory),
238 state_(State::NOT_INITED), 336 state_(State::NOT_INITED),
239 pref_service_(pref_service), 337 pref_service_(pref_service),
240 articles_category_( 338 articles_category_(
241 category_factory->FromKnownCategory(KnownCategories::ARTICLES)), 339 category_factory->FromKnownCategory(KnownCategories::ARTICLES)),
242 application_language_code_(application_language_code), 340 application_language_code_(application_language_code),
243 user_classifier_(user_classifier), 341 user_classifier_(user_classifier),
244 scheduler_(scheduler), 342 scheduler_(scheduler),
245 snippets_fetcher_(std::move(snippets_fetcher)), 343 snippets_fetcher_(std::move(snippets_fetcher)),
246 image_fetcher_(std::move(image_fetcher)),
247 image_decoder_(std::move(image_decoder)),
248 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()),
249 status_service_(std::move(status_service)), 349 status_service_(std::move(status_service)),
250 fetch_when_ready_(false), 350 fetch_when_ready_(false),
251 nuke_when_initialized_(false), 351 nuke_when_initialized_(false),
252 thumbnail_requests_throttler_(
253 pref_service,
254 RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL),
255 clock_(base::MakeUnique<base::DefaultClock>()) { 352 clock_(base::MakeUnique<base::DefaultClock>()) {
256 pref_service_->ClearPref(kDeprecatedSnippetHostsPref); 353 pref_service_->ClearPref(kDeprecatedSnippetHostsPref);
257 354
258 RestoreCategoriesFromPrefs(); 355 RestoreCategoriesFromPrefs();
259 // 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.
260 // TODO(treib): Rethink this. 357 // TODO(treib): Rethink this.
261 category_contents_.insert( 358 category_contents_.insert(
262 std::make_pair(articles_category_, 359 std::make_pair(articles_category_,
263 CategoryContent(BuildArticleCategoryInfo(base::nullopt)))); 360 CategoryContent(BuildArticleCategoryInfo(base::nullopt))));
264 // Tell the observer about all the categories. 361 // Tell the observer about all the categories.
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
429 return; 526 return;
430 } 527 }
431 528
432 auto content_it = category_contents_.find(suggestion_id.category()); 529 auto content_it = category_contents_.find(suggestion_id.category());
433 DCHECK(content_it != category_contents_.end()); 530 DCHECK(content_it != category_contents_.end());
434 CategoryContent* content = &content_it->second; 531 CategoryContent* content = &content_it->second;
435 DismissSuggestionFromCategoryContent(content, 532 DismissSuggestionFromCategoryContent(content,
436 suggestion_id.id_within_category()); 533 suggestion_id.id_within_category());
437 } 534 }
438 535
439 void RemoteSuggestionsProvider::FetchSuggestionImage(
440 const ContentSuggestion::ID& suggestion_id,
441 const ImageFetchedCallback& callback) {
442 database_->LoadImage(
443 suggestion_id.id_within_category(),
444 base::Bind(&RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase,
445 base::Unretained(this), callback, suggestion_id));
446 }
447
448 void RemoteSuggestionsProvider::ClearHistory( 536 void RemoteSuggestionsProvider::ClearHistory(
449 base::Time begin, 537 base::Time begin,
450 base::Time end, 538 base::Time end,
451 const base::Callback<bool(const GURL& url)>& filter) { 539 const base::Callback<bool(const GURL& url)>& filter) {
452 // Both time range and the filter are ignored and all suggestions are removed, 540 // Both time range and the filter are ignored and all suggestions are removed,
453 // because it is not known which history entries were used for the suggestions 541 // because it is not known which history entries were used for the suggestions
454 // personalization. 542 // personalization.
455 if (!ready()) { 543 if (!ready()) {
456 nuke_when_initialized_ = true; 544 nuke_when_initialized_ = true;
457 } else { 545 } else {
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
536 const CategoryContent& content = 624 const CategoryContent& content =
537 category_contents_.at(suggestion_id.category()); 625 category_contents_.at(suggestion_id.category());
538 const NTPSnippet* snippet = 626 const NTPSnippet* snippet =
539 content.FindSnippet(suggestion_id.id_within_category()); 627 content.FindSnippet(suggestion_id.id_within_category());
540 if (!snippet) { 628 if (!snippet) {
541 return GURL(); 629 return GURL();
542 } 630 }
543 return snippet->salient_image_url(); 631 return snippet->salient_image_url();
544 } 632 }
545 633
546 // image_fetcher::ImageFetcherDelegate implementation.
547 void RemoteSuggestionsProvider::OnImageDataFetched(
548 const std::string& id_within_category,
549 const std::string& image_data) {
550 if (image_data.empty()) {
551 return;
552 }
553
554 // Only save the image if the corresponding snippet still exists.
555 bool found = false;
556 for (const auto& entry : category_contents_) {
557 if (entry.second.FindSnippet(id_within_category)) {
558 found = true;
559 break;
560 }
561 }
562 if (!found) {
563 return;
564 }
565
566 // Only cache the data in the DB, the actual serving is done in the callback
567 // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()).
568 database_->SaveImage(id_within_category, image_data);
569 }
570
571 void RemoteSuggestionsProvider::OnDatabaseLoaded( 634 void RemoteSuggestionsProvider::OnDatabaseLoaded(
572 NTPSnippet::PtrVector snippets) { 635 NTPSnippet::PtrVector snippets) {
573 if (state_ == State::ERROR_OCCURRED) { 636 if (state_ == State::ERROR_OCCURRED) {
574 return; 637 return;
575 } 638 }
576 DCHECK(state_ == State::NOT_INITED); 639 DCHECK(state_ == State::NOT_INITED);
577 DCHECK(base::ContainsKey(category_contents_, articles_category_)); 640 DCHECK(base::ContainsKey(category_contents_, articles_category_));
578 641
579 base::TimeDelta database_load_time = 642 base::TimeDelta database_load_time =
580 base::TimeTicks::Now() - database_load_start_; 643 base::TimeTicks::Now() - database_load_start_;
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after
926 } 989 }
927 } 990 }
928 991
929 for (Category category : categories_to_erase) { 992 for (Category category : categories_to_erase) {
930 category_contents_.erase(category); 993 category_contents_.erase(category);
931 } 994 }
932 995
933 StoreCategoriesToPrefs(); 996 StoreCategoriesToPrefs();
934 } 997 }
935 998
936 void RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase( 999 void RemoteSuggestionsProvider::FetchSuggestionImage(
937 const ImageFetchedCallback& callback,
938 const ContentSuggestion::ID& suggestion_id,
939 std::string data) { // SnippetImageCallback requires nonconst reference.
940 // |image_decoder_| is null in tests.
941 if (image_decoder_ && !data.empty()) {
942 image_decoder_->DecodeImage(
943 data, base::Bind(
944 &RemoteSuggestionsProvider::OnSnippetImageDecodedFromDatabase,
945 base::Unretained(this), callback, suggestion_id));
946 return;
947 }
948
949 // Fetching from the DB failed; start a network fetch.
950 FetchSnippetImageFromNetwork(suggestion_id, callback);
951 }
952
953 void RemoteSuggestionsProvider::OnSnippetImageDecodedFromDatabase(
954 const ImageFetchedCallback& callback,
955 const ContentSuggestion::ID& suggestion_id,
956 const gfx::Image& image) {
957 if (!image.IsEmpty()) {
958 callback.Run(image);
959 return;
960 }
961
962 // If decoding the image failed, delete the DB entry.
963 database_->DeleteImage(suggestion_id.id_within_category());
964
965 FetchSnippetImageFromNetwork(suggestion_id, callback);
966 }
967
968 void RemoteSuggestionsProvider::FetchSnippetImageFromNetwork(
969 const ContentSuggestion::ID& suggestion_id, 1000 const ContentSuggestion::ID& suggestion_id,
970 const ImageFetchedCallback& callback) { 1001 const ImageFetchedCallback& callback) {
971 if (!base::ContainsKey(category_contents_, suggestion_id.category())) { 1002 if (!base::ContainsKey(category_contents_, suggestion_id.category())) {
972 OnSnippetImageDecodedFromNetwork( 1003 base::ThreadTaskRunnerHandle::Get()->PostTask(
973 callback, suggestion_id.id_within_category(), gfx::Image()); 1004 FROM_HERE, base::Bind(callback, gfx::Image()));
974 return; 1005 return;
975 } 1006 }
976
977 GURL image_url = FindSnippetImageUrl(suggestion_id); 1007 GURL image_url = FindSnippetImageUrl(suggestion_id);
978 1008 if (image_url.is_empty()) {
979 if (image_url.is_empty() || 1009 // As we don't know the corresponding snippet anymore, we don't expect to
980 !thumbnail_requests_throttler_.DemandQuotaForRequest( 1010 // find it in the database (and also can't fetch it remotely). Cut the
981 /*interactive_request=*/true)) { 1011 // lookup short and return directly.
982 // Return an empty image. Directly, this is never synchronous with the 1012 base::ThreadTaskRunnerHandle::Get()->PostTask(
983 // original FetchSuggestionImage() call - an asynchronous database query has 1013 FROM_HERE, base::Bind(callback, gfx::Image()));
984 // happened in the meantime.
985 OnSnippetImageDecodedFromNetwork(
986 callback, suggestion_id.id_within_category(), gfx::Image());
987 return; 1014 return;
988 } 1015 }
989 1016 image_fetcher_.FetchSuggestionImage(suggestion_id, image_url, callback);
990 image_fetcher_->StartOrQueueNetworkRequest(
991 suggestion_id.id_within_category(), image_url,
992 base::Bind(&RemoteSuggestionsProvider::OnSnippetImageDecodedFromNetwork,
993 base::Unretained(this), callback));
994 }
995
996 void RemoteSuggestionsProvider::OnSnippetImageDecodedFromNetwork(
997 const ImageFetchedCallback& callback,
998 const std::string& id_within_category,
999 const gfx::Image& image) {
1000 callback.Run(image);
1001 } 1017 }
1002 1018
1003 void RemoteSuggestionsProvider::EnterStateReady() { 1019 void RemoteSuggestionsProvider::EnterStateReady() {
1004 if (nuke_when_initialized_) { 1020 if (nuke_when_initialized_) {
1005 NukeAllSnippets(); 1021 NukeAllSnippets();
1006 nuke_when_initialized_ = false; 1022 nuke_when_initialized_ = false;
1007 } 1023 }
1008 1024
1009 auto article_category_it = category_contents_.find(articles_category_); 1025 auto article_category_it = category_contents_.find(articles_category_);
1010 DCHECK(article_category_it != category_contents_.end()); 1026 DCHECK(article_category_it != category_contents_.end());
(...skipping 28 matching lines...) Expand all
1039 } 1055 }
1040 1056
1041 void RemoteSuggestionsProvider::FinishInitialization() { 1057 void RemoteSuggestionsProvider::FinishInitialization() {
1042 if (nuke_when_initialized_) { 1058 if (nuke_when_initialized_) {
1043 // We nuke here in addition to EnterStateReady, so that it happens even if 1059 // We nuke here in addition to EnterStateReady, so that it happens even if
1044 // we enter the DISABLED state below. 1060 // we enter the DISABLED state below.
1045 NukeAllSnippets(); 1061 NukeAllSnippets();
1046 nuke_when_initialized_ = false; 1062 nuke_when_initialized_ = false;
1047 } 1063 }
1048 1064
1049 // |image_fetcher_| can be null in tests.
1050 if (image_fetcher_) {
1051 image_fetcher_->SetImageFetcherDelegate(this);
1052 image_fetcher_->SetDataUseServiceName(
1053 data_use_measurement::DataUseUserData::NTP_SNIPPETS);
1054 }
1055
1056 // Note: Initializing the status service will run the callback right away with 1065 // Note: Initializing the status service will run the callback right away with
1057 // the current state. 1066 // the current state.
1058 status_service_->Init(base::Bind(&RemoteSuggestionsProvider::OnStatusChanged, 1067 status_service_->Init(base::Bind(&RemoteSuggestionsProvider::OnStatusChanged,
1059 base::Unretained(this))); 1068 base::Unretained(this)));
1060 1069
1061 // Always notify here even if we got nothing from the database, because we 1070 // Always notify here even if we got nothing from the database, because we
1062 // don't know how long the fetch will take or if it will even complete. 1071 // don't know how long the fetch will take or if it will even complete.
1063 for (const auto& item : category_contents_) { 1072 for (const auto& item : category_contents_) {
1064 Category category = item.first; 1073 Category category = item.first;
1065 const CategoryContent& content = item.second; 1074 const CategoryContent& content = item.second;
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after
1332 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) = 1341 RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) =
1333 default; 1342 default;
1334 1343
1335 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default; 1344 RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default;
1336 1345
1337 RemoteSuggestionsProvider::CategoryContent& 1346 RemoteSuggestionsProvider::CategoryContent&
1338 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) = 1347 RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) =
1339 default; 1348 default;
1340 1349
1341 } // namespace ntp_snippets 1350 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698