Upstream version 7.36.149.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     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