Home | History | Annotate | Download | only in enhanced_bookmarks
      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
    350