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 "components/suggestions/image_encoder.h"
9 #include "components/suggestions/image_fetcher.h"
11 using leveldb_proto::ProtoDatabase
;
13 namespace suggestions
{
15 ImageManager::ImageManager() : weak_ptr_factory_(this) {}
17 ImageManager::ImageManager(scoped_ptr
<ImageFetcher
> image_fetcher
,
18 scoped_ptr
<ProtoDatabase
<ImageData
> > database
,
19 const base::FilePath
& database_dir
)
20 : image_fetcher_(image_fetcher
.Pass()),
21 database_(database
.Pass()),
22 database_ready_(false),
23 weak_ptr_factory_(this) {
24 image_fetcher_
->SetImageFetcherDelegate(this);
25 database_
->Init(database_dir
, base::Bind(&ImageManager::OnDatabaseInit
,
26 weak_ptr_factory_
.GetWeakPtr()));
29 ImageManager::~ImageManager() {}
31 ImageManager::ImageCacheRequest::ImageCacheRequest() {}
33 ImageManager::ImageCacheRequest::~ImageCacheRequest() {}
35 void ImageManager::Initialize(const SuggestionsProfile
& suggestions
) {
36 image_url_map_
.clear();
37 for (int i
= 0; i
< suggestions
.suggestions_size(); ++i
) {
38 const ChromeSuggestion
& suggestion
= suggestions
.suggestions(i
);
39 if (suggestion
.has_thumbnail()) {
40 image_url_map_
[GURL(suggestion
.url())] = GURL(suggestion
.thumbnail());
45 void ImageManager::GetImageForURL(
47 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
48 DCHECK(thread_checker_
.CalledOnValidThread());
49 // If |url| is not found in |image_url_map_|, then invoke |callback| with
50 // NULL since there is no associated image for this |url|.
52 if (!GetImageURL(url
, &image_url
)) {
53 callback
.Run(url
, NULL
);
57 // |database_| can be NULL if something went wrong in initialization.
58 if (database_
.get() && !database_ready_
) {
59 // Once database is initialized, it will serve pending requests from either
61 QueueCacheRequest(url
, image_url
, callback
);
65 ServeFromCacheOrNetwork(url
, image_url
, callback
);
68 void ImageManager::OnImageFetched(const GURL
& url
, const SkBitmap
* bitmap
) {
69 if (bitmap
) // |bitmap| can be nullptr if image fetch was unsuccessful.
70 SaveImage(url
, *bitmap
);
73 bool ImageManager::GetImageURL(const GURL
& url
, GURL
* image_url
) {
75 std::map
<GURL
, GURL
>::iterator it
= image_url_map_
.find(url
);
76 if (it
== image_url_map_
.end()) return false; // Not found.
77 *image_url
= it
->second
;
81 void ImageManager::QueueCacheRequest(
82 const GURL
& url
, const GURL
& image_url
,
83 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
84 // To be served when the database has loaded.
85 ImageCacheRequestMap::iterator it
= pending_cache_requests_
.find(url
);
86 if (it
== pending_cache_requests_
.end()) {
87 ImageCacheRequest request
;
89 request
.image_url
= image_url
;
90 request
.callbacks
.push_back(callback
);
91 pending_cache_requests_
[url
] = request
;
93 // Request already queued for this url.
94 it
->second
.callbacks
.push_back(callback
);
98 void ImageManager::ServeFromCacheOrNetwork(
99 const GURL
& url
, const GURL
& image_url
,
100 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
101 // If there is a image available in memory, return it.
102 if (!ServeFromCache(url
, callback
)) {
103 image_fetcher_
->StartOrQueueNetworkRequest(url
, image_url
, callback
);
107 bool ImageManager::ServeFromCache(
109 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
110 SkBitmap
* bitmap
= GetBitmapFromCache(url
);
112 callback
.Run(url
, bitmap
);
118 SkBitmap
* ImageManager::GetBitmapFromCache(const GURL
& url
) {
119 ImageMap::iterator image_iter
= image_map_
.find(url
.spec());
120 if (image_iter
!= image_map_
.end()) {
121 return &image_iter
->second
;
126 void ImageManager::SaveImage(const GURL
& url
, const SkBitmap
& bitmap
) {
127 // Update the image map.
128 image_map_
.insert(std::make_pair(url
.spec(), bitmap
));
130 if (!database_ready_
) return;
132 // Attempt to save a JPEG representation to the database. If not successful,
133 // the fetched bitmap will still be inserted in the cache, above.
134 std::vector
<unsigned char> encoded_data
;
135 if (EncodeSkBitmapToJPEG(bitmap
, &encoded_data
)) {
136 // Save the resulting bitmap to the database.
138 data
.set_url(url
.spec());
139 data
.set_data(std::string(encoded_data
.begin(), encoded_data
.end()));
140 scoped_ptr
<ProtoDatabase
<ImageData
>::KeyEntryVector
> entries_to_save(
141 new ProtoDatabase
<ImageData
>::KeyEntryVector());
142 scoped_ptr
<std::vector
<std::string
> > keys_to_remove(
143 new std::vector
<std::string
>());
144 entries_to_save
->push_back(std::make_pair(data
.url(), data
));
145 database_
->UpdateEntries(entries_to_save
.Pass(), keys_to_remove
.Pass(),
146 base::Bind(&ImageManager::OnDatabaseSave
,
147 weak_ptr_factory_
.GetWeakPtr()));
151 void ImageManager::OnDatabaseInit(bool success
) {
153 DVLOG(1) << "Image database init failed.";
155 ServePendingCacheRequests();
158 database_
->LoadEntries(base::Bind(&ImageManager::OnDatabaseLoad
,
159 weak_ptr_factory_
.GetWeakPtr()));
162 void ImageManager::OnDatabaseLoad(bool success
,
163 scoped_ptr
<ImageDataVector
> entries
) {
165 DVLOG(1) << "Image database load failed.";
167 ServePendingCacheRequests();
170 database_ready_
= true;
172 LoadEntriesInCache(entries
.Pass());
173 ServePendingCacheRequests();
176 void ImageManager::OnDatabaseSave(bool success
) {
178 DVLOG(1) << "Image database save failed.";
180 database_ready_
= false;
184 void ImageManager::LoadEntriesInCache(scoped_ptr
<ImageDataVector
> entries
) {
185 for (ImageDataVector::iterator it
= entries
->begin(); it
!= entries
->end();
187 std::vector
<unsigned char> encoded_data(it
->data().begin(),
190 scoped_ptr
<SkBitmap
> bitmap(DecodeJPEGToSkBitmap(encoded_data
));
192 image_map_
.insert(std::make_pair(it
->url(), *bitmap
));
197 void ImageManager::ServePendingCacheRequests() {
198 for (ImageCacheRequestMap::iterator it
= pending_cache_requests_
.begin();
199 it
!= pending_cache_requests_
.end(); ++it
) {
200 const ImageCacheRequest
& request
= it
->second
;
201 for (CallbackVector::const_iterator callback_it
= request
.callbacks
.begin();
202 callback_it
!= request
.callbacks
.end(); ++callback_it
) {
203 ServeFromCacheOrNetwork(request
.url
, request
.image_url
, *callback_it
);
208 } // namespace suggestions