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 13using leveldb_proto::ProtoDatabase; 14 15namespace { 16 17// From JPEG-encoded bytes to SkBitmap. 18SkBitmap* DecodeImage(const std::vector<unsigned char>& encoded_data) { 19 return gfx::JPEGCodec::Decode(&encoded_data[0], encoded_data.size()); 20} 21 22} // namespace 23 24namespace suggestions { 25 26ImageManagerImpl::ImageManagerImpl() : weak_ptr_factory_(this) {} 27 28ImageManagerImpl::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 40ImageManagerImpl::~ImageManagerImpl() {} 41 42ImageManagerImpl::ImageRequest::ImageRequest() : fetcher(NULL) {} 43 44ImageManagerImpl::ImageRequest::ImageRequest(chrome::BitmapFetcher* f) 45 : fetcher(f) {} 46 47ImageManagerImpl::ImageRequest::~ImageRequest() { delete fetcher; } 48 49void 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 59void 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 82bool 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 89void 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 106void 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 115bool 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 126SkBitmap* 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 134void 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 157void 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 183void 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 208void 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 219void 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 233void ImageManagerImpl::OnDatabaseSave(bool success) { 234 if (!success) { 235 DVLOG(1) << "Image database save failed."; 236 database_.reset(); 237 database_ready_ = false; 238 } 239} 240 241void 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 254void 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 266bool 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