Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / enhanced_bookmarks / bookmark_image_service.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 #include "components/enhanced_bookmarks/bookmark_image_service.h"
5
6 #include "base/single_thread_task_runner.h"
7 #include "base/thread_task_runner_handle.h"
8 #include "base/threading/sequenced_worker_pool.h"
9 #include "components/bookmarks/browser/bookmark_model.h"
10 #include "components/bookmarks/browser/bookmark_model_observer.h"
11 #include "components/enhanced_bookmarks/enhanced_bookmark_model.h"
12 #include "components/enhanced_bookmarks/enhanced_bookmark_utils.h"
13 #include "components/enhanced_bookmarks/persistent_image_store.h"
14
15 namespace {
16
17 const char kSequenceToken[] = "BookmarkImagesSequenceToken";
18
19 void ConstructPersistentImageStore(PersistentImageStore* store,
20                                    const base::FilePath& path) {
21   DCHECK(store);
22   new (store) PersistentImageStore(path);
23 }
24
25 void DeleteImageStore(ImageStore* store) {
26   DCHECK(store);
27   delete store;
28 }
29
30 void RetrieveImageFromStoreRelay(
31     ImageStore* store,
32     const GURL& page_url,
33     enhanced_bookmarks::BookmarkImageService::Callback callback,
34     scoped_refptr<base::SingleThreadTaskRunner> origin_loop) {
35   std::pair<gfx::Image, GURL> image_data = store->Get(page_url);
36   origin_loop->PostTask(
37       FROM_HERE, base::Bind(callback, image_data.first, image_data.second));
38 }
39
40 }  // namespace
41
42 namespace enhanced_bookmarks {
43 BookmarkImageService::BookmarkImageService(
44     scoped_ptr<ImageStore> store,
45     EnhancedBookmarkModel* enhanced_bookmark_model,
46     scoped_refptr<base::SequencedWorkerPool> pool)
47     : enhanced_bookmark_model_(enhanced_bookmark_model),
48       store_(store.Pass()),
49       pool_(pool) {
50   DCHECK(CalledOnValidThread());
51   enhanced_bookmark_model_->bookmark_model()->AddObserver(this);
52 }
53
54 BookmarkImageService::BookmarkImageService(
55     const base::FilePath& path,
56     EnhancedBookmarkModel* enhanced_bookmark_model,
57     scoped_refptr<base::SequencedWorkerPool> pool)
58     : enhanced_bookmark_model_(enhanced_bookmark_model), pool_(pool) {
59   DCHECK(CalledOnValidThread());
60   // PersistentImageStore has to be constructed in the thread it will be used,
61   // so we are posting the construction to the thread.  However, we first
62   // allocate memory and keep here. The reason is that, before
63   // PersistentImageStore construction is done, it's possible that
64   // another member function, that posts store_ to the thread, is called.
65   // Although the construction might not be finished yet, we still want to post
66   // the task since it's guaranteed to be constructed by the time it is used, by
67   // the sequential thread task pool.
68   //
69   // Other alternatives:
70   // - Using a lock or WaitableEvent for PersistentImageStore construction.
71   //   But waiting on UI thread is discouraged.
72   // - Posting the current BookmarkImageService instance instead of store_.
73   //   But this will require using a weak pointer and can potentially block
74   //   destroying BookmarkImageService.
75   PersistentImageStore* store =
76       (PersistentImageStore*)::operator new(sizeof(PersistentImageStore));
77   store_.reset(store);
78   pool_->PostNamedSequencedWorkerTask(
79       kSequenceToken,
80       FROM_HERE,
81       base::Bind(&ConstructPersistentImageStore, store, path));
82 }
83
84 BookmarkImageService::~BookmarkImageService() {
85   DCHECK(CalledOnValidThread());
86   pool_->PostNamedSequencedWorkerTask(
87       kSequenceToken,
88       FROM_HERE,
89       base::Bind(&DeleteImageStore, store_.release()));
90 }
91
92 void BookmarkImageService::Shutdown() {
93   DCHECK(CalledOnValidThread());
94   enhanced_bookmark_model_->bookmark_model()->RemoveObserver(this);
95   enhanced_bookmark_model_ = NULL;
96 }
97
98 void BookmarkImageService::SalientImageForUrl(const GURL& page_url,
99                                               Callback callback) {
100   DCHECK(CalledOnValidThread());
101   SalientImageForUrl(page_url, true, callback);
102 }
103
104 void BookmarkImageService::RetrieveImageFromStore(
105     const GURL& page_url,
106     BookmarkImageService::Callback callback) {
107   DCHECK(CalledOnValidThread());
108   pool_->PostSequencedWorkerTaskWithShutdownBehavior(
109       pool_->GetNamedSequenceToken(kSequenceToken),
110       FROM_HERE,
111       base::Bind(&RetrieveImageFromStoreRelay,
112                  base::Unretained(store_.get()),
113                  page_url,
114                  callback,
115                  base::ThreadTaskRunnerHandle::Get()),
116       base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
117 }
118
119 void BookmarkImageService::RetrieveSalientImageForPageUrl(
120     const GURL& page_url) {
121   DCHECK(CalledOnValidThread());
122   if (IsPageUrlInProgress(page_url))
123     return;  // A request for this URL is already in progress.
124
125   in_progress_page_urls_.insert(page_url);
126
127   const BookmarkNode* bookmark =
128       enhanced_bookmark_model_->bookmark_model()
129           ->GetMostRecentlyAddedUserNodeForURL(page_url);
130   GURL image_url;
131   if (bookmark) {
132     int width;
133     int height;
134     enhanced_bookmark_model_->GetThumbnailImage(
135         bookmark, &image_url, &width, &height);
136   }
137
138   RetrieveSalientImage(
139       page_url,
140       image_url,
141       "",
142       net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
143       false);
144 }
145
146 void BookmarkImageService::FetchCallback(const GURL& page_url,
147                                          Callback original_callback,
148                                          const gfx::Image& image,
149                                          const GURL& image_url) {
150   DCHECK(CalledOnValidThread());
151   if (!image.IsEmpty() || !image_url.is_empty()) {
152     // Either the image was in the store or there is no image in the store, but
153     // an URL for an image is present, indicating that a previous attempt to
154     // download the image failed. Just return the image.
155     original_callback.Run(image, image_url);
156   } else {
157     // There is no image in the store, and no previous attempts to retrieve
158     // one. Start a request to retrieve a salient image if there is an image
159     // url set on a bookmark, and then enqueue the request for the image to
160     // be triggered when the retrieval is finished.
161     RetrieveSalientImageForPageUrl(page_url);
162     SalientImageForUrl(page_url, false, original_callback);
163   }
164 }
165
166 void BookmarkImageService::SalientImageForUrl(const GURL& page_url,
167                                               bool fetch_from_bookmark,
168                                               Callback callback) {
169   DCHECK(CalledOnValidThread());
170
171   // If the request is done while the image is currently being retrieved, just
172   // store the appropriate callbacks to call once the image is retrieved.
173   if (IsPageUrlInProgress(page_url)) {
174     callbacks_[page_url].push_back(callback);
175     return;
176   }
177
178   if (!fetch_from_bookmark) {
179     RetrieveImageFromStore(page_url, callback);
180   } else {
181     RetrieveImageFromStore(page_url,
182                            base::Bind(&BookmarkImageService::FetchCallback,
183                                       base::Unretained(this),
184                                       page_url,
185                                       callback));
186   }
187 }
188
189 void BookmarkImageService::ProcessNewImage(const GURL& page_url,
190                                            bool update_bookmarks,
191                                            const gfx::Image& image,
192                                            const GURL& image_url) {
193   DCHECK(CalledOnValidThread());
194   StoreImage(image, image_url, page_url);
195   in_progress_page_urls_.erase(page_url);
196   ProcessRequests(page_url, image, image_url);
197   if (update_bookmarks && image_url.is_valid()) {
198     const BookmarkNode* bookmark =
199         enhanced_bookmark_model_->bookmark_model()
200             ->GetMostRecentlyAddedUserNodeForURL(page_url);
201     if (bookmark) {
202       const gfx::Size& size = image.Size();
203       bool result = enhanced_bookmark_model_->SetOriginalImage(
204           bookmark, image_url, size.width(), size.height());
205       DCHECK(result);
206     }
207   }
208 }
209
210 bool BookmarkImageService::IsPageUrlInProgress(const GURL& page_url) {
211   DCHECK(CalledOnValidThread());
212   return in_progress_page_urls_.find(page_url) != in_progress_page_urls_.end();
213 }
214
215 void BookmarkImageService::StoreImage(const gfx::Image& image,
216                                       const GURL& image_url,
217                                       const GURL& page_url) {
218   DCHECK(CalledOnValidThread());
219   if (!image.IsEmpty()) {
220     pool_->PostNamedSequencedWorkerTask(
221         kSequenceToken,
222         FROM_HERE,
223         base::Bind(&ImageStore::Insert,
224                    base::Unretained(store_.get()),
225                    page_url,
226                    image_url,
227                    image));
228   }
229 }
230
231 void BookmarkImageService::RemoveImageForUrl(const GURL& page_url) {
232   DCHECK(CalledOnValidThread());
233   pool_->PostNamedSequencedWorkerTask(
234       kSequenceToken,
235       FROM_HERE,
236       base::Bind(&ImageStore::Erase, base::Unretained(store_.get()), page_url));
237   in_progress_page_urls_.erase(page_url);
238   ProcessRequests(page_url, gfx::Image(), GURL());
239 }
240
241 void BookmarkImageService::ChangeImageURL(const GURL& from, const GURL& to) {
242   DCHECK(CalledOnValidThread());
243   pool_->PostNamedSequencedWorkerTask(kSequenceToken,
244                                       FROM_HERE,
245                                       base::Bind(&ImageStore::ChangeImageURL,
246                                                  base::Unretained(store_.get()),
247                                                  from,
248                                                  to));
249   in_progress_page_urls_.erase(from);
250   ProcessRequests(from, gfx::Image(), GURL());
251 }
252
253 void BookmarkImageService::ClearAll() {
254   DCHECK(CalledOnValidThread());
255   // Clears and executes callbacks.
256   pool_->PostNamedSequencedWorkerTask(
257       kSequenceToken,
258       FROM_HERE,
259       base::Bind(&ImageStore::ClearAll, base::Unretained(store_.get())));
260
261   for (std::map<const GURL, std::vector<Callback> >::const_iterator it =
262            callbacks_.begin();
263        it != callbacks_.end();
264        ++it) {
265     ProcessRequests(it->first, gfx::Image(), GURL());
266   }
267
268   in_progress_page_urls_.erase(in_progress_page_urls_.begin(),
269                                in_progress_page_urls_.end());
270 }
271
272 void BookmarkImageService::ProcessRequests(const GURL& page_url,
273                                            const gfx::Image& image,
274                                            const GURL& image_url) {
275   DCHECK(CalledOnValidThread());
276
277   std::vector<Callback> callbacks = callbacks_[page_url];
278   for (std::vector<Callback>::const_iterator it = callbacks.begin();
279        it != callbacks.end();
280        ++it) {
281     it->Run(image, image_url);
282   }
283
284   callbacks_.erase(page_url);
285 }
286
287 // BookmarkModelObserver methods.
288
289 void BookmarkImageService::BookmarkNodeRemoved(
290     BookmarkModel* model,
291     const BookmarkNode* parent,
292     int old_index,
293     const BookmarkNode* node,
294     const std::set<GURL>& removed_urls) {
295   DCHECK(CalledOnValidThread());
296   for (std::set<GURL>::const_iterator iter = removed_urls.begin();
297        iter != removed_urls.end();
298        ++iter) {
299     RemoveImageForUrl(*iter);
300   }
301 }
302
303 void BookmarkImageService::BookmarkModelLoaded(BookmarkModel* model,
304                                                bool ids_reassigned) {
305 }
306
307 void BookmarkImageService::BookmarkNodeMoved(BookmarkModel* model,
308                                              const BookmarkNode* old_parent,
309                                              int old_index,
310                                              const BookmarkNode* new_parent,
311                                              int new_index) {
312 }
313
314 void BookmarkImageService::BookmarkNodeAdded(BookmarkModel* model,
315                                              const BookmarkNode* parent,
316                                              int index) {
317 }
318
319 void BookmarkImageService::OnWillChangeBookmarkNode(BookmarkModel* model,
320                                                     const BookmarkNode* node) {
321   DCHECK(CalledOnValidThread());
322   if (node->is_url())
323     previous_url_ = node->url();
324 }
325
326 void BookmarkImageService::BookmarkNodeChanged(BookmarkModel* model,
327                                                const BookmarkNode* node) {
328   DCHECK(CalledOnValidThread());
329   if (node->is_url() && previous_url_ != node->url())
330     ChangeImageURL(previous_url_, node->url());
331 }
332
333 void BookmarkImageService::BookmarkNodeFaviconChanged(
334     BookmarkModel* model,
335     const BookmarkNode* node) {
336 }
337
338 void BookmarkImageService::BookmarkNodeChildrenReordered(
339     BookmarkModel* model,
340     const BookmarkNode* node) {
341 }
342
343 void BookmarkImageService::BookmarkAllUserNodesRemoved(
344     BookmarkModel* model,
345     const std::set<GURL>& removed_urls) {
346   ClearAll();
347 }
348
349 }  // namespace enhanced_bookmarks