blob: 04951cd08227701b2faabf39f31d5eeccec67cec [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 "chrome/browser/safe_browsing/download_feedback_service.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util_proxy.h"
#include "base/metrics/histogram.h"
#include "base/supports_user_data.h"
#include "base/task_runner.h"
#include "chrome/browser/safe_browsing/download_feedback.h"
#include "content/public/browser/download_danger_type.h"
#include "content/public/browser/download_item.h"
namespace safe_browsing {
namespace {
const void* kPingKey = &kPingKey;
class DownloadFeedbackPings : public base::SupportsUserData::Data {
public:
DownloadFeedbackPings(const std::string& ping_request,
const std::string& ping_response);
// Stores the ping data in the given |download|.
static void CreateForDownload(content::DownloadItem* download,
const std::string& ping_request,
const std::string& ping_response);
// Returns the DownloadFeedbackPings object associated with |download|. May
// return NULL.
static DownloadFeedbackPings* FromDownload(
const content::DownloadItem& download);
const std::string& ping_request() const {
return ping_request_;
}
const std::string& ping_response() const {
return ping_response_;
}
private:
std::string ping_request_;
std::string ping_response_;
};
DownloadFeedbackPings::DownloadFeedbackPings(const std::string& ping_request,
const std::string& ping_response)
: ping_request_(ping_request),
ping_response_(ping_response) {
}
// static
void DownloadFeedbackPings::CreateForDownload(
content::DownloadItem* download,
const std::string& ping_request,
const std::string& ping_response) {
DownloadFeedbackPings* pings = new DownloadFeedbackPings(ping_request,
ping_response);
download->SetUserData(kPingKey, pings);
}
// static
DownloadFeedbackPings* DownloadFeedbackPings::FromDownload(
const content::DownloadItem& download) {
return static_cast<DownloadFeedbackPings*>(download.GetUserData(kPingKey));
}
} // namespace
DownloadFeedbackService::DownloadFeedbackService(
net::URLRequestContextGetter* request_context_getter,
base::TaskRunner* file_task_runner)
: request_context_getter_(request_context_getter),
file_task_runner_(file_task_runner),
weak_ptr_factory_(this) {
}
DownloadFeedbackService::~DownloadFeedbackService() {
DCHECK(CalledOnValidThread());
}
// static
void DownloadFeedbackService::MaybeStorePingsForDownload(
DownloadProtectionService::DownloadCheckResult result,
content::DownloadItem* download,
const std::string& ping,
const std::string& response) {
if (result != DownloadProtectionService::UNCOMMON &&
result != DownloadProtectionService::DANGEROUS_HOST)
return;
UMA_HISTOGRAM_COUNTS("SBDownloadFeedback.SizeEligibleKB",
download->GetReceivedBytes() / 1024);
if (download->GetReceivedBytes() > DownloadFeedback::kMaxUploadSize)
return;
DownloadFeedbackPings::CreateForDownload(download, ping, response);
}
// static
bool DownloadFeedbackService::IsEnabledForDownload(
const content::DownloadItem& download) {
return !!DownloadFeedbackPings::FromDownload(download);
}
// static
bool DownloadFeedbackService::GetPingsForDownloadForTesting(
const content::DownloadItem& download,
std::string* ping,
std::string* response) {
DownloadFeedbackPings* pings = DownloadFeedbackPings::FromDownload(download);
if (!pings)
return false;
*ping = pings->ping_request();
*response = pings->ping_response();
return true;
}
// static
void DownloadFeedbackService::RecordEligibleDownloadShown(
content::DownloadDangerType danger_type) {
UMA_HISTOGRAM_ENUMERATION("SBDownloadFeedback.Eligible",
danger_type,
content::DOWNLOAD_DANGER_TYPE_MAX);
}
void DownloadFeedbackService::BeginFeedbackForDownload(
content::DownloadItem* download) {
DCHECK(CalledOnValidThread());
UMA_HISTOGRAM_ENUMERATION("SBDownloadFeedback.Activations",
download->GetDangerType(),
content::DOWNLOAD_DANGER_TYPE_MAX);
DownloadFeedbackPings* pings = DownloadFeedbackPings::FromDownload(*download);
DCHECK(pings);
download->StealDangerousDownload(
base::Bind(&DownloadFeedbackService::BeginFeedbackOrDeleteFile,
file_task_runner_,
weak_ptr_factory_.GetWeakPtr(),
pings->ping_request(),
pings->ping_response()));
}
// static
void DownloadFeedbackService::BeginFeedbackOrDeleteFile(
const scoped_refptr<base::TaskRunner>& file_task_runner,
const base::WeakPtr<DownloadFeedbackService>& service,
const std::string& ping_request,
const std::string& ping_response,
const base::FilePath& path) {
if (service) {
service->BeginFeedback(ping_request, ping_response, path);
} else {
base::FileUtilProxy::DeleteFile(file_task_runner.get(),
path,
false,
base::FileUtilProxy::StatusCallback());
}
}
void DownloadFeedbackService::StartPendingFeedback() {
DCHECK(!active_feedback_.empty());
active_feedback_.front()->Start(base::Bind(
&DownloadFeedbackService::FeedbackComplete, base::Unretained(this)));
}
void DownloadFeedbackService::BeginFeedback(
const std::string& ping_request,
const std::string& ping_response,
const base::FilePath& path) {
DCHECK(CalledOnValidThread());
DownloadFeedback* feedback =
DownloadFeedback::Create(request_context_getter_.get(),
file_task_runner_.get(),
path,
ping_request,
ping_response);
active_feedback_.push_back(feedback);
UMA_HISTOGRAM_COUNTS_100("SBDownloadFeedback.ActiveFeedbacks",
active_feedback_.size());
if (active_feedback_.size() == 1)
StartPendingFeedback();
}
void DownloadFeedbackService::FeedbackComplete() {
DVLOG(1) << __FUNCTION__;
DCHECK(CalledOnValidThread());
DCHECK(!active_feedback_.empty());
active_feedback_.erase(active_feedback_.begin());
if (!active_feedback_.empty())
StartPendingFeedback();
}
} // namespace safe_browsing