Commit eefc52bd authored by noyau's avatar noyau Committed by Commit bot

Bring up of ImageService.

ImageService is an abstract superclass that will have subclasses
for each mobile platform. This class stores a salient image for
a bookmark which is removed when the bookmark node is deleted.

BUG=None

Review URL: https://codereview.chromium.org/513793002

Cr-Commit-Position: refs/heads/master@{#292596}
parent 62c7cf84
...@@ -12,13 +12,17 @@ ...@@ -12,13 +12,17 @@
], ],
'dependencies': [ 'dependencies': [
'../base/base.gyp:base', '../base/base.gyp:base',
'../net/net.gyp:net',
'../sql/sql.gyp:sql', '../sql/sql.gyp:sql',
'../ui/gfx/gfx.gyp:gfx', '../ui/gfx/gfx.gyp:gfx',
'../url/url.gyp:url_lib', '../url/url.gyp:url_lib',
'bookmarks_browser', 'bookmarks_browser',
'enhanced_bookmarks_proto', 'enhanced_bookmarks_proto',
'keyed_service_core',
], ],
'sources': [ 'sources': [
'enhanced_bookmarks/bookmark_image_service.cc',
'enhanced_bookmarks/bookmark_image_service.h',
'enhanced_bookmarks/enhanced_bookmark_utils.cc', 'enhanced_bookmarks/enhanced_bookmark_utils.cc',
'enhanced_bookmarks/enhanced_bookmark_utils.h', 'enhanced_bookmarks/enhanced_bookmark_utils.h',
'enhanced_bookmarks/image_store.cc', 'enhanced_bookmarks/image_store.cc',
......
include_rules = [ include_rules = [
"+components/bookmarks", "+components/bookmarks",
"+components/keyed_service",
"+jni", "+jni",
"+net",
"+sql", "+sql",
"+ui", "+ui",
] ]
......
// 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,
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
// 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.
#ifndef COMPONENTS_ENHANCED_BOOKMARKS_BOOKMARK_IMAGE_SERVICE_H_
#define COMPONENTS_ENHANCED_BOOKMARKS_BOOKMARK_IMAGE_SERVICE_H_
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "components/bookmarks/browser/bookmark_model_observer.h"
#include "components/enhanced_bookmarks/image_store.h"
#include "components/keyed_service/core/keyed_service.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"
namespace base {
class SingleThreadTaskRunner;
}
class BookmarkNode;
namespace enhanced_bookmarks {
// The BookmarkImageService stores salient images for bookmarks.
class BookmarkImageService : public KeyedService,
public BookmarkModelObserver,
public base::NonThreadSafe {
public:
explicit BookmarkImageService(const base::FilePath& path,
BookmarkModel* bookmark_model,
scoped_refptr<base::SequencedWorkerPool> pool);
BookmarkImageService(scoped_ptr<ImageStore> store,
BookmarkModel* bookmark_model,
scoped_refptr<base::SequencedWorkerPool> pool);
virtual ~BookmarkImageService();
typedef base::Callback<void(const gfx::Image&, const GURL& url)> Callback;
// Returns a salient image for a URL. This may trigger a network request for
// the image if the image was not retrieved before and if a bookmark node has
// a URL for this salient image available. The image (which may be empty) is
// sent via the callback. The callback may be called synchronously if it is
// possible. The callback is always triggered on the main thread.
void SalientImageForUrl(const GURL& page_url, Callback callback);
// BookmarkModelObserver methods.
virtual void BookmarkNodeRemoved(BookmarkModel* model,
const BookmarkNode* parent,
int old_index,
const BookmarkNode* node,
const std::set<GURL>& removed_urls) OVERRIDE;
virtual void BookmarkModelLoaded(BookmarkModel* model,
bool ids_reassigned) OVERRIDE;
virtual void BookmarkNodeMoved(BookmarkModel* model,
const BookmarkNode* old_parent,
int old_index,
const BookmarkNode* new_parent,
int new_index) OVERRIDE;
virtual void BookmarkNodeAdded(BookmarkModel* model,
const BookmarkNode* parent,
int index) OVERRIDE;
virtual void OnWillChangeBookmarkNode(BookmarkModel* model,
const BookmarkNode* node) OVERRIDE;
virtual void BookmarkNodeChanged(BookmarkModel* model,
const BookmarkNode* node) OVERRIDE;
virtual void BookmarkNodeFaviconChanged(BookmarkModel* model,
const BookmarkNode* node) OVERRIDE;
virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
const BookmarkNode* node) OVERRIDE;
virtual void BookmarkAllUserNodesRemoved(
BookmarkModel* model,
const std::set<GURL>& removed_urls) OVERRIDE;
protected:
// Returns true if the image for the page_url is currently being fetched.
bool IsPageUrlInProgress(const GURL& page_url);
// Once an image has been retrieved, store the image and notify all the
// consumers that were waiting on it.
void ProcessNewImage(const GURL& page_url,
bool update_bookmarks,
const gfx::Image& image,
const GURL& image_url);
// Sets a new image for a bookmark. If the given page_url is bookmarked and
// the image is retrieved from the image_url, then the image is locally
// stored. If update_bookmark is true the URL is also added to the bookmark.
// This is the only method subclass needs to implement.
virtual void RetrieveSalientImage(
const GURL& page_url,
const GURL& image_url,
const std::string& referrer,
net::URLRequest::ReferrerPolicy referrer_policy,
bool update_bookmark) = 0;
// Retrieves a salient image for a given page_url by downloading the image in
// one of the bookmark.
virtual void RetrieveSalientImageForPageUrl(const GURL& page_url);
// PageUrls currently in the progress of being retrieved.
std::set<GURL> in_progress_page_urls_;
// Cached pointer to the bookmarks model.
BookmarkModel* bookmark_model_; // weak
private:
// Same as SalientImageForUrl(const GURL&, Callback) but can prevent the
// network request if fetch_from_bookmark is false.
void SalientImageForUrl(const GURL& page_url,
bool fetch_from_bookmark,
Callback stack_callback);
// Processes the requests that have been waiting on an image.
void ProcessRequests(const GURL& page_url,
const gfx::Image& image,
const GURL& image_url);
// Once an image is retrieved this method updates the store with it.
void StoreImage(const gfx::Image& image,
const GURL& image_url,
const GURL& page_url);
// Called when retrieving an image from the image store fails, to trigger
// retrieving the image from the url stored in the bookmark (if any).
void FetchCallback(const GURL& page_url,
Callback original_callback,
const gfx::Image& image,
const GURL& image_url);
// Remove the image stored for this bookmark (if it exists). Called when a
// bookmark is deleted.
void RemoveImageForUrl(const GURL& url);
// Moves an image from one url to another.
void ChangeImageURL(const GURL& from, const GURL& to);
// Removes all the entries in the image service.
void ClearAll();
// The image store can only be accessed from the blocking pool.
// RetrieveImageFromStore starts a request to retrieve the image and returns
// the result via a callback. RetrieveImageFromStore must be called on the
// main thread and the callback will be called on the main thread as well. The
// callback will always be called. The returned image is nil if the image is
// not present in the store.
void RetrieveImageFromStore(const GURL& page_url,
BookmarkImageService::Callback callback);
// Maps a pageUrl to an image.
scoped_ptr<ImageStore> store_;
// All the callbacks waiting for a particular image.
std::map<const GURL, std::vector<Callback> > callbacks_;
// When a bookmark is changed, two messages are received on the
// bookmarkModelObserver, one with the old state, one with the new. The url
// before the change is saved in this instance variable.
GURL previous_url_;
// The worker pool to enqueue the store requests onto.
scoped_refptr<base::SequencedWorkerPool> pool_;
DISALLOW_COPY_AND_ASSIGN(BookmarkImageService);
};
} // namespace enhanced_bookmarks
#endif // COMPONENTS_ENHANCED_BOOKMARKS_BOOKMARK_IMAGE_SERVICE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment