Upload upstream chromium 69.0.3497
[platform/framework/web/chromium-efl.git] / components / suggestions / image_manager.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 "components/suggestions/image_manager.h"
6
7 #include <memory>
8 #include <utility>
9
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"
18
19 using leveldb_proto::ProtoDatabase;
20
21 namespace {
22
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";
28
29 std::unique_ptr<SkBitmap> DecodeImage(
30     scoped_refptr<base::RefCountedMemory> encoded_data) {
31   return suggestions::DecodeJPEGToSkBitmap(encoded_data->front(),
32                                            encoded_data->size());
33 }
34
35 constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
36     net::DefineNetworkTrafficAnnotation("suggestions_image_manager", R"(
37         semantics {
38           sender: "Suggestions Service Thumbnail Fetch"
39           description:
40             "Retrieves thumbnails for site suggestions based on the user's "
41             "synced browsing history, for use e.g. on the New Tab page."
42           trigger:
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
47         }
48         policy {
49           cookies_allowed: NO
50           setting:
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."
54         chrome_policy {
55           SyncDisabled {
56             policy_options {mode: MANDATORY}
57             SyncDisabled: true
58           }
59         }
60         chrome_policy {
61           SigninAllowed {
62             policy_options {mode: MANDATORY}
63             SigninAllowed: false
64           }
65         }
66       })");
67
68 }  // namespace
69
70 namespace suggestions {
71
72 ImageManager::ImageManager() : weak_ptr_factory_(this) {}
73
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()));
88 }
89
90 ImageManager::~ImageManager() {}
91
92 ImageManager::ImageCacheRequest::ImageCacheRequest() {}
93
94 ImageManager::ImageCacheRequest::ImageCacheRequest(
95     const ImageCacheRequest& other) = default;
96
97 ImageManager::ImageCacheRequest::~ImageCacheRequest() {}
98
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());
105     }
106   }
107 }
108
109 void ImageManager::AddImageURL(const GURL& url, const GURL& image_url) {
110   DCHECK(thread_checker_.CalledOnValidThread());
111   image_url_map_[url] = image_url;
112 }
113
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|.
118   GURL image_url;
119   if (!GetImageURL(url, &image_url)) {
120     callback.Run(url, gfx::Image());
121     return;
122   }
123
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
127     // cache or network.
128     QueueCacheRequest(url, image_url, callback);
129     return;
130   }
131
132   ServeFromCacheOrNetwork(url, image_url, callback);
133 }
134
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());
143
144   image_callback.Run(GURL(url), image);
145 }
146
147 bool ImageManager::GetImageURL(const GURL& url, GURL* image_url) {
148   DCHECK(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;
153   return true;
154 }
155
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);
164     return;
165   }
166
167   ImageCacheRequest request;
168   request.url = url;
169   request.image_url = image_url;
170   request.callbacks.push_back(callback);
171   pending_cache_requests_[url] = request;
172 }
173
174 void ImageManager::OnCacheImageDecoded(const GURL& url,
175                                        const GURL& image_url,
176                                        const ImageCallback& callback,
177                                        std::unique_ptr<SkBitmap> bitmap) {
178   if (bitmap) {
179     callback.Run(url, gfx::Image::CreateFrom1xBitmap(*bitmap));
180   } else {
181     image_fetcher_->FetchImage(
182         url.spec(), image_url,
183         base::BindRepeating(&ImageManager::SaveImageAndForward,
184                             base::Unretained(this), callback),
185         kTrafficAnnotation);
186   }
187 }
188
189 scoped_refptr<base::RefCountedMemory> ImageManager::GetEncodedImageFromCache(
190     const GURL& url) {
191   ImageMap::iterator image_iter = image_map_.find(url.spec());
192   if (image_iter != image_map_.end()) {
193     return image_iter->second;
194   }
195   return nullptr;
196 }
197
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);
203   if (encoded_data) {
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));
209   } else {
210     image_fetcher_->FetchImage(
211         url.spec(), image_url,
212         base::BindRepeating(&ImageManager::SaveImageAndForward,
213                             base::Unretained(this), callback),
214         kTrafficAnnotation);
215   }
216 }
217
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())) {
224     return;
225   }
226
227   // Update the image map.
228   image_map_.insert({url, encoded_data});
229
230   if (!database_ready_)
231     return;
232
233   // Save the resulting bitmap to the database.
234   ImageData data;
235   data.set_url(url);
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()));
246 }
247
248 void ImageManager::OnDatabaseInit(bool success) {
249   if (!success) {
250     DVLOG(1) << "Image database init failed.";
251     database_.reset();
252     ServePendingCacheRequests();
253     return;
254   }
255   database_->LoadEntries(base::Bind(&ImageManager::OnDatabaseLoad,
256                                     weak_ptr_factory_.GetWeakPtr()));
257 }
258
259 void ImageManager::OnDatabaseLoad(bool success,
260                                   std::unique_ptr<ImageDataVector> entries) {
261   if (!success) {
262     DVLOG(1) << "Image database load failed.";
263     database_.reset();
264     ServePendingCacheRequests();
265     return;
266   }
267   database_ready_ = true;
268
269   LoadEntriesInCache(std::move(entries));
270   ServePendingCacheRequests();
271 }
272
273 void ImageManager::OnDatabaseSave(bool success) {
274   if (!success) {
275     DVLOG(1) << "Image database save failed.";
276     database_.reset();
277     database_ready_ = false;
278   }
279 }
280
281 void ImageManager::LoadEntriesInCache(
282     std::unique_ptr<ImageDataVector> entries) {
283   for (ImageDataVector::iterator it = entries->begin(); it != entries->end();
284        ++it) {
285     std::vector<unsigned char> encoded_data(it->data().begin(),
286                                             it->data().end());
287
288     image_map_.insert(
289         {it->url(), base::RefCountedBytes::TakeVector(&encoded_data)});
290   }
291 }
292
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);
300     }
301   }
302   pending_cache_requests_.clear();
303 }
304
305 }  // namespace suggestions