Add permission callback for user-initated downloads (fixes issue #3183)

This change adds a CefDownloadHandler::CanDownload callback for optionally
blocking user-initiated downloads (e.g. alt + link click or link click that
returns a `Content-Disposition: attachment` response from the server).

To test:
- Run `ceftests --gtest_filter=DownloadTest.*`.
- Run `cefclient --hide-controls`. User-initiated downloads will be blocked.
This commit is contained in:
Marshall Greenblatt 2022-03-22 17:40:28 -04:00
parent 2f5838eaaa
commit 6d7a680187
16 changed files with 280 additions and 38 deletions

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for // by hand. See the translator.README.txt file in the tools directory for
// more information. // more information.
// //
// $hash=ec13ec3e2819e4ac49792c3a1c57bc60b45fb95b$ // $hash=33862915eb4156a70794a346a090154c468763e2$
// //
#ifndef CEF_INCLUDE_CAPI_CEF_DOWNLOAD_HANDLER_CAPI_H_ #ifndef CEF_INCLUDE_CAPI_CEF_DOWNLOAD_HANDLER_CAPI_H_
@ -103,6 +103,18 @@ typedef struct _cef_download_handler_t {
/// ///
cef_base_ref_counted_t base; cef_base_ref_counted_t base;
///
// Called before a download begins in response to a user-initiated action
// (e.g. alt + link click or link click that returns a `Content-Disposition:
// attachment` response from the server). |url| is the target download URL and
// |request_function| is the target function (GET, POST, etc). Return true (1)
// to proceed with the download or false (0) to cancel the download.
///
int(CEF_CALLBACK* can_download)(struct _cef_download_handler_t* self,
struct _cef_browser_t* browser,
const cef_string_t* url,
const cef_string_t* request_method);
/// ///
// Called before a download begins. |suggested_name| is the suggested name for // Called before a download begins. |suggested_name| is the suggested name for
// the download file. By default the download will be canceled. Execute // the download file. By default the download will be canceled. Execute

View File

@ -42,13 +42,13 @@
// way that may cause binary incompatibility with other builds. The universal // way that may cause binary incompatibility with other builds. The universal
// hash value will change if any platform is affected whereas the platform hash // hash value will change if any platform is affected whereas the platform hash
// values will change only if that particular platform is affected. // values will change only if that particular platform is affected.
#define CEF_API_HASH_UNIVERSAL "099de505181a68d5b53885e57d91ae0276259bfa" #define CEF_API_HASH_UNIVERSAL "e42afcebc271841716a6f3197194e3ea02d037cb"
#if defined(OS_WIN) #if defined(OS_WIN)
#define CEF_API_HASH_PLATFORM "0887a2a4ef42c8e2e33aba6662d2891f020e810a" #define CEF_API_HASH_PLATFORM "e08d5cc79cedebc94efc39536c27b6c30d24c9df"
#elif defined(OS_MAC) #elif defined(OS_MAC)
#define CEF_API_HASH_PLATFORM "3d220485ac3df3a74e15db819ea16c98f78116ae" #define CEF_API_HASH_PLATFORM "86626c6c27b29841a4bf01c145e2d7990a73abf0"
#elif defined(OS_LINUX) #elif defined(OS_LINUX)
#define CEF_API_HASH_PLATFORM "ee08ceb06c69dac1ac16361daabe3378c9d2d621" #define CEF_API_HASH_PLATFORM "a19b7983d07d0e6afe0ae46c1768111078995c5e"
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -90,6 +90,20 @@ class CefDownloadItemCallback : public virtual CefBaseRefCounted {
/*--cef(source=client)--*/ /*--cef(source=client)--*/
class CefDownloadHandler : public virtual CefBaseRefCounted { class CefDownloadHandler : public virtual CefBaseRefCounted {
public: public:
///
// Called before a download begins in response to a user-initiated action
// (e.g. alt + link click or link click that returns a `Content-Disposition:
// attachment` response from the server). |url| is the target download URL and
// |request_method| is the target method (GET, POST, etc). Return true to
// proceed with the download or false to cancel the download.
///
/*--cef()--*/
virtual bool CanDownload(CefRefPtr<CefBrowser> browser,
const CefString& url,
const CefString& request_method) {
return true;
}
/// ///
// Called before a download begins. |suggested_name| is the suggested name for // Called before a download begins. |suggested_name| is the suggested name for
// the download file. By default the download will be canceled. Execute // the download file. By default the download will be canceled. Execute

View File

@ -1225,6 +1225,13 @@ bool AlloyBrowserHostImpl::HandleContextMenu(
return HandleContextMenu(web_contents(), params); return HandleContextMenu(web_contents(), params);
} }
void AlloyBrowserHostImpl::CanDownload(
const GURL& url,
const std::string& request_method,
base::OnceCallback<void(bool)> callback) {
contents_delegate_->CanDownload(url, request_method, std::move(callback));
}
KeyboardEventProcessingResult AlloyBrowserHostImpl::PreHandleKeyboardEvent( KeyboardEventProcessingResult AlloyBrowserHostImpl::PreHandleKeyboardEvent(
content::WebContents* source, content::WebContents* source,
const content::NativeWebKeyboardEvent& event) { const content::NativeWebKeyboardEvent& event) {

View File

@ -225,6 +225,9 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase,
bool TakeFocus(content::WebContents* source, bool reverse) override; bool TakeFocus(content::WebContents* source, bool reverse) override;
bool HandleContextMenu(content::RenderFrameHost& render_frame_host, bool HandleContextMenu(content::RenderFrameHost& render_frame_host,
const content::ContextMenuParams& params) override; const content::ContextMenuParams& params) override;
void CanDownload(const GURL& url,
const std::string& request_method,
base::OnceCallback<void(bool)> callback) override;
content::KeyboardEventProcessingResult PreHandleKeyboardEvent( content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source, content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override; const content::NativeWebKeyboardEvent& event) override;

View File

@ -180,6 +180,23 @@ void CefBrowserContentsDelegate::ExitFullscreenModeForTab(
OnFullscreenModeChange(/*fullscreen=*/false); OnFullscreenModeChange(/*fullscreen=*/false);
} }
void CefBrowserContentsDelegate::CanDownload(
const GURL& url,
const std::string& request_method,
base::OnceCallback<void(bool)> callback) {
bool allow = true;
if (auto delegate = platform_delegate()) {
if (auto c = client()) {
if (auto handler = c->GetDownloadHandler()) {
allow = handler->CanDownload(browser(), url.spec(), request_method);
}
}
}
std::move(callback).Run(allow);
}
KeyboardEventProcessingResult KeyboardEventProcessingResult
CefBrowserContentsDelegate::PreHandleKeyboardEvent( CefBrowserContentsDelegate::PreHandleKeyboardEvent(
content::WebContents* source, content::WebContents* source,

View File

@ -101,6 +101,9 @@ class CefBrowserContentsDelegate : public content::WebContentsDelegate,
content::RenderFrameHost* requesting_frame, content::RenderFrameHost* requesting_frame,
const blink::mojom::FullscreenOptions& options) override; const blink::mojom::FullscreenOptions& options) override;
void ExitFullscreenModeForTab(content::WebContents* web_contents) override; void ExitFullscreenModeForTab(content::WebContents* web_contents) override;
void CanDownload(const GURL& url,
const std::string& request_method,
base::OnceCallback<void(bool)> callback) override;
content::KeyboardEventProcessingResult PreHandleKeyboardEvent( content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source, content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override; const content::NativeWebKeyboardEvent& event) override;

View File

@ -235,6 +235,20 @@ void ChromeBrowserDelegate::ExitFullscreenModeForTab(
} }
} }
void ChromeBrowserDelegate::CanDownload(
const GURL& url,
const std::string& request_method,
base::OnceCallback<void(bool)> callback) {
auto source = browser_->tab_strip_model()->GetActiveWebContents();
DCHECK(source);
if (auto delegate = GetDelegateForWebContents(source)) {
delegate->CanDownload(url, request_method, std::move(callback));
return;
}
std::move(callback).Run(true);
}
KeyboardEventProcessingResult ChromeBrowserDelegate::PreHandleKeyboardEvent( KeyboardEventProcessingResult ChromeBrowserDelegate::PreHandleKeyboardEvent(
content::WebContents* source, content::WebContents* source,
const content::NativeWebKeyboardEvent& event) { const content::NativeWebKeyboardEvent& event) {

View File

@ -86,6 +86,9 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate {
content::RenderFrameHost* requesting_frame, content::RenderFrameHost* requesting_frame,
const blink::mojom::FullscreenOptions& options) override; const blink::mojom::FullscreenOptions& options) override;
void ExitFullscreenModeForTab(content::WebContents* web_contents) override; void ExitFullscreenModeForTab(content::WebContents* web_contents) override;
void CanDownload(const GURL& url,
const std::string& request_method,
base::OnceCallback<void(bool)> callback) override;
content::KeyboardEventProcessingResult PreHandleKeyboardEvent( content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source, content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override; const content::NativeWebKeyboardEvent& event) override;

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=496b226297ba7d5fa5e7e7bd4117c417e26fae59$ // $hash=89f651178065dbc03e70e763085bf9125dda6640$
// //
#include "libcef_dll/cpptoc/download_handler_cpptoc.h" #include "libcef_dll/cpptoc/download_handler_cpptoc.h"
@ -23,6 +23,40 @@ namespace {
// MEMBER FUNCTIONS - Body may be edited by hand. // MEMBER FUNCTIONS - Body may be edited by hand.
int CEF_CALLBACK
download_handler_can_download(struct _cef_download_handler_t* self,
cef_browser_t* browser,
const cef_string_t* url,
const cef_string_t* request_method) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return 0;
// Verify param: url; type: string_byref_const
DCHECK(url);
if (!url)
return 0;
// Verify param: request_method; type: string_byref_const
DCHECK(request_method);
if (!request_method)
return 0;
// Execute
bool _retval = CefDownloadHandlerCppToC::Get(self)->CanDownload(
CefBrowserCToCpp::Wrap(browser), CefString(url),
CefString(request_method));
// Return type: bool
return _retval;
}
void CEF_CALLBACK void CEF_CALLBACK
download_handler_on_before_download(struct _cef_download_handler_t* self, download_handler_on_before_download(struct _cef_download_handler_t* self,
cef_browser_t* browser, cef_browser_t* browser,
@ -97,6 +131,7 @@ download_handler_on_download_updated(struct _cef_download_handler_t* self,
// CONSTRUCTOR - Do not edit by hand. // CONSTRUCTOR - Do not edit by hand.
CefDownloadHandlerCppToC::CefDownloadHandlerCppToC() { CefDownloadHandlerCppToC::CefDownloadHandlerCppToC() {
GetStruct()->can_download = download_handler_can_download;
GetStruct()->on_before_download = download_handler_on_before_download; GetStruct()->on_before_download = download_handler_on_before_download;
GetStruct()->on_download_updated = download_handler_on_download_updated; GetStruct()->on_download_updated = download_handler_on_download_updated;
} }

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=c4e47ffd023b528b9c5b658126f4a1d9fd05cf98$ // $hash=bb3a8a9a02dc3d2e4bdf0e926f61adc05e3af351$
// //
#include "libcef_dll/ctocpp/download_handler_ctocpp.h" #include "libcef_dll/ctocpp/download_handler_ctocpp.h"
@ -21,6 +21,40 @@
// VIRTUAL METHODS - Body may be edited by hand. // VIRTUAL METHODS - Body may be edited by hand.
NO_SANITIZE("cfi-icall")
bool CefDownloadHandlerCToCpp::CanDownload(CefRefPtr<CefBrowser> browser,
const CefString& url,
const CefString& request_method) {
shutdown_checker::AssertNotShutdown();
cef_download_handler_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, can_download))
return false;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return false;
// Verify param: url; type: string_byref_const
DCHECK(!url.empty());
if (url.empty())
return false;
// Verify param: request_method; type: string_byref_const
DCHECK(!request_method.empty());
if (request_method.empty())
return false;
// Execute
int _retval =
_struct->can_download(_struct, CefBrowserCppToC::Wrap(browser),
url.GetStruct(), request_method.GetStruct());
// Return type: bool
return _retval ? true : false;
}
NO_SANITIZE("cfi-icall") NO_SANITIZE("cfi-icall")
void CefDownloadHandlerCToCpp::OnBeforeDownload( void CefDownloadHandlerCToCpp::OnBeforeDownload(
CefRefPtr<CefBrowser> browser, CefRefPtr<CefBrowser> browser,

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=172a12dd9e68b65afff9eef5b93f0e480beaf904$ // $hash=fa32af3253cface693ec3ef420863852f5c68379$
// //
#ifndef CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_HANDLER_CTOCPP_H_ #ifndef CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_HANDLER_CTOCPP_H_
@ -35,6 +35,9 @@ class CefDownloadHandlerCToCpp
virtual ~CefDownloadHandlerCToCpp(); virtual ~CefDownloadHandlerCToCpp();
// CefDownloadHandler methods. // CefDownloadHandler methods.
bool CanDownload(CefRefPtr<CefBrowser> browser,
const CefString& url,
const CefString& request_method) override;
void OnBeforeDownload(CefRefPtr<CefBrowser> browser, void OnBeforeDownload(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item, CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name, const CefString& suggested_name,

View File

@ -13,7 +13,7 @@ index 9e534ff1683f1..de406f5879be0 100644
return false; return false;
} }
diff --git chrome/browser/ui/browser.cc chrome/browser/ui/browser.cc diff --git chrome/browser/ui/browser.cc chrome/browser/ui/browser.cc
index 04e327d970b87..6bd83131116d9 100644 index 04e327d970b87..f79b861fe6966 100644
--- chrome/browser/ui/browser.cc --- chrome/browser/ui/browser.cc
+++ chrome/browser/ui/browser.cc +++ chrome/browser/ui/browser.cc
@@ -262,6 +262,25 @@ @@ -262,6 +262,25 @@
@ -159,7 +159,27 @@ index 04e327d970b87..6bd83131116d9 100644
void Browser::ContentsMouseEvent(WebContents* source, void Browser::ContentsMouseEvent(WebContents* source,
bool motion, bool motion,
bool exited) { bool exited) {
@@ -1807,6 +1883,10 @@ void Browser::WebContentsCreated(WebContents* source_contents, @@ -1715,6 +1791,19 @@ bool Browser::TakeFocus(content::WebContents* source, bool reverse) {
return false;
}
+void Browser::CanDownload(const GURL& url,
+ const std::string& request_method,
+ base::OnceCallback<void(bool)> callback) {
+#if BUILDFLAG(ENABLE_CEF)
+ if (cef_browser_delegate_) {
+ cef_browser_delegate_->CanDownload(url, request_method,
+ std::move(callback));
+ return;
+ }
+#endif
+ std::move(callback).Run(true);
+}
+
void Browser::BeforeUnloadFired(WebContents* web_contents,
bool proceed,
bool* proceed_to_fire_unload) {
@@ -1807,6 +1896,10 @@ void Browser::WebContentsCreated(WebContents* source_contents,
// Make the tab show up in the task manager. // Make the tab show up in the task manager.
task_manager::WebContentsTags::CreateForTabContents(new_contents); task_manager::WebContentsTags::CreateForTabContents(new_contents);
@ -170,7 +190,7 @@ index 04e327d970b87..6bd83131116d9 100644
} }
void Browser::PortalWebContentsCreated(WebContents* portal_web_contents) { void Browser::PortalWebContentsCreated(WebContents* portal_web_contents) {
@@ -1851,6 +1931,8 @@ void Browser::RendererResponsive( @@ -1851,6 +1944,8 @@ void Browser::RendererResponsive(
void Browser::DidNavigatePrimaryMainFramePostCommit(WebContents* web_contents) { void Browser::DidNavigatePrimaryMainFramePostCommit(WebContents* web_contents) {
if (web_contents == tab_strip_model_->GetActiveWebContents()) if (web_contents == tab_strip_model_->GetActiveWebContents())
UpdateBookmarkBarState(BOOKMARK_BAR_STATE_CHANGE_TAB_STATE); UpdateBookmarkBarState(BOOKMARK_BAR_STATE_CHANGE_TAB_STATE);
@ -179,7 +199,7 @@ index 04e327d970b87..6bd83131116d9 100644
} }
content::JavaScriptDialogManager* Browser::GetJavaScriptDialogManager( content::JavaScriptDialogManager* Browser::GetJavaScriptDialogManager(
@@ -1906,11 +1988,15 @@ void Browser::EnterFullscreenModeForTab( @@ -1906,11 +2001,15 @@ void Browser::EnterFullscreenModeForTab(
const blink::mojom::FullscreenOptions& options) { const blink::mojom::FullscreenOptions& options) {
exclusive_access_manager_->fullscreen_controller()->EnterFullscreenModeForTab( exclusive_access_manager_->fullscreen_controller()->EnterFullscreenModeForTab(
requesting_frame, options.display_id); requesting_frame, options.display_id);
@ -195,7 +215,7 @@ index 04e327d970b87..6bd83131116d9 100644
} }
bool Browser::IsFullscreenForTabOrPending(const WebContents* web_contents) { bool Browser::IsFullscreenForTabOrPending(const WebContents* web_contents) {
@@ -2620,13 +2706,20 @@ void Browser::RemoveScheduledUpdatesFor(WebContents* contents) { @@ -2620,13 +2719,20 @@ void Browser::RemoveScheduledUpdatesFor(WebContents* contents) {
// Browser, Getters for UI (private): // Browser, Getters for UI (private):
StatusBubble* Browser::GetStatusBubble() { StatusBubble* Browser::GetStatusBubble() {
@ -217,7 +237,7 @@ index 04e327d970b87..6bd83131116d9 100644
return window_ ? window_->GetStatusBubble() : nullptr; return window_ ? window_->GetStatusBubble() : nullptr;
} }
@@ -2753,6 +2846,8 @@ void Browser::SetAsDelegate(WebContents* web_contents, bool set_delegate) { @@ -2753,6 +2859,8 @@ void Browser::SetAsDelegate(WebContents* web_contents, bool set_delegate) {
content_translate_driver->RemoveTranslationObserver(this); content_translate_driver->RemoveTranslationObserver(this);
BookmarkTabHelper::FromWebContents(web_contents)->RemoveObserver(this); BookmarkTabHelper::FromWebContents(web_contents)->RemoveObserver(this);
} }
@ -227,7 +247,7 @@ index 04e327d970b87..6bd83131116d9 100644
void Browser::TabDetachedAtImpl(content::WebContents* contents, void Browser::TabDetachedAtImpl(content::WebContents* contents,
diff --git chrome/browser/ui/browser.h chrome/browser/ui/browser.h diff --git chrome/browser/ui/browser.h chrome/browser/ui/browser.h
index a63ba24be314e..38a6dcbf034ef 100644 index a63ba24be314e..8eb344f1daeb8 100644
--- chrome/browser/ui/browser.h --- chrome/browser/ui/browser.h
+++ chrome/browser/ui/browser.h +++ chrome/browser/ui/browser.h
@@ -21,6 +21,7 @@ @@ -21,6 +21,7 @@
@ -288,7 +308,7 @@ index a63ba24be314e..38a6dcbf034ef 100644
// Get the FindBarController for this browser, creating it if it does not // Get the FindBarController for this browser, creating it if it does not
// yet exist. // yet exist.
FindBarController* GetFindBarController(); FindBarController* GetFindBarController();
@@ -794,6 +817,11 @@ class Browser : public TabStripModelObserver, @@ -794,11 +817,19 @@ class Browser : public TabStripModelObserver,
void SetContentsBounds(content::WebContents* source, void SetContentsBounds(content::WebContents* source,
const gfx::Rect& bounds) override; const gfx::Rect& bounds) override;
void UpdateTargetURL(content::WebContents* source, const GURL& url) override; void UpdateTargetURL(content::WebContents* source, const GURL& url) override;
@ -300,7 +320,15 @@ index a63ba24be314e..38a6dcbf034ef 100644
void ContentsMouseEvent(content::WebContents* source, void ContentsMouseEvent(content::WebContents* source,
bool motion, bool motion,
bool exited) override; bool exited) override;
@@ -1196,6 +1224,8 @@ class Browser : public TabStripModelObserver, void ContentsZoomChange(bool zoom_in) override;
bool TakeFocus(content::WebContents* source, bool reverse) override;
+ void CanDownload(const GURL& url,
+ const std::string& request_method,
+ base::OnceCallback<void(bool)> callback) override;
void BeforeUnloadFired(content::WebContents* source,
bool proceed,
bool* proceed_to_fire_unload) override;
@@ -1196,6 +1227,8 @@ class Browser : public TabStripModelObserver,
const std::string initial_workspace_; const std::string initial_workspace_;
bool initial_visible_on_all_workspaces_state_; bool initial_visible_on_all_workspaces_state_;
@ -309,7 +337,7 @@ index a63ba24be314e..38a6dcbf034ef 100644
CreationSource creation_source_ = CreationSource::kUnknown; CreationSource creation_source_ = CreationSource::kUnknown;
UnloadController unload_controller_; UnloadController unload_controller_;
@@ -1257,6 +1287,10 @@ class Browser : public TabStripModelObserver, @@ -1257,6 +1290,10 @@ class Browser : public TabStripModelObserver,
extension_browser_window_helper_; extension_browser_window_helper_;
#endif #endif

View File

@ -515,6 +515,21 @@ bool ClientHandler::OnCursorChange(CefRefPtr<CefBrowser> browser,
return mouse_cursor_change_disabled_; return mouse_cursor_change_disabled_;
} }
bool ClientHandler::CanDownload(CefRefPtr<CefBrowser> browser,
const CefString& url,
const CefString& request_method) {
CEF_REQUIRE_UI_THREAD();
if (!with_controls_) {
// Block the download.
LOG(INFO) << "Blocking download";
return false;
}
// Allow the download.
return true;
}
void ClientHandler::OnBeforeDownload( void ClientHandler::OnBeforeDownload(
CefRefPtr<CefBrowser> browser, CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item, CefRefPtr<CefDownloadItem> download_item,

View File

@ -170,6 +170,9 @@ class ClientHandler : public CefClient,
const CefCursorInfo& custom_cursor_info) override; const CefCursorInfo& custom_cursor_info) override;
// CefDownloadHandler methods // CefDownloadHandler methods
bool CanDownload(CefRefPtr<CefBrowser> browser,
const CefString& url,
const CefString& request_method) override;
void OnBeforeDownload(CefRefPtr<CefBrowser> browser, void OnBeforeDownload(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item, CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name, const CefString& suggested_name,

View File

@ -169,11 +169,12 @@ class DownloadSchemeHandlerFactory : public CefSchemeHandlerFactory {
class DownloadTestHandler : public TestHandler { class DownloadTestHandler : public TestHandler {
public: public:
enum TestMode { enum TestMode {
PROGAMMATIC, PROGRAMMATIC,
NAVIGATED, NAVIGATED,
PENDING, PENDING,
CLICKED, CLICKED,
CLICKED_REJECTED, CLICKED_INVALID,
CLICKED_BLOCKED,
}; };
DownloadTestHandler(TestMode test_mode, DownloadTestHandler(TestMode test_mode,
@ -186,7 +187,15 @@ class DownloadTestHandler : public TestHandler {
verified_results_(false) {} verified_results_(false) {}
bool is_clicked() const { bool is_clicked() const {
return test_mode_ == CLICKED || test_mode_ == CLICKED_REJECTED; return test_mode_ == CLICKED || test_mode_ == CLICKED_INVALID ||
test_mode_ == CLICKED_BLOCKED;
}
bool is_clicked_and_downloaded() const { return test_mode_ == CLICKED; }
bool is_downloaded() const {
return test_mode_ == PROGRAMMATIC || test_mode_ == NAVIGATED ||
is_clicked_and_downloaded();
} }
void RunTest() override { void RunTest() override {
@ -212,7 +221,7 @@ class DownloadTestHandler : public TestHandler {
CefRegisterSchemeHandlerFactory("http", kTestDomain, scheme_factory); CefRegisterSchemeHandlerFactory("http", kTestDomain, scheme_factory);
} }
if (test_mode_ != CLICKED_REJECTED) { if (!is_clicked() || is_clicked_and_downloaded()) {
// Create a new temporary directory. // Create a new temporary directory.
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
test_path_ = test_path_ =
@ -226,19 +235,19 @@ class DownloadTestHandler : public TestHandler {
} }
if (is_clicked()) { if (is_clicked()) {
std::string url; if (test_mode_ == CLICKED || test_mode_ == CLICKED_BLOCKED) {
if (test_mode_ == CLICKED) { download_url_ = kTestDownloadUrl;
url = kTestDownloadUrl; } else if (test_mode_ == CLICKED_INVALID) {
} else if (test_mode_ == CLICKED_REJECTED) { download_url_ = "invalid:foo@example.com";
url = "invalid:foo@example.com";
} else { } else {
EXPECT_TRUE(false); // Not reached. EXPECT_TRUE(false); // Not reached.
} }
AddResource( AddResource(kTestStartUrl,
kTestStartUrl, "<html><body><a href=\"" + download_url_ +
"<html><body><a href=\"" + url + "\">CLICK ME</a></body></html>", "\">CLICK ME</a></body></html>",
"text/html"); "text/html");
} else { } else {
download_url_ = kTestStartUrl;
AddResource(kTestStartUrl, "<html><body>Download Test</body></html>", AddResource(kTestStartUrl, "<html><body>Download Test</body></html>",
"text/html"); "text/html");
} }
@ -264,9 +273,9 @@ class DownloadTestHandler : public TestHandler {
// Begin the download by clicking a link. // Begin the download by clicking a link.
// ALT key will trigger download of custom protocol links. // ALT key will trigger download of custom protocol links.
SendClick(browser, SendClick(browser,
test_mode_ == CLICKED_REJECTED ? EVENTFLAG_ALT_DOWN : 0); test_mode_ == CLICKED_INVALID ? EVENTFLAG_ALT_DOWN : 0);
if (test_mode_ == CLICKED_REJECTED) { if (is_clicked() && !is_clicked_and_downloaded()) {
// Destroy the test after a bit because there will be no further // Destroy the test after a bit because there will be no further
// callbacks. // callbacks.
CefPostDelayedTask( CefPostDelayedTask(
@ -316,6 +325,23 @@ class DownloadTestHandler : public TestHandler {
} }
} }
bool CanDownload(CefRefPtr<CefBrowser> browser,
const CefString& url,
const CefString& request_method) override {
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
EXPECT_FALSE(got_can_download_);
EXPECT_FALSE(got_on_before_download_);
EXPECT_TRUE(is_clicked());
got_can_download_.yes();
EXPECT_TRUE(browser->IsSame(GetBrowser()));
EXPECT_STREQ(download_url_.c_str(), url.ToString().c_str());
EXPECT_STREQ("GET", request_method.ToString().c_str());
return test_mode_ != CLICKED_BLOCKED;
}
void OnBeforeDownload( void OnBeforeDownload(
CefRefPtr<CefBrowser> browser, CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item, CefRefPtr<CefDownloadItem> download_item,
@ -324,6 +350,12 @@ class DownloadTestHandler : public TestHandler {
EXPECT_TRUE(CefCurrentlyOn(TID_UI)); EXPECT_TRUE(CefCurrentlyOn(TID_UI));
EXPECT_FALSE(got_on_before_download_); EXPECT_FALSE(got_on_before_download_);
if (is_clicked()) {
EXPECT_TRUE(got_can_download_);
} else {
EXPECT_FALSE(got_can_download_);
}
got_on_before_download_.yes(); got_on_before_download_.yes();
EXPECT_TRUE(browser->IsSame(GetBrowser())); EXPECT_TRUE(browser->IsSame(GetBrowser()));
@ -460,12 +492,24 @@ class DownloadTestHandler : public TestHandler {
CefRegisterSchemeHandlerFactory("http", kTestDomain, nullptr); CefRegisterSchemeHandlerFactory("http", kTestDomain, nullptr);
} }
if (test_mode_ == CLICKED_REJECTED) { if (is_clicked()) {
EXPECT_TRUE(got_can_download_);
} else {
EXPECT_FALSE(got_can_download_);
}
if (test_mode_ == CLICKED_INVALID) {
// The invalid protocol request is not handled.
EXPECT_FALSE(got_download_request_); EXPECT_FALSE(got_download_request_);
} else {
EXPECT_TRUE(got_download_request_);
}
if (is_clicked() && !is_clicked_and_downloaded()) {
// The download never proceeds.
EXPECT_FALSE(got_on_before_download_); EXPECT_FALSE(got_on_before_download_);
EXPECT_FALSE(got_on_download_updated_); EXPECT_FALSE(got_on_download_updated_);
} else { } else {
EXPECT_TRUE(got_download_request_);
EXPECT_TRUE(got_on_before_download_); EXPECT_TRUE(got_on_before_download_);
EXPECT_TRUE(got_on_download_updated_); EXPECT_TRUE(got_on_download_updated_);
} }
@ -475,7 +519,8 @@ class DownloadTestHandler : public TestHandler {
else else
EXPECT_FALSE(got_nav_load_); EXPECT_FALSE(got_nav_load_);
if (test_mode_ == PENDING || test_mode_ == CLICKED_REJECTED) { if (!is_downloaded()) {
// The download never completes.
EXPECT_FALSE(got_download_complete_); EXPECT_FALSE(got_download_complete_);
EXPECT_FALSE(got_full_path_); EXPECT_FALSE(got_full_path_);
} else { } else {
@ -519,6 +564,7 @@ class DownloadTestHandler : public TestHandler {
// Used with PENDING test mode. // Used with PENDING test mode.
CefRefPtr<CefDownloadItemCallback> download_item_callback_; CefRefPtr<CefDownloadItemCallback> download_item_callback_;
std::string download_url_;
CefScopedTempDir temp_dir_; CefScopedTempDir temp_dir_;
std::string test_path_; std::string test_path_;
uint32 download_id_; uint32 download_id_;
@ -526,6 +572,7 @@ class DownloadTestHandler : public TestHandler {
bool destroyed_ = false; bool destroyed_ = false;
TrackCallback got_download_request_; TrackCallback got_download_request_;
TrackCallback got_can_download_;
TrackCallback got_on_before_download_; TrackCallback got_on_before_download_;
TrackCallback got_on_download_updated_; TrackCallback got_on_download_updated_;
TrackCallback got_full_path_; TrackCallback got_full_path_;
@ -542,14 +589,18 @@ class DownloadTestHandler : public TestHandler {
RC_TEST_GROUP_ALL(DownloadTest, test_name, DownloadTestHandler, test_mode) RC_TEST_GROUP_ALL(DownloadTest, test_name, DownloadTestHandler, test_mode)
// Test a programmatic download. // Test a programmatic download.
DOWNLOAD_TEST_GROUP(Programmatic, PROGAMMATIC) DOWNLOAD_TEST_GROUP(Programmatic, PROGRAMMATIC)
// Test a clicked download. // Test a clicked download.
DOWNLOAD_TEST_GROUP(Clicked, CLICKED) DOWNLOAD_TEST_GROUP(Clicked, CLICKED)
// Test a clicked download where the protocol is invalid and therefore rejected. // Test a clicked download where the protocol is invalid and therefore rejected.
// There will be no resulting CefDownloadHandler callbacks. // There will be no resulting CefDownloadHandler callbacks.
DOWNLOAD_TEST_GROUP(ClickedRejected, CLICKED_REJECTED) DOWNLOAD_TEST_GROUP(ClickedInvalid, CLICKED_INVALID)
// Test a clicked download where CanDownload returns false.
// There will be no resulting CefDownloadHandler callbacks.
DOWNLOAD_TEST_GROUP(ClickedBlocked, CLICKED_BLOCKED)
// Test where the download completes after cross-origin navigation. // Test where the download completes after cross-origin navigation.
DOWNLOAD_TEST_GROUP(Navigated, NAVIGATED) DOWNLOAD_TEST_GROUP(Navigated, NAVIGATED)