chrome: Add CefDownloadHandler support (see #3681)

Behaves the same as Alloy runtime except that CanDownload is not
called for invalid protocol schemes.
This commit is contained in:
Marshall Greenblatt 2024-04-26 15:21:33 -04:00
parent ed079792b6
commit be6642df3f
23 changed files with 873 additions and 616 deletions

View File

@ -465,6 +465,8 @@ source_set("libcef_static") {
"libcef/browser/alloy/alloy_browser_main.h",
"libcef/browser/alloy/alloy_content_browser_client.cc",
"libcef/browser/alloy/alloy_content_browser_client.h",
"libcef/browser/alloy/alloy_download_manager_delegate.cc",
"libcef/browser/alloy/alloy_download_manager_delegate.h",
"libcef/browser/alloy/alloy_download_util.cc",
"libcef/browser/alloy/alloy_download_util.h",
"libcef/browser/alloy/alloy_web_contents_view_delegate.cc",
@ -576,6 +578,8 @@ source_set("libcef_static") {
"libcef/browser/download_item_impl.h",
"libcef/browser/download_manager_delegate.cc",
"libcef/browser/download_manager_delegate.h",
"libcef/browser/download_manager_delegate_impl.cc",
"libcef/browser/download_manager_delegate_impl.h",
"libcef/browser/extension_impl.cc",
"libcef/browser/extension_impl.h",
"libcef/browser/extensions/api/file_system/cef_file_system_delegate.cc",

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=60a08a60be70e8fe5df17f18f8e5758e1830d5e1$
// $hash=7ecfb07a95315ff81937e9f68d419122fc88f1b7$
//
#ifndef CEF_INCLUDE_CAPI_CEF_DOWNLOAD_HANDLER_CAPI_H_
@ -117,12 +117,13 @@ typedef struct _cef_download_handler_t {
///
/// Called before a download begins. |suggested_name| is the suggested name
/// for the download file. By default the download will be canceled. Execute
/// |callback| either asynchronously or in this function to continue the
/// download if desired. Do not keep a reference to |download_item| outside of
/// this function.
/// for the download file. Return true (1) and execute |callback| either
/// asynchronously or in this function to continue or cancel the download.
/// Return false (0) to proceed with default handling (cancel with Alloy
/// style, download shelf with Chrome style). Do not keep a reference to
/// |download_item| outside of this function.
///
void(CEF_CALLBACK* on_before_download)(
int(CEF_CALLBACK* on_before_download)(
struct _cef_download_handler_t* self,
struct _cef_browser_t* browser,
struct _cef_download_item_t* download_item,

View File

@ -42,13 +42,13 @@
// way that may cause binary incompatibility with other builds. The universal
// hash value will change if any platform is affected whereas the platform hash
// values will change only if that particular platform is affected.
#define CEF_API_HASH_UNIVERSAL "faa8a52df058a26e1228da66052a9900a7120de4"
#define CEF_API_HASH_UNIVERSAL "73e8743408bfeba0df3b567bfd04b2eb642df11d"
#if defined(OS_WIN)
#define CEF_API_HASH_PLATFORM "41a15a683e4a2b927f043c22d8a9f07671168170"
#define CEF_API_HASH_PLATFORM "3a73f4c69062c6f413e00c1821f6e6df91a2a787"
#elif defined(OS_MAC)
#define CEF_API_HASH_PLATFORM "6933ff4566b919c045ff0743682889d45ffcba28"
#define CEF_API_HASH_PLATFORM "f5fb92747082f06a9c42724b0b717accc29e3879"
#elif defined(OS_LINUX)
#define CEF_API_HASH_PLATFORM "b12778ab8fd0974e45a246484a31d12f097c7936"
#define CEF_API_HASH_PLATFORM "8775d620c6b17725bddba2c1c3a21353b4d8acb1"
#endif
#ifdef __cplusplus

View File

@ -106,17 +106,19 @@ class CefDownloadHandler : public virtual CefBaseRefCounted {
///
/// Called before a download begins. |suggested_name| is the suggested name
/// for the download file. By default the download will be canceled. Execute
/// |callback| either asynchronously or in this method to continue the
/// download if desired. Do not keep a reference to |download_item| outside of
/// this method.
/// for the download file. Return true and execute |callback| either
/// asynchronously or in this method to continue or cancel the download.
/// Return false to proceed with default handling (cancel with Alloy style,
/// download shelf with Chrome style). Do not keep a reference to
/// |download_item| outside of this method.
///
/*--cef()--*/
virtual void OnBeforeDownload(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
CefRefPtr<CefBeforeDownloadCallback> callback) = 0;
virtual bool OnBeforeDownload(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
CefRefPtr<CefBeforeDownloadCallback> callback) {
return false;
}
///
/// Called when a download's status or progress information has been updated.

View File

@ -8,7 +8,7 @@
#include <memory>
#include <utility>
#include "libcef/browser/download_manager_delegate.h"
#include "libcef/browser/alloy/alloy_download_manager_delegate.h"
#include "libcef/browser/extensions/extension_system.h"
#include "libcef/browser/prefs/browser_prefs.h"
#include "libcef/browser/ssl_host_state_delegate.h"
@ -336,7 +336,7 @@ content::DownloadManagerDelegate*
AlloyBrowserContext::GetDownloadManagerDelegate() {
if (!download_manager_delegate_) {
download_manager_delegate_ =
std::make_unique<CefDownloadManagerDelegate>(GetDownloadManager());
std::make_unique<AlloyDownloadManagerDelegate>(GetDownloadManager());
}
return download_manager_delegate_.get();
}

View File

@ -15,7 +15,7 @@
#include "components/proxy_config/pref_proxy_config_tracker.h"
#include "components/visitedlink/browser/visitedlink_delegate.h"
class CefDownloadManagerDelegate;
class AlloyDownloadManagerDelegate;
class CefSSLHostStateDelegate;
class CefVisitedLinkListener;
class PrefService;
@ -128,7 +128,7 @@ class AlloyBrowserContext : public ChromeProfileAlloy,
std::unique_ptr<PrefService> pref_service_;
std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
std::unique_ptr<CefDownloadManagerDelegate> download_manager_delegate_;
std::unique_ptr<AlloyDownloadManagerDelegate> download_manager_delegate_;
std::unique_ptr<CefSSLHostStateDelegate> ssl_host_state_delegate_;
std::unique_ptr<visitedlink::VisitedLinkWriter> visitedlink_master_;
// |visitedlink_listener_| is owned by visitedlink_master_.

View File

@ -0,0 +1,22 @@
// Copyright (c) 2012 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 "libcef/browser/alloy/alloy_download_manager_delegate.h"
#include "chrome/common/chrome_constants.h"
#include "components/download/public/common/download_item.h"
AlloyDownloadManagerDelegate::AlloyDownloadManagerDelegate(
content::DownloadManager* manager)
: CefDownloadManagerDelegateImpl(manager, /*alloy_bootstrap=*/true) {}
void AlloyDownloadManagerDelegate::GetNextId(
content::DownloadIdCallback callback) {
static uint32_t next_id = download::DownloadItem::kInvalidId + 1;
std::move(callback).Run(next_id++);
}
std::string AlloyDownloadManagerDelegate::ApplicationClientIdForFileScanning() {
return std::string(chrome::kApplicationClientIDStringForAVScanning);
}

View File

@ -0,0 +1,26 @@
// Copyright (c) 2012 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.
#ifndef CEF_LIBCEF_BROWSER_ALLOY_ALLOY_DOWNLOAD_MANAGER_DELEGATE_H_
#define CEF_LIBCEF_BROWSER_ALLOY_ALLOY_DOWNLOAD_MANAGER_DELEGATE_H_
#pragma once
#include "libcef/browser/download_manager_delegate_impl.h"
// Specialization for the Alloy bootstrap.
class AlloyDownloadManagerDelegate : public CefDownloadManagerDelegateImpl {
public:
explicit AlloyDownloadManagerDelegate(content::DownloadManager* manager);
AlloyDownloadManagerDelegate(const AlloyDownloadManagerDelegate&) = delete;
AlloyDownloadManagerDelegate& operator=(const AlloyDownloadManagerDelegate&) =
delete;
private:
// DownloadManagerDelegate methods.
void GetNextId(content::DownloadIdCallback callback) override;
std::string ApplicationClientIdForFileScanning() override;
};
#endif // CEF_LIBCEF_BROWSER_ALLOY_ALLOY_DOWNLOAD_MANAGER_DELEGATE_H_

View File

@ -1,479 +1,20 @@
// Copyright (c) 2012 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.
// Copyright 2024 The Chromium Embedded Framework 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 "libcef/browser/download_manager_delegate.h"
#include <tuple>
#include "libcef/browser/download_manager_delegate_impl.h"
#include "libcef/features/runtime_checks.h"
#include "include/cef_download_handler.h"
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "libcef/browser/context.h"
#include "libcef/browser/download_item_impl.h"
#include "libcef/browser/thread_util.h"
namespace cef {
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/chrome_constants.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/web_contents.h"
#include "net/base/filename_util.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
using content::DownloadManager;
using content::WebContents;
using download::DownloadItem;
namespace {
// Helper function to retrieve the CefDownloadHandler.
CefRefPtr<CefDownloadHandler> GetDownloadHandler(
CefRefPtr<AlloyBrowserHostImpl> browser) {
CefRefPtr<CefClient> client = browser->GetClient();
if (client.get()) {
return client->GetDownloadHandler();
}
return nullptr;
// static
std::unique_ptr<cef::DownloadManagerDelegate> DownloadManagerDelegate::Create(
content::DownloadManager* download_manager) {
REQUIRE_CHROME_RUNTIME();
return std::make_unique<CefDownloadManagerDelegateImpl>(
download_manager, /*alloy_bootstrap=*/false);
}
void RunDownloadTargetCallback(download::DownloadTargetCallback callback,
const base::FilePath& path) {
download::DownloadTargetInfo target_info;
target_info.target_path = path;
target_info.intermediate_path = path;
std::move(callback).Run(std::move(target_info));
}
// CefBeforeDownloadCallback implementation.
class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
public:
CefBeforeDownloadCallbackImpl(const base::WeakPtr<DownloadManager>& manager,
uint32_t download_id,
const base::FilePath& suggested_name,
download::DownloadTargetCallback callback)
: manager_(manager),
download_id_(download_id),
suggested_name_(suggested_name),
callback_(std::move(callback)) {}
CefBeforeDownloadCallbackImpl(const CefBeforeDownloadCallbackImpl&) = delete;
CefBeforeDownloadCallbackImpl& operator=(
const CefBeforeDownloadCallbackImpl&) = delete;
void Continue(const CefString& download_path, bool show_dialog) override {
if (CEF_CURRENTLY_ON_UIT()) {
if (download_id_ <= 0) {
return;
}
if (manager_) {
base::FilePath path = base::FilePath(download_path);
CEF_POST_USER_VISIBLE_TASK(
base::BindOnce(&CefBeforeDownloadCallbackImpl::GenerateFilename,
manager_, download_id_, suggested_name_, path,
show_dialog, std::move(callback_)));
}
download_id_ = 0;
} else {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefBeforeDownloadCallbackImpl::Continue,
this, download_path, show_dialog));
}
}
private:
static void GenerateFilename(base::WeakPtr<DownloadManager> manager,
uint32_t download_id,
const base::FilePath& suggested_name,
const base::FilePath& download_path,
bool show_dialog,
download::DownloadTargetCallback callback) {
CEF_REQUIRE_BLOCKING();
base::FilePath suggested_path = download_path;
if (!suggested_path.empty()) {
// Create the directory if necessary.
base::FilePath dir_path = suggested_path.DirName();
if (!base::DirectoryExists(dir_path) &&
!base::CreateDirectory(dir_path)) {
DCHECK(false) << "failed to create the download directory";
suggested_path.clear();
}
}
if (suggested_path.empty()) {
if (base::PathService::Get(base::DIR_TEMP, &suggested_path)) {
// Use the temp directory.
suggested_path = suggested_path.Append(suggested_name);
} else {
// Use the current working directory.
suggested_path = suggested_name;
}
}
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(&CefBeforeDownloadCallbackImpl::ChooseDownloadPath,
manager, download_id, suggested_path, show_dialog,
std::move(callback)));
}
static void ChooseDownloadPath(base::WeakPtr<DownloadManager> manager,
uint32_t download_id,
const base::FilePath& suggested_path,
bool show_dialog,
download::DownloadTargetCallback callback) {
if (!manager) {
return;
}
DownloadItem* item = manager->GetDownload(download_id);
if (!item || item->GetState() != DownloadItem::IN_PROGRESS) {
return;
}
bool handled = false;
if (show_dialog) {
WebContents* web_contents =
content::DownloadItemUtils::GetWebContents(item);
CefRefPtr<AlloyBrowserHostImpl> browser =
AlloyBrowserHostImpl::GetBrowserForContents(web_contents);
if (browser.get()) {
handled = true;
blink::mojom::FileChooserParams params;
params.mode = blink::mojom::FileChooserParams::Mode::kSave;
if (!suggested_path.empty()) {
params.default_file_name = suggested_path;
if (!suggested_path.Extension().empty()) {
params.accept_types.push_back(
CefString(suggested_path.Extension()));
}
}
browser->RunFileChooserForBrowser(
params,
base::BindOnce(
&CefBeforeDownloadCallbackImpl::ChooseDownloadPathCallback,
std::move(callback)));
}
}
if (!handled) {
RunDownloadTargetCallback(std::move(callback), suggested_path);
}
}
static void ChooseDownloadPathCallback(
download::DownloadTargetCallback callback,
const std::vector<base::FilePath>& file_paths) {
DCHECK_LE(file_paths.size(), (size_t)1);
base::FilePath path;
if (file_paths.size() > 0) {
path = file_paths.front();
}
// The download will be cancelled if |path| is empty.
RunDownloadTargetCallback(std::move(callback), path);
}
base::WeakPtr<DownloadManager> manager_;
uint32_t download_id_;
base::FilePath suggested_name_;
download::DownloadTargetCallback callback_;
IMPLEMENT_REFCOUNTING(CefBeforeDownloadCallbackImpl);
};
// CefDownloadItemCallback implementation.
class CefDownloadItemCallbackImpl : public CefDownloadItemCallback {
public:
explicit CefDownloadItemCallbackImpl(
const base::WeakPtr<DownloadManager>& manager,
uint32_t download_id)
: manager_(manager), download_id_(download_id) {}
CefDownloadItemCallbackImpl(const CefDownloadItemCallbackImpl&) = delete;
CefDownloadItemCallbackImpl& operator=(const CefDownloadItemCallbackImpl&) =
delete;
void Cancel() override {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefDownloadItemCallbackImpl::DoCancel, this));
}
void Pause() override {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefDownloadItemCallbackImpl::DoPause, this));
}
void Resume() override {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefDownloadItemCallbackImpl::DoResume, this));
}
private:
void DoCancel() {
if (download_id_ <= 0) {
return;
}
if (manager_) {
DownloadItem* item = manager_->GetDownload(download_id_);
if (item && item->GetState() == DownloadItem::IN_PROGRESS) {
item->Cancel(true);
}
}
download_id_ = 0;
}
void DoPause() {
if (download_id_ <= 0) {
return;
}
if (manager_) {
DownloadItem* item = manager_->GetDownload(download_id_);
if (item && item->GetState() == DownloadItem::IN_PROGRESS) {
item->Pause();
}
}
}
void DoResume() {
if (download_id_ <= 0) {
return;
}
if (manager_) {
DownloadItem* item = manager_->GetDownload(download_id_);
if (item && item->CanResume()) {
item->Resume(true);
}
}
}
base::WeakPtr<DownloadManager> manager_;
uint32_t download_id_;
IMPLEMENT_REFCOUNTING(CefDownloadItemCallbackImpl);
};
} // namespace
CefDownloadManagerDelegate::CefDownloadManagerDelegate(DownloadManager* manager)
: manager_(manager), manager_ptr_factory_(manager) {
DCHECK(manager);
manager->AddObserver(this);
DownloadManager::DownloadVector items;
manager->GetAllDownloads(&items);
DownloadManager::DownloadVector::const_iterator it = items.begin();
for (; it != items.end(); ++it) {
OnDownloadCreated(manager, *it);
}
}
CefDownloadManagerDelegate::~CefDownloadManagerDelegate() {
if (manager_) {
manager_->SetDelegate(nullptr);
manager_->RemoveObserver(this);
}
while (!item_browser_map_.empty()) {
OnDownloadDestroyed(item_browser_map_.begin()->first);
}
}
void CefDownloadManagerDelegate::OnDownloadUpdated(DownloadItem* download) {
CefRefPtr<AlloyBrowserHostImpl> browser = GetBrowser(download);
CefRefPtr<CefDownloadHandler> handler;
if (browser.get()) {
handler = GetDownloadHandler(browser);
}
if (handler.get()) {
CefRefPtr<CefDownloadItemImpl> download_item(
new CefDownloadItemImpl(download));
CefRefPtr<CefDownloadItemCallback> callback(new CefDownloadItemCallbackImpl(
manager_ptr_factory_.GetWeakPtr(), download->GetId()));
handler->OnDownloadUpdated(browser.get(), download_item.get(), callback);
std::ignore = download_item->Detach(nullptr);
}
}
void CefDownloadManagerDelegate::OnDownloadDestroyed(DownloadItem* item) {
item->RemoveObserver(this);
AlloyBrowserHostImpl* browser = nullptr;
ItemBrowserMap::iterator it = item_browser_map_.find(item);
DCHECK(it != item_browser_map_.end());
if (it != item_browser_map_.end()) {
browser = it->second;
item_browser_map_.erase(it);
}
if (browser) {
// Determine if any remaining DownloadItems are associated with the same
// browser. If not, then unregister as an observer.
bool has_remaining = false;
ItemBrowserMap::const_iterator it2 = item_browser_map_.begin();
for (; it2 != item_browser_map_.end(); ++it2) {
if (it2->second == browser) {
has_remaining = true;
break;
}
}
if (!has_remaining) {
browser->RemoveObserver(this);
}
}
}
void CefDownloadManagerDelegate::OnDownloadCreated(DownloadManager* manager,
DownloadItem* item) {
// This callback may arrive after DetermineDownloadTarget, so we allow
// association from either method.
CefRefPtr<AlloyBrowserHostImpl> browser = GetOrAssociateBrowser(item);
if (!browser) {
// If the download is rejected (e.g. ALT+click on an invalid protocol link)
// then an "interrupted" download will be started via DownloadManagerImpl::
// StartDownloadWithId (originating from CreateInterruptedDownload) with no
// associated WebContents and consequently no associated CEF browser. In
// that case DetermineDownloadTarget will be called before this method.
// TODO(cef): Figure out how to expose this via a client callback.
const std::vector<GURL>& url_chain = item->GetUrlChain();
if (!url_chain.empty()) {
LOG(INFO) << "Rejected download of " << url_chain.back().spec();
}
item->Cancel(true);
}
}
void CefDownloadManagerDelegate::ManagerGoingDown(DownloadManager* manager) {
DCHECK_EQ(manager, manager_);
manager->SetDelegate(nullptr);
manager->RemoveObserver(this);
manager_ptr_factory_.InvalidateWeakPtrs();
manager_ = nullptr;
}
bool CefDownloadManagerDelegate::DetermineDownloadTarget(
DownloadItem* item,
download::DownloadTargetCallback* callback) {
const auto& forced_path = item->GetForcedFilePath();
if (!forced_path.empty()) {
RunDownloadTargetCallback(std::move(*callback), forced_path);
return true;
}
// This callback may arrive before OnDownloadCreated, so we allow association
// from either method.
CefRefPtr<AlloyBrowserHostImpl> browser = GetOrAssociateBrowser(item);
CefRefPtr<CefDownloadHandler> handler;
if (browser.get()) {
handler = GetDownloadHandler(browser);
}
if (handler.get()) {
base::FilePath suggested_name = net::GenerateFileName(
item->GetURL(), item->GetContentDisposition(), std::string(),
item->GetSuggestedFilename(), item->GetMimeType(), "download");
CefRefPtr<CefDownloadItemImpl> download_item(new CefDownloadItemImpl(item));
CefRefPtr<CefBeforeDownloadCallback> callbackObj(
new CefBeforeDownloadCallbackImpl(manager_ptr_factory_.GetWeakPtr(),
item->GetId(), suggested_name,
std::move(*callback)));
handler->OnBeforeDownload(browser.get(), download_item.get(),
suggested_name.value(), callbackObj);
std::ignore = download_item->Detach(nullptr);
}
return true;
}
void CefDownloadManagerDelegate::GetNextId(
content::DownloadIdCallback callback) {
static uint32_t next_id = DownloadItem::kInvalidId + 1;
std::move(callback).Run(next_id++);
}
std::string CefDownloadManagerDelegate::ApplicationClientIdForFileScanning() {
return std::string(chrome::kApplicationClientIDStringForAVScanning);
}
void CefDownloadManagerDelegate::OnBrowserDestroyed(
CefBrowserHostBase* browser) {
ItemBrowserMap::iterator it = item_browser_map_.begin();
for (; it != item_browser_map_.end(); ++it) {
if (it->second == browser) {
// Don't call back into browsers that have been destroyed. We're not
// canceling the download so it will continue silently until it completes
// or until the associated browser context is destroyed.
it->second = nullptr;
}
}
}
AlloyBrowserHostImpl* CefDownloadManagerDelegate::GetOrAssociateBrowser(
download::DownloadItem* item) {
ItemBrowserMap::const_iterator it = item_browser_map_.find(item);
if (it != item_browser_map_.end()) {
return it->second;
}
AlloyBrowserHostImpl* browser = nullptr;
content::WebContents* contents =
content::DownloadItemUtils::GetWebContents(item);
if (contents) {
browser = AlloyBrowserHostImpl::GetBrowserForContents(contents).get();
DCHECK(browser);
}
if (!browser) {
return nullptr;
}
item->AddObserver(this);
item_browser_map_.insert(std::make_pair(item, browser));
// Register as an observer so that we can cancel associated DownloadItems when
// the browser is destroyed.
if (!browser->HasObserver(this)) {
browser->AddObserver(this);
}
return browser;
}
AlloyBrowserHostImpl* CefDownloadManagerDelegate::GetBrowser(
DownloadItem* item) {
ItemBrowserMap::const_iterator it = item_browser_map_.find(item);
if (it != item_browser_map_.end()) {
return it->second;
}
// If the download is rejected (e.g. ALT+click on an invalid protocol link)
// then an "interrupted" download will be started via DownloadManagerImpl::
// StartDownloadWithId (originating from CreateInterruptedDownload) with no
// associated WebContents and consequently no associated CEF browser. In that
// case DetermineDownloadTarget will be called before OnDownloadCreated.
DCHECK(!content::DownloadItemUtils::GetWebContents(item));
return nullptr;
}
} // namespace cef

View File

@ -1,68 +1,33 @@
// Copyright (c) 2012 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.
// Copyright 2024 The Chromium Embedded Framework Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_H_
#define CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_H_
#pragma once
#include <set>
#include <memory>
#include "libcef/browser/browser_host_base.h"
#include "base/memory/weak_ptr.h"
#include "components/download/public/common/download_item.h"
#include "components/download/public/common/download_target_info.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/download_manager_delegate.h"
class AlloyBrowserHostImpl;
namespace content {
class DownloadManager;
} // namespace content
class CefDownloadManagerDelegate : public download::DownloadItem::Observer,
public content::DownloadManager::Observer,
public content::DownloadManagerDelegate,
public CefBrowserHostBase::Observer {
namespace cef {
class DownloadManagerDelegate : public content::DownloadManagerDelegate {
public:
explicit CefDownloadManagerDelegate(content::DownloadManager* manager);
CefDownloadManagerDelegate(const CefDownloadManagerDelegate&) = delete;
CefDownloadManagerDelegate& operator=(const CefDownloadManagerDelegate&) =
delete;
~CefDownloadManagerDelegate() override;
// Called from the ChromeDownloadManagerDelegate constructor for Chrome
// bootstrap. Alloy bootstrap uses AlloyDownloadManagerDelegate directly.
static std::unique_ptr<DownloadManagerDelegate> Create(
content::DownloadManager* download_manager);
private:
// DownloadItem::Observer methods.
void OnDownloadUpdated(download::DownloadItem* item) override;
void OnDownloadDestroyed(download::DownloadItem* item) override;
// DownloadManager::Observer methods.
void OnDownloadCreated(content::DownloadManager* manager,
download::DownloadItem* item) override;
void ManagerGoingDown(content::DownloadManager* manager) override;
// DownloadManagerDelegate methods.
bool DetermineDownloadTarget(
download::DownloadItem* item,
download::DownloadTargetCallback* callback) override;
void GetNextId(content::DownloadIdCallback callback) override;
std::string ApplicationClientIdForFileScanning() override;
// CefBrowserHostBase::Observer methods.
void OnBrowserDestroyed(CefBrowserHostBase* browser) override;
AlloyBrowserHostImpl* GetOrAssociateBrowser(download::DownloadItem* item);
AlloyBrowserHostImpl* GetBrowser(download::DownloadItem* item);
content::DownloadManager* manager_;
base::WeakPtrFactory<content::DownloadManager> manager_ptr_factory_;
// Map of DownloadItem to originating AlloyBrowserHostImpl. Maintaining this
// map is necessary because DownloadItem::GetWebContents() may return NULL if
// the browser navigates while the download is in progress.
using ItemBrowserMap =
std::map<download::DownloadItem*, AlloyBrowserHostImpl*>;
ItemBrowserMap item_browser_map_;
// Allow deletion via std::unique_ptr only.
friend std::default_delete<DownloadManagerDelegate>;
};
} // namespace cef
#endif // CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_H_

View File

@ -0,0 +1,498 @@
// Copyright (c) 2012 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 "libcef/browser/download_manager_delegate_impl.h"
#include <tuple>
#include "include/cef_download_handler.h"
#include "libcef/browser/browser_host_base.h"
#include "libcef/browser/context.h"
#include "libcef/browser/download_item_impl.h"
#include "libcef/browser/thread_util.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/web_contents.h"
#include "net/base/filename_util.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
using content::DownloadManager;
using content::WebContents;
using download::DownloadItem;
namespace {
// Helper function to retrieve the CefDownloadHandler.
CefRefPtr<CefDownloadHandler> GetDownloadHandler(
CefRefPtr<CefBrowserHostBase> browser) {
CefRefPtr<CefClient> client = browser->GetClient();
if (client.get()) {
return client->GetDownloadHandler();
}
return nullptr;
}
void RunDownloadTargetCallback(download::DownloadTargetCallback callback,
const base::FilePath& path) {
download::DownloadTargetInfo target_info;
target_info.target_path = path;
target_info.intermediate_path = path;
std::move(callback).Run(std::move(target_info));
}
// CefBeforeDownloadCallback implementation.
class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
public:
CefBeforeDownloadCallbackImpl(const base::WeakPtr<DownloadManager>& manager,
uint32_t download_id,
const base::FilePath& suggested_name,
download::DownloadTargetCallback callback)
: manager_(manager),
download_id_(download_id),
suggested_name_(suggested_name),
callback_(std::move(callback)) {}
CefBeforeDownloadCallbackImpl(const CefBeforeDownloadCallbackImpl&) = delete;
CefBeforeDownloadCallbackImpl& operator=(
const CefBeforeDownloadCallbackImpl&) = delete;
void Continue(const CefString& download_path, bool show_dialog) override {
if (CEF_CURRENTLY_ON_UIT()) {
if (download_id_ <= 0) {
return;
}
if (manager_) {
base::FilePath path = base::FilePath(download_path);
CEF_POST_USER_VISIBLE_TASK(
base::BindOnce(&CefBeforeDownloadCallbackImpl::GenerateFilename,
manager_, download_id_, suggested_name_, path,
show_dialog, std::move(callback_)));
}
download_id_ = 0;
} else {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefBeforeDownloadCallbackImpl::Continue,
this, download_path, show_dialog));
}
}
bool IsDetached() const { return callback_.is_null(); }
[[nodiscard]] download::DownloadTargetCallback Detach() {
return std::move(callback_);
}
private:
static void GenerateFilename(base::WeakPtr<DownloadManager> manager,
uint32_t download_id,
const base::FilePath& suggested_name,
const base::FilePath& download_path,
bool show_dialog,
download::DownloadTargetCallback callback) {
CEF_REQUIRE_BLOCKING();
base::FilePath suggested_path = download_path;
if (!suggested_path.empty()) {
// Create the directory if necessary.
base::FilePath dir_path = suggested_path.DirName();
if (!base::DirectoryExists(dir_path) &&
!base::CreateDirectory(dir_path)) {
DCHECK(false) << "failed to create the download directory";
suggested_path.clear();
}
}
if (suggested_path.empty()) {
if (base::PathService::Get(base::DIR_TEMP, &suggested_path)) {
// Use the temp directory.
suggested_path = suggested_path.Append(suggested_name);
} else {
// Use the current working directory.
suggested_path = suggested_name;
}
}
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(&CefBeforeDownloadCallbackImpl::ChooseDownloadPath,
manager, download_id, suggested_path, show_dialog,
std::move(callback)));
}
static void ChooseDownloadPath(base::WeakPtr<DownloadManager> manager,
uint32_t download_id,
const base::FilePath& suggested_path,
bool show_dialog,
download::DownloadTargetCallback callback) {
if (!manager) {
return;
}
DownloadItem* item = manager->GetDownload(download_id);
if (!item || item->GetState() != DownloadItem::IN_PROGRESS) {
return;
}
bool handled = false;
if (show_dialog) {
WebContents* web_contents =
content::DownloadItemUtils::GetWebContents(item);
CefRefPtr<CefBrowserHostBase> browser =
CefBrowserHostBase::GetBrowserForContents(web_contents);
if (browser.get()) {
handled = true;
blink::mojom::FileChooserParams params;
params.mode = blink::mojom::FileChooserParams::Mode::kSave;
if (!suggested_path.empty()) {
params.default_file_name = suggested_path;
if (!suggested_path.Extension().empty()) {
params.accept_types.push_back(
CefString(suggested_path.Extension()));
}
}
browser->RunFileChooserForBrowser(
params,
base::BindOnce(
&CefBeforeDownloadCallbackImpl::ChooseDownloadPathCallback,
std::move(callback)));
}
}
if (!handled) {
RunDownloadTargetCallback(std::move(callback), suggested_path);
}
}
static void ChooseDownloadPathCallback(
download::DownloadTargetCallback callback,
const std::vector<base::FilePath>& file_paths) {
DCHECK_LE(file_paths.size(), (size_t)1);
base::FilePath path;
if (file_paths.size() > 0) {
path = file_paths.front();
}
// The download will be cancelled if |path| is empty.
RunDownloadTargetCallback(std::move(callback), path);
}
base::WeakPtr<DownloadManager> manager_;
uint32_t download_id_;
base::FilePath suggested_name_;
download::DownloadTargetCallback callback_;
IMPLEMENT_REFCOUNTING(CefBeforeDownloadCallbackImpl);
};
// CefDownloadItemCallback implementation.
class CefDownloadItemCallbackImpl : public CefDownloadItemCallback {
public:
explicit CefDownloadItemCallbackImpl(
const base::WeakPtr<DownloadManager>& manager,
uint32_t download_id)
: manager_(manager), download_id_(download_id) {}
CefDownloadItemCallbackImpl(const CefDownloadItemCallbackImpl&) = delete;
CefDownloadItemCallbackImpl& operator=(const CefDownloadItemCallbackImpl&) =
delete;
void Cancel() override {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefDownloadItemCallbackImpl::DoCancel, this));
}
void Pause() override {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefDownloadItemCallbackImpl::DoPause, this));
}
void Resume() override {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefDownloadItemCallbackImpl::DoResume, this));
}
private:
void DoCancel() {
if (download_id_ <= 0) {
return;
}
if (manager_) {
DownloadItem* item = manager_->GetDownload(download_id_);
if (item && item->GetState() == DownloadItem::IN_PROGRESS) {
item->Cancel(true);
}
}
download_id_ = 0;
}
void DoPause() {
if (download_id_ <= 0) {
return;
}
if (manager_) {
DownloadItem* item = manager_->GetDownload(download_id_);
if (item && item->GetState() == DownloadItem::IN_PROGRESS) {
item->Pause();
}
}
}
void DoResume() {
if (download_id_ <= 0) {
return;
}
if (manager_) {
DownloadItem* item = manager_->GetDownload(download_id_);
if (item && item->CanResume()) {
item->Resume(true);
}
}
}
base::WeakPtr<DownloadManager> manager_;
uint32_t download_id_;
IMPLEMENT_REFCOUNTING(CefDownloadItemCallbackImpl);
};
} // namespace
CefDownloadManagerDelegateImpl::CefDownloadManagerDelegateImpl(
DownloadManager* manager,
bool alloy_bootstrap)
: manager_(manager),
manager_ptr_factory_(manager),
alloy_bootstrap_(alloy_bootstrap) {
DCHECK(manager);
manager->AddObserver(this);
DownloadManager::DownloadVector items;
manager->GetAllDownloads(&items);
DownloadManager::DownloadVector::const_iterator it = items.begin();
for (; it != items.end(); ++it) {
OnDownloadCreated(manager, *it);
}
}
CefDownloadManagerDelegateImpl::~CefDownloadManagerDelegateImpl() {
ResetManager();
}
void CefDownloadManagerDelegateImpl::OnDownloadUpdated(DownloadItem* download) {
CefRefPtr<CefBrowserHostBase> browser = GetBrowser(download);
CefRefPtr<CefDownloadHandler> handler;
if (browser.get()) {
handler = GetDownloadHandler(browser);
}
if (handler.get()) {
CefRefPtr<CefDownloadItemImpl> download_item(
new CefDownloadItemImpl(download));
CefRefPtr<CefDownloadItemCallback> callback(new CefDownloadItemCallbackImpl(
manager_ptr_factory_.GetWeakPtr(), download->GetId()));
handler->OnDownloadUpdated(browser.get(), download_item.get(), callback);
std::ignore = download_item->Detach(nullptr);
}
}
void CefDownloadManagerDelegateImpl::OnDownloadDestroyed(DownloadItem* item) {
item->RemoveObserver(this);
CefBrowserHostBase* browser = nullptr;
ItemBrowserMap::iterator it = item_browser_map_.find(item);
DCHECK(it != item_browser_map_.end());
if (it != item_browser_map_.end()) {
browser = it->second;
item_browser_map_.erase(it);
}
if (browser) {
// Determine if any remaining DownloadItems are associated with the same
// browser. If not, then unregister as an observer.
bool has_remaining = false;
ItemBrowserMap::const_iterator it2 = item_browser_map_.begin();
for (; it2 != item_browser_map_.end(); ++it2) {
if (it2->second == browser) {
has_remaining = true;
break;
}
}
if (!has_remaining) {
browser->RemoveObserver(this);
}
}
}
void CefDownloadManagerDelegateImpl::OnDownloadCreated(DownloadManager* manager,
DownloadItem* item) {
// This callback may arrive after DetermineDownloadTarget, so we allow
// association from either method.
CefRefPtr<CefBrowserHostBase> browser = GetOrAssociateBrowser(item);
if (!browser) {
// If the download is rejected (e.g. ALT+click on an invalid protocol link)
// then an "interrupted" download will be started via DownloadManagerImpl::
// StartDownloadWithId (originating from CreateInterruptedDownload) with no
// associated WebContents and consequently no associated CEF browser. In
// that case DetermineDownloadTarget will be called before this method.
// TODO(cef): Figure out how to expose this via a client callback.
const std::vector<GURL>& url_chain = item->GetUrlChain();
if (!url_chain.empty()) {
LOG(INFO) << "Rejected download of " << url_chain.back().spec();
}
item->Cancel(true);
}
}
void CefDownloadManagerDelegateImpl::ManagerGoingDown(
DownloadManager* manager) {
DCHECK_EQ(manager, manager_);
ResetManager();
}
bool CefDownloadManagerDelegateImpl::DetermineDownloadTarget(
DownloadItem* item,
download::DownloadTargetCallback* callback) {
if (alloy_bootstrap_) {
const auto& forced_path = item->GetForcedFilePath();
if (!forced_path.empty()) {
RunDownloadTargetCallback(std::move(*callback), forced_path);
return true;
}
}
// This callback may arrive before OnDownloadCreated, so we allow association
// from either method.
CefRefPtr<CefBrowserHostBase> browser = GetOrAssociateBrowser(item);
if (!browser) {
// Cancel by default with Alloy bootstrap.
return alloy_bootstrap_;
}
bool handled = false;
CefRefPtr<CefDownloadHandler> handler = GetDownloadHandler(browser);
if (handler) {
base::FilePath suggested_name = net::GenerateFileName(
item->GetURL(), item->GetContentDisposition(), std::string(),
item->GetSuggestedFilename(), item->GetMimeType(), "download");
CefRefPtr<CefDownloadItemImpl> download_item(new CefDownloadItemImpl(item));
CefRefPtr<CefBeforeDownloadCallbackImpl> callbackObj(
new CefBeforeDownloadCallbackImpl(manager_ptr_factory_.GetWeakPtr(),
item->GetId(), suggested_name,
std::move(*callback)));
handled =
handler->OnBeforeDownload(browser.get(), download_item.get(),
suggested_name.value(), callbackObj.get());
if (!handled && callbackObj->IsDetached()) {
LOG(ERROR) << "Should return true from OnBeforeDownload when executing "
"the callback";
handled = true;
}
if (!handled) {
*callback = callbackObj->Detach();
}
std::ignore = download_item->Detach(nullptr);
}
// Cancel by default with Alloy style.
return handled ? true : alloy_bootstrap_;
}
void CefDownloadManagerDelegateImpl::OnBrowserDestroyed(
CefBrowserHostBase* browser) {
ItemBrowserMap::iterator it = item_browser_map_.begin();
for (; it != item_browser_map_.end(); ++it) {
if (it->second == browser) {
// Don't call back into browsers that have been destroyed. We're not
// canceling the download so it will continue silently until it completes
// or until the associated browser context is destroyed.
it->second = nullptr;
}
}
}
CefRefPtr<CefBrowserHostBase>
CefDownloadManagerDelegateImpl::GetOrAssociateBrowser(
download::DownloadItem* item) {
ItemBrowserMap::const_iterator it = item_browser_map_.find(item);
if (it != item_browser_map_.end()) {
return it->second;
}
CefRefPtr<CefBrowserHostBase> browser;
content::WebContents* contents =
content::DownloadItemUtils::GetWebContents(item);
if (contents) {
browser = CefBrowserHostBase::GetBrowserForContents(contents);
LOG_IF(WARNING, !browser) << "No CefBrowser for download item";
}
if (!browser) {
return nullptr;
}
item->AddObserver(this);
item_browser_map_.insert(std::make_pair(item, browser.get()));
// Register as an observer so that we can cancel associated DownloadItems when
// the browser is destroyed.
if (!browser->HasObserver(this)) {
browser->AddObserver(this);
}
return browser;
}
CefRefPtr<CefBrowserHostBase> CefDownloadManagerDelegateImpl::GetBrowser(
DownloadItem* item) {
ItemBrowserMap::const_iterator it = item_browser_map_.find(item);
if (it != item_browser_map_.end()) {
return it->second;
}
// If the download is rejected (e.g. ALT+click on an invalid protocol link)
// then an "interrupted" download will be started via DownloadManagerImpl::
// StartDownloadWithId (originating from CreateInterruptedDownload) with no
// associated WebContents and consequently no associated CEF browser. In that
// case DetermineDownloadTarget will be called before OnDownloadCreated.
DCHECK(!content::DownloadItemUtils::GetWebContents(item));
return nullptr;
}
void CefDownloadManagerDelegateImpl::ResetManager() {
if (manager_) {
if (alloy_bootstrap_) {
manager_->SetDelegate(nullptr);
}
manager_->RemoveObserver(this);
manager_ptr_factory_.InvalidateWeakPtrs();
manager_ = nullptr;
}
while (!item_browser_map_.empty()) {
OnDownloadDestroyed(item_browser_map_.begin()->first);
}
}

View File

@ -0,0 +1,72 @@
// Copyright (c) 2012 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.
#ifndef CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_IMPL_H_
#define CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_IMPL_H_
#pragma once
#include <set>
#include "libcef/browser/browser_host_base.h"
#include "libcef/browser/download_manager_delegate.h"
#include "base/memory/weak_ptr.h"
#include "components/download/public/common/download_item.h"
#include "components/download/public/common/download_target_info.h"
#include "content/public/browser/download_manager.h"
class CefBrowserHostBase;
class CefDownloadManagerDelegateImpl
: public download::DownloadItem::Observer,
public content::DownloadManager::Observer,
public cef::DownloadManagerDelegate,
public CefBrowserHostBase::Observer {
public:
CefDownloadManagerDelegateImpl(content::DownloadManager* manager,
bool alloy_bootstrap);
CefDownloadManagerDelegateImpl(const CefDownloadManagerDelegateImpl&) =
delete;
CefDownloadManagerDelegateImpl& operator=(
const CefDownloadManagerDelegateImpl&) = delete;
~CefDownloadManagerDelegateImpl() override;
private:
// DownloadItem::Observer methods.
void OnDownloadUpdated(download::DownloadItem* item) override;
void OnDownloadDestroyed(download::DownloadItem* item) override;
// DownloadManager::Observer methods.
void OnDownloadCreated(content::DownloadManager* manager,
download::DownloadItem* item) override;
void ManagerGoingDown(content::DownloadManager* manager) override;
// DownloadManagerDelegate methods.
bool DetermineDownloadTarget(
download::DownloadItem* item,
download::DownloadTargetCallback* callback) override;
// CefBrowserHostBase::Observer methods.
void OnBrowserDestroyed(CefBrowserHostBase* browser) override;
CefRefPtr<CefBrowserHostBase> GetOrAssociateBrowser(
download::DownloadItem* item);
CefRefPtr<CefBrowserHostBase> GetBrowser(download::DownloadItem* item);
void ResetManager();
content::DownloadManager* manager_;
base::WeakPtrFactory<content::DownloadManager> manager_ptr_factory_;
const bool alloy_bootstrap_;
// Map of DownloadItem to originating CefBrowserHostBase. Maintaining this
// map is necessary because DownloadItem::GetWebContents() may return NULL if
// the browser navigates while the download is in progress.
using ItemBrowserMap = std::map<download::DownloadItem*, CefBrowserHostBase*>;
ItemBrowserMap item_browser_map_;
};
#endif // CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_IMPL_H_

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=c70a949d129e47d660f9fd4200db05e2f5721bed$
// $hash=f310399ebf0026717b1d733a34dd51b90623c452$
//
#include "libcef_dll/cpptoc/download_handler_cpptoc.h"
@ -61,7 +61,7 @@ download_handler_can_download(struct _cef_download_handler_t* self,
return _retval;
}
void CEF_CALLBACK
int CEF_CALLBACK
download_handler_on_before_download(struct _cef_download_handler_t* self,
cef_browser_t* browser,
struct _cef_download_item_t* download_item,
@ -73,34 +73,37 @@ download_handler_on_before_download(struct _cef_download_handler_t* self,
DCHECK(self);
if (!self) {
return;
return 0;
}
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser) {
return;
return 0;
}
// Verify param: download_item; type: refptr_diff
DCHECK(download_item);
if (!download_item) {
return;
return 0;
}
// Verify param: suggested_name; type: string_byref_const
DCHECK(suggested_name);
if (!suggested_name) {
return;
return 0;
}
// Verify param: callback; type: refptr_diff
DCHECK(callback);
if (!callback) {
return;
return 0;
}
// Execute
CefDownloadHandlerCppToC::Get(self)->OnBeforeDownload(
bool _retval = CefDownloadHandlerCppToC::Get(self)->OnBeforeDownload(
CefBrowserCToCpp::Wrap(browser),
CefDownloadItemCToCpp::Wrap(download_item), CefString(suggested_name),
CefBeforeDownloadCallbackCToCpp::Wrap(callback));
// Return type: bool
return _retval;
}
void CEF_CALLBACK

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=690401fe62e7a96ea1c2d72b48144f789207e204$
// $hash=6c28836de30002d764d9dcab7f51260cc447f639$
//
#include "libcef_dll/ctocpp/download_handler_ctocpp.h"
@ -60,7 +60,7 @@ bool CefDownloadHandlerCToCpp::CanDownload(CefRefPtr<CefBrowser> browser,
}
NO_SANITIZE("cfi-icall")
void CefDownloadHandlerCToCpp::OnBeforeDownload(
bool CefDownloadHandlerCToCpp::OnBeforeDownload(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
@ -69,7 +69,7 @@ void CefDownloadHandlerCToCpp::OnBeforeDownload(
cef_download_handler_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_before_download)) {
return;
return false;
}
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
@ -77,29 +77,32 @@ void CefDownloadHandlerCToCpp::OnBeforeDownload(
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get()) {
return;
return false;
}
// Verify param: download_item; type: refptr_diff
DCHECK(download_item.get());
if (!download_item.get()) {
return;
return false;
}
// Verify param: suggested_name; type: string_byref_const
DCHECK(!suggested_name.empty());
if (suggested_name.empty()) {
return;
return false;
}
// Verify param: callback; type: refptr_diff
DCHECK(callback.get());
if (!callback.get()) {
return;
return false;
}
// Execute
_struct->on_before_download(_struct, CefBrowserCppToC::Wrap(browser),
CefDownloadItemCppToC::Wrap(download_item),
suggested_name.GetStruct(),
CefBeforeDownloadCallbackCppToC::Wrap(callback));
int _retval = _struct->on_before_download(
_struct, CefBrowserCppToC::Wrap(browser),
CefDownloadItemCppToC::Wrap(download_item), suggested_name.GetStruct(),
CefBeforeDownloadCallbackCppToC::Wrap(callback));
// Return type: bool
return _retval ? true : false;
}
NO_SANITIZE("cfi-icall")

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=13fc7d443a6c044598f0683e235f682d91798042$
// $hash=fbf27fd04a17a31cb8d279b8a8e1381c7858bc2c$
//
#ifndef CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_HANDLER_CTOCPP_H_
@ -38,7 +38,7 @@ class CefDownloadHandlerCToCpp
bool CanDownload(CefRefPtr<CefBrowser> browser,
const CefString& url,
const CefString& request_method) override;
void OnBeforeDownload(CefRefPtr<CefBrowser> browser,
bool OnBeforeDownload(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
CefRefPtr<CefBeforeDownloadCallback> callback) override;

View File

@ -279,6 +279,12 @@ patches = [
# https://chromium-review.googlesource.com/c/chromium/src/+/5006355
'name': 'chrome_browser_dialogs_widget',
},
{
# alloy: Support override of DownloadPrefs::FromBrowserContext
# chrome: Support custom DownloadManagerDelegate handling.
# https://github.com/chromiumembedded/cef/issues/3681
'name': 'chrome_browser_download',
},
{
# chrome: Support override of ChromeMimeHandlerViewGuestDelegate.
# https://github.com/chromiumembedded/cef/issues/2969

View File

@ -0,0 +1,121 @@
diff --git chrome/browser/download/chrome_download_manager_delegate.cc chrome/browser/download/chrome_download_manager_delegate.cc
index 447e7d0d5dc6d..7bba632f1331c 100644
--- chrome/browser/download/chrome_download_manager_delegate.cc
+++ chrome/browser/download/chrome_download_manager_delegate.cc
@@ -146,6 +146,10 @@
#include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
#endif
+#if BUILDFLAG(ENABLE_CEF)
+#include "cef/libcef/browser/download_manager_delegate.h"
+#endif
+
using content::BrowserThread;
using content::DownloadManager;
using download::DownloadItem;
@@ -490,6 +494,11 @@ ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate(Profile* profile)
download_dialog_bridge_ = std::make_unique<DownloadDialogBridge>();
download_message_bridge_ = std::make_unique<DownloadMessageBridge>();
#endif
+
+#if BUILDFLAG(ENABLE_CEF)
+ cef_delegate_ =
+ cef::DownloadManagerDelegate::Create(profile_->GetDownloadManager());
+#endif
}
ChromeDownloadManagerDelegate::~ChromeDownloadManagerDelegate() {
@@ -549,6 +558,9 @@ void ChromeDownloadManagerDelegate::Shutdown() {
download_manager_->RemoveObserver(this);
download_manager_ = nullptr;
}
+#if BUILDFLAG(ENABLE_CEF)
+ cef_delegate_.reset();
+#endif
}
void ChromeDownloadManagerDelegate::OnDownloadCanceledAtShutdown(
@@ -617,6 +629,12 @@ bool ChromeDownloadManagerDelegate::DetermineDownloadTarget(
ReportPDFLoadStatus(PDFLoadStatus::kTriggeredNoGestureDriveByDownload);
}
+#if BUILDFLAG(ENABLE_CEF)
+ if (cef_delegate_->DetermineDownloadTarget(download, callback)) {
+ return true;
+ }
+#endif
+
DownloadTargetDeterminer::CompletionCallback target_determined_callback =
base::BindOnce(&ChromeDownloadManagerDelegate::OnDownloadTargetDetermined,
weak_ptr_factory_.GetWeakPtr(), download->GetId(),
diff --git chrome/browser/download/chrome_download_manager_delegate.h chrome/browser/download/chrome_download_manager_delegate.h
index e2cf12d2c8fee..376818e28798c 100644
--- chrome/browser/download/chrome_download_manager_delegate.h
+++ chrome/browser/download/chrome_download_manager_delegate.h
@@ -19,6 +19,7 @@
#include "base/task/sequenced_task_runner.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
+#include "cef/libcef/features/runtime.h"
#include "chrome/browser/download/download_completion_blocker.h"
#include "chrome/browser/download/download_target_determiner_delegate.h"
#include "components/download/public/common/download_danger_type.h"
@@ -57,6 +58,12 @@ class CrxInstallError;
}
#endif
+#if BUILDFLAG(ENABLE_CEF)
+namespace cef {
+class DownloadManagerDelegate;
+}
+#endif
+
// This is the Chrome side helper for the download system.
class ChromeDownloadManagerDelegate
: public content::DownloadManagerDelegate,
@@ -387,6 +394,10 @@ class ChromeDownloadManagerDelegate
// Whether a file picker dialog is showing.
bool is_file_picker_showing_;
+#if BUILDFLAG(ENABLE_CEF)
+ std::unique_ptr<cef::DownloadManagerDelegate> cef_delegate_;
+#endif
+
base::WeakPtrFactory<ChromeDownloadManagerDelegate> weak_ptr_factory_{this};
};
diff --git chrome/browser/download/download_prefs.cc chrome/browser/download/download_prefs.cc
index 773f72da82f90..6d0307988406f 100644
--- chrome/browser/download/download_prefs.cc
+++ chrome/browser/download/download_prefs.cc
@@ -23,6 +23,7 @@
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
+#include "cef/libcef/features/runtime.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_core_service_impl.h"
@@ -64,6 +65,10 @@
#include "chrome/browser/flags/android/chrome_feature_list.h"
#endif
+#if BUILDFLAG(ENABLE_CEF)
+#include "cef/libcef/browser/alloy/alloy_download_util.h"
+#endif
+
using content::BrowserContext;
using content::BrowserThread;
using content::DownloadManager;
@@ -355,6 +360,11 @@ DownloadPrefs* DownloadPrefs::FromDownloadManager(
// static
DownloadPrefs* DownloadPrefs::FromBrowserContext(
content::BrowserContext* context) {
+#if BUILDFLAG(ENABLE_CEF)
+ if (cef::IsAlloyRuntimeEnabled()) {
+ return alloy::GetDownloadPrefsFromBrowserContext(context);
+ }
+#endif
return FromDownloadManager(context->GetDownloadManager());
}

View File

@ -186,3 +186,18 @@ index 2f769363f8519..228c20926634b 100644
// FYI: Do NOT add any more friends here. The functions above are the ONLY
// ones that need to call AttachTabHelpers; if you think you do, re-read the
// design document linked above, especially the section "Reusing tab helpers".
diff --git chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
index a76c331ec5344..ffe3cbe7ce37c 100644
--- chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
+++ chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -850,6 +850,10 @@ void DownloadToolbarButtonView::ShowPendingDownloadStartedAnimation() {
if (!gfx::Animation::ShouldRenderRichAnimation()) {
return;
}
+ if (!IsDrawn()) {
+ // Don't animate with a hidden download button.
+ return;
+ }
content::WebContents* const web_contents =
browser_->tab_strip_model()->GetActiveWebContents();
if (!web_contents ||

View File

@ -1,38 +1,3 @@
diff --git chrome/browser/download/download_prefs.cc chrome/browser/download/download_prefs.cc
index 773f72da82f90..6d0307988406f 100644
--- chrome/browser/download/download_prefs.cc
+++ chrome/browser/download/download_prefs.cc
@@ -23,6 +23,7 @@
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
+#include "cef/libcef/features/runtime.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_core_service_impl.h"
@@ -64,6 +65,10 @@
#include "chrome/browser/flags/android/chrome_feature_list.h"
#endif
+#if BUILDFLAG(ENABLE_CEF)
+#include "cef/libcef/browser/alloy/alloy_download_util.h"
+#endif
+
using content::BrowserContext;
using content::BrowserThread;
using content::DownloadManager;
@@ -355,6 +360,11 @@ DownloadPrefs* DownloadPrefs::FromDownloadManager(
// static
DownloadPrefs* DownloadPrefs::FromBrowserContext(
content::BrowserContext* context) {
+#if BUILDFLAG(ENABLE_CEF)
+ if (cef::IsAlloyRuntimeEnabled()) {
+ return alloy::GetDownloadPrefsFromBrowserContext(context);
+ }
+#endif
return FromDownloadManager(context->GetDownloadManager());
}
diff --git chrome/browser/printing/print_preview_dialog_controller.cc chrome/browser/printing/print_preview_dialog_controller.cc
index 54efe456fd86e..a685c4e0722cc 100644
--- chrome/browser/printing/print_preview_dialog_controller.cc

View File

@ -858,7 +858,7 @@ bool ClientHandler::CanDownload(CefRefPtr<CefBrowser> browser,
return true;
}
void ClientHandler::OnBeforeDownload(
bool ClientHandler::OnBeforeDownload(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
@ -867,6 +867,7 @@ void ClientHandler::OnBeforeDownload(
// Continue the download and show the "Save As" dialog.
callback->Continue(MainContext::Get()->GetDownloadPath(suggested_name), true);
return true;
}
void ClientHandler::OnDownloadUpdated(

View File

@ -182,7 +182,7 @@ class ClientHandler : public BaseClientHandler,
bool CanDownload(CefRefPtr<CefBrowser> browser,
const CefString& url,
const CefString& request_method) override;
void OnBeforeDownload(CefRefPtr<CefBrowser> browser,
bool OnBeforeDownload(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
CefRefPtr<CefBeforeDownloadCallback> callback) override;

View File

@ -49,7 +49,7 @@ class DownloadSchemeHandler : public CefResourceHandler {
content_disposition_ = kTestContentDisposition;
should_delay_ = true;
} else {
EXPECT_TRUE(false); // Not reached.
EXPECT_TRUE(IgnoreURL(url)) << url;
// Cancel immediately.
handle_request = true;
@ -187,6 +187,8 @@ class DownloadTestHandler : public TestHandler {
test_mode_ == CLICKED_BLOCKED;
}
bool is_clicked_invalid() const { return test_mode_ == CLICKED_INVALID; }
bool is_clicked_and_downloaded() const { return test_mode_ == CLICKED; }
bool is_downloaded() const {
@ -276,6 +278,14 @@ class DownloadTestHandler : public TestHandler {
// ALT key will trigger download of custom protocol links.
SendClick(browser,
test_mode_ == CLICKED_INVALID ? EVENTFLAG_ALT_DOWN : 0);
if (IsChromeBootstrap() && is_clicked_invalid()) {
// Destroy the test after a bit because there will be no further
// callbacks.
CefPostDelayedTask(
TID_UI, base::BindOnce(&DownloadTestHandler::DestroyTest, this),
200);
}
} else {
// Begin the download progammatically.
browser->GetHost()->StartDownload(kTestDownloadUrl);
@ -343,7 +353,7 @@ class DownloadTestHandler : public TestHandler {
return test_mode_ != CLICKED_BLOCKED;
}
void OnBeforeDownload(
bool OnBeforeDownload(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
@ -393,6 +403,8 @@ class DownloadTestHandler : public TestHandler {
} else if (test_mode_ == PENDING) {
ContinuePendingIfReady();
}
return true;
}
void OnDownloadUpdated(CefRefPtr<CefBrowser> browser,
@ -500,7 +512,14 @@ class DownloadTestHandler : public TestHandler {
CefRegisterSchemeHandlerFactory("https", kTestDomain, nullptr);
}
if (is_clicked()) {
if (is_clicked_invalid()) {
if (IsChromeBootstrap()) {
// No CanDownload for invalid protocol links.
EXPECT_FALSE(got_can_download_);
} else {
EXPECT_TRUE(got_can_download_);
}
} else if (is_clicked()) {
EXPECT_TRUE(got_can_download_);
} else {
EXPECT_FALSE(got_can_download_);

View File

@ -150,13 +150,6 @@ class TestHandler : public CefClient,
CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; }
CefRefPtr<CefRequestHandler> GetRequestHandler() override { return this; }
// CefDownloadHandler methods
void OnBeforeDownload(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
CefRefPtr<CefBeforeDownloadCallback> callback) override {}
// CefLifeSpanHandler methods
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;