Home | History | Annotate | Download | only in core
      1 // Copyright 2013 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 
      5 #include "components/dom_distiller/core/task_tracker.h"
      6 
      7 #include "base/auto_reset.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "components/dom_distiller/core/distilled_content_store.h"
     10 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
     11 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
     12 
     13 namespace dom_distiller {
     14 
     15 ViewerHandle::ViewerHandle(CancelCallback callback)
     16     : cancel_callback_(callback) {}
     17 
     18 ViewerHandle::~ViewerHandle() {
     19   if (!cancel_callback_.is_null()) {
     20     cancel_callback_.Run();
     21   }
     22 }
     23 
     24 TaskTracker::TaskTracker(const ArticleEntry& entry,
     25                          CancelCallback callback,
     26                          DistilledContentStore* content_store)
     27     : cancel_callback_(callback),
     28       content_store_(content_store),
     29       blob_fetcher_running_(false),
     30       entry_(entry),
     31       distilled_article_(),
     32       content_ready_(false),
     33       destruction_allowed_(true),
     34       weak_ptr_factory_(this) {}
     35 
     36 TaskTracker::~TaskTracker() {
     37   DCHECK(destruction_allowed_);
     38   DCHECK(viewers_.empty());
     39 }
     40 
     41 void TaskTracker::StartDistiller(DistillerFactory* factory,
     42                                  scoped_ptr<DistillerPage> distiller_page) {
     43   if (distiller_) {
     44     return;
     45   }
     46   if (entry_.pages_size() == 0) {
     47     return;
     48   }
     49   GURL url(entry_.pages(0).url());
     50   DCHECK(url.is_valid());
     51 
     52   distiller_ = factory->CreateDistiller();
     53   distiller_->DistillPage(url,
     54                           distiller_page.Pass(),
     55                           base::Bind(&TaskTracker::OnDistillerFinished,
     56                                      weak_ptr_factory_.GetWeakPtr()),
     57                           base::Bind(&TaskTracker::OnArticleDistillationUpdated,
     58                                      weak_ptr_factory_.GetWeakPtr()));
     59 }
     60 
     61 void TaskTracker::StartBlobFetcher() {
     62   if (content_store_) {
     63     content_store_->LoadContent(entry_,
     64                                 base::Bind(&TaskTracker::OnBlobFetched,
     65                                            weak_ptr_factory_.GetWeakPtr()));
     66   }
     67 }
     68 
     69 void TaskTracker::AddSaveCallback(const SaveCallback& callback) {
     70   DCHECK(!callback.is_null());
     71   save_callbacks_.push_back(callback);
     72   if (content_ready_) {
     73     // Distillation for this task has already completed, and so it can be
     74     // immediately saved.
     75     ScheduleSaveCallbacks(true);
     76   }
     77 }
     78 
     79 scoped_ptr<ViewerHandle> TaskTracker::AddViewer(ViewRequestDelegate* delegate) {
     80   viewers_.push_back(delegate);
     81   if (content_ready_) {
     82     // Distillation for this task has already completed, and so the delegate can
     83     // be immediately told of the result.
     84     base::MessageLoop::current()->PostTask(
     85         FROM_HERE,
     86         base::Bind(&TaskTracker::NotifyViewer,
     87                    weak_ptr_factory_.GetWeakPtr(),
     88                    delegate));
     89   }
     90   return scoped_ptr<ViewerHandle>(new ViewerHandle(base::Bind(
     91       &TaskTracker::RemoveViewer, weak_ptr_factory_.GetWeakPtr(), delegate)));
     92 }
     93 
     94 const std::string& TaskTracker::GetEntryId() const { return entry_.entry_id(); }
     95 
     96 bool TaskTracker::HasEntryId(const std::string& entry_id) const {
     97   return entry_.entry_id() == entry_id;
     98 }
     99 
    100 bool TaskTracker::HasUrl(const GURL& url) const {
    101   for (int i = 0; i < entry_.pages_size(); ++i) {
    102     if (entry_.pages(i).url() == url.spec()) {
    103       return true;
    104     }
    105   }
    106   return false;
    107 }
    108 
    109 void TaskTracker::RemoveViewer(ViewRequestDelegate* delegate) {
    110   viewers_.erase(std::remove(viewers_.begin(), viewers_.end(), delegate));
    111   if (viewers_.empty()) {
    112     MaybeCancel();
    113   }
    114 }
    115 
    116 void TaskTracker::MaybeCancel() {
    117   if (!save_callbacks_.empty() || !viewers_.empty()) {
    118     // There's still work to be done.
    119     return;
    120   }
    121 
    122   CancelPendingSources();
    123 
    124   base::AutoReset<bool> dont_delete_this_in_callback(&destruction_allowed_,
    125                                                      false);
    126   cancel_callback_.Run(this);
    127 }
    128 
    129 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
    130 
    131 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded) {
    132   base::MessageLoop::current()->PostTask(
    133       FROM_HERE,
    134       base::Bind(&TaskTracker::DoSaveCallbacks,
    135                  weak_ptr_factory_.GetWeakPtr(),
    136                  distillation_succeeded));
    137 }
    138 
    139 void TaskTracker::OnDistillerFinished(
    140     scoped_ptr<DistilledArticleProto> distilled_article) {
    141   if (content_ready_) {
    142     return;
    143   }
    144 
    145   DistilledArticleReady(distilled_article.Pass());
    146   if (content_ready_) {
    147     AddDistilledContentToStore(*distilled_article_);
    148   }
    149 
    150   ContentSourceFinished();
    151 }
    152 
    153 void TaskTracker::CancelPendingSources() {
    154   base::MessageLoop::current()->DeleteSoon(FROM_HERE, distiller_.release());
    155 }
    156 
    157 void TaskTracker::OnBlobFetched(
    158     bool success,
    159     scoped_ptr<DistilledArticleProto> distilled_article) {
    160   blob_fetcher_running_ = false;
    161 
    162   if (content_ready_) {
    163     return;
    164   }
    165 
    166   DistilledArticleReady(distilled_article.Pass());
    167 
    168   ContentSourceFinished();
    169 }
    170 
    171 bool TaskTracker::IsAnySourceRunning() const {
    172   return distiller_ || blob_fetcher_running_;
    173 }
    174 
    175 void TaskTracker::ContentSourceFinished() {
    176   if (content_ready_) {
    177     CancelPendingSources();
    178   } else if (!IsAnySourceRunning()) {
    179     distilled_article_.reset(new DistilledArticleProto());
    180     NotifyViewersAndCallbacks();
    181   }
    182 }
    183 
    184 void TaskTracker::DistilledArticleReady(
    185     scoped_ptr<DistilledArticleProto> distilled_article) {
    186   DCHECK(!content_ready_);
    187 
    188   if (distilled_article->pages_size() == 0) {
    189     return;
    190   }
    191 
    192   content_ready_ = true;
    193 
    194   distilled_article_ = distilled_article.Pass();
    195   entry_.set_title(distilled_article_->title());
    196   entry_.clear_pages();
    197   for (int i = 0; i < distilled_article_->pages_size(); ++i) {
    198     sync_pb::ArticlePage* page = entry_.add_pages();
    199     page->set_url(distilled_article_->pages(i).url());
    200   }
    201 
    202   NotifyViewersAndCallbacks();
    203 }
    204 
    205 void TaskTracker::NotifyViewersAndCallbacks() {
    206   for (size_t i = 0; i < viewers_.size(); ++i) {
    207     NotifyViewer(viewers_[i]);
    208   }
    209 
    210   // Already inside a callback run SaveCallbacks directly.
    211   DoSaveCallbacks(content_ready_);
    212 }
    213 
    214 void TaskTracker::NotifyViewer(ViewRequestDelegate* delegate) {
    215   delegate->OnArticleReady(distilled_article_.get());
    216 }
    217 
    218 void TaskTracker::DoSaveCallbacks(bool success) {
    219   if (!save_callbacks_.empty()) {
    220     for (size_t i = 0; i < save_callbacks_.size(); ++i) {
    221       DCHECK(!save_callbacks_[i].is_null());
    222       save_callbacks_[i].Run(
    223           entry_, distilled_article_.get(), success);
    224     }
    225 
    226     save_callbacks_.clear();
    227     MaybeCancel();
    228   }
    229 }
    230 
    231 void TaskTracker::OnArticleDistillationUpdated(
    232     const ArticleDistillationUpdate& article_update) {
    233   for (size_t i = 0; i < viewers_.size(); ++i) {
    234     viewers_[i]->OnArticleUpdated(article_update);
    235   }
    236 }
    237 
    238 void TaskTracker::AddDistilledContentToStore(
    239     const DistilledArticleProto& content) {
    240   if (content_store_) {
    241     content_store_->SaveContent(
    242         entry_, content, DistilledContentStore::SaveCallback());
    243   }
    244 }
    245 
    246 
    247 }  // namespace dom_distiller
    248