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 15namespace dom_distiller { 16 17namespace { 18 19ArticleEntry 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 29void 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 39DomDistillerService::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()) { 49} 50 51DomDistillerService::~DomDistillerService() { 52} 53 54syncer::SyncableService* DomDistillerService::GetSyncableService() const { 55 return store_->GetSyncableService(); 56} 57 58scoped_ptr<DistillerPage> DomDistillerService::CreateDefaultDistillerPage( 59 const gfx::Size& render_view_size) { 60 return distiller_page_factory_->CreateDistillerPage(render_view_size).Pass(); 61} 62 63scoped_ptr<DistillerPage> 64DomDistillerService::CreateDefaultDistillerPageWithHandle( 65 scoped_ptr<SourcePageHandle> handle) { 66 return distiller_page_factory_->CreateDistillerPageWithHandle(handle.Pass()) 67 .Pass(); 68} 69 70const std::string DomDistillerService::AddToList( 71 const GURL& url, 72 scoped_ptr<DistillerPage> distiller_page, 73 const ArticleAvailableCallback& article_cb) { 74 ArticleEntry entry; 75 const bool is_already_added = store_->GetEntryByUrl(url, &entry); 76 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 83 // true. 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(); 90 } 91 } else { 92 task_tracker = GetOrCreateTaskTrackerForUrl(url); 93 } 94 95 if (!article_cb.is_null()) { 96 task_tracker->AddSaveCallback( 97 base::Bind(&RunArticleAvailableCallback, article_cb)); 98 } 99 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(); 106 } 107 108 return task_tracker->GetEntryId(); 109} 110 111bool DomDistillerService::HasEntry(const std::string& entry_id) { 112 return store_->GetEntryById(entry_id, NULL); 113} 114 115std::string DomDistillerService::GetUrlForEntry(const std::string& entry_id) { 116 ArticleEntry entry; 117 if (store_->GetEntryById(entry_id, &entry)) { 118 return entry.pages().Get(0).url(); 119 } 120 return ""; 121} 122 123std::vector<ArticleEntry> DomDistillerService::GetEntries() const { 124 return store_->GetEntries(); 125} 126 127scoped_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(); 134 } 135 136 if (!store_->GetEntryById(entry_id, entry.get())) { 137 return scoped_ptr<ArticleEntry>(); 138 } 139 140 if (store_->RemoveEntry(*entry)) { 141 return entry.Pass(); 142 } 143 return scoped_ptr<ArticleEntry>(); 144} 145 146scoped_ptr<ViewerHandle> DomDistillerService::ViewEntry( 147 ViewRequestDelegate* delegate, 148 scoped_ptr<DistillerPage> distiller_page, 149 const std::string& entry_id) { 150 ArticleEntry entry; 151 if (!store_->GetEntryById(entry_id, &entry)) { 152 return scoped_ptr<ViewerHandle>(); 153 } 154 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(); 159 160 return viewer_handle.Pass(); 161} 162 163scoped_ptr<ViewerHandle> DomDistillerService::ViewUrl( 164 ViewRequestDelegate* delegate, 165 scoped_ptr<DistillerPage> distiller_page, 166 const GURL& url) { 167 if (!url.is_valid()) { 168 return scoped_ptr<ViewerHandle>(); 169 } 170 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(); 175 176 return viewer_handle.Pass(); 177} 178 179TaskTracker* DomDistillerService::GetOrCreateTaskTrackerForUrl( 180 const GURL& url) { 181 ArticleEntry entry; 182 if (store_->GetEntryByUrl(url, &entry)) { 183 return GetOrCreateTaskTrackerForEntry(entry); 184 } 185 186 for (TaskList::iterator it = tasks_.begin(); it != tasks_.end(); ++it) { 187 if ((*it)->HasUrl(url)) { 188 return *it; 189 } 190 } 191 192 ArticleEntry skeleton_entry = CreateSkeletonEntryForUrl(url); 193 TaskTracker* task_tracker = CreateTaskTracker(skeleton_entry); 194 return task_tracker; 195} 196 197TaskTracker* 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)) { 202 return *it; 203 } 204 } 205 return NULL; 206} 207 208TaskTracker* DomDistillerService::GetOrCreateTaskTrackerForEntry( 209 const ArticleEntry& entry) { 210 TaskTracker* task_tracker = GetTaskTrackerForEntry(entry); 211 if (task_tracker == NULL) { 212 task_tracker = CreateTaskTracker(entry); 213 } 214 return task_tracker; 215} 216 217TaskTracker* 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); 223 return tracker; 224} 225 226void 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); 231 } 232} 233 234void 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()); 244 } 245} 246 247void DomDistillerService::AddObserver(DomDistillerObserver* observer) { 248 DCHECK(observer); 249 store_->AddObserver(observer); 250} 251 252void DomDistillerService::RemoveObserver(DomDistillerObserver* observer) { 253 DCHECK(observer); 254 store_->RemoveObserver(observer); 255} 256 257DistilledPagePrefs* DomDistillerService::GetDistilledPagePrefs() { 258 return distilled_page_prefs_.get(); 259} 260 261} // namespace dom_distiller 262