Chromium Code Reviews| Index: components/enhanced_bookmarks/bookmark_image_service.cc |
| diff --git a/components/enhanced_bookmarks/bookmark_image_service.cc b/components/enhanced_bookmarks/bookmark_image_service.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..5f515a9fbfd56e46d30323b27fd49f2dfd317b91 |
| --- /dev/null |
| +++ b/components/enhanced_bookmarks/bookmark_image_service.cc |
| @@ -0,0 +1,340 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| +#include "components/enhanced_bookmarks/bookmark_image_service.h" |
| + |
| +#include "base/single_thread_task_runner.h" |
| +#include "base/thread_task_runner_handle.h" |
| +#include "base/threading/sequenced_worker_pool.h" |
| +#include "components/bookmarks/browser/bookmark_model.h" |
| +#include "components/bookmarks/browser/bookmark_model_observer.h" |
| +#include "components/enhanced_bookmarks/enhanced_bookmark_utils.h" |
| +#include "components/enhanced_bookmarks/metadata_accessor.h" |
| +#include "components/enhanced_bookmarks/persistent_image_store.h" |
| + |
| +namespace { |
| + |
| +const char kSequenceToken[] = "BookmarkImagesSequenceToken"; |
| + |
| +void ConstructPersistentImageStore(PersistentImageStore* store, |
| + const base::FilePath& path) { |
| + DCHECK(store); |
| + new (store) PersistentImageStore(path); |
| +} |
| + |
| +void DeleteImageStore(ImageStore* store) { |
| + DCHECK(store); |
| + delete store; |
| +} |
| + |
| +void RetrieveImageFromStoreRelay( |
| + ImageStore* store, |
| + const GURL& page_url, |
| + enhanced_bookmarks::BookmarkImageService::Callback callback, |
| + scoped_refptr<base::SingleThreadTaskRunner> origin_loop) { |
| + std::pair<gfx::Image, GURL> image_data = store->Get(page_url); |
| + origin_loop->PostTask( |
| + FROM_HERE, base::Bind(callback, image_data.first, image_data.second)); |
| +} |
| + |
| +} // namespace |
| + |
| +namespace enhanced_bookmarks { |
| +BookmarkImageService::BookmarkImageService( |
| + scoped_ptr<ImageStore> store, |
| + BookmarkModel* bookmark_model, |
| + scoped_refptr<base::SequencedWorkerPool> pool) |
| + : bookmark_model_(bookmark_model), store_(store.Pass()), pool_(pool) { |
| + DCHECK(CalledOnValidThread()); |
| + bookmark_model_->AddObserver(this); |
| +} |
| + |
| +BookmarkImageService::BookmarkImageService( |
| + const base::FilePath& path, |
| + BookmarkModel* bookmark_model, |
| + scoped_refptr<base::SequencedWorkerPool> pool) |
| + : bookmark_model_(bookmark_model), pool_(pool) { |
| + DCHECK(CalledOnValidThread()); |
| + // PersistentImageStore has to be constructed in the thread it will be used, |
| + // so we are posting the construction to the thread. However, we first |
| + // allocate memory and keep here. The reason is that, before |
| + // PersistentImageStore construction is done, it's possible that |
| + // another member function, that posts store_ to the thread, is called. |
| + // Although the construction might not be finished yet, we still want to post |
| + // the task since it's guaranteed to be constructed by the time it is used, by |
| + // the sequential thread task pool. |
| + // |
| + // Other alternatives: |
| + // - Using a lock or WaitableEvent for PersistentImageStore construction. |
| + // But waiting on UI thread is discouraged. |
| + // - Posting the current BookmarkImageService instance instead of store_. |
| + // But this will require using a weak pointer and can potentially block |
| + // destroying BookmarkImageService. |
| + PersistentImageStore* store = |
| + (PersistentImageStore*)::operator new(sizeof(PersistentImageStore)); |
| + store_.reset(store); |
| + pool_->PostNamedSequencedWorkerTask( |
| + kSequenceToken, |
| + FROM_HERE, |
| + base::Bind(&ConstructPersistentImageStore, store, path)); |
| +} |
| + |
| +BookmarkImageService::~BookmarkImageService() { |
| + DCHECK(CalledOnValidThread()); |
| + bookmark_model_->RemoveObserver(this); |
| + pool_->PostNamedSequencedWorkerTask( |
| + kSequenceToken, |
| + FROM_HERE, |
| + base::Bind(&DeleteImageStore, store_.release())); |
| +} |
| + |
| +void BookmarkImageService::SalientImageForUrl(const GURL& page_url, |
| + Callback callback) { |
| + DCHECK(CalledOnValidThread()); |
| + SalientImageForUrl(page_url, true, callback); |
| +} |
| + |
| +void BookmarkImageService::RetrieveImageFromStore( |
| + const GURL& page_url, |
| + BookmarkImageService::Callback callback) { |
| + DCHECK(CalledOnValidThread()); |
| + pool_->PostSequencedWorkerTaskWithShutdownBehavior( |
| + pool_->GetNamedSequenceToken(kSequenceToken), |
| + FROM_HERE, |
| + base::Bind(&RetrieveImageFromStoreRelay, |
| + base::Unretained(store_.get()), |
| + page_url, |
| + callback, |
| + base::ThreadTaskRunnerHandle::Get()), |
| + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
| +} |
| + |
| +void BookmarkImageService::RetrieveSalientImageForPageUrl( |
| + const GURL& page_url) { |
| + DCHECK(CalledOnValidThread()); |
| + if (IsPageUrlInProgress(page_url)) |
| + return; // A request for this URL is already in progress. |
| + |
| + in_progress_page_urls_.insert(page_url); |
| + |
| + const BookmarkNode* bookmark = |
| + bookmark_model_->GetMostRecentlyAddedUserNodeForURL(page_url); |
| + GURL image_url; |
| + if (bookmark) { |
| + int width; |
| + int height; |
| + enhanced_bookmarks::ThumbnailImageFromBookmark( |
| + bookmark, &image_url, &width, &height); |
| + } |
| + |
| + RetrieveSalientImage( |
| + page_url, |
| + image_url, |
| + "", |
| + net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
|
wtc
2014/08/28 23:45:50
If we're passing a hardcoded value here, does Retr
noyau (Ping after 24h)
2014/08/29 08:44:01
RetrieveSalientImage() is protected and also calle
|
| + false); |
| +} |
| + |
| +void BookmarkImageService::FetchCallback(const GURL& page_url, |
| + Callback original_callback, |
| + const gfx::Image& image, |
| + const GURL& image_url) { |
| + DCHECK(CalledOnValidThread()); |
| + if (!image.IsEmpty() || !image_url.is_empty()) { |
| + // Either the image was in the store or there is no image in the store, but |
| + // an URL for an image is present, indicating that a previous attempt to |
| + // download the image failed. Just return the image. |
| + original_callback.Run(image, image_url); |
| + } else { |
| + // There is no image in the store, and no previous attempts to retrieve |
| + // one. Start a request to retrieve a salient image if there is an image |
| + // url set on a bookmark, and then enqueue the request for the image to |
| + // be triggered when the retrieval is finished. |
| + RetrieveSalientImageForPageUrl(page_url); |
| + SalientImageForUrl(page_url, false, original_callback); |
| + } |
| +} |
| + |
| +void BookmarkImageService::SalientImageForUrl(const GURL& page_url, |
| + bool fetch_from_bookmark, |
| + Callback callback) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + // If the request is done while the image is currently being retrieved, just |
| + // store the appropriate callbacks to call once the image is retrieved. |
| + if (IsPageUrlInProgress(page_url)) { |
| + callbacks_[page_url].push_back(callback); |
| + return; |
| + } |
| + |
| + if (!fetch_from_bookmark) { |
| + RetrieveImageFromStore(page_url, callback); |
| + } else { |
| + RetrieveImageFromStore(page_url, |
| + base::Bind(&BookmarkImageService::FetchCallback, |
| + base::Unretained(this), |
| + page_url, |
| + callback)); |
| + } |
| +} |
| + |
| +void BookmarkImageService::ProcessNewImage(const GURL& page_url, |
| + bool update_bookmarks, |
| + const gfx::Image& image, |
| + const GURL& image_url) { |
| + DCHECK(CalledOnValidThread()); |
| + StoreImage(image, image_url, page_url); |
| + in_progress_page_urls_.erase(page_url); |
| + ProcessRequests(page_url, image, image_url); |
| + if (update_bookmarks && image_url.is_valid()) { |
| + const BookmarkNode* bookmark = |
| + bookmark_model_->GetMostRecentlyAddedUserNodeForURL(page_url); |
| + if (bookmark) { |
| + const gfx::Size& size = image.Size(); |
| + bool result = enhanced_bookmarks::SetOriginalImageForBookmark( |
| + bookmark_model_, bookmark, image_url, size.width(), size.height()); |
| + DCHECK(result); |
| + } |
| + } |
| +} |
| + |
| +bool BookmarkImageService::IsPageUrlInProgress(const GURL& page_url) { |
| + DCHECK(CalledOnValidThread()); |
| + return in_progress_page_urls_.find(page_url) != in_progress_page_urls_.end(); |
| +} |
| + |
| +void BookmarkImageService::StoreImage(const gfx::Image& image, |
| + const GURL& image_url, |
| + const GURL& page_url) { |
| + DCHECK(CalledOnValidThread()); |
| + if (!image.IsEmpty()) { |
| + pool_->PostNamedSequencedWorkerTask( |
| + kSequenceToken, |
| + FROM_HERE, |
| + base::Bind(&ImageStore::Insert, |
| + base::Unretained(store_.get()), |
| + page_url, |
| + image_url, |
| + image)); |
| + } |
| +} |
| + |
| +void BookmarkImageService::RemoveImageForUrl(const GURL& page_url) { |
| + DCHECK(CalledOnValidThread()); |
| + pool_->PostNamedSequencedWorkerTask( |
| + kSequenceToken, |
| + FROM_HERE, |
| + base::Bind(&ImageStore::Erase, base::Unretained(store_.get()), page_url)); |
| + in_progress_page_urls_.erase(page_url); |
| + ProcessRequests(page_url, gfx::Image(), GURL()); |
| +} |
| + |
| +void BookmarkImageService::ChangeImageURL(const GURL& from, const GURL& to) { |
| + DCHECK(CalledOnValidThread()); |
| + pool_->PostNamedSequencedWorkerTask(kSequenceToken, |
| + FROM_HERE, |
| + base::Bind(&ImageStore::ChangeImageURL, |
| + base::Unretained(store_.get()), |
| + from, |
| + to)); |
| + in_progress_page_urls_.erase(from); |
| + ProcessRequests(from, gfx::Image(), GURL()); |
| +} |
| + |
| +void BookmarkImageService::ClearAll() { |
| + DCHECK(CalledOnValidThread()); |
| + // Clears and executes callbacks. |
| + pool_->PostNamedSequencedWorkerTask( |
| + kSequenceToken, |
| + FROM_HERE, |
| + base::Bind(&ImageStore::ClearAll, base::Unretained(store_.get()))); |
| + |
| + for (std::map<const GURL, std::vector<Callback> >::const_iterator it = |
| + callbacks_.begin(); |
| + it != callbacks_.end(); |
| + ++it) { |
| + ProcessRequests(it->first, gfx::Image(), GURL()); |
| + } |
| + |
| + in_progress_page_urls_.erase(in_progress_page_urls_.begin(), |
| + in_progress_page_urls_.end()); |
| +} |
| + |
| +void BookmarkImageService::ProcessRequests(const GURL& page_url, |
| + const gfx::Image& image, |
| + const GURL& image_url) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + std::vector<Callback> callbacks = callbacks_[page_url]; |
| + for (std::vector<Callback>::const_iterator it = callbacks.begin(); |
| + it != callbacks.end(); |
| + ++it) { |
| + it->Run(image, image_url); |
| + } |
| + |
| + callbacks_.erase(page_url); |
| +} |
| + |
| +// BookmarkModelObserver methods. |
| + |
| +void BookmarkImageService::BookmarkNodeRemoved( |
| + BookmarkModel* model, |
| + const BookmarkNode* parent, |
| + int old_index, |
| + const BookmarkNode* node, |
| + const std::set<GURL>& removed_urls) { |
| + DCHECK(CalledOnValidThread()); |
| + for (std::set<GURL>::const_iterator iter = removed_urls.begin(); |
| + iter != removed_urls.end(); |
| + ++iter) { |
| + RemoveImageForUrl(*iter); |
| + } |
| +} |
| + |
| +void BookmarkImageService::BookmarkModelLoaded(BookmarkModel* model, |
| + bool ids_reassigned) { |
| +} |
| + |
| +void BookmarkImageService::BookmarkNodeMoved(BookmarkModel* model, |
| + const BookmarkNode* old_parent, |
| + int old_index, |
| + const BookmarkNode* new_parent, |
| + int new_index) { |
| +} |
| + |
| +void BookmarkImageService::BookmarkNodeAdded(BookmarkModel* model, |
| + const BookmarkNode* parent, |
| + int index) { |
| +} |
| + |
| +void BookmarkImageService::OnWillChangeBookmarkNode(BookmarkModel* model, |
| + const BookmarkNode* node) { |
| + DCHECK(CalledOnValidThread()); |
| + if (node->is_url()) |
| + previous_url_ = node->url(); |
| +} |
| + |
| +void BookmarkImageService::BookmarkNodeChanged(BookmarkModel* model, |
| + const BookmarkNode* node) { |
| + DCHECK(CalledOnValidThread()); |
| + if (node->is_url() && previous_url_ != node->url()) |
| + ChangeImageURL(previous_url_, node->url()); |
| +} |
| + |
| +void BookmarkImageService::BookmarkNodeFaviconChanged( |
| + BookmarkModel* model, |
| + const BookmarkNode* node) { |
| +} |
| + |
| +void BookmarkImageService::BookmarkNodeChildrenReordered( |
| + BookmarkModel* model, |
| + const BookmarkNode* node) { |
| +} |
| + |
| +void BookmarkImageService::BookmarkAllUserNodesRemoved( |
| + BookmarkModel* model, |
| + const std::set<GURL>& removed_urls) { |
| + ClearAll(); |
| +} |
| + |
| +} // namespace enhanced_bookmarks |