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/location.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "components/dom_distiller/core/distilled_content_store.h"
13 #include "components/dom_distiller/core/dom_distiller_store.h"
14 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
15 #include "components/dom_distiller/core/task_tracker.h"
18 namespace dom_distiller
{
22 ArticleEntry
CreateSkeletonEntryForUrl(const GURL
& url
) {
23 ArticleEntry skeleton
;
24 skeleton
.set_entry_id(base::GenerateGUID());
25 ArticleEntryPage
* page
= skeleton
.add_pages();
26 page
->set_url(url
.spec());
28 DCHECK(IsEntryValid(skeleton
));
32 void RunArticleAvailableCallback(
33 const DomDistillerService::ArticleAvailableCallback
& article_cb
,
34 const ArticleEntry
& entry
,
35 const DistilledArticleProto
* article_proto
,
36 bool distillation_succeeded
) {
37 article_cb
.Run(distillation_succeeded
);
42 DomDistillerService::DomDistillerService(
43 scoped_ptr
<DomDistillerStoreInterface
> store
,
44 scoped_ptr
<DistillerFactory
> distiller_factory
,
45 scoped_ptr
<DistillerPageFactory
> distiller_page_factory
,
46 scoped_ptr
<DistilledPagePrefs
> distilled_page_prefs
)
47 : store_(store
.Pass()),
48 content_store_(new InMemoryContentStore(kDefaultMaxNumCachedEntries
)),
49 distiller_factory_(distiller_factory
.Pass()),
50 distiller_page_factory_(distiller_page_factory
.Pass()),
51 distilled_page_prefs_(distilled_page_prefs
.Pass()) {
54 DomDistillerService::~DomDistillerService() {
57 syncer::SyncableService
* DomDistillerService::GetSyncableService() const {
58 return store_
->GetSyncableService();
61 scoped_ptr
<DistillerPage
> DomDistillerService::CreateDefaultDistillerPage(
62 const gfx::Size
& render_view_size
) {
63 return distiller_page_factory_
->CreateDistillerPage(render_view_size
).Pass();
66 scoped_ptr
<DistillerPage
>
67 DomDistillerService::CreateDefaultDistillerPageWithHandle(
68 scoped_ptr
<SourcePageHandle
> handle
) {
69 return distiller_page_factory_
->CreateDistillerPageWithHandle(handle
.Pass())
73 const std::string
DomDistillerService::AddToList(
75 scoped_ptr
<DistillerPage
> distiller_page
,
76 const ArticleAvailableCallback
& article_cb
) {
78 const bool is_already_added
= store_
->GetEntryByUrl(url
, &entry
);
80 TaskTracker
* task_tracker
= nullptr;
81 if (is_already_added
) {
82 task_tracker
= GetTaskTrackerForEntry(entry
);
83 if (task_tracker
== NULL
) {
84 // Entry is in the store but there is no task tracker. This could
85 // happen when distillation has already completed. For now just return
87 // TODO(shashishekhar): Change this to check if article is available,
88 // An article may not be available for a variety of reasons, e.g.
89 // distillation failure or blobs not available locally.
90 base::ThreadTaskRunnerHandle::Get()->PostTask(
91 FROM_HERE
, base::Bind(article_cb
, true));
92 return entry
.entry_id();
95 GetOrCreateTaskTrackerForUrl(url
, &task_tracker
);
98 if (!article_cb
.is_null()) {
99 task_tracker
->AddSaveCallback(
100 base::Bind(&RunArticleAvailableCallback
, article_cb
));
103 if (!is_already_added
) {
104 task_tracker
->AddSaveCallback(base::Bind(
105 &DomDistillerService::AddDistilledPageToList
, base::Unretained(this)));
106 task_tracker
->StartDistiller(distiller_factory_
.get(),
107 distiller_page
.Pass());
108 task_tracker
->StartBlobFetcher();
111 return task_tracker
->GetEntryId();
114 bool DomDistillerService::HasEntry(const std::string
& entry_id
) {
115 return store_
->GetEntryById(entry_id
, NULL
);
118 std::string
DomDistillerService::GetUrlForEntry(const std::string
& entry_id
) {
120 if (store_
->GetEntryById(entry_id
, &entry
)) {
121 return entry
.pages().Get(0).url();
126 std::vector
<ArticleEntry
> DomDistillerService::GetEntries() const {
127 return store_
->GetEntries();
130 scoped_ptr
<ArticleEntry
> DomDistillerService::RemoveEntry(
131 const std::string
& entry_id
) {
132 scoped_ptr
<ArticleEntry
> entry(new ArticleEntry
);
133 entry
->set_entry_id(entry_id
);
134 TaskTracker
* task_tracker
= GetTaskTrackerForEntry(*entry
);
135 if (task_tracker
!= NULL
) {
136 task_tracker
->CancelSaveCallbacks();
139 if (!store_
->GetEntryById(entry_id
, entry
.get())) {
140 return scoped_ptr
<ArticleEntry
>();
143 if (store_
->RemoveEntry(*entry
)) {
146 return scoped_ptr
<ArticleEntry
>();
149 scoped_ptr
<ViewerHandle
> DomDistillerService::ViewEntry(
150 ViewRequestDelegate
* delegate
,
151 scoped_ptr
<DistillerPage
> distiller_page
,
152 const std::string
& entry_id
) {
154 if (!store_
->GetEntryById(entry_id
, &entry
)) {
155 return scoped_ptr
<ViewerHandle
>();
158 TaskTracker
* task_tracker
= nullptr;
159 bool was_created
= GetOrCreateTaskTrackerForEntry(entry
, &task_tracker
);
160 scoped_ptr
<ViewerHandle
> viewer_handle
= task_tracker
->AddViewer(delegate
);
162 task_tracker
->StartDistiller(distiller_factory_
.get(),
163 distiller_page
.Pass());
164 task_tracker
->StartBlobFetcher();
167 return viewer_handle
.Pass();
170 scoped_ptr
<ViewerHandle
> DomDistillerService::ViewUrl(
171 ViewRequestDelegate
* delegate
,
172 scoped_ptr
<DistillerPage
> distiller_page
,
174 if (!url
.is_valid()) {
175 return scoped_ptr
<ViewerHandle
>();
178 TaskTracker
* task_tracker
= nullptr;
179 bool was_created
= GetOrCreateTaskTrackerForUrl(url
, &task_tracker
);
180 scoped_ptr
<ViewerHandle
> viewer_handle
= task_tracker
->AddViewer(delegate
);
181 // If a distiller is already running for one URL, don't start another.
183 task_tracker
->StartDistiller(distiller_factory_
.get(),
184 distiller_page
.Pass());
185 task_tracker
->StartBlobFetcher();
188 return viewer_handle
.Pass();
191 bool DomDistillerService::GetOrCreateTaskTrackerForUrl(
193 TaskTracker
** task_tracker
) {
195 if (store_
->GetEntryByUrl(url
, &entry
)) {
196 return GetOrCreateTaskTrackerForEntry(entry
, task_tracker
);
199 *task_tracker
= GetTaskTrackerForUrl(url
);
204 ArticleEntry skeleton_entry
= CreateSkeletonEntryForUrl(url
);
205 *task_tracker
= CreateTaskTracker(skeleton_entry
);
209 TaskTracker
* DomDistillerService::GetTaskTrackerForUrl(const GURL
& url
) const {
210 for (TaskList::const_iterator it
= tasks_
.begin(); it
!= tasks_
.end(); ++it
) {
211 if ((*it
)->HasUrl(url
)) {
218 TaskTracker
* DomDistillerService::GetTaskTrackerForEntry(
219 const ArticleEntry
& entry
) const {
220 const std::string
& entry_id
= entry
.entry_id();
221 for (TaskList::const_iterator it
= tasks_
.begin(); it
!= tasks_
.end(); ++it
) {
222 if ((*it
)->HasEntryId(entry_id
)) {
229 bool DomDistillerService::GetOrCreateTaskTrackerForEntry(
230 const ArticleEntry
& entry
,
231 TaskTracker
** task_tracker
) {
232 *task_tracker
= GetTaskTrackerForEntry(entry
);
233 if (!*task_tracker
) {
234 *task_tracker
= CreateTaskTracker(entry
);
240 TaskTracker
* DomDistillerService::CreateTaskTracker(const ArticleEntry
& entry
) {
241 TaskTracker::CancelCallback cancel_callback
=
242 base::Bind(&DomDistillerService::CancelTask
, base::Unretained(this));
243 TaskTracker
* tracker
=
244 new TaskTracker(entry
, cancel_callback
, content_store_
.get());
245 tasks_
.push_back(tracker
);
249 void DomDistillerService::CancelTask(TaskTracker
* task
) {
250 TaskList::iterator it
= std::find(tasks_
.begin(), tasks_
.end(), task
);
251 if (it
!= tasks_
.end()) {
252 tasks_
.weak_erase(it
);
253 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, task
);
257 void DomDistillerService::AddDistilledPageToList(
258 const ArticleEntry
& entry
,
259 const DistilledArticleProto
* article_proto
,
260 bool distillation_succeeded
) {
261 DCHECK(IsEntryValid(entry
));
262 if (distillation_succeeded
) {
263 DCHECK(article_proto
);
264 DCHECK_GT(article_proto
->pages_size(), 0);
265 store_
->AddEntry(entry
);
266 DCHECK_EQ(article_proto
->pages_size(), entry
.pages_size());
270 void DomDistillerService::AddObserver(DomDistillerObserver
* observer
) {
272 store_
->AddObserver(observer
);
275 void DomDistillerService::RemoveObserver(DomDistillerObserver
* observer
) {
277 store_
->RemoveObserver(observer
);
280 DistilledPagePrefs
* DomDistillerService::GetDistilledPagePrefs() {
281 return distilled_page_prefs_
.get();
284 } // namespace dom_distiller