Home | History | Annotate | Download | only in suggestions
      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
    279