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/message_loop/message_loop.h"
8 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
9 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
11 namespace dom_distiller
{
13 ViewerHandle::ViewerHandle(CancelCallback callback
)
14 : cancel_callback_(callback
) {}
16 ViewerHandle::~ViewerHandle() {
17 if (!cancel_callback_
.is_null()) {
18 cancel_callback_
.Run();
22 TaskTracker::TaskTracker(const ArticleEntry
& entry
, CancelCallback callback
)
23 : cancel_callback_(callback
),
26 distillation_complete_(false),
27 weak_ptr_factory_(this) {}
29 TaskTracker::~TaskTracker() { DCHECK(viewers_
.empty()); }
31 void TaskTracker::StartDistiller(DistillerFactory
* factory
) {
35 if (entry_
.pages_size() == 0) {
39 GURL
url(entry_
.pages(0).url());
40 DCHECK(url
.is_valid());
42 distiller_
= factory
->CreateDistiller();
43 distiller_
->DistillPage(url
,
44 base::Bind(&TaskTracker::OnDistilledDataReady
,
45 weak_ptr_factory_
.GetWeakPtr()));
48 void TaskTracker::StartBlobFetcher() {
49 // TODO(cjhopman): There needs to be some local storage for the distilled
50 // blob. When that happens, this should start some task to fetch the blob for
51 // |entry_| and asynchronously notify |this| when it is done.
54 void TaskTracker::AddSaveCallback(const SaveCallback
& callback
) {
55 DCHECK(!callback
.is_null());
56 save_callbacks_
.push_back(callback
);
57 if (distillation_complete_
) {
58 // Distillation for this task has already completed, and so it can be
60 ScheduleSaveCallbacks(true);
64 scoped_ptr
<ViewerHandle
> TaskTracker::AddViewer(ViewRequestDelegate
* delegate
) {
65 viewers_
.push_back(delegate
);
66 if (distillation_complete_
) {
67 // Distillation for this task has already completed, and so the delegate can
68 // be immediately told of the result.
69 base::MessageLoop::current()->PostTask(
71 base::Bind(&TaskTracker::NotifyViewer
,
72 weak_ptr_factory_
.GetWeakPtr(),
75 return scoped_ptr
<ViewerHandle
>(new ViewerHandle(base::Bind(
76 &TaskTracker::RemoveViewer
, weak_ptr_factory_
.GetWeakPtr(), delegate
)));
79 const std::string
& TaskTracker::GetEntryId() const { return entry_
.entry_id(); }
81 bool TaskTracker::HasEntryId(const std::string
& entry_id
) const {
82 return entry_
.entry_id() == entry_id
;
85 bool TaskTracker::HasUrl(const GURL
& url
) const {
86 for (int i
= 0; i
< entry_
.pages_size(); ++i
) {
87 if (entry_
.pages(i
).url() == url
.spec()) {
94 void TaskTracker::RemoveViewer(ViewRequestDelegate
* delegate
) {
95 viewers_
.erase(std::remove(viewers_
.begin(), viewers_
.end(), delegate
));
96 if (viewers_
.empty()) {
101 void TaskTracker::MaybeCancel() {
102 if (!save_callbacks_
.empty() || !viewers_
.empty()) {
103 // There's still work to be done.
107 // The cancel callback should not delete this. To ensure that it doesn't, grab
108 // a weak pointer and check that it has not been invalidated.
109 base::WeakPtr
<TaskTracker
> self(weak_ptr_factory_
.GetWeakPtr());
110 cancel_callback_
.Run(this);
114 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
116 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded
) {
117 base::MessageLoop::current()->PostTask(
119 base::Bind(&TaskTracker::DoSaveCallbacks
,
120 weak_ptr_factory_
.GetWeakPtr(),
121 distillation_succeeded
));
124 void TaskTracker::DoSaveCallbacks(bool distillation_succeeded
) {
125 if (!save_callbacks_
.empty()) {
126 for (size_t i
= 0; i
< save_callbacks_
.size(); ++i
) {
127 DCHECK(!save_callbacks_
[i
].is_null());
128 save_callbacks_
[i
].Run(
129 entry_
, distilled_article_
.get(), distillation_succeeded
);
132 save_callbacks_
.clear();
137 void TaskTracker::NotifyViewer(ViewRequestDelegate
* delegate
) {
138 DCHECK(distillation_complete_
);
139 delegate
->OnArticleReady(distilled_article_
.get());
142 void TaskTracker::OnDistilledDataReady(
143 scoped_ptr
<DistilledArticleProto
> distilled_article
) {
144 distilled_article_
= distilled_article
.Pass();
145 bool distillation_successful
= false;
146 if (distilled_article_
->pages_size() > 0) {
147 distillation_successful
= true;
148 entry_
.set_title(distilled_article_
->title());
150 entry_
.clear_pages();
151 for (int i
= 0; i
< distilled_article_
->pages_size(); ++i
) {
152 sync_pb::ArticlePage
* page
= entry_
.add_pages();
153 page
->set_url(distilled_article_
->pages(i
).url());
157 distillation_complete_
= true;
159 for (size_t i
= 0; i
< viewers_
.size(); ++i
) {
160 NotifyViewer(viewers_
[i
]);
163 // Already inside a callback run SaveCallbacks directly.
164 DoSaveCallbacks(distillation_successful
);
167 } // namespace dom_distiller