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
->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()));
61 void TaskTracker::StartBlobFetcher() {
63 content_store_
->LoadContent(entry_
,
64 base::Bind(&TaskTracker::OnBlobFetched
,
65 weak_ptr_factory_
.GetWeakPtr()));
69 void TaskTracker::AddSaveCallback(const SaveCallback
& callback
) {
70 DCHECK(!callback
.is_null());
71 save_callbacks_
.push_back(callback
);
73 // Distillation for this task has already completed, and so it can be
75 ScheduleSaveCallbacks(true);
79 scoped_ptr
<ViewerHandle
> TaskTracker::AddViewer(ViewRequestDelegate
* delegate
) {
80 viewers_
.push_back(delegate
);
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(
86 base::Bind(&TaskTracker::NotifyViewer
,
87 weak_ptr_factory_
.GetWeakPtr(),
90 return scoped_ptr
<ViewerHandle
>(new ViewerHandle(base::Bind(
91 &TaskTracker::RemoveViewer
, weak_ptr_factory_
.GetWeakPtr(), delegate
)));
94 const std::string
& TaskTracker::GetEntryId() const { return entry_
.entry_id(); }
96 bool TaskTracker::HasEntryId(const std::string
& entry_id
) const {
97 return entry_
.entry_id() == entry_id
;
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()) {
109 void TaskTracker::RemoveViewer(ViewRequestDelegate
* delegate
) {
110 viewers_
.erase(std::remove(viewers_
.begin(), viewers_
.end(), delegate
));
111 if (viewers_
.empty()) {
116 void TaskTracker::MaybeCancel() {
117 if (!save_callbacks_
.empty() || !viewers_
.empty()) {
118 // There's still work to be done.
122 CancelPendingSources();
124 base::AutoReset
<bool> dont_delete_this_in_callback(&destruction_allowed_
,
126 cancel_callback_
.Run(this);
129 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
131 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded
) {
132 base::MessageLoop::current()->PostTask(
134 base::Bind(&TaskTracker::DoSaveCallbacks
,
135 weak_ptr_factory_
.GetWeakPtr(),
136 distillation_succeeded
));
139 void TaskTracker::OnDistillerFinished(
140 scoped_ptr
<DistilledArticleProto
> distilled_article
) {
141 if (content_ready_
) {
145 DistilledArticleReady(distilled_article
.Pass());
146 if (content_ready_
) {
147 AddDistilledContentToStore(*distilled_article_
);
150 ContentSourceFinished();
153 void TaskTracker::CancelPendingSources() {
154 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, distiller_
.release());
157 void TaskTracker::OnBlobFetched(
159 scoped_ptr
<DistilledArticleProto
> distilled_article
) {
160 blob_fetcher_running_
= false;
162 if (content_ready_
) {
166 DistilledArticleReady(distilled_article
.Pass());
168 ContentSourceFinished();
171 bool TaskTracker::IsAnySourceRunning() const {
172 return distiller_
|| blob_fetcher_running_
;
175 void TaskTracker::ContentSourceFinished() {
176 if (content_ready_
) {
177 CancelPendingSources();
178 } else if (!IsAnySourceRunning()) {
179 distilled_article_
.reset(new DistilledArticleProto());
180 NotifyViewersAndCallbacks();
184 void TaskTracker::DistilledArticleReady(
185 scoped_ptr
<DistilledArticleProto
> distilled_article
) {
186 DCHECK(!content_ready_
);
188 if (distilled_article
->pages_size() == 0) {
192 content_ready_
= true;
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());
202 NotifyViewersAndCallbacks();
205 void TaskTracker::NotifyViewersAndCallbacks() {
206 for (size_t i
= 0; i
< viewers_
.size(); ++i
) {
207 NotifyViewer(viewers_
[i
]);
210 // Already inside a callback run SaveCallbacks directly.
211 DoSaveCallbacks(content_ready_
);
214 void TaskTracker::NotifyViewer(ViewRequestDelegate
* delegate
) {
215 delegate
->OnArticleReady(distilled_article_
.get());
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
);
226 save_callbacks_
.clear();
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
);
238 void TaskTracker::AddDistilledContentToStore(
239 const DistilledArticleProto
& content
) {
240 if (content_store_
) {
241 content_store_
->SaveContent(
242 entry_
, content
, DistilledContentStore::SaveCallback());
247 } // namespace dom_distiller