Upstream version 5.34.104.0
[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/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"
10
11 namespace dom_distiller {
12
13 ViewerHandle::ViewerHandle(CancelCallback callback)
14     : cancel_callback_(callback) {}
15
16 ViewerHandle::~ViewerHandle() {
17   if (!cancel_callback_.is_null()) {
18     cancel_callback_.Run();
19   }
20 }
21
22 TaskTracker::TaskTracker(const ArticleEntry& entry, CancelCallback callback)
23     : cancel_callback_(callback),
24       entry_(entry),
25       distilled_article_(),
26       distillation_complete_(false),
27       weak_ptr_factory_(this) {}
28
29 TaskTracker::~TaskTracker() { DCHECK(viewers_.empty()); }
30
31 void TaskTracker::StartDistiller(DistillerFactory* factory) {
32   if (distiller_) {
33     return;
34   }
35   if (entry_.pages_size() == 0) {
36     return;
37   }
38
39   GURL url(entry_.pages(0).url());
40   DCHECK(url.is_valid());
41
42   distiller_ = factory->CreateDistiller();
43   distiller_->DistillPage(url,
44                           base::Bind(&TaskTracker::OnDistilledDataReady,
45                                      weak_ptr_factory_.GetWeakPtr()));
46 }
47
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.
52 }
53
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
59     // immediately saved.
60     ScheduleSaveCallbacks(true);
61   }
62 }
63
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(
70         FROM_HERE,
71         base::Bind(&TaskTracker::NotifyViewer,
72                    weak_ptr_factory_.GetWeakPtr(),
73                    delegate));
74   }
75   return scoped_ptr<ViewerHandle>(new ViewerHandle(base::Bind(
76       &TaskTracker::RemoveViewer, weak_ptr_factory_.GetWeakPtr(), delegate)));
77 }
78
79 const std::string& TaskTracker::GetEntryId() const { return entry_.entry_id(); }
80
81 bool TaskTracker::HasEntryId(const std::string& entry_id) const {
82   return entry_.entry_id() == entry_id;
83 }
84
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()) {
88       return true;
89     }
90   }
91   return false;
92 }
93
94 void TaskTracker::RemoveViewer(ViewRequestDelegate* delegate) {
95   viewers_.erase(std::remove(viewers_.begin(), viewers_.end(), delegate));
96   if (viewers_.empty()) {
97     MaybeCancel();
98   }
99 }
100
101 void TaskTracker::MaybeCancel() {
102   if (!save_callbacks_.empty() || !viewers_.empty()) {
103     // There's still work to be done.
104     return;
105   }
106
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);
111   DCHECK(self);
112 }
113
114 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
115
116 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded) {
117   base::MessageLoop::current()->PostTask(
118       FROM_HERE,
119       base::Bind(&TaskTracker::DoSaveCallbacks,
120                  weak_ptr_factory_.GetWeakPtr(),
121                  distillation_succeeded));
122 }
123
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);
130     }
131
132     save_callbacks_.clear();
133     MaybeCancel();
134   }
135 }
136
137 void TaskTracker::NotifyViewer(ViewRequestDelegate* delegate) {
138   DCHECK(distillation_complete_);
139   delegate->OnArticleReady(distilled_article_.get());
140 }
141
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());
149     // Reset the pages.
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());
154     }
155   }
156
157   distillation_complete_ = true;
158
159   for (size_t i = 0; i < viewers_.size(); ++i) {
160     NotifyViewer(viewers_[i]);
161   }
162
163   // Already inside a callback run SaveCallbacks directly.
164   DoSaveCallbacks(distillation_successful);
165 }
166
167 }  // namespace dom_distiller