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/proto/distilled_article.pb.h"
10 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
12 namespace dom_distiller {
14 ViewerHandle::ViewerHandle(CancelCallback callback)
15 : cancel_callback_(callback) {}
17 ViewerHandle::~ViewerHandle() {
18 if (!cancel_callback_.is_null()) {
19 cancel_callback_.Run();
23 TaskTracker::TaskTracker(const ArticleEntry& entry, CancelCallback callback)
24 : cancel_callback_(callback),
27 content_ready_(false),
28 destruction_allowed_(true),
29 weak_ptr_factory_(this) {}
31 TaskTracker::~TaskTracker() {
32 DCHECK(destruction_allowed_);
33 DCHECK(viewers_.empty());
36 void TaskTracker::StartDistiller(DistillerFactory* factory) {
40 if (entry_.pages_size() == 0) {
44 GURL url(entry_.pages(0).url());
45 DCHECK(url.is_valid());
47 distiller_ = factory->CreateDistiller();
48 distiller_->DistillPage(url,
49 base::Bind(&TaskTracker::OnDistillerFinished,
50 weak_ptr_factory_.GetWeakPtr()),
51 base::Bind(&TaskTracker::OnArticleDistillationUpdated,
52 weak_ptr_factory_.GetWeakPtr()));
55 void TaskTracker::StartBlobFetcher() {
56 // TODO(cjhopman): There needs to be some local storage for the distilled
57 // blob. When that happens, this should start some task to fetch the blob for
58 // |entry_| and asynchronously notify |this| when it is done.
61 void TaskTracker::AddSaveCallback(const SaveCallback& callback) {
62 DCHECK(!callback.is_null());
63 save_callbacks_.push_back(callback);
65 // Distillation for this task has already completed, and so it can be
67 ScheduleSaveCallbacks(true);
71 scoped_ptr<ViewerHandle> TaskTracker::AddViewer(ViewRequestDelegate* delegate) {
72 viewers_.push_back(delegate);
74 // Distillation for this task has already completed, and so the delegate can
75 // be immediately told of the result.
76 base::MessageLoop::current()->PostTask(
78 base::Bind(&TaskTracker::NotifyViewer,
79 weak_ptr_factory_.GetWeakPtr(),
82 return scoped_ptr<ViewerHandle>(new ViewerHandle(base::Bind(
83 &TaskTracker::RemoveViewer, weak_ptr_factory_.GetWeakPtr(), delegate)));
86 const std::string& TaskTracker::GetEntryId() const { return entry_.entry_id(); }
88 bool TaskTracker::HasEntryId(const std::string& entry_id) const {
89 return entry_.entry_id() == entry_id;
92 bool TaskTracker::HasUrl(const GURL& url) const {
93 for (int i = 0; i < entry_.pages_size(); ++i) {
94 if (entry_.pages(i).url() == url.spec()) {
101 void TaskTracker::RemoveViewer(ViewRequestDelegate* delegate) {
102 viewers_.erase(std::remove(viewers_.begin(), viewers_.end(), delegate));
103 if (viewers_.empty()) {
108 void TaskTracker::MaybeCancel() {
109 if (!save_callbacks_.empty() || !viewers_.empty()) {
110 // There's still work to be done.
114 base::AutoReset<bool> dont_delete_this_in_callback(&destruction_allowed_,
116 cancel_callback_.Run(this);
119 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
121 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded) {
122 base::MessageLoop::current()->PostTask(
124 base::Bind(&TaskTracker::DoSaveCallbacks,
125 weak_ptr_factory_.GetWeakPtr(),
126 distillation_succeeded));
129 void TaskTracker::DoSaveCallbacks(bool distillation_succeeded) {
130 if (!save_callbacks_.empty()) {
131 for (size_t i = 0; i < save_callbacks_.size(); ++i) {
132 DCHECK(!save_callbacks_[i].is_null());
133 save_callbacks_[i].Run(
134 entry_, distilled_article_.get(), distillation_succeeded);
137 save_callbacks_.clear();
142 void TaskTracker::NotifyViewer(ViewRequestDelegate* delegate) {
143 DCHECK(content_ready_);
144 delegate->OnArticleReady(distilled_article_.get());
147 void TaskTracker::OnDistillerFinished(
148 scoped_ptr<DistilledArticleProto> distilled_article) {
149 OnDistilledArticleReady(distilled_article.Pass());
152 void TaskTracker::OnDistilledArticleReady(
153 scoped_ptr<DistilledArticleProto> distilled_article) {
154 distilled_article_ = distilled_article.Pass();
155 bool distillation_successful = false;
156 if (distilled_article_->pages_size() > 0) {
157 distillation_successful = true;
158 entry_.set_title(distilled_article_->title());
160 entry_.clear_pages();
161 for (int i = 0; i < distilled_article_->pages_size(); ++i) {
162 sync_pb::ArticlePage* page = entry_.add_pages();
163 page->set_url(distilled_article_->pages(i).url());
167 content_ready_ = true;
169 for (size_t i = 0; i < viewers_.size(); ++i) {
170 NotifyViewer(viewers_[i]);
173 // Already inside a callback run SaveCallbacks directly.
174 DoSaveCallbacks(distillation_successful);
177 void TaskTracker::OnArticleDistillationUpdated(
178 const ArticleDistillationUpdate& article_update) {
179 for (size_t i = 0; i < viewers_.size(); ++i) {
180 viewers_[i]->OnArticleUpdated(article_update);
184 } // namespace dom_distiller