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