Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / dom_distiller / core / task_tracker.cc
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     blob_fetcher_running_ = true;
64     content_store_->LoadContent(entry_,
65                                 base::Bind(&TaskTracker::OnBlobFetched,
66                                            weak_ptr_factory_.GetWeakPtr()));
67   }
68 }
69
70 void TaskTracker::AddSaveCallback(const SaveCallback& callback) {
71   DCHECK(!callback.is_null());
72   save_callbacks_.push_back(callback);
73   if (content_ready_) {
74     // Distillation for this task has already completed, and so it can be
75     // immediately saved.
76     ScheduleSaveCallbacks(true);
77   }
78 }
79
80 scoped_ptr<ViewerHandle> TaskTracker::AddViewer(ViewRequestDelegate* delegate) {
81   viewers_.push_back(delegate);
82   if (content_ready_) {
83     // Distillation for this task has already completed, and so the delegate can
84     // be immediately told of the result.
85     base::MessageLoop::current()->PostTask(
86         FROM_HERE,
87         base::Bind(&TaskTracker::NotifyViewer,
88                    weak_ptr_factory_.GetWeakPtr(),
89                    delegate));
90   }
91   return scoped_ptr<ViewerHandle>(new ViewerHandle(base::Bind(
92       &TaskTracker::RemoveViewer, weak_ptr_factory_.GetWeakPtr(), delegate)));
93 }
94
95 const std::string& TaskTracker::GetEntryId() const { return entry_.entry_id(); }
96
97 bool TaskTracker::HasEntryId(const std::string& entry_id) const {
98   return entry_.entry_id() == entry_id;
99 }
100
101 bool TaskTracker::HasUrl(const GURL& url) const {
102   for (int i = 0; i < entry_.pages_size(); ++i) {
103     if (entry_.pages(i).url() == url.spec()) {
104       return true;
105     }
106   }
107   return false;
108 }
109
110 void TaskTracker::RemoveViewer(ViewRequestDelegate* delegate) {
111   viewers_.erase(std::remove(viewers_.begin(), viewers_.end(), delegate));
112   if (viewers_.empty()) {
113     MaybeCancel();
114   }
115 }
116
117 void TaskTracker::MaybeCancel() {
118   if (!save_callbacks_.empty() || !viewers_.empty()) {
119     // There's still work to be done.
120     return;
121   }
122
123   CancelPendingSources();
124
125   base::AutoReset<bool> dont_delete_this_in_callback(&destruction_allowed_,
126                                                      false);
127   cancel_callback_.Run(this);
128 }
129
130 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
131
132 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded) {
133   base::MessageLoop::current()->PostTask(
134       FROM_HERE,
135       base::Bind(&TaskTracker::DoSaveCallbacks,
136                  weak_ptr_factory_.GetWeakPtr(),
137                  distillation_succeeded));
138 }
139
140 void TaskTracker::OnDistillerFinished(
141     scoped_ptr<DistilledArticleProto> distilled_article) {
142   if (content_ready_) {
143     return;
144   }
145
146   DistilledArticleReady(distilled_article.Pass());
147   if (content_ready_) {
148     AddDistilledContentToStore(*distilled_article_);
149   }
150
151   // 'distiller_ != null' is used as a signal that distillation is in progress,
152   // so it needs to be released so that we know distillation is done.
153   base::MessageLoop::current()->DeleteSoon(FROM_HERE, distiller_.release());
154
155   ContentSourceFinished();
156 }
157
158 void TaskTracker::CancelPendingSources() {
159   if (distiller_) {
160     base::MessageLoop::current()->DeleteSoon(FROM_HERE, distiller_.release());
161   }
162 }
163
164 void TaskTracker::OnBlobFetched(
165     bool success,
166     scoped_ptr<DistilledArticleProto> distilled_article) {
167   blob_fetcher_running_ = false;
168
169   if (content_ready_) {
170     return;
171   }
172
173   DistilledArticleReady(distilled_article.Pass());
174
175   ContentSourceFinished();
176 }
177
178 bool TaskTracker::IsAnySourceRunning() const {
179   return distiller_ || blob_fetcher_running_;
180 }
181
182 void TaskTracker::ContentSourceFinished() {
183   if (content_ready_) {
184     CancelPendingSources();
185   } else if (!IsAnySourceRunning()) {
186     distilled_article_.reset(new DistilledArticleProto());
187     NotifyViewersAndCallbacks();
188   }
189 }
190
191 void TaskTracker::DistilledArticleReady(
192     scoped_ptr<DistilledArticleProto> distilled_article) {
193   DCHECK(!content_ready_);
194
195   if (distilled_article->pages_size() == 0) {
196     return;
197   }
198
199   content_ready_ = true;
200
201   distilled_article_ = distilled_article.Pass();
202   entry_.set_title(distilled_article_->title());
203   entry_.clear_pages();
204   for (int i = 0; i < distilled_article_->pages_size(); ++i) {
205     sync_pb::ArticlePage* page = entry_.add_pages();
206     page->set_url(distilled_article_->pages(i).url());
207   }
208
209   NotifyViewersAndCallbacks();
210 }
211
212 void TaskTracker::NotifyViewersAndCallbacks() {
213   for (size_t i = 0; i < viewers_.size(); ++i) {
214     NotifyViewer(viewers_[i]);
215   }
216
217   // Already inside a callback run SaveCallbacks directly.
218   DoSaveCallbacks(content_ready_);
219 }
220
221 void TaskTracker::NotifyViewer(ViewRequestDelegate* delegate) {
222   delegate->OnArticleReady(distilled_article_.get());
223 }
224
225 void TaskTracker::DoSaveCallbacks(bool success) {
226   if (!save_callbacks_.empty()) {
227     for (size_t i = 0; i < save_callbacks_.size(); ++i) {
228       DCHECK(!save_callbacks_[i].is_null());
229       save_callbacks_[i].Run(
230           entry_, distilled_article_.get(), success);
231     }
232
233     save_callbacks_.clear();
234     MaybeCancel();
235   }
236 }
237
238 void TaskTracker::OnArticleDistillationUpdated(
239     const ArticleDistillationUpdate& article_update) {
240   for (size_t i = 0; i < viewers_.size(); ++i) {
241     viewers_[i]->OnArticleUpdated(article_update);
242   }
243 }
244
245 void TaskTracker::AddDistilledContentToStore(
246     const DistilledArticleProto& content) {
247   if (content_store_) {
248     content_store_->SaveContent(
249         entry_, content, DistilledContentStore::SaveCallback());
250   }
251 }
252
253
254 }  // namespace dom_distiller