• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ViewerHandle(CancelCallback callback)15 ViewerHandle::ViewerHandle(CancelCallback callback)
16     : cancel_callback_(callback) {}
17 
~ViewerHandle()18 ViewerHandle::~ViewerHandle() {
19   if (!cancel_callback_.is_null()) {
20     cancel_callback_.Run();
21   }
22 }
23 
TaskTracker(const ArticleEntry & entry,CancelCallback callback,DistilledContentStore * content_store)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 
~TaskTracker()36 TaskTracker::~TaskTracker() {
37   DCHECK(destruction_allowed_);
38   DCHECK(viewers_.empty());
39 }
40 
StartDistiller(DistillerFactory * factory,scoped_ptr<DistillerPage> distiller_page)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 
StartBlobFetcher()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 
AddSaveCallback(const SaveCallback & callback)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 
AddViewer(ViewRequestDelegate * delegate)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 
GetEntryId() const94 const std::string& TaskTracker::GetEntryId() const { return entry_.entry_id(); }
95 
HasEntryId(const std::string & entry_id) const96 bool TaskTracker::HasEntryId(const std::string& entry_id) const {
97   return entry_.entry_id() == entry_id;
98 }
99 
HasUrl(const GURL & url) const100 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 
RemoveViewer(ViewRequestDelegate * delegate)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 
MaybeCancel()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 
CancelSaveCallbacks()129 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
130 
ScheduleSaveCallbacks(bool distillation_succeeded)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 
OnDistillerFinished(scoped_ptr<DistilledArticleProto> distilled_article)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 
CancelPendingSources()153 void TaskTracker::CancelPendingSources() {
154   base::MessageLoop::current()->DeleteSoon(FROM_HERE, distiller_.release());
155 }
156 
OnBlobFetched(bool success,scoped_ptr<DistilledArticleProto> distilled_article)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 
IsAnySourceRunning() const171 bool TaskTracker::IsAnySourceRunning() const {
172   return distiller_ || blob_fetcher_running_;
173 }
174 
ContentSourceFinished()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 
DistilledArticleReady(scoped_ptr<DistilledArticleProto> distilled_article)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 
NotifyViewersAndCallbacks()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 
NotifyViewer(ViewRequestDelegate * delegate)214 void TaskTracker::NotifyViewer(ViewRequestDelegate* delegate) {
215   delegate->OnArticleReady(distilled_article_.get());
216 }
217 
DoSaveCallbacks(bool success)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 
OnArticleDistillationUpdated(const ArticleDistillationUpdate & article_update)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 
AddDistilledContentToStore(const DistilledArticleProto & content)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