5dd17d3a598b6976caabd263374948ae3f358c58
[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/proto/distilled_article.pb.h"
10 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
11
12 namespace dom_distiller {
13
14 ViewerHandle::ViewerHandle(CancelCallback callback)
15     : cancel_callback_(callback) {}
16
17 ViewerHandle::~ViewerHandle() {
18   if (!cancel_callback_.is_null()) {
19     cancel_callback_.Run();
20   }
21 }
22
23 TaskTracker::TaskTracker(const ArticleEntry& entry, CancelCallback callback)
24     : cancel_callback_(callback),
25       entry_(entry),
26       distilled_article_(),
27       content_ready_(false),
28       destruction_allowed_(true),
29       weak_ptr_factory_(this) {}
30
31 TaskTracker::~TaskTracker() {
32   DCHECK(destruction_allowed_);
33   DCHECK(viewers_.empty());
34 }
35
36 void TaskTracker::StartDistiller(DistillerFactory* factory) {
37   if (distiller_) {
38     return;
39   }
40   if (entry_.pages_size() == 0) {
41     return;
42   }
43
44   GURL url(entry_.pages(0).url());
45   DCHECK(url.is_valid());
46
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()));
53 }
54
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.
59 }
60
61 void TaskTracker::AddSaveCallback(const SaveCallback& callback) {
62   DCHECK(!callback.is_null());
63   save_callbacks_.push_back(callback);
64   if (content_ready_) {
65     // Distillation for this task has already completed, and so it can be
66     // immediately saved.
67     ScheduleSaveCallbacks(true);
68   }
69 }
70
71 scoped_ptr<ViewerHandle> TaskTracker::AddViewer(ViewRequestDelegate* delegate) {
72   viewers_.push_back(delegate);
73   if (content_ready_) {
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(
77         FROM_HERE,
78         base::Bind(&TaskTracker::NotifyViewer,
79                    weak_ptr_factory_.GetWeakPtr(),
80                    delegate));
81   }
82   return scoped_ptr<ViewerHandle>(new ViewerHandle(base::Bind(
83       &TaskTracker::RemoveViewer, weak_ptr_factory_.GetWeakPtr(), delegate)));
84 }
85
86 const std::string& TaskTracker::GetEntryId() const { return entry_.entry_id(); }
87
88 bool TaskTracker::HasEntryId(const std::string& entry_id) const {
89   return entry_.entry_id() == entry_id;
90 }
91
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()) {
95       return true;
96     }
97   }
98   return false;
99 }
100
101 void TaskTracker::RemoveViewer(ViewRequestDelegate* delegate) {
102   viewers_.erase(std::remove(viewers_.begin(), viewers_.end(), delegate));
103   if (viewers_.empty()) {
104     MaybeCancel();
105   }
106 }
107
108 void TaskTracker::MaybeCancel() {
109   if (!save_callbacks_.empty() || !viewers_.empty()) {
110     // There's still work to be done.
111     return;
112   }
113
114   base::AutoReset<bool> dont_delete_this_in_callback(&destruction_allowed_,
115                                                      false);
116   cancel_callback_.Run(this);
117 }
118
119 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
120
121 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded) {
122   base::MessageLoop::current()->PostTask(
123       FROM_HERE,
124       base::Bind(&TaskTracker::DoSaveCallbacks,
125                  weak_ptr_factory_.GetWeakPtr(),
126                  distillation_succeeded));
127 }
128
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);
135     }
136
137     save_callbacks_.clear();
138     MaybeCancel();
139   }
140 }
141
142 void TaskTracker::NotifyViewer(ViewRequestDelegate* delegate) {
143   DCHECK(content_ready_);
144   delegate->OnArticleReady(distilled_article_.get());
145 }
146
147 void TaskTracker::OnDistillerFinished(
148     scoped_ptr<DistilledArticleProto> distilled_article) {
149   OnDistilledArticleReady(distilled_article.Pass());
150 }
151
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());
159     // Reset the pages.
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());
164     }
165   }
166
167   content_ready_ = true;
168
169   for (size_t i = 0; i < viewers_.size(); ++i) {
170     NotifyViewer(viewers_[i]);
171   }
172
173   // Already inside a callback run SaveCallbacks directly.
174   DoSaveCallbacks(distillation_successful);
175 }
176
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);
181   }
182 }
183
184 }  // namespace dom_distiller