• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
13 #include "url/gurl.h"
14 
15 namespace dom_distiller {
16 
17 namespace {
18 
CreateSkeletonEntryForUrl(const GURL & url)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());
24 
25   DCHECK(IsEntryValid(skeleton));
26   return skeleton;
27 }
28 
RunArticleAvailableCallback(const DomDistillerService::ArticleAvailableCallback & article_cb,const ArticleEntry & entry,const DistilledArticleProto * article_proto,bool distillation_succeeded)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);
35 }
36 
37 }  // namespace
38 
DomDistillerService(scoped_ptr<DomDistillerStoreInterface> store,scoped_ptr<DistillerFactory> distiller_factory,scoped_ptr<DistillerPageFactory> distiller_page_factory)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(kDefaultMaxNumCachedEntries)),
45       distiller_factory_(distiller_factory.Pass()),
46       distiller_page_factory_(distiller_page_factory.Pass()) {
47 }
48 
~DomDistillerService()49 DomDistillerService::~DomDistillerService() {
50 }
51 
GetSyncableService() const52 syncer::SyncableService* DomDistillerService::GetSyncableService() const {
53   return store_->GetSyncableService();
54 }
55 
CreateDefaultDistillerPage()56 scoped_ptr<DistillerPage> DomDistillerService::CreateDefaultDistillerPage() {
57   return distiller_page_factory_->CreateDistillerPage().Pass();
58 }
59 
60 scoped_ptr<DistillerPage>
CreateDefaultDistillerPageWithHandle(scoped_ptr<SourcePageHandle> handle)61 DomDistillerService::CreateDefaultDistillerPageWithHandle(
62     scoped_ptr<SourcePageHandle> handle) {
63   return distiller_page_factory_->CreateDistillerPageWithHandle(handle.Pass())
64       .Pass();
65 }
66 
AddToList(const GURL & url,scoped_ptr<DistillerPage> distiller_page,const ArticleAvailableCallback & article_cb)67 const std::string DomDistillerService::AddToList(
68     const GURL& url,
69     scoped_ptr<DistillerPage> distiller_page,
70     const ArticleAvailableCallback& article_cb) {
71   ArticleEntry entry;
72   const bool is_already_added = store_->GetEntryByUrl(url, &entry);
73 
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
80       // true.
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();
87     }
88   } else {
89     task_tracker = GetOrCreateTaskTrackerForUrl(url);
90   }
91 
92   if (!article_cb.is_null()) {
93     task_tracker->AddSaveCallback(
94         base::Bind(&RunArticleAvailableCallback, article_cb));
95   }
96 
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();
103   }
104 
105   return task_tracker->GetEntryId();
106 }
107 
GetEntries() const108 std::vector<ArticleEntry> DomDistillerService::GetEntries() const {
109   return store_->GetEntries();
110 }
111 
RemoveEntry(const std::string & entry_id)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();
119   }
120 
121   if (!store_->GetEntryById(entry_id, entry.get())) {
122     return scoped_ptr<ArticleEntry>();
123   }
124 
125   if (store_->RemoveEntry(*entry)) {
126     return entry.Pass();
127   }
128   return scoped_ptr<ArticleEntry>();
129 }
130 
ViewEntry(ViewRequestDelegate * delegate,scoped_ptr<DistillerPage> distiller_page,const std::string & entry_id)131 scoped_ptr<ViewerHandle> DomDistillerService::ViewEntry(
132     ViewRequestDelegate* delegate,
133     scoped_ptr<DistillerPage> distiller_page,
134     const std::string& entry_id) {
135   ArticleEntry entry;
136   if (!store_->GetEntryById(entry_id, &entry)) {
137     return scoped_ptr<ViewerHandle>();
138   }
139 
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();
144 
145   return viewer_handle.Pass();
146 }
147 
ViewUrl(ViewRequestDelegate * delegate,scoped_ptr<DistillerPage> distiller_page,const GURL & url)148 scoped_ptr<ViewerHandle> DomDistillerService::ViewUrl(
149     ViewRequestDelegate* delegate,
150     scoped_ptr<DistillerPage> distiller_page,
151     const GURL& url) {
152   if (!url.is_valid()) {
153     return scoped_ptr<ViewerHandle>();
154   }
155 
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();
160 
161   return viewer_handle.Pass();
162 }
163 
GetOrCreateTaskTrackerForUrl(const GURL & url)164 TaskTracker* DomDistillerService::GetOrCreateTaskTrackerForUrl(
165     const GURL& url) {
166   ArticleEntry entry;
167   if (store_->GetEntryByUrl(url, &entry)) {
168     return GetOrCreateTaskTrackerForEntry(entry);
169   }
170 
171   for (TaskList::iterator it = tasks_.begin(); it != tasks_.end(); ++it) {
172     if ((*it)->HasUrl(url)) {
173       return *it;
174     }
175   }
176 
177   ArticleEntry skeleton_entry = CreateSkeletonEntryForUrl(url);
178   TaskTracker* task_tracker = CreateTaskTracker(skeleton_entry);
179   return task_tracker;
180 }
181 
GetTaskTrackerForEntry(const ArticleEntry & entry) const182 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)) {
187       return *it;
188     }
189   }
190   return NULL;
191 }
192 
GetOrCreateTaskTrackerForEntry(const ArticleEntry & entry)193 TaskTracker* DomDistillerService::GetOrCreateTaskTrackerForEntry(
194     const ArticleEntry& entry) {
195   TaskTracker* task_tracker = GetTaskTrackerForEntry(entry);
196   if (task_tracker == NULL) {
197     task_tracker = CreateTaskTracker(entry);
198   }
199   return task_tracker;
200 }
201 
CreateTaskTracker(const ArticleEntry & 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);
208   return tracker;
209 }
210 
CancelTask(TaskTracker * task)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);
216   }
217 }
218 
AddDistilledPageToList(const ArticleEntry & entry,const DistilledArticleProto * article_proto,bool distillation_succeeded)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());
229   }
230 }
231 
AddObserver(DomDistillerObserver * observer)232 void DomDistillerService::AddObserver(DomDistillerObserver* observer) {
233   DCHECK(observer);
234   store_->AddObserver(observer);
235 }
236 
RemoveObserver(DomDistillerObserver * observer)237 void DomDistillerService::RemoveObserver(DomDistillerObserver* observer) {
238   DCHECK(observer);
239   store_->RemoveObserver(observer);
240 }
241 
242 }  // namespace dom_distiller
243