1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/suggestions/image_manager.h"
8 #include "base/location.h"
9 #include "base/task_runner_util.h"
10 #include "components/suggestions/image_encoder.h"
11 #include "components/suggestions/image_fetcher.h"
13 using leveldb_proto::ProtoDatabase
;
17 scoped_ptr
<SkBitmap
> DecodeImage(
18 scoped_refptr
<base::RefCountedMemory
> encoded_data
) {
19 return scoped_ptr
<SkBitmap
>(suggestions::DecodeJPEGToSkBitmap(
20 encoded_data
->front(), encoded_data
->size()));
25 namespace suggestions
{
27 ImageManager::ImageManager() : weak_ptr_factory_(this) {}
29 ImageManager::ImageManager(
30 scoped_ptr
<ImageFetcher
> image_fetcher
,
31 scoped_ptr
<ProtoDatabase
<ImageData
>> database
,
32 const base::FilePath
& database_dir
,
33 scoped_refptr
<base::TaskRunner
> background_task_runner
)
34 : image_fetcher_(image_fetcher
.Pass()),
35 database_(database
.Pass()),
36 background_task_runner_(background_task_runner
),
37 database_ready_(false),
38 weak_ptr_factory_(this) {
39 image_fetcher_
->SetImageFetcherDelegate(this);
40 database_
->Init(database_dir
, base::Bind(&ImageManager::OnDatabaseInit
,
41 weak_ptr_factory_
.GetWeakPtr()));
44 ImageManager::~ImageManager() {}
46 ImageManager::ImageCacheRequest::ImageCacheRequest() {}
48 ImageManager::ImageCacheRequest::~ImageCacheRequest() {}
50 void ImageManager::Initialize(const SuggestionsProfile
& suggestions
) {
51 image_url_map_
.clear();
52 for (int i
= 0; i
< suggestions
.suggestions_size(); ++i
) {
53 const ChromeSuggestion
& suggestion
= suggestions
.suggestions(i
);
54 if (suggestion
.has_thumbnail()) {
55 image_url_map_
[GURL(suggestion
.url())] = GURL(suggestion
.thumbnail());
60 void ImageManager::AddImageURL(const GURL
& url
, const GURL
& image_url
) {
61 DCHECK(thread_checker_
.CalledOnValidThread());
62 image_url_map_
[url
] = image_url
;
65 void ImageManager::GetImageForURL(
67 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
68 DCHECK(thread_checker_
.CalledOnValidThread());
69 // If |url| is not found in |image_url_map_|, then invoke |callback| with
70 // NULL since there is no associated image for this |url|.
72 if (!GetImageURL(url
, &image_url
)) {
73 callback
.Run(url
, NULL
);
77 // |database_| can be NULL if something went wrong in initialization.
78 if (database_
.get() && !database_ready_
) {
79 // Once database is initialized, it will serve pending requests from either
81 QueueCacheRequest(url
, image_url
, callback
);
85 ServeFromCacheOrNetwork(url
, image_url
, callback
);
88 void ImageManager::OnImageFetched(const GURL
& url
, const SkBitmap
* bitmap
) {
89 if (bitmap
) // |bitmap| can be nullptr if image fetch was unsuccessful.
90 SaveImage(url
, *bitmap
);
93 bool ImageManager::GetImageURL(const GURL
& url
, GURL
* image_url
) {
95 std::map
<GURL
, GURL
>::iterator it
= image_url_map_
.find(url
);
96 if (it
== image_url_map_
.end()) return false; // Not found.
97 *image_url
= it
->second
;
101 void ImageManager::QueueCacheRequest(
102 const GURL
& url
, const GURL
& image_url
,
103 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
104 // To be served when the database has loaded.
105 ImageCacheRequestMap::iterator it
= pending_cache_requests_
.find(url
);
106 if (it
== pending_cache_requests_
.end()) {
107 ImageCacheRequest request
;
109 request
.image_url
= image_url
;
110 request
.callbacks
.push_back(callback
);
111 pending_cache_requests_
[url
] = request
;
113 // Request already queued for this url.
114 it
->second
.callbacks
.push_back(callback
);
118 void ImageManager::OnCacheImageDecoded(
120 const GURL
& image_url
,
121 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
,
122 scoped_ptr
<SkBitmap
> bitmap
) {
124 callback
.Run(url
, bitmap
.get());
126 image_fetcher_
->StartOrQueueNetworkRequest(url
, image_url
, callback
);
130 scoped_refptr
<base::RefCountedMemory
> ImageManager::GetEncodedImageFromCache(
132 ImageMap::iterator image_iter
= image_map_
.find(url
.spec());
133 if (image_iter
!= image_map_
.end()) {
134 return image_iter
->second
;
139 void ImageManager::ServeFromCacheOrNetwork(
141 const GURL
& image_url
,
142 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
143 scoped_refptr
<base::RefCountedMemory
> encoded_data
=
144 GetEncodedImageFromCache(url
);
145 if (encoded_data
.get()) {
146 base::PostTaskAndReplyWithResult(
147 background_task_runner_
.get(), FROM_HERE
,
148 base::Bind(&DecodeImage
, encoded_data
),
149 base::Bind(&ImageManager::OnCacheImageDecoded
,
150 weak_ptr_factory_
.GetWeakPtr(), url
, image_url
, callback
));
152 image_fetcher_
->StartOrQueueNetworkRequest(url
, image_url
, callback
);
156 void ImageManager::SaveImage(const GURL
& url
, const SkBitmap
& bitmap
) {
157 scoped_refptr
<base::RefCountedBytes
> encoded_data(
158 new base::RefCountedBytes());
159 if (!EncodeSkBitmapToJPEG(bitmap
, &encoded_data
->data())) {
163 // Update the image map.
164 image_map_
.insert({url
.spec(), encoded_data
});
166 if (!database_ready_
) return;
168 // Save the resulting bitmap to the database.
170 data
.set_url(url
.spec());
171 data
.set_data(encoded_data
->front(), encoded_data
->size());
172 scoped_ptr
<ProtoDatabase
<ImageData
>::KeyEntryVector
> entries_to_save(
173 new ProtoDatabase
<ImageData
>::KeyEntryVector());
174 scoped_ptr
<std::vector
<std::string
>> keys_to_remove(
175 new std::vector
<std::string
>());
176 entries_to_save
->push_back(std::make_pair(data
.url(), data
));
177 database_
->UpdateEntries(entries_to_save
.Pass(), keys_to_remove
.Pass(),
178 base::Bind(&ImageManager::OnDatabaseSave
,
179 weak_ptr_factory_
.GetWeakPtr()));
182 void ImageManager::OnDatabaseInit(bool success
) {
184 DVLOG(1) << "Image database init failed.";
186 ServePendingCacheRequests();
189 database_
->LoadEntries(base::Bind(&ImageManager::OnDatabaseLoad
,
190 weak_ptr_factory_
.GetWeakPtr()));
193 void ImageManager::OnDatabaseLoad(bool success
,
194 scoped_ptr
<ImageDataVector
> entries
) {
196 DVLOG(1) << "Image database load failed.";
198 ServePendingCacheRequests();
201 database_ready_
= true;
203 LoadEntriesInCache(entries
.Pass());
204 ServePendingCacheRequests();
207 void ImageManager::OnDatabaseSave(bool success
) {
209 DVLOG(1) << "Image database save failed.";
211 database_ready_
= false;
215 void ImageManager::LoadEntriesInCache(scoped_ptr
<ImageDataVector
> entries
) {
216 for (ImageDataVector::iterator it
= entries
->begin(); it
!= entries
->end();
218 std::vector
<unsigned char> encoded_data(it
->data().begin(),
222 {it
->url(), base::RefCountedBytes::TakeVector(&encoded_data
)});
226 void ImageManager::ServePendingCacheRequests() {
227 for (ImageCacheRequestMap::iterator it
= pending_cache_requests_
.begin();
228 it
!= pending_cache_requests_
.end(); ++it
) {
229 const ImageCacheRequest
& request
= it
->second
;
230 for (CallbackVector::const_iterator callback_it
= request
.callbacks
.begin();
231 callback_it
!= request
.callbacks
.end(); ++callback_it
) {
232 ServeFromCacheOrNetwork(request
.url
, request
.image_url
, *callback_it
);
237 } // namespace suggestions