blob: 898c66380559e1f02067885a2c8d90c3a7421dcb [file] [log] [blame]
// Copyright (c) 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 "base/file_util.h"
#include "base/threading/worker_pool.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/recovery_private/error_messages.h"
#include "chrome/browser/extensions/api/recovery_private/recovery_operation_manager.h"
#include "chrome/browser/extensions/api/recovery_private/write_from_url_operation.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "extensions/common/error_utils.h"
namespace extensions {
namespace recovery {
using content::BrowserThread;
WriteFromUrlOperation::WriteFromUrlOperation(RecoveryOperationManager* manager,
const ExtensionId& extension_id,
content::RenderViewHost* rvh,
GURL url,
const std::string& hash,
bool saveImageAsDownload,
const std::string& storage_unit_id)
: RecoveryOperation(manager, extension_id, storage_unit_id),
rvh_(rvh),
url_(url),
hash_(hash),
saveImageAsDownload_(saveImageAsDownload),
download_(NULL){
}
WriteFromUrlOperation::~WriteFromUrlOperation() {
if (stage_ == recovery_api::STAGE_DOWNLOAD && download_) {
download_->RemoveObserver(this);
download_->Cancel(false);
}
}
void WriteFromUrlOperation::Start() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
stage_ = recovery_api::STAGE_DOWNLOAD;
progress_ = 0;
if (saveImageAsDownload_){
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&WriteFromUrlOperation::DownloadStart, this));
} else {
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&WriteFromUrlOperation::CreateTempFile, this));
}
}
void WriteFromUrlOperation::Cancel() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
if (stage_ == recovery_api::STAGE_DOWNLOAD && download_) {
download_->RemoveObserver(this);
download_->Cancel(true);
}
RecoveryOperation::Cancel();
}
void WriteFromUrlOperation::CreateTempFile() {
if (IsCancelled()) {
return;
}
tmp_file_.reset(new base::FilePath());
if (file_util::CreateTemporaryFile(tmp_file_.get())) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&WriteFromUrlOperation::DownloadStart, this));
} else {
Error(error::kTempFile);
}
}
// The downloader runs on the UI thread.
void WriteFromUrlOperation::DownloadStart() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DVLOG(1) << "Starting download of URL: " << url_;
if (IsCancelled()) {
return;
}
stage_ = recovery_api::STAGE_DOWNLOAD;
progress_ = 0;
Profile* current_profile = manager_->profile();
scoped_ptr<content::DownloadUrlParameters> download_params(
new content::DownloadUrlParameters(
url_,
rvh_->GetProcess()->GetID(),
rvh_->GetRoutingID(),
current_profile->GetResourceContext()));
if (tmp_file_.get()) {
download_params->set_file_path(*tmp_file_);
}
download_params->set_callback(
base::Bind(&WriteFromUrlOperation::OnDownloadStarted, this));
content::DownloadManager* download_manager =
content::BrowserContext::GetDownloadManager(current_profile);
download_manager->DownloadUrl(download_params.Pass());
}
void WriteFromUrlOperation::OnDownloadStarted(content::DownloadItem* item,
net::Error error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (item) {
DCHECK_EQ(net::OK, error);
download_ = item;
download_->AddObserver(this);
// Run at least once.
OnDownloadUpdated(download_);
} else {
DCHECK_NE(net::OK, error);
std::string error_message = ErrorUtils::FormatErrorMessage(
"Download failed: *",
net::ErrorToString(error));
Error(error_message);
}
}
// Always called from the UI thread.
void WriteFromUrlOperation::OnDownloadUpdated(
content::DownloadItem* download) {
if (IsCancelled()) {
download->Cancel(false);
return;
}
progress_ = download->PercentComplete();
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&WriteFromUrlOperation::SendProgress, this));
if (download->GetState() == content::DownloadItem::COMPLETE) {
download_path_ = download_->GetTargetFilePath();
download_->RemoveObserver(this);
download_ = NULL;
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&WriteFromUrlOperation::DownloadComplete, this));
} else if (download->GetState() == content::DownloadItem::INTERRUPTED) {
Error(error::kDownloadInterrupted);
} else if (download->GetState() == content::DownloadItem::CANCELLED) {
Error(error::kDownloadCancelled);
}
}
void WriteFromUrlOperation::DownloadComplete() {
DVLOG(1) << "Download complete.";
progress_ = 100;
SendProgress();
VerifyDownloadStart();
}
void WriteFromUrlOperation::VerifyDownloadStart() {
if (IsCancelled()) {
return;
}
// Skip verify if no hash.
if (hash_.empty()) {
scoped_ptr<base::FilePath> download_path(
new base::FilePath(download_path_));
UnzipStart(download_path.Pass());
return;
}
DVLOG(1) << "Download verification started.";
stage_ = recovery_api::STAGE_VERIFYDOWNLOAD;
progress_ = 0;
SendProgress();
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&WriteFromUrlOperation::VerifyDownloadRun, this));
}
void WriteFromUrlOperation::VerifyDownloadRun() {
scoped_ptr<base::FilePath> download_path(new base::FilePath(download_path_));
GetMD5SumOfFile(
download_path.Pass(),
0,
0,
100,
base::Bind(&WriteFromUrlOperation::VerifyDownloadCompare, this));
}
void WriteFromUrlOperation::VerifyDownloadCompare(
scoped_ptr<std::string> download_hash) {
if (*download_hash != hash_) {
Error(error::kDownloadHash);
return;
}
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&WriteFromUrlOperation::VerifyDownloadComplete, this));
}
void WriteFromUrlOperation::VerifyDownloadComplete() {
if (IsCancelled()) {
return;
}
DVLOG(1) << "Download verification complete.";
progress_ = 100;
SendProgress();
scoped_ptr<base::FilePath> download_path(new base::FilePath(download_path_));
UnzipStart(download_path.Pass());
}
} // namespace recovery
} // namespace extensions