Fix crash when ALT+Clicking invalid protocol link (issue #2306)

This commit is contained in:
Marshall Greenblatt 2017-12-22 16:05:32 -05:00
parent f052e282dd
commit 54a015f785
6 changed files with 326 additions and 104 deletions

View File

@ -126,10 +126,11 @@ bool CefBrowserContextProxy::IsOffTheRecord() const {
content::DownloadManagerDelegate*
CefBrowserContextProxy::GetDownloadManagerDelegate() {
DCHECK(!download_manager_delegate_.get());
content::DownloadManager* manager = BrowserContext::GetDownloadManager(this);
download_manager_delegate_.reset(new CefDownloadManagerDelegate(manager));
if (!download_manager_delegate_) {
content::DownloadManager* manager =
BrowserContext::GetDownloadManager(this);
download_manager_delegate_.reset(new CefDownloadManagerDelegate(manager));
}
return download_manager_delegate_.get();
}

View File

@ -315,13 +315,28 @@ void CefDownloadManagerDelegate::OnDownloadDestroyed(DownloadItem* item) {
void CefDownloadManagerDelegate::OnDownloadCreated(DownloadManager* manager,
DownloadItem* item) {
item->AddObserver(this);
CefBrowserHostImpl* browser = nullptr;
content::WebContents* contents = item->GetWebContents();
if (contents)
if (contents) {
browser = CefBrowserHostImpl::GetBrowserForContents(contents).get();
DCHECK(browser);
DCHECK(browser);
}
if (!browser) {
// If the download is rejected (e.g. ALT+click on an invalid protocol link)
// then an "interrupted" download will be started via DownloadManagerImpl::
// StartDownloadWithId (originating from CreateInterruptedDownload) with no
// associated WebContents and consequently no associated CEF browser. In
// that case DetermineDownloadTarget will be called before this method.
// TODO(cef): Figure out how to expose this via a client callback.
const std::vector<GURL>& url_chain = item->GetUrlChain();
if (!url_chain.empty()) {
LOG(INFO) << "Rejected download of " << url_chain.back().spec();
}
item->Cancel(true);
return;
}
item->AddObserver(this);
item_browser_map_.insert(std::make_pair(item, browser));
@ -399,7 +414,11 @@ CefBrowserHostImpl* CefDownloadManagerDelegate::GetBrowser(DownloadItem* item) {
if (it != item_browser_map_.end())
return it->second;
// An entry should always exist for a DownloadItem.
NOTREACHED();
// 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(!item->GetWebContents());
return nullptr;
}

View File

@ -6,13 +6,14 @@
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_scoped_temp_dir.h"
#include "tests/ceftests/test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/browser/file_util.h"
namespace {
const char kTestDomain[] = "test-download.com";
const char kTestEntryUrl[] = "http://test-download.com/test.html";
const char kTestStartUrl[] = "http://test-download.com/test.html";
const char kTestDownloadUrl[] = "http://test-download.com/download.txt";
const char kTestNavUrl[] = "http://test-download-nav.com/nav.html";
const char kTestFileName[] = "download_test.txt";
@ -35,10 +36,7 @@ class DownloadSchemeHandler : public CefResourceHandler {
bool ProcessRequest(CefRefPtr<CefRequest> request,
CefRefPtr<CefCallback> callback) override {
std::string url = request->GetURL();
if (url == kTestEntryUrl) {
content_ = "<html><body>Download Test</body></html>";
mime_type_ = "text/html";
} else if (url == kTestDownloadUrl) {
if (url == kTestDownloadUrl) {
got_download_request_->yes();
content_ = kTestContent;
mime_type_ = kTestMimeType;
@ -136,27 +134,48 @@ class DownloadSchemeHandlerFactory : public CefSchemeHandlerFactory {
class DownloadTestHandler : public TestHandler {
public:
enum TestMode {
NORMAL,
PROGAMMATIC,
NAVIGATED,
PENDING,
CLICKED,
CLICKED_REJECTED,
};
DownloadTestHandler(TestMode test_mode) : test_mode_(test_mode) {}
DownloadTestHandler(TestMode test_mode,
TestRequestContextMode rc_mode,
const std::string& rc_cache_path)
: test_mode_(test_mode),
rc_mode_(rc_mode),
rc_cache_path_(rc_cache_path) {}
bool is_clicked() const {
return test_mode_ == CLICKED || test_mode_ == CLICKED_REJECTED;
}
void RunTest() override {
DelayCallback delay_callback;
if (test_mode_ == NAVIGATED || test_mode_ == PENDING)
delay_callback = base::Bind(&DownloadTestHandler::OnDelayCallback, this);
CefRegisterSchemeHandlerFactory(
"http", kTestDomain,
CefRefPtr<CefSchemeHandlerFactory> scheme_factory =
new DownloadSchemeHandlerFactory(delay_callback,
&got_download_request_));
&got_download_request_);
// Create a new temporary directory.
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
test_path_ =
client::file_util::JoinPath(temp_dir_.GetPath(), kTestFileName);
CefRefPtr<CefRequestContext> request_context =
CreateTestRequestContext(rc_mode_, rc_cache_path_);
if (request_context) {
request_context->RegisterSchemeHandlerFactory("http", kTestDomain,
scheme_factory);
} else {
CefRegisterSchemeHandlerFactory("http", kTestDomain, scheme_factory);
}
if (test_mode_ != CLICKED_REJECTED) {
// Create a new temporary directory.
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
test_path_ =
client::file_util::JoinPath(temp_dir_.GetPath(), kTestFileName);
}
if (test_mode_ == NAVIGATED) {
// Add the resource that we'll navigate to.
@ -164,8 +183,26 @@ class DownloadTestHandler : public TestHandler {
"text/html");
}
if (is_clicked()) {
std::string url;
if (test_mode_ == CLICKED) {
url = kTestDownloadUrl;
} else if (test_mode_ == CLICKED_REJECTED) {
url = "invalid:foo@example.com";
} else {
EXPECT_TRUE(false); // Not reached.
}
AddResource(
kTestStartUrl,
"<html><body><a href=\"" + url + "\">CLICK ME</a></body></html>",
"text/html");
} else {
AddResource(kTestStartUrl, "<html><body>Download Test</body></html>",
"text/html");
}
// Create the browser
CreateBrowser(kTestEntryUrl);
CreateBrowser(kTestStartUrl, request_context);
// Time out the test after a reasonable period of time.
SetTestTimeout();
@ -175,12 +212,27 @@ class DownloadTestHandler : public TestHandler {
CefRefPtr<CefFrame> frame,
int httpStatusCode) override {
const std::string& url = frame->GetURL().ToString();
if (url == kTestEntryUrl) {
// Begin the download.
browser->GetHost()->StartDownload(kTestDownloadUrl);
} else if (url == kTestNavUrl) {
if (url == kTestNavUrl) {
got_nav_load_.yes();
ContinueNavigatedIfReady();
return;
}
if (is_clicked()) {
// 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);
if (test_mode_ == CLICKED_REJECTED) {
// Destroy the test after a bit because there will be no further
// callbacks.
CefPostDelayedTask(
TID_UI, base::Bind(&DownloadTestHandler::DestroyTest, this), 200);
}
} else {
// Begin the download progammatically.
browser->GetHost()->StartDownload(kTestDownloadUrl);
}
}
@ -205,7 +257,7 @@ class DownloadTestHandler : public TestHandler {
}
void ContinueNavigatedIfReady() {
DCHECK_EQ(test_mode_, NAVIGATED);
EXPECT_EQ(test_mode_, NAVIGATED);
if (got_delay_callback_ && got_nav_load_) {
delay_callback_->Continue();
delay_callback_ = nullptr;
@ -213,7 +265,7 @@ class DownloadTestHandler : public TestHandler {
}
void ContinuePendingIfReady() {
DCHECK_EQ(test_mode_, PENDING);
EXPECT_EQ(test_mode_, PENDING);
if (got_delay_callback_ && got_on_before_download_ &&
got_on_download_updated_) {
// Destroy the test without waiting for the download to complete.
@ -338,18 +390,30 @@ class DownloadTestHandler : public TestHandler {
return;
}
CefRegisterSchemeHandlerFactory("http", kTestDomain, NULL);
if (request_context_) {
request_context_->RegisterSchemeHandlerFactory("http", kTestDomain,
nullptr);
request_context_ = nullptr;
} else {
CefRegisterSchemeHandlerFactory("http", kTestDomain, nullptr);
}
EXPECT_TRUE(got_download_request_);
EXPECT_TRUE(got_on_before_download_);
EXPECT_TRUE(got_on_download_updated_);
if (test_mode_ == CLICKED_REJECTED) {
EXPECT_FALSE(got_download_request_);
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_);
}
if (test_mode_ == NAVIGATED)
EXPECT_TRUE(got_nav_load_);
else
EXPECT_FALSE(got_nav_load_);
if (test_mode_ == PENDING) {
if (test_mode_ == PENDING || test_mode_ == CLICKED_REJECTED) {
EXPECT_FALSE(got_download_complete_);
EXPECT_FALSE(got_full_path_);
} else {
@ -361,7 +425,21 @@ class DownloadTestHandler : public TestHandler {
}
private:
void SendClick(CefRefPtr<CefBrowser> browser, uint32_t modifiers) {
EXPECT_TRUE(is_clicked());
CefMouseEvent mouse_event;
mouse_event.x = 20;
mouse_event.y = 20;
mouse_event.modifiers = modifiers;
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1);
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1);
}
const TestMode test_mode_;
const TestRequestContextMode rc_mode_;
const std::string rc_cache_path_;
CefRefPtr<CefRequestContext> request_context_;
// Used with NAVIGATED test mode.
CefRefPtr<CefCallback> delay_callback_;
@ -383,26 +461,21 @@ class DownloadTestHandler : public TestHandler {
} // namespace
// Test a basic download.
TEST(DownloadTest, Download) {
CefRefPtr<DownloadTestHandler> handler =
new DownloadTestHandler(DownloadTestHandler::NORMAL);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
#define DOWNLOAD_TEST_GROUP(test_name, test_mode) \
RC_TEST_GROUP_ALL(DownloadTest, test_name, DownloadTestHandler, test_mode)
// Test a programmatic download.
DOWNLOAD_TEST_GROUP(Programmatic, PROGAMMATIC);
// 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);
// Test where the download completes after cross-origin navigation.
TEST(DownloadTest, DownloadNavigated) {
CefRefPtr<DownloadTestHandler> handler =
new DownloadTestHandler(DownloadTestHandler::NAVIGATED);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
DOWNLOAD_TEST_GROUP(Navigated, NAVIGATED);
// Test where the download is still pending when the browser is destroyed.
TEST(DownloadTest, DownloadPending) {
CefRefPtr<DownloadTestHandler> handler =
new DownloadTestHandler(DownloadTestHandler::PENDING);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
DOWNLOAD_TEST_GROUP(Pending, PENDING);

View File

@ -8,6 +8,7 @@
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_scoped_temp_dir.h"
#include "tests/ceftests/test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h"
TEST(RequestContextTest, GetGlobalContext) {
@ -608,7 +609,7 @@ const char kPopupNavPopupName[] = "my_popup";
// Browser side.
class PopupNavTestHandler : public TestHandler {
public:
enum Mode {
enum TestMode {
ALLOW_CLOSE_POPUP_FIRST,
ALLOW_CLOSE_POPUP_LAST,
DENY,
@ -620,14 +621,11 @@ class PopupNavTestHandler : public TestHandler {
DESTROY_PARENT_AFTER_CREATION,
DESTROY_PARENT_AFTER_CREATION_FORCE,
};
enum RCMode {
RC_MODE_NONE,
RC_MODE_IMPL,
RC_MODE_PROXY,
};
PopupNavTestHandler(Mode mode, RCMode rc_mode)
: mode_(mode), rc_mode_(rc_mode) {}
PopupNavTestHandler(TestMode test_mode,
TestRequestContextMode rc_mode,
const std::string& rc_cache_path)
: mode_(test_mode), rc_mode_(rc_mode), rc_cache_path_(rc_cache_path) {}
void RunTest() override {
// Add the resources that we will navigate to/from.
@ -640,23 +638,8 @@ class PopupNavTestHandler : public TestHandler {
if (mode_ == NAVIGATE_AFTER_CREATION)
AddResource(kPopupNavPopupUrl2, "<html>Popup2</html>", "text/html");
CefRefPtr<CefRequestContext> request_context;
CefRefPtr<CefRequestContextHandler> rc_handler;
if (rc_mode_ == RC_MODE_PROXY) {
class Handler : public CefRequestContextHandler {
public:
Handler() {}
private:
IMPLEMENT_REFCOUNTING(Handler);
};
rc_handler = new Handler();
}
if (rc_mode_ != RC_MODE_NONE) {
CefRequestContextSettings settings;
request_context = CefRequestContext::CreateContext(settings, rc_handler);
}
CefRefPtr<CefRequestContext> request_context =
CreateTestRequestContext(rc_mode_, rc_cache_path_);
// Create the browser.
CreateBrowser(kPopupNavPageUrl, request_context);
@ -872,8 +855,9 @@ class PopupNavTestHandler : public TestHandler {
TestHandler::DestroyTest();
}
const Mode mode_;
const RCMode rc_mode_;
const TestMode mode_;
const TestRequestContextMode rc_mode_;
const std::string rc_cache_path_;
TrackCallback got_on_before_popup_;
TrackCallback got_load_start_;
@ -890,36 +874,28 @@ class PopupNavTestHandler : public TestHandler {
};
} // namespace
#define POPUP_TEST(name, test_mode, rc_mode) \
TEST(RequestContextTest, Popup##name) { \
CefRefPtr<PopupNavTestHandler> handler = new PopupNavTestHandler( \
PopupNavTestHandler::test_mode, PopupNavTestHandler::rc_mode); \
handler->ExecuteTest(); \
ReleaseAndWaitForDestructor(handler); \
}
#define POPUP_TEST_GROUP(name, test_mode) \
POPUP_TEST(name##RCNone, test_mode, RC_MODE_NONE); \
POPUP_TEST(name##RCImpl, test_mode, RC_MODE_IMPL); \
POPUP_TEST(name##RCProxy, test_mode, RC_MODE_PROXY);
#define POPUP_TEST_GROUP(test_name, test_mode) \
RC_TEST_GROUP_IN_MEMORY(RequestContextTest, Popup##test_name, \
PopupNavTestHandler, test_mode)
// Test allowing popups and closing the popup browser first.
POPUP_TEST_GROUP(AllowClosePopupFirst, ALLOW_CLOSE_POPUP_FIRST);
// Test allowing popups and closing the main browser first to verify that
// internal objects are tracked correctly (see issue #2162).
// Test allowing popups and closing the main browser first to verify
// that internal objects are tracked correctly (see issue #2162).
POPUP_TEST_GROUP(AllowClosePopupLast, ALLOW_CLOSE_POPUP_LAST);
// Test denying popups.
POPUP_TEST_GROUP(Deny, DENY);
// Test navigation to a different origin after popup creation to verify that
// internal objects are tracked correctly (see issue #1392).
// Test navigation to a different origin after popup creation to
// verify that internal objects are tracked correctly (see issue
// #1392).
POPUP_TEST_GROUP(NavigateAfterCreation, NAVIGATE_AFTER_CREATION);
// Test destroying the parent browser during or immediately after popup creation
// to verify that internal objects are tracked correctly (see issue #2041).
// Test destroying the parent browser during or immediately after
// popup creation to verify that internal objects are tracked
// correctly (see issue #2041).
POPUP_TEST_GROUP(DestroyParentBeforeCreation, DESTROY_PARENT_BEFORE_CREATION);
POPUP_TEST_GROUP(DestroyParentBeforeCreationForce,
DESTROY_PARENT_BEFORE_CREATION_FORCE);
@ -1058,7 +1034,8 @@ class MethodTestHandler : public TestHandler {
} // namespace
// Test CefRequestContext::ClearCertificateExceptions with the global context.
// Test CefRequestContext::ClearCertificateExceptions with the global
// context.
TEST(RequestContextTest, ClearCertificateExceptionsGlobal) {
CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
true, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS);
@ -1066,7 +1043,8 @@ TEST(RequestContextTest, ClearCertificateExceptionsGlobal) {
ReleaseAndWaitForDestructor(handler);
}
// Test CefRequestContext::ClearCertificateExceptions with a custom context.
// Test CefRequestContext::ClearCertificateExceptions with a custom
// context.
TEST(RequestContextTest, ClearCertificateExceptionsCustom) {
CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
false, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS);
@ -1074,7 +1052,8 @@ TEST(RequestContextTest, ClearCertificateExceptionsCustom) {
ReleaseAndWaitForDestructor(handler);
}
// Test CefRequestContext::CloseAllConnections with the global context.
// Test CefRequestContext::CloseAllConnections with the global
// context.
TEST(RequestContextTest, CloseAllConnectionsGlobal) {
CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
true, MethodTestHandler::METHOD_CLOSE_ALL_CONNECTIONS);

View File

@ -261,3 +261,39 @@ void TestStringVectorEqual(const std::vector<CefString>& val1,
for (size_t i = 0; i < val1.size(); ++i)
EXPECT_STREQ(val1[i].ToString().c_str(), val2[i].ToString().c_str());
}
CefRefPtr<CefRequestContext> CreateTestRequestContext(
TestRequestContextMode mode,
const std::string& cache_path) {
EXPECT_TRUE(cache_path.empty() || mode == TEST_RC_MODE_CUSTOM ||
mode == TEST_RC_MODE_CUSTOM_WITH_HANDLER);
if (mode == TEST_RC_MODE_NONE)
return nullptr;
if (mode == TEST_RC_MODE_GLOBAL)
return CefRequestContext::GetGlobalContext();
CefRefPtr<CefRequestContextHandler> rc_handler;
if (mode == TEST_RC_MODE_GLOBAL_WITH_HANDLER ||
mode == TEST_RC_MODE_CUSTOM_WITH_HANDLER) {
class Handler : public CefRequestContextHandler {
public:
Handler() {}
private:
IMPLEMENT_REFCOUNTING(Handler);
};
rc_handler = new Handler();
}
if (mode == TEST_RC_MODE_CUSTOM || mode == TEST_RC_MODE_CUSTOM_WITH_HANDLER) {
CefRequestContextSettings settings;
if (!cache_path.empty())
CefString(&settings.cache_path) = cache_path;
return CefRequestContext::CreateContext(settings, rc_handler);
}
EXPECT_EQ(mode, TEST_RC_MODE_GLOBAL_WITH_HANDLER);
return CefRequestContext::CreateContext(CefRequestContext::GetGlobalContext(),
rc_handler);
}

View File

@ -8,8 +8,10 @@
#include "include/cef_process_message.h"
#include "include/cef_request.h"
#include "include/cef_request_context.h"
#include "include/cef_response.h"
#include "include/cef_values.h"
#include "tests/ceftests/test_suite.h"
// Test that CefRequest::HeaderMap objects are equal
// If |allowExtras| is true then additional header fields will be allowed in
@ -62,4 +64,116 @@ void TestProcessMessageEqual(CefRefPtr<CefProcessMessage> val1,
void TestStringVectorEqual(const std::vector<CefString>& val1,
const std::vector<CefString>& val2);
enum TestRequestContextMode {
TEST_RC_MODE_NONE,
TEST_RC_MODE_GLOBAL,
TEST_RC_MODE_GLOBAL_WITH_HANDLER,
TEST_RC_MODE_CUSTOM,
TEST_RC_MODE_CUSTOM_WITH_HANDLER,
};
inline bool IsTestRequestContextModeCustom(TestRequestContextMode mode) {
return mode == TEST_RC_MODE_CUSTOM ||
mode == TEST_RC_MODE_CUSTOM_WITH_HANDLER;
}
// Return a RequestContext object matching the specified |mode|.
// |cache_path| may be specified for CUSTOM modes.
// Use the RC_TEST_GROUP_BASE macro to test all valid combinations.
CefRefPtr<CefRequestContext> CreateTestRequestContext(
TestRequestContextMode mode,
const std::string& cache_path);
// Helper macro for testing a single RequestContextMode value.
// See RC_TEST_GROUP_ALL documentation for example usage.
#define RC_TEST_BASE(test_case_name, test_name, test_class, test_mode, \
rc_mode, with_cache_path) \
TEST(test_case_name, test_name) { \
CefScopedTempDir scoped_temp_dir; \
std::string cache_path; \
if (with_cache_path) { \
EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); \
cache_path = scoped_temp_dir.GetPath(); \
} \
CefRefPtr<test_class> handler = \
new test_class(test_class::test_mode, rc_mode, cache_path); \
handler->ExecuteTest(); \
ReleaseAndWaitForDestructor(handler); \
if (!scoped_temp_dir.IsEmpty()) { \
CefTestSuite::GetInstance()->RegisterTempDirectory( \
scoped_temp_dir.Take()); \
} \
}
// RequestContextModes that operate in memory.
#define RC_TEST_GROUP_IN_MEMORY(test_case_name, test_name, test_class, \
test_mode) \
RC_TEST_BASE(test_case_name, test_name##RCNone, test_class, test_mode, \
TEST_RC_MODE_NONE, false); \
RC_TEST_BASE(test_case_name, test_name##RCGlobal, test_class, test_mode, \
TEST_RC_MODE_GLOBAL, false); \
RC_TEST_BASE(test_case_name, test_name##RCGlobalWithHandler, test_class, \
test_mode, TEST_RC_MODE_GLOBAL_WITH_HANDLER, false); \
RC_TEST_BASE(test_case_name, test_name##RCCustomInMemory, test_class, \
test_mode, TEST_RC_MODE_CUSTOM, false); \
RC_TEST_BASE(test_case_name, test_name##RCCustomInMemoryWithHandler, \
test_class, test_mode, TEST_RC_MODE_CUSTOM_WITH_HANDLER, \
false);
// RequestContextModes that operate on disk.
#define RC_TEST_GROUP_ON_DISK(test_case_name, test_name, test_class, \
test_mode) \
RC_TEST_BASE(test_case_name, test_name##RCCustomOnDisk, test_class, \
test_mode, TEST_RC_MODE_CUSTOM, true); \
RC_TEST_BASE(test_case_name, test_name##RCCustomOnDiskWithHandler, \
test_class, test_mode, TEST_RC_MODE_CUSTOM_WITH_HANDLER, true);
// Helper macro for testing all valid combinations of RequestContextMode values.
// For example:
//
// // Test handler implementation.
// class MyTestHandler : public TestHandler {
// public:
// // Test modes supported by MyTestHandler.
// enum TestMode {
// FIRST,
// SECOND,
// };
//
// // Constructor always accepts three arguments.
// MyTestHandler(TestMode test_mode,
// TestRequestContextMode rc_mode,
// const std::string& rc_cache_path)
// : test_mode_(test_mode), rc_mode_(rc_mode),
// rc_cache_path_(rc_cache_path) {}
//
// void RunTest() override {
// // Create a RequestContext with the specified attributes.
// CefRefPtr<CefRequestContext> request_context =
// CreateTestRequestContext(rc_mode_, rc_cache_path_);
//
// // Do something with |test_mode_| and |request_context|...
// }
//
// private:
// const TestMode test_mode_;
// const TestRequestContextMode rc_mode_;
// const std::string rc_cache_path_;
//
// IMPLEMENT_REFCOUNTING(MyTestHandler);
// };
//
// // Helper macro for defining tests using MyTestHandler.
// #define MY_TEST_GROUP(test_name, test_mode) \
// RC_TEST_GROUP_ALL(MyTest, test_name, MyTestHandler, test_mode)
//
// // Implementation for MyTest.First* tests.
// MY_TEST_GROUP(First, FIRST);
// // Implementation for MyTest.Second* tests.
// MY_TEST_GROUP(Second, SECOND);
//
#define RC_TEST_GROUP_ALL(test_case_name, test_name, test_class, test_mode) \
RC_TEST_GROUP_IN_MEMORY(test_case_name, test_name, test_class, test_mode) \
RC_TEST_GROUP_ON_DISK(test_case_name, test_name, test_class, test_mode)
#endif // CEF_TESTS_UNITTESTS_TEST_UTIL_H_