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"
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"
17 const char kSequenceToken
[] = "BookmarkImagesSequenceToken";
19 void ConstructPersistentImageStore(PersistentImageStore
* store
,
20 const base::FilePath
& path
) {
22 new (store
) PersistentImageStore(path
);
25 void DeleteImageStore(ImageStore
* store
) {
30 void RetrieveImageFromStoreRelay(
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
));
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
),
50 DCHECK(CalledOnValidThread());
51 enhanced_bookmark_model_
->bookmark_model()->AddObserver(this);
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.
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
));
78 pool_
->PostNamedSequencedWorkerTask(
81 base::Bind(&ConstructPersistentImageStore
, store
, path
));
84 BookmarkImageService::~BookmarkImageService() {
85 DCHECK(CalledOnValidThread());
86 pool_
->PostNamedSequencedWorkerTask(
89 base::Bind(&DeleteImageStore
, store_
.release()));
92 void BookmarkImageService::Shutdown() {
93 DCHECK(CalledOnValidThread());
94 enhanced_bookmark_model_
->bookmark_model()->RemoveObserver(this);
95 enhanced_bookmark_model_
= NULL
;
98 void BookmarkImageService::SalientImageForUrl(const GURL
& page_url
,
100 DCHECK(CalledOnValidThread());
101 SalientImageForUrl(page_url
, true, callback
);
104 void BookmarkImageService::RetrieveImageFromStore(
105 const GURL
& page_url
,
106 BookmarkImageService::Callback callback
) {
107 DCHECK(CalledOnValidThread());
108 pool_
->PostSequencedWorkerTaskWithShutdownBehavior(
109 pool_
->GetNamedSequenceToken(kSequenceToken
),
111 base::Bind(&RetrieveImageFromStoreRelay
,
112 base::Unretained(store_
.get()),
115 base::ThreadTaskRunnerHandle::Get()),
116 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN
);
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.
125 in_progress_page_urls_
.insert(page_url
);
127 const BookmarkNode
* bookmark
=
128 enhanced_bookmark_model_
->bookmark_model()
129 ->GetMostRecentlyAddedUserNodeForURL(page_url
);
134 enhanced_bookmark_model_
->GetThumbnailImage(
135 bookmark
, &image_url
, &width
, &height
);
138 RetrieveSalientImage(
142 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE
,
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
);
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
);
166 void BookmarkImageService::SalientImageForUrl(const GURL
& page_url
,
167 bool fetch_from_bookmark
,
169 DCHECK(CalledOnValidThread());
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
);
178 if (!fetch_from_bookmark
) {
179 RetrieveImageFromStore(page_url
, callback
);
181 RetrieveImageFromStore(page_url
,
182 base::Bind(&BookmarkImageService::FetchCallback
,
183 base::Unretained(this),
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
);
202 const gfx::Size
& size
= image
.Size();
203 bool result
= enhanced_bookmark_model_
->SetOriginalImage(
204 bookmark
, image_url
, size
.width(), size
.height());
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();
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(
223 base::Bind(&ImageStore::Insert
,
224 base::Unretained(store_
.get()),
231 void BookmarkImageService::RemoveImageForUrl(const GURL
& page_url
) {
232 DCHECK(CalledOnValidThread());
233 pool_
->PostNamedSequencedWorkerTask(
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());
241 void BookmarkImageService::ChangeImageURL(const GURL
& from
, const GURL
& to
) {
242 DCHECK(CalledOnValidThread());
243 pool_
->PostNamedSequencedWorkerTask(kSequenceToken
,
245 base::Bind(&ImageStore::ChangeImageURL
,
246 base::Unretained(store_
.get()),
249 in_progress_page_urls_
.erase(from
);
250 ProcessRequests(from
, gfx::Image(), GURL());
253 void BookmarkImageService::ClearAll() {
254 DCHECK(CalledOnValidThread());
255 // Clears and executes callbacks.
256 pool_
->PostNamedSequencedWorkerTask(
259 base::Bind(&ImageStore::ClearAll
, base::Unretained(store_
.get())));
261 for (std::map
<const GURL
, std::vector
<Callback
> >::const_iterator it
=
263 it
!= callbacks_
.end();
265 ProcessRequests(it
->first
, gfx::Image(), GURL());
268 in_progress_page_urls_
.erase(in_progress_page_urls_
.begin(),
269 in_progress_page_urls_
.end());
272 void BookmarkImageService::ProcessRequests(const GURL
& page_url
,
273 const gfx::Image
& image
,
274 const GURL
& image_url
) {
275 DCHECK(CalledOnValidThread());
277 std::vector
<Callback
> callbacks
= callbacks_
[page_url
];
278 for (std::vector
<Callback
>::const_iterator it
= callbacks
.begin();
279 it
!= callbacks
.end();
281 it
->Run(image
, image_url
);
284 callbacks_
.erase(page_url
);
287 // BookmarkModelObserver methods.
289 void BookmarkImageService::BookmarkNodeRemoved(
290 BookmarkModel
* model
,
291 const BookmarkNode
* parent
,
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();
299 RemoveImageForUrl(*iter
);
303 void BookmarkImageService::BookmarkModelLoaded(BookmarkModel
* model
,
304 bool ids_reassigned
) {
307 void BookmarkImageService::BookmarkNodeMoved(BookmarkModel
* model
,
308 const BookmarkNode
* old_parent
,
310 const BookmarkNode
* new_parent
,
314 void BookmarkImageService::BookmarkNodeAdded(BookmarkModel
* model
,
315 const BookmarkNode
* parent
,
319 void BookmarkImageService::OnWillChangeBookmarkNode(BookmarkModel
* model
,
320 const BookmarkNode
* node
) {
321 DCHECK(CalledOnValidThread());
323 previous_url_
= node
->url();
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());
333 void BookmarkImageService::BookmarkNodeFaviconChanged(
334 BookmarkModel
* model
,
335 const BookmarkNode
* node
) {
338 void BookmarkImageService::BookmarkNodeChildrenReordered(
339 BookmarkModel
* model
,
340 const BookmarkNode
* node
) {
343 void BookmarkImageService::BookmarkAllUserNodesRemoved(
344 BookmarkModel
* model
,
345 const std::set
<GURL
>& removed_urls
) {
349 } // namespace enhanced_bookmarks