diff --git a/include/capi/cef_download_handler_capi.h b/include/capi/cef_download_handler_capi.h index 29b058710..aa690f0f0 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=ec13ec3e2819e4ac49792c3a1c57bc60b45fb95b$ +// $hash=33862915eb4156a70794a346a090154c468763e2$ // #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; + /// + // 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 // the download file. By default the download will be canceled. Execute diff --git a/include/cef_api_hash.h b/include/cef_api_hash.h index 78786363e..b60d4051b 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 "099de505181a68d5b53885e57d91ae0276259bfa" +#define CEF_API_HASH_UNIVERSAL "e42afcebc271841716a6f3197194e3ea02d037cb" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "0887a2a4ef42c8e2e33aba6662d2891f020e810a" +#define CEF_API_HASH_PLATFORM "e08d5cc79cedebc94efc39536c27b6c30d24c9df" #elif defined(OS_MAC) -#define CEF_API_HASH_PLATFORM "3d220485ac3df3a74e15db819ea16c98f78116ae" +#define CEF_API_HASH_PLATFORM "86626c6c27b29841a4bf01c145e2d7990a73abf0" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "ee08ceb06c69dac1ac16361daabe3378c9d2d621" +#define CEF_API_HASH_PLATFORM "a19b7983d07d0e6afe0ae46c1768111078995c5e" #endif #ifdef __cplusplus diff --git a/include/cef_download_handler.h b/include/cef_download_handler.h index dfaeba500..357954919 100644 --- a/include/cef_download_handler.h +++ b/include/cef_download_handler.h @@ -90,6 +90,20 @@ class CefDownloadItemCallback : public virtual CefBaseRefCounted { /*--cef(source=client)--*/ class CefDownloadHandler : public virtual CefBaseRefCounted { 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 browser, + const CefString& url, + const CefString& request_method) { + return true; + } + /// // Called before a download begins. |suggested_name| is the suggested name for // the download file. By default the download will be canceled. Execute diff --git a/libcef/browser/alloy/alloy_browser_host_impl.cc b/libcef/browser/alloy/alloy_browser_host_impl.cc index 10a68e6dc..c1de34737 100644 --- a/libcef/browser/alloy/alloy_browser_host_impl.cc +++ b/libcef/browser/alloy/alloy_browser_host_impl.cc @@ -1225,6 +1225,13 @@ bool AlloyBrowserHostImpl::HandleContextMenu( return HandleContextMenu(web_contents(), params); } +void AlloyBrowserHostImpl::CanDownload( + const GURL& url, + const std::string& request_method, + base::OnceCallback callback) { + contents_delegate_->CanDownload(url, request_method, std::move(callback)); +} + KeyboardEventProcessingResult AlloyBrowserHostImpl::PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { diff --git a/libcef/browser/alloy/alloy_browser_host_impl.h b/libcef/browser/alloy/alloy_browser_host_impl.h index ebd536792..dcc0d14a7 100644 --- a/libcef/browser/alloy/alloy_browser_host_impl.h +++ b/libcef/browser/alloy/alloy_browser_host_impl.h @@ -225,6 +225,9 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase, bool TakeFocus(content::WebContents* source, bool reverse) override; bool HandleContextMenu(content::RenderFrameHost& render_frame_host, const content::ContextMenuParams& params) override; + void CanDownload(const GURL& url, + const std::string& request_method, + base::OnceCallback callback) override; content::KeyboardEventProcessingResult PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) override; diff --git a/libcef/browser/browser_contents_delegate.cc b/libcef/browser/browser_contents_delegate.cc index cb15ad31f..47780c093 100644 --- a/libcef/browser/browser_contents_delegate.cc +++ b/libcef/browser/browser_contents_delegate.cc @@ -180,6 +180,23 @@ void CefBrowserContentsDelegate::ExitFullscreenModeForTab( OnFullscreenModeChange(/*fullscreen=*/false); } +void CefBrowserContentsDelegate::CanDownload( + const GURL& url, + const std::string& request_method, + base::OnceCallback 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 CefBrowserContentsDelegate::PreHandleKeyboardEvent( content::WebContents* source, diff --git a/libcef/browser/browser_contents_delegate.h b/libcef/browser/browser_contents_delegate.h index 9a11e9ce5..862386123 100644 --- a/libcef/browser/browser_contents_delegate.h +++ b/libcef/browser/browser_contents_delegate.h @@ -101,6 +101,9 @@ class CefBrowserContentsDelegate : public content::WebContentsDelegate, content::RenderFrameHost* requesting_frame, const blink::mojom::FullscreenOptions& options) override; void ExitFullscreenModeForTab(content::WebContents* web_contents) override; + void CanDownload(const GURL& url, + const std::string& request_method, + base::OnceCallback callback) override; content::KeyboardEventProcessingResult PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) override; diff --git a/libcef/browser/chrome/chrome_browser_delegate.cc b/libcef/browser/chrome/chrome_browser_delegate.cc index 2b60ba3b4..87a99e4ef 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.cc +++ b/libcef/browser/chrome/chrome_browser_delegate.cc @@ -235,6 +235,20 @@ void ChromeBrowserDelegate::ExitFullscreenModeForTab( } } +void ChromeBrowserDelegate::CanDownload( + const GURL& url, + const std::string& request_method, + base::OnceCallback 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( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { diff --git a/libcef/browser/chrome/chrome_browser_delegate.h b/libcef/browser/chrome/chrome_browser_delegate.h index 560ad01ae..0f9d45156 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.h +++ b/libcef/browser/chrome/chrome_browser_delegate.h @@ -86,6 +86,9 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate { content::RenderFrameHost* requesting_frame, const blink::mojom::FullscreenOptions& options) override; void ExitFullscreenModeForTab(content::WebContents* web_contents) override; + void CanDownload(const GURL& url, + const std::string& request_method, + base::OnceCallback callback) override; content::KeyboardEventProcessingResult PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) override; diff --git a/libcef_dll/cpptoc/download_handler_cpptoc.cc b/libcef_dll/cpptoc/download_handler_cpptoc.cc index 8c2aa1f20..6759bfd90 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=496b226297ba7d5fa5e7e7bd4117c417e26fae59$ +// $hash=89f651178065dbc03e70e763085bf9125dda6640$ // #include "libcef_dll/cpptoc/download_handler_cpptoc.h" @@ -23,6 +23,40 @@ namespace { // 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 download_handler_on_before_download(struct _cef_download_handler_t* self, 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. CefDownloadHandlerCppToC::CefDownloadHandlerCppToC() { + GetStruct()->can_download = download_handler_can_download; GetStruct()->on_before_download = download_handler_on_before_download; GetStruct()->on_download_updated = download_handler_on_download_updated; } diff --git a/libcef_dll/ctocpp/download_handler_ctocpp.cc b/libcef_dll/ctocpp/download_handler_ctocpp.cc index 981d59333..cc3bec621 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=c4e47ffd023b528b9c5b658126f4a1d9fd05cf98$ +// $hash=bb3a8a9a02dc3d2e4bdf0e926f61adc05e3af351$ // #include "libcef_dll/ctocpp/download_handler_ctocpp.h" @@ -21,6 +21,40 @@ // VIRTUAL METHODS - Body may be edited by hand. +NO_SANITIZE("cfi-icall") +bool CefDownloadHandlerCToCpp::CanDownload(CefRefPtr 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") void CefDownloadHandlerCToCpp::OnBeforeDownload( CefRefPtr browser, diff --git a/libcef_dll/ctocpp/download_handler_ctocpp.h b/libcef_dll/ctocpp/download_handler_ctocpp.h index a74b923cf..a14250680 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=172a12dd9e68b65afff9eef5b93f0e480beaf904$ +// $hash=fa32af3253cface693ec3ef420863852f5c68379$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_HANDLER_CTOCPP_H_ @@ -35,6 +35,9 @@ class CefDownloadHandlerCToCpp virtual ~CefDownloadHandlerCToCpp(); // CefDownloadHandler methods. + bool CanDownload(CefRefPtr browser, + const CefString& url, + const CefString& request_method) override; void OnBeforeDownload(CefRefPtr browser, CefRefPtr download_item, const CefString& suggested_name, diff --git a/patch/patches/chrome_browser_browser.patch b/patch/patches/chrome_browser_browser.patch index 03624a143..b1de19d6e 100644 --- a/patch/patches/chrome_browser_browser.patch +++ b/patch/patches/chrome_browser_browser.patch @@ -13,7 +13,7 @@ index 9e534ff1683f1..de406f5879be0 100644 return false; } 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 @@ -262,6 +262,25 @@ @@ -159,7 +159,27 @@ index 04e327d970b87..6bd83131116d9 100644 void Browser::ContentsMouseEvent(WebContents* source, bool motion, 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 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. task_manager::WebContentsTags::CreateForTabContents(new_contents); @@ -170,7 +190,7 @@ index 04e327d970b87..6bd83131116d9 100644 } 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) { if (web_contents == tab_strip_model_->GetActiveWebContents()) UpdateBookmarkBarState(BOOKMARK_BAR_STATE_CHANGE_TAB_STATE); @@ -179,7 +199,7 @@ index 04e327d970b87..6bd83131116d9 100644 } content::JavaScriptDialogManager* Browser::GetJavaScriptDialogManager( -@@ -1906,11 +1988,15 @@ void Browser::EnterFullscreenModeForTab( +@@ -1906,11 +2001,15 @@ void Browser::EnterFullscreenModeForTab( const blink::mojom::FullscreenOptions& options) { exclusive_access_manager_->fullscreen_controller()->EnterFullscreenModeForTab( requesting_frame, options.display_id); @@ -195,7 +215,7 @@ index 04e327d970b87..6bd83131116d9 100644 } 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): StatusBubble* Browser::GetStatusBubble() { @@ -217,7 +237,7 @@ index 04e327d970b87..6bd83131116d9 100644 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); BookmarkTabHelper::FromWebContents(web_contents)->RemoveObserver(this); } @@ -227,7 +247,7 @@ index 04e327d970b87..6bd83131116d9 100644 void Browser::TabDetachedAtImpl(content::WebContents* contents, 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 @@ -21,6 +21,7 @@ @@ -288,7 +308,7 @@ index a63ba24be314e..38a6dcbf034ef 100644 // Get the FindBarController for this browser, creating it if it does not // yet exist. FindBarController* GetFindBarController(); -@@ -794,6 +817,11 @@ class Browser : public TabStripModelObserver, +@@ -794,11 +817,19 @@ class Browser : public TabStripModelObserver, void SetContentsBounds(content::WebContents* source, const gfx::Rect& bounds) override; void UpdateTargetURL(content::WebContents* source, const GURL& url) override; @@ -300,7 +320,15 @@ index a63ba24be314e..38a6dcbf034ef 100644 void ContentsMouseEvent(content::WebContents* source, bool motion, 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 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_; bool initial_visible_on_all_workspaces_state_; @@ -309,7 +337,7 @@ index a63ba24be314e..38a6dcbf034ef 100644 CreationSource creation_source_ = CreationSource::kUnknown; UnloadController unload_controller_; -@@ -1257,6 +1287,10 @@ class Browser : public TabStripModelObserver, +@@ -1257,6 +1290,10 @@ class Browser : public TabStripModelObserver, extension_browser_window_helper_; #endif diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index c46d187ef..e253bddc2 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -515,6 +515,21 @@ bool ClientHandler::OnCursorChange(CefRefPtr browser, return mouse_cursor_change_disabled_; } +bool ClientHandler::CanDownload(CefRefPtr 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( CefRefPtr browser, CefRefPtr download_item, diff --git a/tests/cefclient/browser/client_handler.h b/tests/cefclient/browser/client_handler.h index 00e3b3f26..e58b01786 100644 --- a/tests/cefclient/browser/client_handler.h +++ b/tests/cefclient/browser/client_handler.h @@ -170,6 +170,9 @@ class ClientHandler : public CefClient, const CefCursorInfo& custom_cursor_info) override; // CefDownloadHandler methods + bool CanDownload(CefRefPtr browser, + const CefString& url, + const CefString& request_method) override; void OnBeforeDownload(CefRefPtr browser, CefRefPtr download_item, const CefString& suggested_name, diff --git a/tests/ceftests/download_unittest.cc b/tests/ceftests/download_unittest.cc index 7802e2a54..842ae5598 100644 --- a/tests/ceftests/download_unittest.cc +++ b/tests/ceftests/download_unittest.cc @@ -169,11 +169,12 @@ class DownloadSchemeHandlerFactory : public CefSchemeHandlerFactory { class DownloadTestHandler : public TestHandler { public: enum TestMode { - PROGAMMATIC, + PROGRAMMATIC, NAVIGATED, PENDING, CLICKED, - CLICKED_REJECTED, + CLICKED_INVALID, + CLICKED_BLOCKED, }; DownloadTestHandler(TestMode test_mode, @@ -186,7 +187,15 @@ class DownloadTestHandler : public TestHandler { verified_results_(false) {} 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 { @@ -212,7 +221,7 @@ class DownloadTestHandler : public TestHandler { CefRegisterSchemeHandlerFactory("http", kTestDomain, scheme_factory); } - if (test_mode_ != CLICKED_REJECTED) { + if (!is_clicked() || is_clicked_and_downloaded()) { // Create a new temporary directory. EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); test_path_ = @@ -226,19 +235,19 @@ class DownloadTestHandler : public TestHandler { } if (is_clicked()) { - std::string url; - if (test_mode_ == CLICKED) { - url = kTestDownloadUrl; - } else if (test_mode_ == CLICKED_REJECTED) { - url = "invalid:foo@example.com"; + if (test_mode_ == CLICKED || test_mode_ == CLICKED_BLOCKED) { + download_url_ = kTestDownloadUrl; + } else if (test_mode_ == CLICKED_INVALID) { + download_url_ = "invalid:foo@example.com"; } else { EXPECT_TRUE(false); // Not reached. } - AddResource( - kTestStartUrl, - "CLICK ME", - "text/html"); + AddResource(kTestStartUrl, + "CLICK ME", + "text/html"); } else { + download_url_ = kTestStartUrl; AddResource(kTestStartUrl, "Download Test", "text/html"); } @@ -264,9 +273,9 @@ class DownloadTestHandler : public TestHandler { // Begin the download by clicking a link. // ALT key will trigger download of custom protocol links. 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 // callbacks. CefPostDelayedTask( @@ -316,6 +325,23 @@ class DownloadTestHandler : public TestHandler { } } + bool CanDownload(CefRefPtr 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( CefRefPtr browser, CefRefPtr download_item, @@ -324,6 +350,12 @@ class DownloadTestHandler : public TestHandler { EXPECT_TRUE(CefCurrentlyOn(TID_UI)); 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(); EXPECT_TRUE(browser->IsSame(GetBrowser())); @@ -460,12 +492,24 @@ class DownloadTestHandler : public TestHandler { 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_); + } 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_download_updated_); } else { - EXPECT_TRUE(got_download_request_); EXPECT_TRUE(got_on_before_download_); EXPECT_TRUE(got_on_download_updated_); } @@ -475,7 +519,8 @@ class DownloadTestHandler : public TestHandler { else 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_full_path_); } else { @@ -519,6 +564,7 @@ class DownloadTestHandler : public TestHandler { // Used with PENDING test mode. CefRefPtr download_item_callback_; + std::string download_url_; CefScopedTempDir temp_dir_; std::string test_path_; uint32 download_id_; @@ -526,6 +572,7 @@ class DownloadTestHandler : public TestHandler { bool destroyed_ = false; TrackCallback got_download_request_; + TrackCallback got_can_download_; TrackCallback got_on_before_download_; TrackCallback got_on_download_updated_; TrackCallback got_full_path_; @@ -542,14 +589,18 @@ class DownloadTestHandler : public TestHandler { RC_TEST_GROUP_ALL(DownloadTest, test_name, DownloadTestHandler, test_mode) // Test a programmatic download. -DOWNLOAD_TEST_GROUP(Programmatic, PROGAMMATIC) +DOWNLOAD_TEST_GROUP(Programmatic, PROGRAMMATIC) // Test a clicked download. DOWNLOAD_TEST_GROUP(Clicked, CLICKED) // Test a clicked download where the protocol is invalid and therefore rejected. // 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. DOWNLOAD_TEST_GROUP(Navigated, NAVIGATED)