Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / components / dom_distiller / core / dom_distiller_service.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/dom_distiller_service.h"
6
7 #include "base/guid.h"
8 #include "base/message_loop/message_loop.h"
9 #include "components/dom_distiller/core/dom_distiller_store.h"
10 #include "components/dom_distiller/core/task_tracker.h"
11 #include "url/gurl.h"
12
13 namespace dom_distiller {
14
15 namespace {
16
17 ArticleEntry CreateSkeletonEntryForUrl(const GURL& url) {
18   ArticleEntry skeleton;
19   skeleton.set_entry_id(base::GenerateGUID());
20   ArticleEntryPage* page = skeleton.add_pages();
21   page->set_url(url.spec());
22
23   DCHECK(IsEntryValid(skeleton));
24   return skeleton;
25 }
26
27 void RunArticleAvailableCallback(
28     const DomDistillerService::ArticleAvailableCallback& article_cb,
29     const ArticleEntry& entry,
30     DistilledPageProto* proto,
31     bool distillation_succeeded) {
32   article_cb.Run(distillation_succeeded);
33 }
34
35 }  // namespace
36
37 DomDistillerService::DomDistillerService(
38     scoped_ptr<DomDistillerStoreInterface> store,
39     scoped_ptr<DistillerFactory> distiller_factory)
40     : store_(store.Pass()), distiller_factory_(distiller_factory.Pass()) {}
41
42 DomDistillerService::~DomDistillerService() {}
43
44 syncer::SyncableService* DomDistillerService::GetSyncableService() const {
45   return store_->GetSyncableService();
46 }
47
48 const std::string DomDistillerService::AddToList(
49     const GURL& url,
50     const ArticleAvailableCallback& article_cb) {
51   ArticleEntry entry;
52   const bool is_already_added = store_->GetEntryByUrl(url, &entry);
53
54   TaskTracker* task_tracker;
55   if (is_already_added) {
56     task_tracker = GetTaskTrackerForEntry(entry);
57     if (task_tracker == NULL) {
58       // Entry is in the store but there is no task tracker. This could
59       // happen when distillation has already completed. For now just return
60       // true.
61       // TODO(shashishekhar): Change this to check if article is available,
62       // An article may not be available for a variety of reasons, e.g.
63       // distillation failure or blobs not available locally.
64       base::MessageLoop::current()->PostTask(FROM_HERE,
65                                              base::Bind(article_cb, true));
66       return entry.entry_id();
67     }
68   } else {
69     task_tracker = GetOrCreateTaskTrackerForUrl(url);
70   }
71
72   if (!article_cb.is_null()) {
73     task_tracker->AddSaveCallback(
74         base::Bind(&RunArticleAvailableCallback, article_cb));
75   }
76
77   if (!is_already_added) {
78     task_tracker->AddSaveCallback(base::Bind(
79         &DomDistillerService::AddDistilledPageToList, base::Unretained(this)));
80     task_tracker->StartDistiller(distiller_factory_.get());
81   }
82
83   return task_tracker->GetEntryId();
84 }
85
86 std::vector<ArticleEntry> DomDistillerService::GetEntries() const {
87   return store_->GetEntries();
88 }
89
90 scoped_ptr<ArticleEntry> DomDistillerService::RemoveEntry(
91     const std::string& entry_id) {
92   scoped_ptr<ArticleEntry> entry(new ArticleEntry);
93   if (!store_->GetEntryById(entry_id, entry.get())) {
94     return scoped_ptr<ArticleEntry>();
95   }
96
97   TaskTracker* task_tracker = GetTaskTrackerForEntry(*entry);
98   if (task_tracker != NULL) {
99     task_tracker->CancelSaveCallbacks();
100   }
101
102   if (store_->RemoveEntry(*entry)) {
103     return entry.Pass();
104   }
105   return scoped_ptr<ArticleEntry>();
106 }
107
108 scoped_ptr<ViewerHandle> DomDistillerService::ViewEntry(
109     ViewRequestDelegate* delegate,
110     const std::string& entry_id) {
111   ArticleEntry entry;
112   if (!store_->GetEntryById(entry_id, &entry)) {
113     return scoped_ptr<ViewerHandle>();
114   }
115
116   TaskTracker* task_tracker = GetOrCreateTaskTrackerForEntry(entry);
117   scoped_ptr<ViewerHandle> viewer_handle = task_tracker->AddViewer(delegate);
118   task_tracker->StartDistiller(distiller_factory_.get());
119
120   return viewer_handle.Pass();
121 }
122
123 scoped_ptr<ViewerHandle> DomDistillerService::ViewUrl(
124     ViewRequestDelegate* delegate,
125     const GURL& url) {
126   if (!url.is_valid()) {
127     return scoped_ptr<ViewerHandle>();
128   }
129
130   TaskTracker* task_tracker = GetOrCreateTaskTrackerForUrl(url);
131   scoped_ptr<ViewerHandle> viewer_handle = task_tracker->AddViewer(delegate);
132   task_tracker->StartDistiller(distiller_factory_.get());
133
134   return viewer_handle.Pass();
135 }
136
137 TaskTracker* DomDistillerService::GetOrCreateTaskTrackerForUrl(
138     const GURL& url) {
139   ArticleEntry entry;
140   if (store_->GetEntryByUrl(url, &entry)) {
141     return GetOrCreateTaskTrackerForEntry(entry);
142   }
143
144   for (TaskList::iterator it = tasks_.begin(); it != tasks_.end(); ++it) {
145     if ((*it)->HasUrl(url)) {
146       return *it;
147     }
148   }
149
150   ArticleEntry skeleton_entry = CreateSkeletonEntryForUrl(url);
151   TaskTracker* task_tracker = CreateTaskTracker(skeleton_entry);
152   store_->AddEntry(skeleton_entry);
153   return task_tracker;
154 }
155
156 TaskTracker* DomDistillerService::GetTaskTrackerForEntry(
157     const ArticleEntry& entry) const {
158   const std::string& entry_id = entry.entry_id();
159   for (TaskList::const_iterator it = tasks_.begin(); it != tasks_.end(); ++it) {
160     if ((*it)->HasEntryId(entry_id)) {
161       return *it;
162     }
163   }
164   return NULL;
165 }
166
167 TaskTracker* DomDistillerService::GetOrCreateTaskTrackerForEntry(
168     const ArticleEntry& entry) {
169   TaskTracker* task_tracker = GetTaskTrackerForEntry(entry);
170   if (task_tracker == NULL) {
171     task_tracker = CreateTaskTracker(entry);
172   }
173   return task_tracker;
174 }
175
176 TaskTracker* DomDistillerService::CreateTaskTracker(const ArticleEntry& entry) {
177   TaskTracker::CancelCallback cancel_callback =
178       base::Bind(&DomDistillerService::CancelTask, base::Unretained(this));
179   TaskTracker* tracker = new TaskTracker(entry, cancel_callback);
180   tasks_.push_back(tracker);
181   return tracker;
182 }
183
184 void DomDistillerService::CancelTask(TaskTracker* task) {
185   TaskList::iterator it = std::find(tasks_.begin(), tasks_.end(), task);
186   if (it != tasks_.end()) {
187     tasks_.weak_erase(it);
188     base::MessageLoop::current()->DeleteSoon(FROM_HERE, task);
189   }
190 }
191
192 void DomDistillerService::AddDistilledPageToList(const ArticleEntry& entry,
193                                                  DistilledPageProto* proto,
194                                                  bool distillation_succeeded) {
195   DCHECK(IsEntryValid(entry));
196   if (distillation_succeeded) {
197     DCHECK(proto);
198     store_->UpdateEntry(entry);
199   }
200 }
201
202 void DomDistillerService::AddObserver(DomDistillerObserver* observer) {
203   DCHECK(observer);
204   store_->AddObserver(observer);
205 }
206
207 void DomDistillerService::RemoveObserver(DomDistillerObserver* observer) {
208   DCHECK(observer);
209   store_->RemoveObserver(observer);
210 }
211
212 }  // namespace dom_distiller