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* content::DownloadManagerDelegate*
CefBrowserContextProxy::GetDownloadManagerDelegate() { CefBrowserContextProxy::GetDownloadManagerDelegate() {
DCHECK(!download_manager_delegate_.get()); if (!download_manager_delegate_) {
content::DownloadManager* manager =
content::DownloadManager* manager = BrowserContext::GetDownloadManager(this); BrowserContext::GetDownloadManager(this);
download_manager_delegate_.reset(new CefDownloadManagerDelegate(manager)); download_manager_delegate_.reset(new CefDownloadManagerDelegate(manager));
}
return download_manager_delegate_.get(); return download_manager_delegate_.get();
} }

View File

@ -315,13 +315,28 @@ void CefDownloadManagerDelegate::OnDownloadDestroyed(DownloadItem* item) {
void CefDownloadManagerDelegate::OnDownloadCreated(DownloadManager* manager, void CefDownloadManagerDelegate::OnDownloadCreated(DownloadManager* manager,
DownloadItem* item) { DownloadItem* item) {
item->AddObserver(this);
CefBrowserHostImpl* browser = nullptr; CefBrowserHostImpl* browser = nullptr;
content::WebContents* contents = item->GetWebContents(); content::WebContents* contents = item->GetWebContents();
if (contents) if (contents) {
browser = CefBrowserHostImpl::GetBrowserForContents(contents).get(); 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)); item_browser_map_.insert(std::make_pair(item, browser));
@ -399,7 +414,11 @@ CefBrowserHostImpl* CefDownloadManagerDelegate::GetBrowser(DownloadItem* item) {
if (it != item_browser_map_.end()) if (it != item_browser_map_.end())
return it->second; return it->second;
// An entry should always exist for a DownloadItem. // If the download is rejected (e.g. ALT+click on an invalid protocol link)
NOTREACHED(); // 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; return nullptr;
} }

View File

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

View File

@ -8,6 +8,7 @@
#include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_scoped_temp_dir.h" #include "include/wrapper/cef_scoped_temp_dir.h"
#include "tests/ceftests/test_handler.h" #include "tests/ceftests/test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h" #include "tests/gtest/include/gtest/gtest.h"
TEST(RequestContextTest, GetGlobalContext) { TEST(RequestContextTest, GetGlobalContext) {
@ -608,7 +609,7 @@ const char kPopupNavPopupName[] = "my_popup";
// Browser side. // Browser side.
class PopupNavTestHandler : public TestHandler { class PopupNavTestHandler : public TestHandler {
public: public:
enum Mode { enum TestMode {
ALLOW_CLOSE_POPUP_FIRST, ALLOW_CLOSE_POPUP_FIRST,
ALLOW_CLOSE_POPUP_LAST, ALLOW_CLOSE_POPUP_LAST,
DENY, DENY,
@ -620,14 +621,11 @@ class PopupNavTestHandler : public TestHandler {
DESTROY_PARENT_AFTER_CREATION, DESTROY_PARENT_AFTER_CREATION,
DESTROY_PARENT_AFTER_CREATION_FORCE, DESTROY_PARENT_AFTER_CREATION_FORCE,
}; };
enum RCMode {
RC_MODE_NONE,
RC_MODE_IMPL,
RC_MODE_PROXY,
};
PopupNavTestHandler(Mode mode, RCMode rc_mode) PopupNavTestHandler(TestMode test_mode,
: mode_(mode), rc_mode_(rc_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 { void RunTest() override {
// Add the resources that we will navigate to/from. // Add the resources that we will navigate to/from.
@ -640,23 +638,8 @@ class PopupNavTestHandler : public TestHandler {
if (mode_ == NAVIGATE_AFTER_CREATION) if (mode_ == NAVIGATE_AFTER_CREATION)
AddResource(kPopupNavPopupUrl2, "<html>Popup2</html>", "text/html"); AddResource(kPopupNavPopupUrl2, "<html>Popup2</html>", "text/html");
CefRefPtr<CefRequestContext> request_context; CefRefPtr<CefRequestContext> request_context =
CefRefPtr<CefRequestContextHandler> rc_handler; CreateTestRequestContext(rc_mode_, rc_cache_path_);
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);
}
// Create the browser. // Create the browser.
CreateBrowser(kPopupNavPageUrl, request_context); CreateBrowser(kPopupNavPageUrl, request_context);
@ -872,8 +855,9 @@ class PopupNavTestHandler : public TestHandler {
TestHandler::DestroyTest(); TestHandler::DestroyTest();
} }
const Mode mode_; const TestMode mode_;
const RCMode rc_mode_; const TestRequestContextMode rc_mode_;
const std::string rc_cache_path_;
TrackCallback got_on_before_popup_; TrackCallback got_on_before_popup_;
TrackCallback got_load_start_; TrackCallback got_load_start_;
@ -890,36 +874,28 @@ class PopupNavTestHandler : public TestHandler {
}; };
} // namespace } // namespace
#define POPUP_TEST_GROUP(test_name, test_mode) \
#define POPUP_TEST(name, test_mode, rc_mode) \ RC_TEST_GROUP_IN_MEMORY(RequestContextTest, Popup##test_name, \
TEST(RequestContextTest, Popup##name) { \ PopupNavTestHandler, test_mode)
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);
// Test allowing popups and closing the popup browser first. // Test allowing popups and closing the popup browser first.
POPUP_TEST_GROUP(AllowClosePopupFirst, ALLOW_CLOSE_POPUP_FIRST); POPUP_TEST_GROUP(AllowClosePopupFirst, ALLOW_CLOSE_POPUP_FIRST);
// Test allowing popups and closing the main browser first to verify that // Test allowing popups and closing the main browser first to verify
// internal objects are tracked correctly (see issue #2162). // that internal objects are tracked correctly (see issue #2162).
POPUP_TEST_GROUP(AllowClosePopupLast, ALLOW_CLOSE_POPUP_LAST); POPUP_TEST_GROUP(AllowClosePopupLast, ALLOW_CLOSE_POPUP_LAST);
// Test denying popups. // Test denying popups.
POPUP_TEST_GROUP(Deny, DENY); POPUP_TEST_GROUP(Deny, DENY);
// Test navigation to a different origin after popup creation to verify that // Test navigation to a different origin after popup creation to
// internal objects are tracked correctly (see issue #1392). // verify that internal objects are tracked correctly (see issue
// #1392).
POPUP_TEST_GROUP(NavigateAfterCreation, NAVIGATE_AFTER_CREATION); POPUP_TEST_GROUP(NavigateAfterCreation, NAVIGATE_AFTER_CREATION);
// Test destroying the parent browser during or immediately after popup creation // Test destroying the parent browser during or immediately after
// to verify that internal objects are tracked correctly (see issue #2041). // popup creation to verify that internal objects are tracked
// correctly (see issue #2041).
POPUP_TEST_GROUP(DestroyParentBeforeCreation, DESTROY_PARENT_BEFORE_CREATION); POPUP_TEST_GROUP(DestroyParentBeforeCreation, DESTROY_PARENT_BEFORE_CREATION);
POPUP_TEST_GROUP(DestroyParentBeforeCreationForce, POPUP_TEST_GROUP(DestroyParentBeforeCreationForce,
DESTROY_PARENT_BEFORE_CREATION_FORCE); DESTROY_PARENT_BEFORE_CREATION_FORCE);
@ -1058,7 +1034,8 @@ class MethodTestHandler : public TestHandler {
} // namespace } // namespace
// Test CefRequestContext::ClearCertificateExceptions with the global context. // Test CefRequestContext::ClearCertificateExceptions with the global
// context.
TEST(RequestContextTest, ClearCertificateExceptionsGlobal) { TEST(RequestContextTest, ClearCertificateExceptionsGlobal) {
CefRefPtr<MethodTestHandler> handler = new MethodTestHandler( CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
true, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS); true, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS);
@ -1066,7 +1043,8 @@ TEST(RequestContextTest, ClearCertificateExceptionsGlobal) {
ReleaseAndWaitForDestructor(handler); ReleaseAndWaitForDestructor(handler);
} }
// Test CefRequestContext::ClearCertificateExceptions with a custom context. // Test CefRequestContext::ClearCertificateExceptions with a custom
// context.
TEST(RequestContextTest, ClearCertificateExceptionsCustom) { TEST(RequestContextTest, ClearCertificateExceptionsCustom) {
CefRefPtr<MethodTestHandler> handler = new MethodTestHandler( CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
false, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS); false, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS);
@ -1074,7 +1052,8 @@ TEST(RequestContextTest, ClearCertificateExceptionsCustom) {
ReleaseAndWaitForDestructor(handler); ReleaseAndWaitForDestructor(handler);
} }
// Test CefRequestContext::CloseAllConnections with the global context. // Test CefRequestContext::CloseAllConnections with the global
// context.
TEST(RequestContextTest, CloseAllConnectionsGlobal) { TEST(RequestContextTest, CloseAllConnectionsGlobal) {
CefRefPtr<MethodTestHandler> handler = new MethodTestHandler( CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
true, MethodTestHandler::METHOD_CLOSE_ALL_CONNECTIONS); 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) for (size_t i = 0; i < val1.size(); ++i)
EXPECT_STREQ(val1[i].ToString().c_str(), val2[i].ToString().c_str()); 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_process_message.h"
#include "include/cef_request.h" #include "include/cef_request.h"
#include "include/cef_request_context.h"
#include "include/cef_response.h" #include "include/cef_response.h"
#include "include/cef_values.h" #include "include/cef_values.h"
#include "tests/ceftests/test_suite.h"
// Test that CefRequest::HeaderMap objects are equal // Test that CefRequest::HeaderMap objects are equal
// If |allowExtras| is true then additional header fields will be allowed in // 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, void TestStringVectorEqual(const std::vector<CefString>& val1,
const std::vector<CefString>& val2); 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_ #endif // CEF_TESTS_UNITTESTS_TEST_UTIL_H_