From 54a015f785c08083c21ec5360e154a5af6f52c57 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Fri, 22 Dec 2017 16:05:32 -0500 Subject: [PATCH] Fix crash when ALT+Clicking invalid protocol link (issue #2306) --- libcef/browser/browser_context_proxy.cc | 9 +- libcef/browser/download_manager_delegate.cc | 31 +++- tests/ceftests/download_unittest.cc | 163 ++++++++++++++------ tests/ceftests/request_context_unittest.cc | 77 ++++----- tests/ceftests/test_util.cc | 36 +++++ tests/ceftests/test_util.h | 114 ++++++++++++++ 6 files changed, 326 insertions(+), 104 deletions(-) diff --git a/libcef/browser/browser_context_proxy.cc b/libcef/browser/browser_context_proxy.cc index 0117ea6f0..e4645fadb 100644 --- a/libcef/browser/browser_context_proxy.cc +++ b/libcef/browser/browser_context_proxy.cc @@ -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(); } diff --git a/libcef/browser/download_manager_delegate.cc b/libcef/browser/download_manager_delegate.cc index 5b900fa53..5db1154d0 100644 --- a/libcef/browser/download_manager_delegate.cc +++ b/libcef/browser/download_manager_delegate.cc @@ -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& 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; } diff --git a/tests/ceftests/download_unittest.cc b/tests/ceftests/download_unittest.cc index 6a24e39f5..8170ea5f1 100644 --- a/tests/ceftests/download_unittest.cc +++ b/tests/ceftests/download_unittest.cc @@ -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 request, CefRefPtr callback) override { std::string url = request->GetURL(); - if (url == kTestEntryUrl) { - content_ = "Download Test"; - 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 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 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, + "CLICK ME", + "text/html"); + } else { + AddResource(kTestStartUrl, "Download Test", + "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 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 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 request_context_; // Used with NAVIGATED test mode. CefRefPtr delay_callback_; @@ -383,26 +461,21 @@ class DownloadTestHandler : public TestHandler { } // namespace -// Test a basic download. -TEST(DownloadTest, Download) { - CefRefPtr 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 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 handler = - new DownloadTestHandler(DownloadTestHandler::PENDING); - handler->ExecuteTest(); - ReleaseAndWaitForDestructor(handler); -} +DOWNLOAD_TEST_GROUP(Pending, PENDING); diff --git a/tests/ceftests/request_context_unittest.cc b/tests/ceftests/request_context_unittest.cc index 9b177fa72..2108faf8b 100644 --- a/tests/ceftests/request_context_unittest.cc +++ b/tests/ceftests/request_context_unittest.cc @@ -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, "Popup2", "text/html"); - CefRefPtr request_context; - CefRefPtr 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 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 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 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 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 handler = new MethodTestHandler( true, MethodTestHandler::METHOD_CLOSE_ALL_CONNECTIONS); diff --git a/tests/ceftests/test_util.cc b/tests/ceftests/test_util.cc index 83fd63929..c9d146b15 100644 --- a/tests/ceftests/test_util.cc +++ b/tests/ceftests/test_util.cc @@ -261,3 +261,39 @@ void TestStringVectorEqual(const std::vector& val1, for (size_t i = 0; i < val1.size(); ++i) EXPECT_STREQ(val1[i].ToString().c_str(), val2[i].ToString().c_str()); } + +CefRefPtr 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 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); +} diff --git a/tests/ceftests/test_util.h b/tests/ceftests/test_util.h index 80816f609..3bb601014 100644 --- a/tests/ceftests/test_util.h +++ b/tests/ceftests/test_util.h @@ -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 val1, void TestStringVectorEqual(const std::vector& val1, const std::vector& 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 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 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 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_