blob: d4f4ef38cd9c2fec1151f4252cbdb969ba857a73 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/devtools/protocol/devtools_download_manager_delegate.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/task/thread_pool.h"
#include "components/download/public/common/download_target_info.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/download_manager.h"
#include "net/base/filename_util.h"
namespace content {
class WebContents;
namespace protocol {
const char kDevToolsDownloadManagerDelegateName[] =
"devtools_download_manager_delegate";
DevToolsDownloadManagerDelegate::DevToolsDownloadManagerDelegate(
content::BrowserContext* browser_context) {
download_manager_ = browser_context->GetDownloadManager();
DCHECK(download_manager_);
original_download_delegate_ = download_manager_->GetDelegate();
download_manager_->SetDelegate(this);
}
// static
DevToolsDownloadManagerDelegate*
DevToolsDownloadManagerDelegate::GetOrCreateInstance(BrowserContext* context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!DevToolsDownloadManagerDelegate::GetInstance(context)) {
auto delegate_owned =
base::WrapUnique(new DevToolsDownloadManagerDelegate(context));
context->SetUserData(kDevToolsDownloadManagerDelegateName,
std::move(delegate_owned));
}
return DevToolsDownloadManagerDelegate::GetInstance(context);
}
// static
DevToolsDownloadManagerDelegate* DevToolsDownloadManagerDelegate::GetInstance(
BrowserContext* context) {
return static_cast<DevToolsDownloadManagerDelegate*>(
context->GetUserData(kDevToolsDownloadManagerDelegateName));
}
void DevToolsDownloadManagerDelegate::Shutdown() {
if (original_download_delegate_)
original_download_delegate_.ExtractAsDangling()->Shutdown();
// Revoke any pending callbacks. download_manager_ et. al. are no longer safe
// to access after this point.
download_manager_ = nullptr;
}
bool DevToolsDownloadManagerDelegate::DetermineDownloadTarget(
download::DownloadItem* item,
download::DownloadTargetCallback* callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Check if we should failback to delegate.
if (original_download_delegate_ &&
download_behavior_ == DownloadBehavior::DEFAULT) {
return original_download_delegate_->DetermineDownloadTarget(item, callback);
}
// In headless mode there's no no proxy delegate set, so if there's no
// information associated to the download, we deny it by default.
if (download_behavior_ != DownloadBehavior::ALLOW &&
download_behavior_ != DownloadBehavior::ALLOW_AND_NAME) {
download::DownloadTargetInfo target_info;
target_info.interrupt_reason =
download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED;
std::move(*callback).Run(std::move(target_info));
return true;
}
base::FilePath download_path = base::FilePath::FromUTF8Unsafe(download_path_);
if (download_behavior_ == DownloadBehavior::ALLOW_AND_NAME) {
base::FilePath suggested_path(download_path.AppendASCII(item->GetGuid()));
OnDownloadPathGenerated(item->GetId(), std::move(*callback),
suggested_path);
return true;
}
FilenameDeterminedCallback filename_determined_callback = base::BindOnce(
&DevToolsDownloadManagerDelegate::OnDownloadPathGenerated,
base::Unretained(this), item->GetId(), std::move(*callback));
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
base::TaskPriority::USER_VISIBLE},
base::BindOnce(&DevToolsDownloadManagerDelegate::GenerateFilename,
item->GetURL(), item->GetContentDisposition(),
item->GetSuggestedFilename(), item->GetMimeType(),
download_path, std::move(filename_determined_callback)));
return true;
}
bool DevToolsDownloadManagerDelegate::ShouldOpenDownload(
download::DownloadItem* item,
content::DownloadOpenDelayedCallback callback) {
if (original_download_delegate_ &&
download_behavior_ == DownloadBehavior::DEFAULT) {
return original_download_delegate_->ShouldOpenDownload(item,
std::move(callback));
}
// Immediately transition to the completed stage.
return true;
}
void DevToolsDownloadManagerDelegate::GetNextId(
content::DownloadIdCallback callback) {
static uint32_t next_id = download::DownloadItem::kInvalidId + 1;
// Be sure to follow the proxy delegate Ids to avoid compatibility problems
// with the download manager.
if (original_download_delegate_) {
original_download_delegate_->GetNextId(std::move(callback));
return;
}
std::move(callback).Run(next_id++);
}
// static
void DevToolsDownloadManagerDelegate::GenerateFilename(
const GURL& url,
const std::string& content_disposition,
const std::string& suggested_filename,
const std::string& mime_type,
const base::FilePath& suggested_directory,
FilenameDeterminedCallback callback) {
base::FilePath generated_name =
net::GenerateFileName(url, content_disposition, std::string(),
suggested_filename, mime_type, "download");
if (!base::PathExists(suggested_directory))
base::CreateDirectory(suggested_directory);
base::FilePath suggested_path(suggested_directory.Append(generated_name));
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), suggested_path));
}
void DevToolsDownloadManagerDelegate::OnDownloadPathGenerated(
uint32_t download_id,
download::DownloadTargetCallback callback,
const base::FilePath& suggested_path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
download::DownloadTargetInfo target_info;
target_info.target_path = suggested_path;
target_info.intermediate_path =
suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload"));
target_info.display_name = suggested_path.BaseName();
target_info.danger_type =
download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT;
std::move(callback).Run(std::move(target_info));
}
download::DownloadItem* DevToolsDownloadManagerDelegate::GetDownloadByGuid(
const std::string& guid) {
if (!download_manager_) {
return nullptr;
}
return download_manager_->GetDownloadByGuid(guid);
}
} // namespace protocol
} // namespace content