| // 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, |
| download::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::BindOnce(&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::AssertBlockingAllowed(); |
| 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::BindOnce(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, |
| download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT, |
| suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")), |
| content::DOWNLOAD_INTERRUPT_REASON_NONE); |
| } |
| |
| } // namespace protocol |
| } // namespace content |