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"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/task_runner_util.h"
13 #include "base/task_scheduler/post_task.h"
14 #include "components/image_fetcher/core/image_fetcher.h"
15 #include "components/suggestions/image_encoder.h"
16 #include "net/traffic_annotation/network_traffic_annotation.h"
17 #include "ui/gfx/image/image.h"
19 using leveldb_proto::ProtoDatabase;
23 // Statistics are logged to UMA with this string as part of histogram name. They
24 // can all be found under LevelDB.*.ImageManager. Changing this needs to
25 // synchronize with histograms.xml, AND will also become incompatible with older
26 // browsers still reporting the previous values.
27 const char kDatabaseUMAClientName[] = "ImageManager";
29 std::unique_ptr<SkBitmap> DecodeImage(
30 scoped_refptr<base::RefCountedMemory> encoded_data) {
31 return suggestions::DecodeJPEGToSkBitmap(encoded_data->front(),
32 encoded_data->size());
35 constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
36 net::DefineNetworkTrafficAnnotation("suggestions_image_manager", R"(
38 sender: "Suggestions Service Thumbnail Fetch"
40 "Retrieves thumbnails for site suggestions based on the user's "
41 "synced browsing history, for use e.g. on the New Tab page."
43 "Triggered when a thumbnail for a suggestion is required, and no "
44 "local thumbnail is available."
45 data: "The URL for which to retrieve a thumbnail."
46 destination: GOOGLE_OWNED_SERVICE
51 "Users can disable this feature by signing out of Chrome, or "
52 "disabling Sync or History Sync in Chrome settings under 'Advanced "
53 "sync settings...'. The feature is enabled by default."
56 policy_options {mode: MANDATORY}
62 policy_options {mode: MANDATORY}
70 namespace suggestions {
72 ImageManager::ImageManager() : weak_ptr_factory_(this) {}
74 ImageManager::ImageManager(
75 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
76 std::unique_ptr<ProtoDatabase<ImageData>> database,
77 const base::FilePath& database_dir)
78 : image_fetcher_(std::move(image_fetcher)),
79 database_(std::move(database)),
80 background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
81 {base::TaskPriority::USER_VISIBLE})),
82 database_ready_(false),
83 weak_ptr_factory_(this) {
84 database_->Init(kDatabaseUMAClientName, database_dir,
85 leveldb_proto::CreateSimpleOptions(),
86 base::Bind(&ImageManager::OnDatabaseInit,
87 weak_ptr_factory_.GetWeakPtr()));
90 ImageManager::~ImageManager() {}
92 ImageManager::ImageCacheRequest::ImageCacheRequest() {}
94 ImageManager::ImageCacheRequest::ImageCacheRequest(
95 const ImageCacheRequest& other) = default;
97 ImageManager::ImageCacheRequest::~ImageCacheRequest() {}
99 void ImageManager::Initialize(const SuggestionsProfile& suggestions) {
100 image_url_map_.clear();
101 for (int i = 0; i < suggestions.suggestions_size(); ++i) {
102 const ChromeSuggestion& suggestion = suggestions.suggestions(i);
103 if (suggestion.has_thumbnail()) {
104 image_url_map_[GURL(suggestion.url())] = GURL(suggestion.thumbnail());
109 void ImageManager::AddImageURL(const GURL& url, const GURL& image_url) {
110 DCHECK(thread_checker_.CalledOnValidThread());
111 image_url_map_[url] = image_url;
114 void ImageManager::GetImageForURL(const GURL& url, ImageCallback callback) {
115 DCHECK(thread_checker_.CalledOnValidThread());
116 // If |url| is not found in |image_url_map_|, then invoke |callback| with
117 // NULL since there is no associated image for this |url|.
119 if (!GetImageURL(url, &image_url)) {
120 callback.Run(url, gfx::Image());
124 // |database_| can be NULL if something went wrong in initialization.
125 if (database_.get() && !database_ready_) {
126 // Once database is initialized, it will serve pending requests from either
128 QueueCacheRequest(url, image_url, callback);
132 ServeFromCacheOrNetwork(url, image_url, callback);
135 void ImageManager::SaveImageAndForward(
136 const ImageCallback& image_callback,
137 const std::string& url,
138 const gfx::Image& image,
139 const image_fetcher::RequestMetadata& metadata) {
140 // |image| can be empty if image fetch was unsuccessful.
141 if (!image.IsEmpty())
142 SaveImage(url, *image.ToSkBitmap());
144 image_callback.Run(GURL(url), image);
147 bool ImageManager::GetImageURL(const GURL& url, GURL* image_url) {
149 std::map<GURL, GURL>::iterator it = image_url_map_.find(url);
150 if (it == image_url_map_.end())
151 return false; // Not found.
152 *image_url = it->second;
156 void ImageManager::QueueCacheRequest(const GURL& url,
157 const GURL& image_url,
158 ImageCallback callback) {
159 // To be served when the database has loaded.
160 ImageCacheRequestMap::iterator it = pending_cache_requests_.find(url);
161 if (it != pending_cache_requests_.end()) {
162 // Request already queued for this url.
163 it->second.callbacks.push_back(callback);
167 ImageCacheRequest request;
169 request.image_url = image_url;
170 request.callbacks.push_back(callback);
171 pending_cache_requests_[url] = request;
174 void ImageManager::OnCacheImageDecoded(const GURL& url,
175 const GURL& image_url,
176 const ImageCallback& callback,
177 std::unique_ptr<SkBitmap> bitmap) {
179 callback.Run(url, gfx::Image::CreateFrom1xBitmap(*bitmap));
181 image_fetcher_->FetchImage(
182 url.spec(), image_url,
183 base::BindRepeating(&ImageManager::SaveImageAndForward,
184 base::Unretained(this), callback),
189 scoped_refptr<base::RefCountedMemory> ImageManager::GetEncodedImageFromCache(
191 ImageMap::iterator image_iter = image_map_.find(url.spec());
192 if (image_iter != image_map_.end()) {
193 return image_iter->second;
198 void ImageManager::ServeFromCacheOrNetwork(const GURL& url,
199 const GURL& image_url,
200 ImageCallback callback) {
201 scoped_refptr<base::RefCountedMemory> encoded_data =
202 GetEncodedImageFromCache(url);
204 base::PostTaskAndReplyWithResult(
205 background_task_runner_.get(), FROM_HERE,
206 base::Bind(&DecodeImage, encoded_data),
207 base::Bind(&ImageManager::OnCacheImageDecoded,
208 weak_ptr_factory_.GetWeakPtr(), url, image_url, callback));
210 image_fetcher_->FetchImage(
211 url.spec(), image_url,
212 base::BindRepeating(&ImageManager::SaveImageAndForward,
213 base::Unretained(this), callback),
218 void ImageManager::SaveImage(const std::string& url, const SkBitmap& bitmap) {
219 scoped_refptr<base::RefCountedBytes> encoded_data(
220 new base::RefCountedBytes());
221 // TODO(treib): Should encoding happen on the |background_task_runner_|?
222 // *De*coding happens there.
223 if (!EncodeSkBitmapToJPEG(bitmap, &encoded_data->data())) {
227 // Update the image map.
228 image_map_.insert({url, encoded_data});
230 if (!database_ready_)
233 // Save the resulting bitmap to the database.
236 data.set_data(encoded_data->front(), encoded_data->size());
237 std::unique_ptr<ProtoDatabase<ImageData>::KeyEntryVector> entries_to_save(
238 new ProtoDatabase<ImageData>::KeyEntryVector());
239 std::unique_ptr<std::vector<std::string>> keys_to_remove(
240 new std::vector<std::string>());
241 entries_to_save->push_back(std::make_pair(data.url(), data));
242 database_->UpdateEntries(std::move(entries_to_save),
243 std::move(keys_to_remove),
244 base::Bind(&ImageManager::OnDatabaseSave,
245 weak_ptr_factory_.GetWeakPtr()));
248 void ImageManager::OnDatabaseInit(bool success) {
250 DVLOG(1) << "Image database init failed.";
252 ServePendingCacheRequests();
255 database_->LoadEntries(base::Bind(&ImageManager::OnDatabaseLoad,
256 weak_ptr_factory_.GetWeakPtr()));
259 void ImageManager::OnDatabaseLoad(bool success,
260 std::unique_ptr<ImageDataVector> entries) {
262 DVLOG(1) << "Image database load failed.";
264 ServePendingCacheRequests();
267 database_ready_ = true;
269 LoadEntriesInCache(std::move(entries));
270 ServePendingCacheRequests();
273 void ImageManager::OnDatabaseSave(bool success) {
275 DVLOG(1) << "Image database save failed.";
277 database_ready_ = false;
281 void ImageManager::LoadEntriesInCache(
282 std::unique_ptr<ImageDataVector> entries) {
283 for (ImageDataVector::iterator it = entries->begin(); it != entries->end();
285 std::vector<unsigned char> encoded_data(it->data().begin(),
289 {it->url(), base::RefCountedBytes::TakeVector(&encoded_data)});
293 void ImageManager::ServePendingCacheRequests() {
294 for (ImageCacheRequestMap::iterator it = pending_cache_requests_.begin();
295 it != pending_cache_requests_.end(); ++it) {
296 const ImageCacheRequest& request = it->second;
297 for (CallbackVector::const_iterator callback_it = request.callbacks.begin();
298 callback_it != request.callbacks.end(); ++callback_it) {
299 ServeFromCacheOrNetwork(request.url, request.image_url, *callback_it);
302 pending_cache_requests_.clear();
305 } // namespace suggestions