Fix initial focus finding, ignore div nodes, and resolve aria-label name calculation.
[chromium-blink-merge.git] / components / dom_distiller / core / dom_distiller_service.cc
blob80c2e9f54759d10cf2cab15c5c8fd332c42940a5
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"
7 #include "base/guid.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"
16 #include "url/gurl.h"
18 namespace dom_distiller {
20 namespace {
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));
29 return 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);
40 } // namespace
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())
70 .Pass();
73 const std::string DomDistillerService::AddToList(
74 const GURL& url,
75 scoped_ptr<DistillerPage> distiller_page,
76 const ArticleAvailableCallback& article_cb) {
77 ArticleEntry entry;
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
86 // true.
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();
94 } else {
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) {
119 ArticleEntry entry;
120 if (store_->GetEntryById(entry_id, &entry)) {
121 return entry.pages().Get(0).url();
123 return "";
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)) {
144 return entry.Pass();
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) {
153 ArticleEntry entry;
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);
161 if (was_created) {
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,
173 const GURL& url) {
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.
182 if (was_created) {
183 task_tracker->StartDistiller(distiller_factory_.get(),
184 distiller_page.Pass());
185 task_tracker->StartBlobFetcher();
188 return viewer_handle.Pass();
191 bool DomDistillerService::GetOrCreateTaskTrackerForUrl(
192 const GURL& url,
193 TaskTracker** task_tracker) {
194 ArticleEntry entry;
195 if (store_->GetEntryByUrl(url, &entry)) {
196 return GetOrCreateTaskTrackerForEntry(entry, task_tracker);
199 *task_tracker = GetTaskTrackerForUrl(url);
200 if (*task_tracker) {
201 return false;
204 ArticleEntry skeleton_entry = CreateSkeletonEntryForUrl(url);
205 *task_tracker = CreateTaskTracker(skeleton_entry);
206 return true;
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)) {
212 return *it;
215 return nullptr;
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)) {
223 return *it;
226 return nullptr;
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);
235 return true;
237 return false;
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);
246 return 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) {
271 DCHECK(observer);
272 store_->AddObserver(observer);
275 void DomDistillerService::RemoveObserver(DomDistillerObserver* observer) {
276 DCHECK(observer);
277 store_->RemoveObserver(observer);
280 DistilledPagePrefs* DomDistillerService::GetDistilledPagePrefs() {
281 return distilled_page_prefs_.get();
284 } // namespace dom_distiller