Don't preload rarely seen large images
[chromium-blink-merge.git] / components / dom_distiller / core / task_tracker.cc
blob32ab05d26fd1d06493915d554ffc88d208f1ce81
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/task_tracker.h"
7 #include "base/auto_reset.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/proto/distilled_article.pb.h"
14 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
16 namespace dom_distiller {
18 ViewerHandle::ViewerHandle(CancelCallback callback)
19 : cancel_callback_(callback) {}
21 ViewerHandle::~ViewerHandle() {
22 if (!cancel_callback_.is_null()) {
23 cancel_callback_.Run();
27 TaskTracker::TaskTracker(const ArticleEntry& entry,
28 CancelCallback callback,
29 DistilledContentStore* content_store)
30 : cancel_callback_(callback),
31 content_store_(content_store),
32 blob_fetcher_running_(false),
33 entry_(entry),
34 distilled_article_(),
35 content_ready_(false),
36 destruction_allowed_(true),
37 weak_ptr_factory_(this) {}
39 TaskTracker::~TaskTracker() {
40 DCHECK(destruction_allowed_);
41 DCHECK(viewers_.empty());
44 void TaskTracker::StartDistiller(DistillerFactory* factory,
45 scoped_ptr<DistillerPage> distiller_page) {
46 if (distiller_) {
47 return;
49 if (entry_.pages_size() == 0) {
50 return;
52 GURL url(entry_.pages(0).url());
53 DCHECK(url.is_valid());
55 distiller_ = factory->CreateDistillerForUrl(url);
56 distiller_->DistillPage(url,
57 distiller_page.Pass(),
58 base::Bind(&TaskTracker::OnDistillerFinished,
59 weak_ptr_factory_.GetWeakPtr()),
60 base::Bind(&TaskTracker::OnArticleDistillationUpdated,
61 weak_ptr_factory_.GetWeakPtr()));
64 void TaskTracker::StartBlobFetcher() {
65 if (content_store_) {
66 blob_fetcher_running_ = true;
67 content_store_->LoadContent(entry_,
68 base::Bind(&TaskTracker::OnBlobFetched,
69 weak_ptr_factory_.GetWeakPtr()));
73 void TaskTracker::AddSaveCallback(const SaveCallback& callback) {
74 DCHECK(!callback.is_null());
75 save_callbacks_.push_back(callback);
76 if (content_ready_) {
77 // Distillation for this task has already completed, and so it can be
78 // immediately saved.
79 ScheduleSaveCallbacks(true);
83 scoped_ptr<ViewerHandle> TaskTracker::AddViewer(ViewRequestDelegate* delegate) {
84 viewers_.push_back(delegate);
85 if (content_ready_) {
86 // Distillation for this task has already completed, and so the delegate can
87 // be immediately told of the result.
88 base::ThreadTaskRunnerHandle::Get()->PostTask(
89 FROM_HERE, base::Bind(&TaskTracker::NotifyViewer,
90 weak_ptr_factory_.GetWeakPtr(), delegate));
92 return scoped_ptr<ViewerHandle>(new ViewerHandle(base::Bind(
93 &TaskTracker::RemoveViewer, weak_ptr_factory_.GetWeakPtr(), delegate)));
96 const std::string& TaskTracker::GetEntryId() const { return entry_.entry_id(); }
98 bool TaskTracker::HasEntryId(const std::string& entry_id) const {
99 return entry_.entry_id() == entry_id;
102 bool TaskTracker::HasUrl(const GURL& url) const {
103 for (int i = 0; i < entry_.pages_size(); ++i) {
104 if (entry_.pages(i).url() == url.spec()) {
105 return true;
108 return false;
111 void TaskTracker::RemoveViewer(ViewRequestDelegate* delegate) {
112 viewers_.erase(std::remove(viewers_.begin(), viewers_.end(), delegate));
113 if (viewers_.empty()) {
114 MaybeCancel();
118 void TaskTracker::MaybeCancel() {
119 if (!save_callbacks_.empty() || !viewers_.empty()) {
120 // There's still work to be done.
121 return;
124 CancelPendingSources();
126 base::AutoReset<bool> dont_delete_this_in_callback(&destruction_allowed_,
127 false);
128 cancel_callback_.Run(this);
131 void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
133 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded) {
134 base::ThreadTaskRunnerHandle::Get()->PostTask(
135 FROM_HERE,
136 base::Bind(&TaskTracker::DoSaveCallbacks, weak_ptr_factory_.GetWeakPtr(),
137 distillation_succeeded));
140 void TaskTracker::OnDistillerFinished(
141 scoped_ptr<DistilledArticleProto> distilled_article) {
142 if (content_ready_) {
143 return;
146 DistilledArticleReady(distilled_article.Pass());
147 if (content_ready_) {
148 AddDistilledContentToStore(*distilled_article_);
151 // 'distiller_ != null' is used as a signal that distillation is in progress,
152 // so it needs to be released so that we know distillation is done.
153 base::MessageLoop::current()->DeleteSoon(FROM_HERE, distiller_.release());
155 ContentSourceFinished();
158 void TaskTracker::CancelPendingSources() {
159 if (distiller_) {
160 base::MessageLoop::current()->DeleteSoon(FROM_HERE, distiller_.release());
164 void TaskTracker::OnBlobFetched(
165 bool success,
166 scoped_ptr<DistilledArticleProto> distilled_article) {
167 blob_fetcher_running_ = false;
169 if (content_ready_) {
170 return;
173 DistilledArticleReady(distilled_article.Pass());
175 ContentSourceFinished();
178 bool TaskTracker::IsAnySourceRunning() const {
179 return distiller_ || blob_fetcher_running_;
182 void TaskTracker::ContentSourceFinished() {
183 if (content_ready_) {
184 CancelPendingSources();
185 } else if (!IsAnySourceRunning()) {
186 distilled_article_.reset(new DistilledArticleProto());
187 NotifyViewersAndCallbacks();
191 void TaskTracker::DistilledArticleReady(
192 scoped_ptr<DistilledArticleProto> distilled_article) {
193 DCHECK(!content_ready_);
195 if (distilled_article->pages_size() == 0) {
196 return;
199 content_ready_ = true;
201 distilled_article_ = distilled_article.Pass();
202 entry_.set_title(distilled_article_->title());
203 entry_.clear_pages();
204 for (int i = 0; i < distilled_article_->pages_size(); ++i) {
205 sync_pb::ArticlePage* page = entry_.add_pages();
206 page->set_url(distilled_article_->pages(i).url());
209 NotifyViewersAndCallbacks();
212 void TaskTracker::NotifyViewersAndCallbacks() {
213 for (size_t i = 0; i < viewers_.size(); ++i) {
214 NotifyViewer(viewers_[i]);
217 // Already inside a callback run SaveCallbacks directly.
218 DoSaveCallbacks(content_ready_);
221 void TaskTracker::NotifyViewer(ViewRequestDelegate* delegate) {
222 delegate->OnArticleReady(distilled_article_.get());
225 void TaskTracker::DoSaveCallbacks(bool success) {
226 if (!save_callbacks_.empty()) {
227 for (size_t i = 0; i < save_callbacks_.size(); ++i) {
228 DCHECK(!save_callbacks_[i].is_null());
229 save_callbacks_[i].Run(
230 entry_, distilled_article_.get(), success);
233 save_callbacks_.clear();
234 MaybeCancel();
238 void TaskTracker::OnArticleDistillationUpdated(
239 const ArticleDistillationUpdate& article_update) {
240 for (size_t i = 0; i < viewers_.size(); ++i) {
241 viewers_[i]->OnArticleUpdated(article_update);
245 void TaskTracker::AddDistilledContentToStore(
246 const DistilledArticleProto& content) {
247 if (content_store_) {
248 content_store_->SaveContent(
249 entry_, content, DistilledContentStore::SaveCallback());
254 } // namespace dom_distiller