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::GetImageForURL(
62 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
63 DCHECK(thread_checker_
.CalledOnValidThread());
64 // If |url| is not found in |image_url_map_|, then invoke |callback| with
65 // NULL since there is no associated image for this |url|.
67 if (!GetImageURL(url
, &image_url
)) {
68 callback
.Run(url
, NULL
);
72 // |database_| can be NULL if something went wrong in initialization.
73 if (database_
.get() && !database_ready_
) {
74 // Once database is initialized, it will serve pending requests from either
76 QueueCacheRequest(url
, image_url
, callback
);
80 ServeFromCacheOrNetwork(url
, image_url
, callback
);
83 void ImageManager::OnImageFetched(const GURL
& url
, const SkBitmap
* bitmap
) {
84 if (bitmap
) // |bitmap| can be nullptr if image fetch was unsuccessful.
85 SaveImage(url
, *bitmap
);
88 bool ImageManager::GetImageURL(const GURL
& url
, GURL
* image_url
) {
90 std::map
<GURL
, GURL
>::iterator it
= image_url_map_
.find(url
);
91 if (it
== image_url_map_
.end()) return false; // Not found.
92 *image_url
= it
->second
;
96 void ImageManager::QueueCacheRequest(
97 const GURL
& url
, const GURL
& image_url
,
98 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
99 // To be served when the database has loaded.
100 ImageCacheRequestMap::iterator it
= pending_cache_requests_
.find(url
);
101 if (it
== pending_cache_requests_
.end()) {
102 ImageCacheRequest request
;
104 request
.image_url
= image_url
;
105 request
.callbacks
.push_back(callback
);
106 pending_cache_requests_
[url
] = request
;
108 // Request already queued for this url.
109 it
->second
.callbacks
.push_back(callback
);
113 void ImageManager::OnCacheImageDecoded(
115 const GURL
& image_url
,
116 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
,
117 scoped_ptr
<SkBitmap
> bitmap
) {
119 callback
.Run(url
, bitmap
.get());
121 image_fetcher_
->StartOrQueueNetworkRequest(url
, image_url
, callback
);
125 scoped_refptr
<base::RefCountedMemory
> ImageManager::GetEncodedImageFromCache(
127 ImageMap::iterator image_iter
= image_map_
.find(url
.spec());
128 if (image_iter
!= image_map_
.end()) {
129 return image_iter
->second
;
134 void ImageManager::ServeFromCacheOrNetwork(
136 const GURL
& image_url
,
137 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
138 scoped_refptr
<base::RefCountedMemory
> encoded_data
=
139 GetEncodedImageFromCache(url
);
140 if (encoded_data
.get()) {
141 base::PostTaskAndReplyWithResult(
142 background_task_runner_
.get(), FROM_HERE
,
143 base::Bind(&DecodeImage
, encoded_data
),
144 base::Bind(&ImageManager::OnCacheImageDecoded
,
145 weak_ptr_factory_
.GetWeakPtr(), url
, image_url
, callback
));
147 image_fetcher_
->StartOrQueueNetworkRequest(url
, image_url
, callback
);
151 void ImageManager::SaveImage(const GURL
& url
, const SkBitmap
& bitmap
) {
152 scoped_refptr
<base::RefCountedBytes
> encoded_data(
153 new base::RefCountedBytes());
154 if (!EncodeSkBitmapToJPEG(bitmap
, &encoded_data
->data())) {
158 // Update the image map.
159 image_map_
.insert({url
.spec(), encoded_data
});
161 if (!database_ready_
) return;
163 // Save the resulting bitmap to the database.
165 data
.set_url(url
.spec());
166 data
.set_data(encoded_data
->front(), encoded_data
->size());
167 scoped_ptr
<ProtoDatabase
<ImageData
>::KeyEntryVector
> entries_to_save(
168 new ProtoDatabase
<ImageData
>::KeyEntryVector());
169 scoped_ptr
<std::vector
<std::string
>> keys_to_remove(
170 new std::vector
<std::string
>());
171 entries_to_save
->push_back(std::make_pair(data
.url(), data
));
172 database_
->UpdateEntries(entries_to_save
.Pass(), keys_to_remove
.Pass(),
173 base::Bind(&ImageManager::OnDatabaseSave
,
174 weak_ptr_factory_
.GetWeakPtr()));
177 void ImageManager::OnDatabaseInit(bool success
) {
179 DVLOG(1) << "Image database init failed.";
181 ServePendingCacheRequests();
184 database_
->LoadEntries(base::Bind(&ImageManager::OnDatabaseLoad
,
185 weak_ptr_factory_
.GetWeakPtr()));
188 void ImageManager::OnDatabaseLoad(bool success
,
189 scoped_ptr
<ImageDataVector
> entries
) {
191 DVLOG(1) << "Image database load failed.";
193 ServePendingCacheRequests();
196 database_ready_
= true;
198 LoadEntriesInCache(entries
.Pass());
199 ServePendingCacheRequests();
202 void ImageManager::OnDatabaseSave(bool success
) {
204 DVLOG(1) << "Image database save failed.";
206 database_ready_
= false;
210 void ImageManager::LoadEntriesInCache(scoped_ptr
<ImageDataVector
> entries
) {
211 for (ImageDataVector::iterator it
= entries
->begin(); it
!= entries
->end();
213 std::vector
<unsigned char> encoded_data(it
->data().begin(),
217 {it
->url(), base::RefCountedBytes::TakeVector(&encoded_data
)});
221 void ImageManager::ServePendingCacheRequests() {
222 for (ImageCacheRequestMap::iterator it
= pending_cache_requests_
.begin();
223 it
!= pending_cache_requests_
.end(); ++it
) {
224 const ImageCacheRequest
& request
= it
->second
;
225 for (CallbackVector::const_iterator callback_it
= request
.callbacks
.begin();
226 callback_it
!= request
.callbacks
.end(); ++callback_it
) {
227 ServeFromCacheOrNetwork(request
.url
, request
.image_url
, *callback_it
);
232 } // namespace suggestions