blob: faf6fc196b29703c09409c31efe38914c86120cc [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/dom_distiller/core/task_tracker.h"
#include "base/message_loop/message_loop.h"
namespace dom_distiller {
ViewerHandle::ViewerHandle(CancelCallback callback)
: cancel_callback_(callback) {}
ViewerHandle::~ViewerHandle() {
if (!cancel_callback_.is_null()) {
cancel_callback_.Run();
}
}
TaskTracker::TaskTracker(const ArticleEntry& entry, CancelCallback callback)
: cancel_callback_(callback),
entry_(entry),
distilled_page_(),
weak_ptr_factory_(this) {}
TaskTracker::~TaskTracker() { DCHECK(viewers_.empty()); }
void TaskTracker::StartDistiller(DistillerFactory* factory) {
if (distiller_) {
return;
}
if (entry_.pages_size() == 0) {
return;
}
GURL url(entry_.pages(0).url());
DCHECK(url.is_valid());
distiller_ = factory->CreateDistiller();
distiller_->DistillPage(url,
base::Bind(&TaskTracker::OnDistilledDataReady,
weak_ptr_factory_.GetWeakPtr()));
}
void TaskTracker::StartBlobFetcher() {
// TODO(cjhopman): There needs to be some local storage for the distilled
// blob. When that happens, this should start some task to fetch the blob for
// |entry_| and asynchronously notify |this| when it is done.
}
void TaskTracker::SetSaveCallback(SaveCallback callback) {
save_callback_ = callback;
if (save_callback_.is_null()) {
MaybeCancel();
} else if (distilled_page_) {
// Distillation for this task has already completed, and so it can be
// immediately saved.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&TaskTracker::DoSaveCallback,
weak_ptr_factory_.GetWeakPtr()));
}
}
scoped_ptr<ViewerHandle> TaskTracker::AddViewer(ViewRequestDelegate* delegate) {
viewers_.push_back(delegate);
if (distilled_page_) {
// Distillation for this task has already completed, and so the delegate can
// be immediately told of the result.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&TaskTracker::NotifyViewer,
weak_ptr_factory_.GetWeakPtr(),
delegate));
}
return scoped_ptr<ViewerHandle>(new ViewerHandle(base::Bind(
&TaskTracker::RemoveViewer, weak_ptr_factory_.GetWeakPtr(), delegate)));
}
bool TaskTracker::HasEntryId(const std::string& entry_id) const {
return entry_.entry_id() == entry_id;
}
bool TaskTracker::HasUrl(const GURL& url) const {
for (int i = 0; i < entry_.pages_size(); ++i) {
if (entry_.pages(i).url() == url.spec()) {
return true;
}
}
return false;
}
void TaskTracker::RemoveViewer(ViewRequestDelegate* delegate) {
viewers_.erase(std::remove(viewers_.begin(), viewers_.end(), delegate));
if (viewers_.empty()) {
MaybeCancel();
}
}
void TaskTracker::MaybeCancel() {
if (!save_callback_.is_null() || !viewers_.empty()) {
// There's still work to be done.
return;
}
// The cancel callback should not delete this. To ensure that it doesn't, grab
// a weak pointer and check that it has not been invalidated.
base::WeakPtr<TaskTracker> self(weak_ptr_factory_.GetWeakPtr());
cancel_callback_.Run(this);
DCHECK(self);
}
void TaskTracker::DoSaveCallback() {
DCHECK(distilled_page_);
if (!save_callback_.is_null()) {
save_callback_.Run(entry_, distilled_page_.get());
save_callback_.Reset();
MaybeCancel();
}
}
void TaskTracker::NotifyViewer(ViewRequestDelegate* delegate) {
DCHECK(distilled_page_);
delegate->OnArticleReady(distilled_page_.get());
}
void TaskTracker::OnDistilledDataReady(scoped_ptr<DistilledPageProto> proto) {
distilled_page_ = proto.Pass();
entry_.set_title(distilled_page_->title());
for (size_t i = 0; i < viewers_.size(); ++i) {
NotifyViewer(viewers_[i]);
}
DoSaveCallback();
}
} // namespace dom_distiller