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 scoped_ptr
<DistilledPagePrefs
> distilled_page_prefs
)
44 : store_(store
.Pass()),
45 content_store_(new InMemoryContentStore(kDefaultMaxNumCachedEntries
)),
46 distiller_factory_(distiller_factory
.Pass()),
47 distiller_page_factory_(distiller_page_factory
.Pass()),
48 distilled_page_prefs_(distilled_page_prefs
.Pass()) {
51 DomDistillerService::~DomDistillerService() {
54 syncer::SyncableService
* DomDistillerService::GetSyncableService() const {
55 return store_
->GetSyncableService();
58 scoped_ptr
<DistillerPage
> DomDistillerService::CreateDefaultDistillerPage(
59 const gfx::Size
& render_view_size
) {
60 return distiller_page_factory_
->CreateDistillerPage(render_view_size
).Pass();
63 scoped_ptr
<DistillerPage
>
64 DomDistillerService::CreateDefaultDistillerPageWithHandle(
65 scoped_ptr
<SourcePageHandle
> handle
) {
66 return distiller_page_factory_
->CreateDistillerPageWithHandle(handle
.Pass())
70 const std::string
DomDistillerService::AddToList(
72 scoped_ptr
<DistillerPage
> distiller_page
,
73 const ArticleAvailableCallback
& article_cb
) {
75 const bool is_already_added
= store_
->GetEntryByUrl(url
, &entry
);
77 TaskTracker
* task_tracker
;
78 if (is_already_added
) {
79 task_tracker
= GetTaskTrackerForEntry(entry
);
80 if (task_tracker
== NULL
) {
81 // Entry is in the store but there is no task tracker. This could
82 // happen when distillation has already completed. For now just return
84 // TODO(shashishekhar): Change this to check if article is available,
85 // An article may not be available for a variety of reasons, e.g.
86 // distillation failure or blobs not available locally.
87 base::MessageLoop::current()->PostTask(FROM_HERE
,
88 base::Bind(article_cb
, true));
89 return entry
.entry_id();
92 task_tracker
= GetOrCreateTaskTrackerForUrl(url
);
95 if (!article_cb
.is_null()) {
96 task_tracker
->AddSaveCallback(
97 base::Bind(&RunArticleAvailableCallback
, article_cb
));
100 if (!is_already_added
) {
101 task_tracker
->AddSaveCallback(base::Bind(
102 &DomDistillerService::AddDistilledPageToList
, base::Unretained(this)));
103 task_tracker
->StartDistiller(distiller_factory_
.get(),
104 distiller_page
.Pass());
105 task_tracker
->StartBlobFetcher();
108 return task_tracker
->GetEntryId();
111 bool DomDistillerService::HasEntry(const std::string
& entry_id
) {
112 return store_
->GetEntryById(entry_id
, NULL
);
115 std::string
DomDistillerService::GetUrlForEntry(const std::string
& entry_id
) {
117 if (store_
->GetEntryById(entry_id
, &entry
)) {
118 return entry
.pages().Get(0).url();
123 std::vector
<ArticleEntry
> DomDistillerService::GetEntries() const {
124 return store_
->GetEntries();
127 scoped_ptr
<ArticleEntry
> DomDistillerService::RemoveEntry(
128 const std::string
& entry_id
) {
129 scoped_ptr
<ArticleEntry
> entry(new ArticleEntry
);
130 entry
->set_entry_id(entry_id
);
131 TaskTracker
* task_tracker
= GetTaskTrackerForEntry(*entry
);
132 if (task_tracker
!= NULL
) {
133 task_tracker
->CancelSaveCallbacks();
136 if (!store_
->GetEntryById(entry_id
, entry
.get())) {
137 return scoped_ptr
<ArticleEntry
>();
140 if (store_
->RemoveEntry(*entry
)) {
143 return scoped_ptr
<ArticleEntry
>();
146 scoped_ptr
<ViewerHandle
> DomDistillerService::ViewEntry(
147 ViewRequestDelegate
* delegate
,
148 scoped_ptr
<DistillerPage
> distiller_page
,
149 const std::string
& entry_id
) {
151 if (!store_
->GetEntryById(entry_id
, &entry
)) {
152 return scoped_ptr
<ViewerHandle
>();
155 TaskTracker
* task_tracker
= GetOrCreateTaskTrackerForEntry(entry
);
156 scoped_ptr
<ViewerHandle
> viewer_handle
= task_tracker
->AddViewer(delegate
);
157 task_tracker
->StartDistiller(distiller_factory_
.get(), distiller_page
.Pass());
158 task_tracker
->StartBlobFetcher();
160 return viewer_handle
.Pass();
163 scoped_ptr
<ViewerHandle
> DomDistillerService::ViewUrl(
164 ViewRequestDelegate
* delegate
,
165 scoped_ptr
<DistillerPage
> distiller_page
,
167 if (!url
.is_valid()) {
168 return scoped_ptr
<ViewerHandle
>();
171 TaskTracker
* task_tracker
= GetOrCreateTaskTrackerForUrl(url
);
172 scoped_ptr
<ViewerHandle
> viewer_handle
= task_tracker
->AddViewer(delegate
);
173 task_tracker
->StartDistiller(distiller_factory_
.get(), distiller_page
.Pass());
174 task_tracker
->StartBlobFetcher();
176 return viewer_handle
.Pass();
179 TaskTracker
* DomDistillerService::GetOrCreateTaskTrackerForUrl(
182 if (store_
->GetEntryByUrl(url
, &entry
)) {
183 return GetOrCreateTaskTrackerForEntry(entry
);
186 for (TaskList::iterator it
= tasks_
.begin(); it
!= tasks_
.end(); ++it
) {
187 if ((*it
)->HasUrl(url
)) {
192 ArticleEntry skeleton_entry
= CreateSkeletonEntryForUrl(url
);
193 TaskTracker
* task_tracker
= CreateTaskTracker(skeleton_entry
);
197 TaskTracker
* DomDistillerService::GetTaskTrackerForEntry(
198 const ArticleEntry
& entry
) const {
199 const std::string
& entry_id
= entry
.entry_id();
200 for (TaskList::const_iterator it
= tasks_
.begin(); it
!= tasks_
.end(); ++it
) {
201 if ((*it
)->HasEntryId(entry_id
)) {
208 TaskTracker
* DomDistillerService::GetOrCreateTaskTrackerForEntry(
209 const ArticleEntry
& entry
) {
210 TaskTracker
* task_tracker
= GetTaskTrackerForEntry(entry
);
211 if (task_tracker
== NULL
) {
212 task_tracker
= CreateTaskTracker(entry
);
217 TaskTracker
* DomDistillerService::CreateTaskTracker(const ArticleEntry
& entry
) {
218 TaskTracker::CancelCallback cancel_callback
=
219 base::Bind(&DomDistillerService::CancelTask
, base::Unretained(this));
220 TaskTracker
* tracker
=
221 new TaskTracker(entry
, cancel_callback
, content_store_
.get());
222 tasks_
.push_back(tracker
);
226 void DomDistillerService::CancelTask(TaskTracker
* task
) {
227 TaskList::iterator it
= std::find(tasks_
.begin(), tasks_
.end(), task
);
228 if (it
!= tasks_
.end()) {
229 tasks_
.weak_erase(it
);
230 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, task
);
234 void DomDistillerService::AddDistilledPageToList(
235 const ArticleEntry
& entry
,
236 const DistilledArticleProto
* article_proto
,
237 bool distillation_succeeded
) {
238 DCHECK(IsEntryValid(entry
));
239 if (distillation_succeeded
) {
240 DCHECK(article_proto
);
241 DCHECK_GT(article_proto
->pages_size(), 0);
242 store_
->AddEntry(entry
);
243 DCHECK_EQ(article_proto
->pages_size(), entry
.pages_size());
247 void DomDistillerService::AddObserver(DomDistillerObserver
* observer
) {
249 store_
->AddObserver(observer
);
252 void DomDistillerService::RemoveObserver(DomDistillerObserver
* observer
) {
254 store_
->RemoveObserver(observer
);
257 DistilledPagePrefs
* DomDistillerService::GetDistilledPagePrefs() {
258 return distilled_page_prefs_
.get();
261 } // namespace dom_distiller