Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / components / enhanced_bookmarks / bookmark_image_service.cc
blob4037733f9e28a8403f3c0b54ea9d7cf402b898cf
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/enhanced_bookmarks/bookmark_image_service.h"
7 #include "base/single_thread_task_runner.h"
8 #include "base/task_runner_util.h"
9 #include "base/thread_task_runner_handle.h"
10 #include "base/threading/sequenced_worker_pool.h"
11 #include "components/bookmarks/browser/bookmark_model.h"
12 #include "components/bookmarks/browser/bookmark_model_observer.h"
13 #include "components/enhanced_bookmarks/enhanced_bookmark_model.h"
14 #include "components/enhanced_bookmarks/enhanced_bookmark_utils.h"
15 #include "components/enhanced_bookmarks/image_store_util.h"
16 #include "components/enhanced_bookmarks/persistent_image_store.h"
18 using bookmarks::BookmarkModel;
19 using bookmarks::BookmarkNode;
21 namespace {
23 const char kSequenceToken[] = "BookmarkImagesSequenceToken";
25 void ConstructPersistentImageStore(PersistentImageStore* store,
26 const base::FilePath& path) {
27 DCHECK(store);
28 new (store) PersistentImageStore(path);
31 void DeleteImageStore(ImageStore* store) {
32 DCHECK(store);
33 delete store;
36 void RetrieveImageFromStoreRelay(
37 ImageStore* store,
38 const GURL& page_url,
39 enhanced_bookmarks::BookmarkImageService::ImageCallback callback,
40 scoped_refptr<base::SingleThreadTaskRunner> origin_loop) {
41 origin_loop->PostTask(FROM_HERE, base::Bind(callback, store->Get(page_url)));
44 } // namespace
46 namespace enhanced_bookmarks {
47 BookmarkImageService::BookmarkImageService(
48 scoped_ptr<ImageStore> store,
49 EnhancedBookmarkModel* enhanced_bookmark_model,
50 scoped_refptr<base::SequencedWorkerPool> pool)
51 : enhanced_bookmark_model_(enhanced_bookmark_model),
52 store_(store.Pass()),
53 pool_(pool) {
54 DCHECK(CalledOnValidThread());
55 enhanced_bookmark_model_->bookmark_model()->AddObserver(this);
58 BookmarkImageService::BookmarkImageService(
59 const base::FilePath& path,
60 EnhancedBookmarkModel* enhanced_bookmark_model,
61 scoped_refptr<base::SequencedWorkerPool> pool)
62 : enhanced_bookmark_model_(enhanced_bookmark_model), pool_(pool) {
63 DCHECK(CalledOnValidThread());
64 // PersistentImageStore has to be constructed in the thread it will be used,
65 // so we are posting the construction to the thread. However, we first
66 // allocate memory and keep here. The reason is that, before
67 // PersistentImageStore construction is done, it's possible that
68 // another member function, that posts store_ to the thread, is called.
69 // Although the construction might not be finished yet, we still want to post
70 // the task since it's guaranteed to be constructed by the time it is used, by
71 // the sequential thread task pool.
73 // Other alternatives:
74 // - Using a lock or WaitableEvent for PersistentImageStore construction.
75 // But waiting on UI thread is discouraged.
76 // - Posting the current BookmarkImageService instance instead of store_.
77 // But this will require using a weak pointer and can potentially block
78 // destroying BookmarkImageService.
79 PersistentImageStore* store =
80 (PersistentImageStore*)::operator new(sizeof(PersistentImageStore));
81 store_.reset(store);
82 pool_->PostNamedSequencedWorkerTask(
83 kSequenceToken,
84 FROM_HERE,
85 base::Bind(&ConstructPersistentImageStore, store, path));
88 BookmarkImageService::~BookmarkImageService() {
89 DCHECK(CalledOnValidThread());
90 pool_->PostNamedSequencedWorkerTask(
91 kSequenceToken,
92 FROM_HERE,
93 base::Bind(&DeleteImageStore, store_.release()));
96 void BookmarkImageService::Shutdown() {
97 DCHECK(CalledOnValidThread());
98 enhanced_bookmark_model_->bookmark_model()->RemoveObserver(this);
99 enhanced_bookmark_model_ = NULL;
102 void BookmarkImageService::SalientImageForUrl(const GURL& page_url,
103 ImageCallback callback) {
104 DCHECK(CalledOnValidThread());
105 SalientImageForUrl(page_url, true, callback);
108 void BookmarkImageService::RetrieveImageFromStore(const GURL& page_url,
109 ImageCallback callback) {
110 DCHECK(CalledOnValidThread());
111 pool_->PostSequencedWorkerTaskWithShutdownBehavior(
112 pool_->GetNamedSequenceToken(kSequenceToken),
113 FROM_HERE,
114 base::Bind(&RetrieveImageFromStoreRelay,
115 base::Unretained(store_.get()),
116 page_url,
117 callback,
118 base::ThreadTaskRunnerHandle::Get()),
119 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
122 void BookmarkImageService::RetrieveSalientImageForPageUrl(
123 const GURL& page_url) {
124 DCHECK(CalledOnValidThread());
125 if (IsPageUrlInProgress(page_url))
126 return; // A request for this URL is already in progress.
128 in_progress_page_urls_.insert(page_url);
130 const BookmarkNode* bookmark =
131 enhanced_bookmark_model_->bookmark_model()
132 ->GetMostRecentlyAddedUserNodeForURL(page_url);
133 GURL image_url;
134 if (bookmark) {
135 int width;
136 int height;
137 enhanced_bookmark_model_->GetThumbnailImage(
138 bookmark, &image_url, &width, &height);
141 RetrieveSalientImage(
142 page_url,
143 image_url,
145 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
146 false);
149 void BookmarkImageService::FetchCallback(const GURL& page_url,
150 ImageCallback original_callback,
151 scoped_refptr<ImageRecord> record) {
152 DCHECK(CalledOnValidThread());
153 if (!record->image->IsEmpty() || !record->url.is_empty()) {
154 // Either the record was in the store or there is no image in the store, but
155 // an URL for a record is present, indicating that a previous attempt to
156 // download the image failed. Just return the record.
157 original_callback.Run(record);
158 } else {
159 // There is no record in the store, and no previous attempts to retrieve
160 // one. Start a request to retrieve a salient image if there is an image
161 // url set on a bookmark, and then enqueue the request for the record to
162 // be triggered when the retrieval is finished.
163 RetrieveSalientImageForPageUrl(page_url);
164 SalientImageForUrl(page_url, false, original_callback);
168 void BookmarkImageService::SalientImageForUrl(const GURL& page_url,
169 bool fetch_from_web,
170 ImageCallback callback) {
171 DCHECK(CalledOnValidThread());
173 // If the request is done while the image is currently being retrieved, just
174 // store the appropriate callbacks to call once the image is retrieved.
175 if (IsPageUrlInProgress(page_url)) {
176 callbacks_[page_url].push_back(callback);
177 return;
180 if (!fetch_from_web) {
181 RetrieveImageFromStore(page_url, callback);
182 } else {
183 RetrieveImageFromStore(page_url,
184 base::Bind(&BookmarkImageService::FetchCallback,
185 base::Unretained(this),
186 page_url,
187 callback));
191 void BookmarkImageService::ProcessNewImage(const GURL& page_url,
192 bool update_bookmarks,
193 const GURL& image_url,
194 scoped_ptr<gfx::Image> image) {
195 DCHECK(CalledOnValidThread());
197 gfx::Size size = image->Size();
198 PostTaskToStoreImage(image.Pass(), image_url, page_url);
200 if (update_bookmarks && image_url.is_valid()) {
201 const BookmarkNode* bookmark =
202 enhanced_bookmark_model_->bookmark_model()
203 ->GetMostRecentlyAddedUserNodeForURL(page_url);
204 if (bookmark) {
206 bool result = enhanced_bookmark_model_->SetOriginalImage(
207 bookmark, image_url, size.width(), size.height());
208 DCHECK(result);
213 bool BookmarkImageService::IsPageUrlInProgress(const GURL& page_url) {
214 DCHECK(CalledOnValidThread());
215 return in_progress_page_urls_.find(page_url) != in_progress_page_urls_.end();
218 scoped_refptr<ImageRecord> BookmarkImageService::ResizeAndStoreImage(
219 scoped_refptr<ImageRecord> image_info,
220 const GURL& page_url) {
222 if (!image_info->image->IsEmpty()) {
223 image_info->image = ResizeImage(*image_info->image);
224 image_info->dominant_color = DominantColorForImage(*image_info->image);
225 // TODO(lpromero): this should be saved all the time, even when there is an
226 // empty image. http://crbug.com/451450
227 pool_->PostNamedSequencedWorkerTask(
228 kSequenceToken, FROM_HERE,
229 base::Bind(&ImageStore::Insert, base::Unretained(store_.get()),
230 page_url, image_info));
233 return image_info;
236 void BookmarkImageService::PostTaskToStoreImage(
237 scoped_ptr<gfx::Image> image,
238 const GURL& image_url,
239 const GURL& page_url) {
240 DCHECK(CalledOnValidThread());
242 scoped_refptr<ImageRecord> image_info(
243 new ImageRecord(image.Pass(), image_url));
245 // TODO ensure image thread safety.
246 base::Callback<scoped_refptr<ImageRecord>(void)> task =
247 base::Bind(&BookmarkImageService::ResizeAndStoreImage,
248 base::Unretained(this), image_info, page_url);
249 base::Callback<void(scoped_refptr<ImageRecord>)> reply =
250 base::Bind(&BookmarkImageService::OnStoreImagePosted,
251 base::Unretained(this), page_url);
253 base::PostTaskAndReplyWithResult(pool_.get(), FROM_HERE, task, reply);
256 void BookmarkImageService::OnStoreImagePosted(
257 const GURL& page_url,
258 scoped_refptr<ImageRecord> image) {
259 DCHECK(CalledOnValidThread());
260 in_progress_page_urls_.erase(page_url);
261 ProcessRequests(page_url, image);
264 void BookmarkImageService::RemoveImageForUrl(const GURL& page_url) {
265 DCHECK(CalledOnValidThread());
266 pool_->PostNamedSequencedWorkerTask(
267 kSequenceToken,
268 FROM_HERE,
269 base::Bind(&ImageStore::Erase, base::Unretained(store_.get()), page_url));
270 in_progress_page_urls_.erase(page_url);
271 ProcessRequests(page_url, scoped_refptr<ImageRecord>(new ImageRecord()));
274 void BookmarkImageService::ChangeImageURL(const GURL& from, const GURL& to) {
275 DCHECK(CalledOnValidThread());
276 pool_->PostNamedSequencedWorkerTask(kSequenceToken,
277 FROM_HERE,
278 base::Bind(&ImageStore::ChangeImageURL,
279 base::Unretained(store_.get()),
280 from,
281 to));
282 in_progress_page_urls_.erase(from);
283 ProcessRequests(from, scoped_refptr<ImageRecord>(new ImageRecord()));
286 void BookmarkImageService::ClearAll() {
287 DCHECK(CalledOnValidThread());
288 // Clears and executes callbacks.
289 pool_->PostNamedSequencedWorkerTask(
290 kSequenceToken,
291 FROM_HERE,
292 base::Bind(&ImageStore::ClearAll, base::Unretained(store_.get())));
294 for (std::map<const GURL, std::vector<ImageCallback>>::const_iterator it =
295 callbacks_.begin();
296 it != callbacks_.end(); ++it) {
297 ProcessRequests(it->first, scoped_refptr<ImageRecord>(new ImageRecord()));
300 in_progress_page_urls_.erase(in_progress_page_urls_.begin(),
301 in_progress_page_urls_.end());
304 void BookmarkImageService::ProcessRequests(const GURL& page_url,
305 scoped_refptr<ImageRecord> record) {
306 DCHECK(CalledOnValidThread());
308 std::vector<ImageCallback> callbacks = callbacks_[page_url];
309 for (std::vector<ImageCallback>::const_iterator it = callbacks.begin();
310 it != callbacks.end(); ++it) {
311 it->Run(record);
314 callbacks_.erase(page_url);
317 // BookmarkModelObserver methods.
319 void BookmarkImageService::BookmarkNodeRemoved(
320 BookmarkModel* model,
321 const BookmarkNode* parent,
322 int old_index,
323 const BookmarkNode* node,
324 const std::set<GURL>& removed_urls) {
325 DCHECK(CalledOnValidThread());
326 for (std::set<GURL>::const_iterator iter = removed_urls.begin();
327 iter != removed_urls.end();
328 ++iter) {
329 RemoveImageForUrl(*iter);
333 void BookmarkImageService::BookmarkModelLoaded(BookmarkModel* model,
334 bool ids_reassigned) {
337 void BookmarkImageService::BookmarkNodeMoved(BookmarkModel* model,
338 const BookmarkNode* old_parent,
339 int old_index,
340 const BookmarkNode* new_parent,
341 int new_index) {
344 void BookmarkImageService::BookmarkNodeAdded(BookmarkModel* model,
345 const BookmarkNode* parent,
346 int index) {
349 void BookmarkImageService::OnWillChangeBookmarkNode(BookmarkModel* model,
350 const BookmarkNode* node) {
351 DCHECK(CalledOnValidThread());
352 if (node->is_url())
353 previous_url_ = node->url();
356 void BookmarkImageService::BookmarkNodeChanged(BookmarkModel* model,
357 const BookmarkNode* node) {
358 DCHECK(CalledOnValidThread());
359 if (node->is_url() && previous_url_ != node->url())
360 ChangeImageURL(previous_url_, node->url());
363 void BookmarkImageService::BookmarkNodeFaviconChanged(
364 BookmarkModel* model,
365 const BookmarkNode* node) {
368 void BookmarkImageService::BookmarkNodeChildrenReordered(
369 BookmarkModel* model,
370 const BookmarkNode* node) {
373 void BookmarkImageService::BookmarkAllUserNodesRemoved(
374 BookmarkModel* model,
375 const std::set<GURL>& removed_urls) {
376 ClearAll();
379 } // namespace enhanced_bookmarks