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/dom_distiller_service.h"
8 #include "base/message_loop/message_loop.h"
9 #include "components/dom_distiller/core/distilled_content_store.h"
10 #include "components/dom_distiller/core/dom_distiller_store.h"
11 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
12 #include "components/dom_distiller/core/task_tracker.h"
15 namespace dom_distiller
{
19 ArticleEntry
CreateSkeletonEntryForUrl(const GURL
& url
) {
20 ArticleEntry skeleton
;
21 skeleton
.set_entry_id(base::GenerateGUID());
22 ArticleEntryPage
* page
= skeleton
.add_pages();
23 page
->set_url(url
.spec());
25 DCHECK(IsEntryValid(skeleton
));
29 void RunArticleAvailableCallback(
30 const DomDistillerService::ArticleAvailableCallback
& article_cb
,
31 const ArticleEntry
& entry
,
32 const DistilledArticleProto
* article_proto
,
33 bool distillation_succeeded
) {
34 article_cb
.Run(distillation_succeeded
);
39 DomDistillerService::DomDistillerService(
40 scoped_ptr
<DomDistillerStoreInterface
> store
,
41 scoped_ptr
<DistillerFactory
> distiller_factory
,
42 scoped_ptr
<DistillerPageFactory
> distiller_page_factory
)
43 : store_(store
.Pass()),
44 content_store_(new InMemoryContentStore()),
45 distiller_factory_(distiller_factory
.Pass()),
46 distiller_page_factory_(distiller_page_factory
.Pass()) {
49 DomDistillerService::~DomDistillerService() {
52 syncer::SyncableService
* DomDistillerService::GetSyncableService() const {
53 return store_
->GetSyncableService();
56 scoped_ptr
<DistillerPage
> DomDistillerService::CreateDefaultDistillerPage() {
57 return distiller_page_factory_
->CreateDistillerPage().Pass();
60 scoped_ptr
<DistillerPage
>
61 DomDistillerService::CreateDefaultDistillerPageWithHandle(
62 scoped_ptr
<SourcePageHandle
> handle
) {
63 return distiller_page_factory_
->CreateDistillerPageWithHandle(handle
.Pass())
67 const std::string
DomDistillerService::AddToList(
69 scoped_ptr
<DistillerPage
> distiller_page
,
70 const ArticleAvailableCallback
& article_cb
) {
72 const bool is_already_added
= store_
->GetEntryByUrl(url
, &entry
);
74 TaskTracker
* task_tracker
;
75 if (is_already_added
) {
76 task_tracker
= GetTaskTrackerForEntry(entry
);
77 if (task_tracker
== NULL
) {
78 // Entry is in the store but there is no task tracker. This could
79 // happen when distillation has already completed. For now just return
81 // TODO(shashishekhar): Change this to check if article is available,
82 // An article may not be available for a variety of reasons, e.g.
83 // distillation failure or blobs not available locally.
84 base::MessageLoop::current()->PostTask(FROM_HERE
,
85 base::Bind(article_cb
, true));
86 return entry
.entry_id();
89 task_tracker
= GetOrCreateTaskTrackerForUrl(url
);
92 if (!article_cb
.is_null()) {
93 task_tracker
->AddSaveCallback(
94 base::Bind(&RunArticleAvailableCallback
, article_cb
));
97 if (!is_already_added
) {
98 task_tracker
->AddSaveCallback(base::Bind(
99 &DomDistillerService::AddDistilledPageToList
, base::Unretained(this)));
100 task_tracker
->StartDistiller(distiller_factory_
.get(),
101 distiller_page
.Pass());
102 task_tracker
->StartBlobFetcher();
105 return task_tracker
->GetEntryId();
108 std::vector
<ArticleEntry
> DomDistillerService::GetEntries() const {
109 return store_
->GetEntries();
112 scoped_ptr
<ArticleEntry
> DomDistillerService::RemoveEntry(
113 const std::string
& entry_id
) {
114 scoped_ptr
<ArticleEntry
> entry(new ArticleEntry
);
115 entry
->set_entry_id(entry_id
);
116 TaskTracker
* task_tracker
= GetTaskTrackerForEntry(*entry
);
117 if (task_tracker
!= NULL
) {
118 task_tracker
->CancelSaveCallbacks();
121 if (!store_
->GetEntryById(entry_id
, entry
.get())) {
122 return scoped_ptr
<ArticleEntry
>();
125 if (store_
->RemoveEntry(*entry
)) {
128 return scoped_ptr
<ArticleEntry
>();
131 scoped_ptr
<ViewerHandle
> DomDistillerService::ViewEntry(
132 ViewRequestDelegate
* delegate
,
133 scoped_ptr
<DistillerPage
> distiller_page
,
134 const std::string
& entry_id
) {
136 if (!store_
->GetEntryById(entry_id
, &entry
)) {
137 return scoped_ptr
<ViewerHandle
>();
140 TaskTracker
* task_tracker
= GetOrCreateTaskTrackerForEntry(entry
);
141 scoped_ptr
<ViewerHandle
> viewer_handle
= task_tracker
->AddViewer(delegate
);
142 task_tracker
->StartDistiller(distiller_factory_
.get(), distiller_page
.Pass());
143 task_tracker
->StartBlobFetcher();
145 return viewer_handle
.Pass();
148 scoped_ptr
<ViewerHandle
> DomDistillerService::ViewUrl(
149 ViewRequestDelegate
* delegate
,
150 scoped_ptr
<DistillerPage
> distiller_page
,
152 if (!url
.is_valid()) {
153 return scoped_ptr
<ViewerHandle
>();
156 TaskTracker
* task_tracker
= GetOrCreateTaskTrackerForUrl(url
);
157 scoped_ptr
<ViewerHandle
> viewer_handle
= task_tracker
->AddViewer(delegate
);
158 task_tracker
->StartDistiller(distiller_factory_
.get(), distiller_page
.Pass());
159 task_tracker
->StartBlobFetcher();
161 return viewer_handle
.Pass();
164 TaskTracker
* DomDistillerService::GetOrCreateTaskTrackerForUrl(
167 if (store_
->GetEntryByUrl(url
, &entry
)) {
168 return GetOrCreateTaskTrackerForEntry(entry
);
171 for (TaskList::iterator it
= tasks_
.begin(); it
!= tasks_
.end(); ++it
) {
172 if ((*it
)->HasUrl(url
)) {
177 ArticleEntry skeleton_entry
= CreateSkeletonEntryForUrl(url
);
178 TaskTracker
* task_tracker
= CreateTaskTracker(skeleton_entry
);
182 TaskTracker
* DomDistillerService::GetTaskTrackerForEntry(
183 const ArticleEntry
& entry
) const {
184 const std::string
& entry_id
= entry
.entry_id();
185 for (TaskList::const_iterator it
= tasks_
.begin(); it
!= tasks_
.end(); ++it
) {
186 if ((*it
)->HasEntryId(entry_id
)) {
193 TaskTracker
* DomDistillerService::GetOrCreateTaskTrackerForEntry(
194 const ArticleEntry
& entry
) {
195 TaskTracker
* task_tracker
= GetTaskTrackerForEntry(entry
);
196 if (task_tracker
== NULL
) {
197 task_tracker
= CreateTaskTracker(entry
);
202 TaskTracker
* DomDistillerService::CreateTaskTracker(const ArticleEntry
& entry
) {
203 TaskTracker::CancelCallback cancel_callback
=
204 base::Bind(&DomDistillerService::CancelTask
, base::Unretained(this));
205 TaskTracker
* tracker
=
206 new TaskTracker(entry
, cancel_callback
, content_store_
.get());
207 tasks_
.push_back(tracker
);
211 void DomDistillerService::CancelTask(TaskTracker
* task
) {
212 TaskList::iterator it
= std::find(tasks_
.begin(), tasks_
.end(), task
);
213 if (it
!= tasks_
.end()) {
214 tasks_
.weak_erase(it
);
215 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, task
);
219 void DomDistillerService::AddDistilledPageToList(
220 const ArticleEntry
& entry
,
221 const DistilledArticleProto
* article_proto
,
222 bool distillation_succeeded
) {
223 DCHECK(IsEntryValid(entry
));
224 if (distillation_succeeded
) {
225 DCHECK(article_proto
);
226 DCHECK_GT(article_proto
->pages_size(), 0);
227 store_
->AddEntry(entry
);
228 DCHECK_EQ(article_proto
->pages_size(), entry
.pages_size());
232 void DomDistillerService::AddObserver(DomDistillerObserver
* observer
) {
234 store_
->AddObserver(observer
);
237 void DomDistillerService::RemoveObserver(DomDistillerObserver
* observer
) {
239 store_
->RemoveObserver(observer
);
242 } // namespace dom_distiller