• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
DecodeImage(const std::vector<unsigned char> & encoded_data)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 
ImageManagerImpl()26 ImageManagerImpl::ImageManagerImpl() : weak_ptr_factory_(this) {}
27 
ImageManagerImpl(net::URLRequestContextGetter * url_request_context,scoped_ptr<ProtoDatabase<ImageData>> database,const base::FilePath & database_dir)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 
~ImageManagerImpl()40 ImageManagerImpl::~ImageManagerImpl() {}
41 
ImageRequest()42 ImageManagerImpl::ImageRequest::ImageRequest() : fetcher(NULL) {}
43 
ImageRequest(chrome::BitmapFetcher * f)44 ImageManagerImpl::ImageRequest::ImageRequest(chrome::BitmapFetcher* f)
45     : fetcher(f) {}
46 
~ImageRequest()47 ImageManagerImpl::ImageRequest::~ImageRequest() { delete fetcher; }
48 
Initialize(const SuggestionsProfile & suggestions)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 
GetImageForURL(const GURL & url,base::Callback<void (const GURL &,const SkBitmap *)> callback)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 
GetImageURL(const GURL & url,GURL * image_url)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 
QueueCacheRequest(const GURL & url,const GURL & image_url,base::Callback<void (const GURL &,const SkBitmap *)> callback)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 
ServeFromCacheOrNetwork(const GURL & url,const GURL & image_url,base::Callback<void (const GURL &,const SkBitmap *)> callback)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 
ServeFromCache(const GURL & url,base::Callback<void (const GURL &,const SkBitmap *)> callback)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 
GetBitmapFromCache(const GURL & url)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 
StartOrQueueNetworkRequest(const GURL & url,const GURL & image_url,base::Callback<void (const GURL &,const SkBitmap *)> callback)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 
OnFetchComplete(const GURL image_url,const SkBitmap * bitmap)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 
SaveImage(const GURL & url,const SkBitmap & bitmap)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 
OnDatabaseInit(bool success)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 
OnDatabaseLoad(bool success,scoped_ptr<ImageDataVector> entries)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 
OnDatabaseSave(bool success)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 
LoadEntriesInCache(scoped_ptr<ImageDataVector> entries)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 
ServePendingCacheRequests()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
EncodeImage(const SkBitmap & bitmap,std::vector<unsigned char> * dest)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
279