Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / search / suggestions / image_manager_impl.cc
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.
4
5 #include "chrome/browser/search/suggestions/image_manager_impl.h"
6
7 #include "base/memory/ref_counted_memory.h"
8 #include "content/public/browser/browser_thread.h"
9 #include "net/base/load_flags.h"
10 #include "net/url_request/url_request_context_getter.h"
11 #include "ui/gfx/codec/jpeg_codec.h"
12
13 using leveldb_proto::ProtoDatabase;
14
15 namespace {
16
17 // From JPEG-encoded bytes to SkBitmap.
18 SkBitmap* DecodeImage(const std::vector<unsigned char>& encoded_data) {
19   return gfx::JPEGCodec::Decode(&encoded_data[0], encoded_data.size());
20 }
21
22 }  // namespace
23
24 namespace suggestions {
25
26 ImageManagerImpl::ImageManagerImpl() : weak_ptr_factory_(this) {}
27
28 ImageManagerImpl::ImageManagerImpl(
29     net::URLRequestContextGetter* url_request_context,
30     scoped_ptr<ProtoDatabase<ImageData> > database,
31     const base::FilePath& database_dir)
32     : url_request_context_(url_request_context),
33       database_(database.Pass()),
34       database_ready_(false),
35       weak_ptr_factory_(this) {
36   database_->Init(database_dir, base::Bind(&ImageManagerImpl::OnDatabaseInit,
37                                            weak_ptr_factory_.GetWeakPtr()));
38 }
39
40 ImageManagerImpl::~ImageManagerImpl() {}
41
42 ImageManagerImpl::ImageRequest::ImageRequest() : fetcher(NULL) {}
43
44 ImageManagerImpl::ImageRequest::ImageRequest(chrome::BitmapFetcher* f)
45     : fetcher(f) {}
46
47 ImageManagerImpl::ImageRequest::~ImageRequest() { delete fetcher; }
48
49 void ImageManagerImpl::Initialize(const SuggestionsProfile& suggestions) {
50   image_url_map_.clear();
51   for (int i = 0; i < suggestions.suggestions_size(); ++i) {
52     const ChromeSuggestion& suggestion = suggestions.suggestions(i);
53     if (suggestion.has_thumbnail()) {
54       image_url_map_[GURL(suggestion.url())] = GURL(suggestion.thumbnail());
55     }
56   }
57 }
58
59 void ImageManagerImpl::GetImageForURL(
60     const GURL& url,
61     base::Callback<void(const GURL&, const SkBitmap*)> callback) {
62   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
63   // If |url| is not found in |image_url_map_|, then invoke |callback| with
64   // NULL since there is no associated image for this |url|.
65   GURL image_url;
66   if (!GetImageURL(url, &image_url)) {
67     callback.Run(url, NULL);
68     return;
69   }
70
71   // |database_| can be NULL if something went wrong in initialization.
72   if (database_.get() && !database_ready_) {
73     // Once database is initialized, it will serve pending requests from either
74     // cache or network.
75     QueueCacheRequest(url, image_url, callback);
76     return;
77   }
78
79   ServeFromCacheOrNetwork(url, image_url, callback);
80 }
81
82 bool ImageManagerImpl::GetImageURL(const GURL& url, GURL* image_url) {
83   std::map<GURL, GURL>::iterator it = image_url_map_.find(url);
84   if (it == image_url_map_.end()) return false;  // Not found.
85   *image_url = it->second;
86   return true;
87 }
88
89 void ImageManagerImpl::QueueCacheRequest(
90     const GURL& url, const GURL& image_url,
91     base::Callback<void(const GURL&, const SkBitmap*)> callback) {
92   // To be served when the database has loaded.
93   ImageRequestMap::iterator it = pending_cache_requests_.find(url);
94   if (it == pending_cache_requests_.end()) {
95     ImageRequest request(NULL);
96     request.url = url;
97     request.image_url = image_url;
98     request.callbacks.push_back(callback);
99     pending_cache_requests_[url].swap(&request);
100   } else {
101     // Request already queued for this url.
102     it->second.callbacks.push_back(callback);
103   }
104 }
105
106 void ImageManagerImpl::ServeFromCacheOrNetwork(
107     const GURL& url, const GURL& image_url,
108     base::Callback<void(const GURL&, const SkBitmap*)> callback) {
109   // If there is a image available in memory, return it.
110   if (!ServeFromCache(url, callback)) {
111     StartOrQueueNetworkRequest(url, image_url, callback);
112   }
113 }
114
115 bool ImageManagerImpl::ServeFromCache(
116     const GURL& url,
117     base::Callback<void(const GURL&, const SkBitmap*)> callback) {
118   SkBitmap* bitmap = GetBitmapFromCache(url);
119   if (bitmap) {
120     callback.Run(url, bitmap);
121     return true;
122   }
123   return false;
124 }
125
126 SkBitmap* ImageManagerImpl::GetBitmapFromCache(const GURL& url) {
127   ImageMap::iterator image_iter = image_map_.find(url.spec());
128   if (image_iter != image_map_.end()) {
129     return &image_iter->second;
130   }
131   return NULL;
132 }
133
134 void ImageManagerImpl::StartOrQueueNetworkRequest(
135     const GURL& url, const GURL& image_url,
136     base::Callback<void(const GURL&, const SkBitmap*)> callback) {
137   // Before starting to fetch the image. Look for a request in progress for
138   // |image_url|, and queue if appropriate.
139   ImageRequestMap::iterator it = pending_net_requests_.find(image_url);
140   if (it == pending_net_requests_.end()) {
141     // |image_url| is not being fetched, so create a request and initiate
142     // the fetch.
143     ImageRequest request(new chrome::BitmapFetcher(image_url, this));
144     request.url = url;
145     request.callbacks.push_back(callback);
146     request.fetcher->Start(
147         url_request_context_, std::string(),
148         net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
149         net::LOAD_NORMAL);
150     pending_net_requests_[image_url].swap(&request);
151   } else {
152     // Request in progress. Register as an interested callback.
153     it->second.callbacks.push_back(callback);
154   }
155 }
156
157 void ImageManagerImpl::OnFetchComplete(const GURL image_url,
158                                        const SkBitmap* bitmap) {
159   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
160
161   ImageRequestMap::iterator image_iter = pending_net_requests_.find(image_url);
162   DCHECK(image_iter != pending_net_requests_.end());
163
164   ImageRequest* request = &image_iter->second;
165
166   // Here |bitmap| could be NULL or a pointer to a bitmap which is owned by the
167   // BitmapFetcher and which ceases to exist after this function. Pass the
168   // un-owned pointer to the registered callbacks.
169   for (CallbackVector::iterator callback_iter = request->callbacks.begin();
170        callback_iter != request->callbacks.end(); ++callback_iter) {
171     callback_iter->Run(request->url, bitmap);
172   }
173
174   // Save the bitmap to the in-memory model as well as the database, because it
175   // is now the freshest representation of the image.
176   // TODO(mathp): Handle null (no image found), possible deletion in DB.
177   if (bitmap) SaveImage(request->url, *bitmap);
178
179   // Erase the completed ImageRequest.
180   pending_net_requests_.erase(image_iter);
181 }
182
183 void ImageManagerImpl::SaveImage(const GURL& url, const SkBitmap& bitmap) {
184   // Update the image map.
185   image_map_.insert(std::make_pair(url.spec(), bitmap));
186
187   if (!database_ready_) return;
188
189   // Attempt to save a JPEG representation to the database. If not successful,
190   // the fetched bitmap will still be inserted in the cache, above.
191   std::vector<unsigned char> encoded_data;
192   if (EncodeImage(bitmap, &encoded_data)) {
193     // Save the resulting bitmap to the database.
194     ImageData data;
195     data.set_url(url.spec());
196     data.set_data(std::string(encoded_data.begin(), encoded_data.end()));
197     scoped_ptr<ProtoDatabase<ImageData>::KeyEntryVector> entries_to_save(
198         new ProtoDatabase<ImageData>::KeyEntryVector());
199     scoped_ptr<std::vector<std::string> > keys_to_remove(
200         new std::vector<std::string>());
201     entries_to_save->push_back(std::make_pair(data.url(), data));
202     database_->UpdateEntries(entries_to_save.Pass(), keys_to_remove.Pass(),
203                              base::Bind(&ImageManagerImpl::OnDatabaseSave,
204                                         weak_ptr_factory_.GetWeakPtr()));
205   }
206 }
207
208 void ImageManagerImpl::OnDatabaseInit(bool success) {
209   if (!success) {
210     DVLOG(1) << "Image database init failed.";
211     database_.reset();
212     ServePendingCacheRequests();
213     return;
214   }
215   database_->LoadEntries(base::Bind(&ImageManagerImpl::OnDatabaseLoad,
216                                     weak_ptr_factory_.GetWeakPtr()));
217 }
218
219 void ImageManagerImpl::OnDatabaseLoad(bool success,
220                                       scoped_ptr<ImageDataVector> entries) {
221   if (!success) {
222     DVLOG(1) << "Image database load failed.";
223     database_.reset();
224     ServePendingCacheRequests();
225     return;
226   }
227   database_ready_ = true;
228
229   LoadEntriesInCache(entries.Pass());
230   ServePendingCacheRequests();
231 }
232
233 void ImageManagerImpl::OnDatabaseSave(bool success) {
234   if (!success) {
235     DVLOG(1) << "Image database save failed.";
236     database_.reset();
237     database_ready_ = false;
238   }
239 }
240
241 void ImageManagerImpl::LoadEntriesInCache(scoped_ptr<ImageDataVector> entries) {
242   for (ImageDataVector::iterator it = entries->begin(); it != entries->end();
243        ++it) {
244     std::vector<unsigned char> encoded_data(it->data().begin(),
245                                             it->data().end());
246
247     scoped_ptr<SkBitmap> bitmap(DecodeImage(encoded_data));
248     if (bitmap.get()) {
249       image_map_.insert(std::make_pair(it->url(), *bitmap));
250     }
251   }
252 }
253
254 void ImageManagerImpl::ServePendingCacheRequests() {
255   for (ImageRequestMap::iterator it = pending_cache_requests_.begin();
256        it != pending_cache_requests_.end(); ++it) {
257     const ImageRequest& request = it->second;
258     for (CallbackVector::const_iterator callback_it = request.callbacks.begin();
259          callback_it != request.callbacks.end(); ++callback_it) {
260       ServeFromCacheOrNetwork(request.url, request.image_url, *callback_it);
261     }
262   }
263 }
264
265 // static
266 bool ImageManagerImpl::EncodeImage(const SkBitmap& bitmap,
267                                    std::vector<unsigned char>* dest) {
268   SkAutoLockPixels bitmap_lock(bitmap);
269   if (!bitmap.readyToDraw() || bitmap.isNull()) {
270     return false;
271   }
272   return gfx::JPEGCodec::Encode(
273       reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
274       gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(),
275       bitmap.width() * bitmap.bytesPerPixel(), 100, dest);
276 }
277
278 }  // namespace suggestions