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
// 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

View File

@ -42,13 +42,13 @@
// way that may cause binary incompatibility with other builds. The universal
// hash value will change if any platform is affected whereas the platform hash
// values will change only if that particular platform is affected.
#define CEF_API_HASH_UNIVERSAL "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

View File

@ -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<CefBrowser> 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

View File

@ -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<void(bool)> callback) {
contents_delegate_->CanDownload(url, request_method, std::move(callback));
}
KeyboardEventProcessingResult AlloyBrowserHostImpl::PreHandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {

View File

@ -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<void(bool)> callback) override;
content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override;

View File

@ -180,6 +180,23 @@ void CefBrowserContentsDelegate::ExitFullscreenModeForTab(
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
CefBrowserContentsDelegate::PreHandleKeyboardEvent(
content::WebContents* source,

View File

@ -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<void(bool)> callback) override;
content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source,
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(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {

View File

@ -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<void(bool)> callback) override;
content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override;

View File

@ -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;
}

View File

@ -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<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")
void CefDownloadHandlerCToCpp::OnBeforeDownload(
CefRefPtr<CefBrowser> browser,

View File

@ -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<CefBrowser> browser,
const CefString& url,
const CefString& request_method) override;
void OnBeforeDownload(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,

View File

@ -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<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.
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<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_;
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

View File

@ -515,6 +515,21 @@ bool ClientHandler::OnCursorChange(CefRefPtr<CefBrowser> browser,
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(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,

View File

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

View File

@ -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,
"<html><body><a href=\"" + url + "\">CLICK ME</a></body></html>",
AddResource(kTestStartUrl,
"<html><body><a href=\"" + download_url_ +
"\">CLICK ME</a></body></html>",
"text/html");
} else {
download_url_ = kTestStartUrl;
AddResource(kTestStartUrl, "<html><body>Download Test</body></html>",
"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<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(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> 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<CefDownloadItemCallback> 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)