blob: 2458951d1494ea002ffc0d64ef121d19ee122345 [file] [log] [blame]
// Copyright 2017 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 "content/browser/devtools/protocol/devtools_download_manager_delegate.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/task_scheduler/post_task.h"
#include "base/threading/thread_restrictions.h"
#include "content/browser/devtools/protocol/devtools_download_manager_helper.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager.h"
#include "net/base/filename_util.h"
namespace content {
class WebContents;
namespace protocol {
namespace {
DevToolsDownloadManagerDelegate* g_devtools_manager_delegate = nullptr;
} // namespace
DevToolsDownloadManagerDelegate::DevToolsDownloadManagerDelegate()
: download_manager_(nullptr), proxy_download_delegate_(nullptr) {
g_devtools_manager_delegate = this;
}
// static
DevToolsDownloadManagerDelegate*
DevToolsDownloadManagerDelegate::GetInstance() {
if (!g_devtools_manager_delegate)
new DevToolsDownloadManagerDelegate();
return g_devtools_manager_delegate;
}
DevToolsDownloadManagerDelegate::~DevToolsDownloadManagerDelegate() {
// Reset the proxy delegate.
DevToolsDownloadManagerDelegate* download_delegate = GetInstance();
download_delegate->download_manager_->SetDelegate(
download_delegate->proxy_download_delegate_);
download_delegate->download_manager_ = nullptr;
if (download_manager_) {
download_manager_->SetDelegate(proxy_download_delegate_);
download_manager_ = nullptr;
}
g_devtools_manager_delegate = nullptr;
}
scoped_refptr<DevToolsDownloadManagerDelegate>
DevToolsDownloadManagerDelegate::TakeOver(
content::DownloadManager* download_manager) {
CHECK(download_manager);
DevToolsDownloadManagerDelegate* download_delegate = GetInstance();
if (download_manager == download_delegate->download_manager_)
return download_delegate;
// Recover state of previously owned download manager.
if (download_delegate->download_manager_)
download_delegate->download_manager_->SetDelegate(
download_delegate->proxy_download_delegate_);
download_delegate->proxy_download_delegate_ = download_manager->GetDelegate();
// Take over delegate in download_manager.
download_delegate->download_manager_ = download_manager;
download_manager->SetDelegate(download_delegate);
return download_delegate;
}
void DevToolsDownloadManagerDelegate::Shutdown() {
if (proxy_download_delegate_)
proxy_download_delegate_->Shutdown();
// Revoke any pending callbacks. download_manager_ et. al. are no longer safe
// to access after this point.
download_manager_ = nullptr;
}
bool DevToolsDownloadManagerDelegate::DetermineDownloadTarget(
content::DownloadItem* item,
const content::DownloadTargetCallback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DevToolsDownloadManagerHelper* download_helper =
DevToolsDownloadManagerHelper::FromWebContents(item->GetWebContents());
// Check if we should failback to delegate.
if (proxy_download_delegate_ && !download_helper)
return proxy_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_helper ||
download_helper->GetDownloadBehavior() !=
DevToolsDownloadManagerHelper::DownloadBehavior::ALLOW) {
base::FilePath empty_path = base::FilePath();
callback.Run(empty_path,
content::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, empty_path,
content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED);
return true;
}
base::FilePath download_path =
base::FilePath::FromUTF8Unsafe(download_helper->GetDownloadPath());
FilenameDeterminedCallback filename_determined_callback =
base::Bind(&DevToolsDownloadManagerDelegate::OnDownloadPathGenerated,
base::Unretained(this), item->GetId(), callback);
PostTaskWithTraits(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
base::TaskPriority::USER_VISIBLE},
base::Bind(&DevToolsDownloadManagerDelegate::GenerateFilename,
item->GetURL(), item->GetContentDisposition(),
item->GetSuggestedFilename(), item->GetMimeType(),
download_path, filename_determined_callback));
return true;
}
bool DevToolsDownloadManagerDelegate::ShouldOpenDownload(
content::DownloadItem* item,
const content::DownloadOpenDelayedCallback& callback) {
DevToolsDownloadManagerHelper* download_helper =
DevToolsDownloadManagerHelper::FromWebContents(item->GetWebContents());
if (download_helper)
return true;
if (proxy_download_delegate_)
return proxy_download_delegate_->ShouldOpenDownload(item, callback);
return false;
}
void DevToolsDownloadManagerDelegate::GetNextId(
const content::DownloadIdCallback& callback) {
static uint32_t next_id = content::DownloadItem::kInvalidId + 1;
// Be sure to follow the proxy delegate Ids to avoid compatibility problems
// with the download manager.
if (proxy_download_delegate_) {
proxy_download_delegate_->GetNextId(callback);
return;
}
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,
const FilenameDeterminedCallback& callback) {
base::ThreadRestrictions::AssertIOAllowed();
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));
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::Bind(callback, suggested_path));
}
void DevToolsDownloadManagerDelegate::OnDownloadPathGenerated(
uint32_t download_id,
const content::DownloadTargetCallback& callback,
const base::FilePath& suggested_path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
callback.Run(suggested_path,
content::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")),
content::DOWNLOAD_INTERRUPT_REASON_NONE);
}
} // namespace protocol
} // namespace content