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.
5 #include "components/dom_distiller/core/task_tracker.h"
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"
13 namespace dom_distiller
{
15 ViewerHandle::ViewerHandle(CancelCallback callback
)
16 : cancel_callback_(callback
) {}
18 ViewerHandle::~ViewerHandle() {
19 if (!cancel_callback_
.is_null()) {
20 cancel_callback_
.Run();
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),
32 content_ready_(false),
33 destruction_allowed_(true),
34 weak_ptr_factory_(this) {}
36 TaskTracker::~TaskTracker() {
37 DCHECK(destruction_allowed_
);
38 DCHECK(viewers_
.empty());
41 void TaskTracker::StartDistiller(DistillerFactory
* factory
,
42 scoped_ptr
<DistillerPage
> distiller_page
) {
46 if (entry_
.pages_size() == 0) {
49 GURL
url(entry_
.pages(0).url());
50 DCHECK(url
.is_valid());
52 distiller_
= factory
->CreateDistillerForUrl(url
);
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()));
61 void TaskTracker::StartBlobFetcher() {
63 blob_fetcher_running_
= true;
64 content_store_
->LoadContent(entry_
,
65 base::Bind(&TaskTracker::OnBlobFetched
,
66 weak_ptr_factory_
.GetWeakPtr()));
70 void TaskTracker::AddSaveCallback(const SaveCallback
& callback
) {
71 DCHECK(!callback
.is_null());
72 save_callbacks_
.push_back(callback
);
74 // Distillation for this task has already completed, and so it can be
76 ScheduleSaveCallbacks(true);
80 scoped_ptr
<ViewerHandle
> TaskTracker::AddViewer(ViewRequestDelegate
* delegate
) {
81 viewers_
.push_back(delegate
);
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(
87 base::Bind(&TaskTracker::NotifyViewer
,
88 weak_ptr_factory_
.GetWeakPtr(),
91 return scoped_ptr
<ViewerHandle
>(new ViewerHandle(base::Bind(
92 &TaskTracker::RemoveViewer
, weak_ptr_factory_
.GetWeakPtr(), delegate
)));
95 const std::string
& TaskTracker::GetEntryId() const { return entry_
.entry_id(); }
97 bool TaskTracker::HasEntryId(const std::string
& entry_id
) const {
98 return entry_
.entry_id() == entry_id
;
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()) {
110 void TaskTracker::RemoveViewer(ViewRequestDelegate
* delegate
) {
111 viewers_
.erase(std::remove(viewers_
.begin(), viewers_
.end(), delegate
));
112 if (viewers_
.empty()) {
117 void TaskTracker::MaybeCancel() {
118 if (!save_callbacks_
.empty() || !viewers_
.empty()) {
119 // There's still work to be done.
123 CancelPendingSources();
125 base::AutoReset
<bool> dont_delete_this_in_callback(&destruction_allowed_
,
127 cancel_callback_
.Run(this);
130 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
132 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded
) {
133 base::MessageLoop::current()->PostTask(
135 base::Bind(&TaskTracker::DoSaveCallbacks
,
136 weak_ptr_factory_
.GetWeakPtr(),
137 distillation_succeeded
));
140 void TaskTracker::OnDistillerFinished(
141 scoped_ptr
<DistilledArticleProto
> distilled_article
) {
142 if (content_ready_
) {
146 DistilledArticleReady(distilled_article
.Pass());
147 if (content_ready_
) {
148 AddDistilledContentToStore(*distilled_article_
);
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());
155 ContentSourceFinished();
158 void TaskTracker::CancelPendingSources() {
160 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, distiller_
.release());
164 void TaskTracker::OnBlobFetched(
166 scoped_ptr
<DistilledArticleProto
> distilled_article
) {
167 blob_fetcher_running_
= false;
169 if (content_ready_
) {
173 DistilledArticleReady(distilled_article
.Pass());
175 ContentSourceFinished();
178 bool TaskTracker::IsAnySourceRunning() const {
179 return distiller_
|| blob_fetcher_running_
;
182 void TaskTracker::ContentSourceFinished() {
183 if (content_ready_
) {
184 CancelPendingSources();
185 } else if (!IsAnySourceRunning()) {
186 distilled_article_
.reset(new DistilledArticleProto());
187 NotifyViewersAndCallbacks();
191 void TaskTracker::DistilledArticleReady(
192 scoped_ptr
<DistilledArticleProto
> distilled_article
) {
193 DCHECK(!content_ready_
);
195 if (distilled_article
->pages_size() == 0) {
199 content_ready_
= true;
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());
209 NotifyViewersAndCallbacks();
212 void TaskTracker::NotifyViewersAndCallbacks() {
213 for (size_t i
= 0; i
< viewers_
.size(); ++i
) {
214 NotifyViewer(viewers_
[i
]);
217 // Already inside a callback run SaveCallbacks directly.
218 DoSaveCallbacks(content_ready_
);
221 void TaskTracker::NotifyViewer(ViewRequestDelegate
* delegate
) {
222 delegate
->OnArticleReady(distilled_article_
.get());
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
);
233 save_callbacks_
.clear();
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
);
245 void TaskTracker::AddDistilledContentToStore(
246 const DistilledArticleProto
& content
) {
247 if (content_store_
) {
248 content_store_
->SaveContent(
249 entry_
, content
, DistilledContentStore::SaveCallback());
254 } // namespace dom_distiller