From be6642df3f3e0b723cc013678b90f180c8e0b9e6 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Fri, 26 Apr 2024 15:21:33 -0400 Subject: [PATCH] chrome: Add CefDownloadHandler support (see #3681) Behaves the same as Alloy runtime except that CanDownload is not called for invalid protocol schemes. --- BUILD.gn | 4 + include/capi/cef_download_handler_capi.h | 13 +- include/cef_api_hash.h | 8 +- include/cef_download_handler.h | 20 +- libcef/browser/alloy/alloy_browser_context.cc | 4 +- libcef/browser/alloy/alloy_browser_context.h | 4 +- .../alloy/alloy_download_manager_delegate.cc | 22 + .../alloy/alloy_download_manager_delegate.h | 26 + libcef/browser/download_manager_delegate.cc | 485 +---------------- libcef/browser/download_manager_delegate.h | 71 +-- .../browser/download_manager_delegate_impl.cc | 498 ++++++++++++++++++ .../browser/download_manager_delegate_impl.h | 72 +++ libcef_dll/cpptoc/download_handler_cpptoc.cc | 19 +- libcef_dll/ctocpp/download_handler_ctocpp.cc | 25 +- libcef_dll/ctocpp/download_handler_ctocpp.h | 4 +- patch/patch.cfg | 6 + patch/patches/chrome_browser_download.patch | 121 +++++ patch/patches/chrome_browser_extensions.patch | 15 + patch/patches/print_preview_123.patch | 35 -- tests/cefclient/browser/client_handler.cc | 3 +- tests/cefclient/browser/client_handler.h | 2 +- tests/ceftests/download_unittest.cc | 25 +- tests/ceftests/test_handler.h | 7 - 23 files changed, 873 insertions(+), 616 deletions(-) create mode 100644 libcef/browser/alloy/alloy_download_manager_delegate.cc create mode 100644 libcef/browser/alloy/alloy_download_manager_delegate.h create mode 100644 libcef/browser/download_manager_delegate_impl.cc create mode 100644 libcef/browser/download_manager_delegate_impl.h create mode 100644 patch/patches/chrome_browser_download.patch diff --git a/BUILD.gn b/BUILD.gn index ef712e992..8e7ed4c38 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -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", diff --git a/include/capi/cef_download_handler_capi.h b/include/capi/cef_download_handler_capi.h index b9c3016ef..e1ca2eb7b 100644 --- a/include/capi/cef_download_handler_capi.h +++ b/include/capi/cef_download_handler_capi.h @@ -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, diff --git a/include/cef_api_hash.h b/include/cef_api_hash.h index 83ea1e815..d731c22d8 100644 --- a/include/cef_api_hash.h +++ b/include/cef_api_hash.h @@ -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 diff --git a/include/cef_download_handler.h b/include/cef_download_handler.h index de911b663..64e3a4b3a 100644 --- a/include/cef_download_handler.h +++ b/include/cef_download_handler.h @@ -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 browser, - CefRefPtr download_item, - const CefString& suggested_name, - CefRefPtr callback) = 0; + virtual bool OnBeforeDownload(CefRefPtr browser, + CefRefPtr download_item, + const CefString& suggested_name, + CefRefPtr callback) { + return false; + } /// /// Called when a download's status or progress information has been updated. diff --git a/libcef/browser/alloy/alloy_browser_context.cc b/libcef/browser/alloy/alloy_browser_context.cc index 72a346c71..cf26722e0 100644 --- a/libcef/browser/alloy/alloy_browser_context.cc +++ b/libcef/browser/alloy/alloy_browser_context.cc @@ -8,7 +8,7 @@ #include #include -#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(GetDownloadManager()); + std::make_unique(GetDownloadManager()); } return download_manager_delegate_.get(); } diff --git a/libcef/browser/alloy/alloy_browser_context.h b/libcef/browser/alloy/alloy_browser_context.h index 60a48e91b..5fafd328c 100644 --- a/libcef/browser/alloy/alloy_browser_context.h +++ b/libcef/browser/alloy/alloy_browser_context.h @@ -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 pref_service_; std::unique_ptr pref_proxy_config_tracker_; - std::unique_ptr download_manager_delegate_; + std::unique_ptr download_manager_delegate_; std::unique_ptr ssl_host_state_delegate_; std::unique_ptr visitedlink_master_; // |visitedlink_listener_| is owned by visitedlink_master_. diff --git a/libcef/browser/alloy/alloy_download_manager_delegate.cc b/libcef/browser/alloy/alloy_download_manager_delegate.cc new file mode 100644 index 000000000..8c80e5165 --- /dev/null +++ b/libcef/browser/alloy/alloy_download_manager_delegate.cc @@ -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); +} diff --git a/libcef/browser/alloy/alloy_download_manager_delegate.h b/libcef/browser/alloy/alloy_download_manager_delegate.h new file mode 100644 index 000000000..5dfe0a75c --- /dev/null +++ b/libcef/browser/alloy/alloy_download_manager_delegate.h @@ -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_ diff --git a/libcef/browser/download_manager_delegate.cc b/libcef/browser/download_manager_delegate.cc index b5208e3d9..6801ba8f5 100644 --- a/libcef/browser/download_manager_delegate.cc +++ b/libcef/browser/download_manager_delegate.cc @@ -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 +#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 GetDownloadHandler( - CefRefPtr browser) { - CefRefPtr client = browser->GetClient(); - if (client.get()) { - return client->GetDownloadHandler(); - } - return nullptr; +// static +std::unique_ptr DownloadManagerDelegate::Create( + content::DownloadManager* download_manager) { + REQUIRE_CHROME_RUNTIME(); + return std::make_unique( + 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& 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 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 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 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& 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 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& 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 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 browser = GetBrowser(download); - CefRefPtr handler; - if (browser.get()) { - handler = GetDownloadHandler(browser); - } - - if (handler.get()) { - CefRefPtr download_item( - new CefDownloadItemImpl(download)); - CefRefPtr 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 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& 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 browser = GetOrAssociateBrowser(item); - CefRefPtr 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 download_item(new CefDownloadItemImpl(item)); - CefRefPtr 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 diff --git a/libcef/browser/download_manager_delegate.h b/libcef/browser/download_manager_delegate.h index 2886f2ac7..4bd798fd4 100644 --- a/libcef/browser/download_manager_delegate.h +++ b/libcef/browser/download_manager_delegate.h @@ -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 +#include -#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 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 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; - ItemBrowserMap item_browser_map_; + // Allow deletion via std::unique_ptr only. + friend std::default_delete; }; +} // namespace cef + #endif // CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_H_ diff --git a/libcef/browser/download_manager_delegate_impl.cc b/libcef/browser/download_manager_delegate_impl.cc new file mode 100644 index 000000000..2322b0c92 --- /dev/null +++ b/libcef/browser/download_manager_delegate_impl.cc @@ -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 + +#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 GetDownloadHandler( + CefRefPtr browser) { + CefRefPtr 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& 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 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 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 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& 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 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& 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 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 browser = GetBrowser(download); + CefRefPtr handler; + if (browser.get()) { + handler = GetDownloadHandler(browser); + } + + if (handler.get()) { + CefRefPtr download_item( + new CefDownloadItemImpl(download)); + CefRefPtr 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 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& 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 browser = GetOrAssociateBrowser(item); + if (!browser) { + // Cancel by default with Alloy bootstrap. + return alloy_bootstrap_; + } + + bool handled = false; + CefRefPtr handler = GetDownloadHandler(browser); + if (handler) { + base::FilePath suggested_name = net::GenerateFileName( + item->GetURL(), item->GetContentDisposition(), std::string(), + item->GetSuggestedFilename(), item->GetMimeType(), "download"); + + CefRefPtr download_item(new CefDownloadItemImpl(item)); + CefRefPtr 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 +CefDownloadManagerDelegateImpl::GetOrAssociateBrowser( + download::DownloadItem* item) { + ItemBrowserMap::const_iterator it = item_browser_map_.find(item); + if (it != item_browser_map_.end()) { + return it->second; + } + + CefRefPtr 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 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); + } +} diff --git a/libcef/browser/download_manager_delegate_impl.h b/libcef/browser/download_manager_delegate_impl.h new file mode 100644 index 000000000..bef450e94 --- /dev/null +++ b/libcef/browser/download_manager_delegate_impl.h @@ -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 + +#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 GetOrAssociateBrowser( + download::DownloadItem* item); + CefRefPtr GetBrowser(download::DownloadItem* item); + + void ResetManager(); + + content::DownloadManager* manager_; + base::WeakPtrFactory 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; + ItemBrowserMap item_browser_map_; +}; + +#endif // CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_IMPL_H_ diff --git a/libcef_dll/cpptoc/download_handler_cpptoc.cc b/libcef_dll/cpptoc/download_handler_cpptoc.cc index 2678b7ce3..4ff803d6d 100644 --- a/libcef_dll/cpptoc/download_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/download_handler_cpptoc.cc @@ -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 diff --git a/libcef_dll/ctocpp/download_handler_ctocpp.cc b/libcef_dll/ctocpp/download_handler_ctocpp.cc index 0fcf8d318..f6426ae28 100644 --- a/libcef_dll/ctocpp/download_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/download_handler_ctocpp.cc @@ -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 browser, } NO_SANITIZE("cfi-icall") -void CefDownloadHandlerCToCpp::OnBeforeDownload( +bool CefDownloadHandlerCToCpp::OnBeforeDownload( CefRefPtr browser, CefRefPtr 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") diff --git a/libcef_dll/ctocpp/download_handler_ctocpp.h b/libcef_dll/ctocpp/download_handler_ctocpp.h index e41ccc648..905d42dd4 100644 --- a/libcef_dll/ctocpp/download_handler_ctocpp.h +++ b/libcef_dll/ctocpp/download_handler_ctocpp.h @@ -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 browser, const CefString& url, const CefString& request_method) override; - void OnBeforeDownload(CefRefPtr browser, + bool OnBeforeDownload(CefRefPtr browser, CefRefPtr download_item, const CefString& suggested_name, CefRefPtr callback) override; diff --git a/patch/patch.cfg b/patch/patch.cfg index a25a1ecf5..eaa234c0c 100644 --- a/patch/patch.cfg +++ b/patch/patch.cfg @@ -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 diff --git a/patch/patches/chrome_browser_download.patch b/patch/patches/chrome_browser_download.patch new file mode 100644 index 000000000..8b815c702 --- /dev/null +++ b/patch/patches/chrome_browser_download.patch @@ -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(); + download_message_bridge_ = std::make_unique(); + #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_delegate_; ++#endif ++ + base::WeakPtrFactory 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()); + } + diff --git a/patch/patches/chrome_browser_extensions.patch b/patch/patches/chrome_browser_extensions.patch index f76e8a4f7..04949646e 100644 --- a/patch/patches/chrome_browser_extensions.patch +++ b/patch/patches/chrome_browser_extensions.patch @@ -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 || diff --git a/patch/patches/print_preview_123.patch b/patch/patches/print_preview_123.patch index 83120b2e4..64cb84216 100644 --- a/patch/patches/print_preview_123.patch +++ b/patch/patches/print_preview_123.patch @@ -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 diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index 65c9cd16c..965c3883d 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -858,7 +858,7 @@ bool ClientHandler::CanDownload(CefRefPtr browser, return true; } -void ClientHandler::OnBeforeDownload( +bool ClientHandler::OnBeforeDownload( CefRefPtr browser, CefRefPtr 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( diff --git a/tests/cefclient/browser/client_handler.h b/tests/cefclient/browser/client_handler.h index fd672294c..6eb9d4517 100644 --- a/tests/cefclient/browser/client_handler.h +++ b/tests/cefclient/browser/client_handler.h @@ -182,7 +182,7 @@ class ClientHandler : public BaseClientHandler, bool CanDownload(CefRefPtr browser, const CefString& url, const CefString& request_method) override; - void OnBeforeDownload(CefRefPtr browser, + bool OnBeforeDownload(CefRefPtr browser, CefRefPtr download_item, const CefString& suggested_name, CefRefPtr callback) override; diff --git a/tests/ceftests/download_unittest.cc b/tests/ceftests/download_unittest.cc index cba27a1df..f9e8bf646 100644 --- a/tests/ceftests/download_unittest.cc +++ b/tests/ceftests/download_unittest.cc @@ -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 browser, CefRefPtr 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 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_); diff --git a/tests/ceftests/test_handler.h b/tests/ceftests/test_handler.h index f5c91c041..9bbf42f6e 100644 --- a/tests/ceftests/test_handler.h +++ b/tests/ceftests/test_handler.h @@ -150,13 +150,6 @@ class TestHandler : public CefClient, CefRefPtr GetLoadHandler() override { return this; } CefRefPtr GetRequestHandler() override { return this; } - // CefDownloadHandler methods - void OnBeforeDownload( - CefRefPtr browser, - CefRefPtr download_item, - const CefString& suggested_name, - CefRefPtr callback) override {} - // CefLifeSpanHandler methods void OnAfterCreated(CefRefPtr browser) override; void OnBeforeClose(CefRefPtr browser) override;