// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. #include "include/base/cef_callback.h" #include "include/base/cef_weak_ptr.h" #include "include/cef_request_context.h" #include "include/cef_request_context_handler.h" #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, BasicGetGlobal) { CefRefPtr context1 = CefRequestContext::GetGlobalContext(); EXPECT_TRUE(context1.get()); EXPECT_TRUE(context1->IsGlobal()); EXPECT_TRUE(context1->IsSame(context1)); EXPECT_TRUE(context1->IsSharingWith(context1)); CefRefPtr context2 = CefRequestContext::GetGlobalContext(); EXPECT_TRUE(context2.get()); EXPECT_TRUE(context2->IsGlobal()); EXPECT_TRUE(context2->IsSame(context2)); EXPECT_TRUE(context2->IsSharingWith(context2)); EXPECT_TRUE(context1->IsSame(context2)); EXPECT_TRUE(context2->IsSame(context1)); EXPECT_TRUE(context1->IsSharingWith(context2)); EXPECT_TRUE(context2->IsSharingWith(context1)); } TEST(RequestContextTest, BasicCreate) { class Handler : public CefRequestContextHandler { public: Handler() = default; private: IMPLEMENT_REFCOUNTING(Handler); }; CefRefPtr handler = new Handler(); CefRequestContextSettings settings; CefRefPtr context1 = CefRequestContext::CreateContext(settings, handler.get()); EXPECT_TRUE(context1.get()); EXPECT_FALSE(context1->IsGlobal()); EXPECT_TRUE(context1->IsSame(context1)); EXPECT_TRUE(context1->IsSharingWith(context1)); EXPECT_EQ(context1->GetHandler().get(), handler.get()); CefRefPtr context2 = CefRequestContext::CreateContext(settings, handler.get()); EXPECT_TRUE(context2.get()); EXPECT_FALSE(context2->IsGlobal()); EXPECT_TRUE(context2->IsSame(context2)); EXPECT_TRUE(context2->IsSharingWith(context2)); EXPECT_EQ(context2->GetHandler().get(), handler.get()); EXPECT_FALSE(context1->IsSame(context2)); EXPECT_FALSE(context1->IsSharingWith(context2)); EXPECT_FALSE(context2->IsSame(context1)); EXPECT_FALSE(context2->IsSharingWith(context1)); CefRefPtr context3 = CefRequestContext::GetGlobalContext(); EXPECT_TRUE(context3.get()); EXPECT_FALSE(context3->IsSame(context1)); EXPECT_FALSE(context3->IsSharingWith(context1)); EXPECT_FALSE(context3->IsSame(context2)); EXPECT_FALSE(context3->IsSharingWith(context2)); EXPECT_FALSE(context1->IsSame(context3)); EXPECT_FALSE(context1->IsSharingWith(context3)); EXPECT_FALSE(context2->IsSame(context3)); EXPECT_FALSE(context2->IsSharingWith(context3)); } TEST(RequestContextTest, BasicCreateNoHandler) { CefRequestContextSettings settings; CefRefPtr context1 = CefRequestContext::CreateContext(settings, nullptr); EXPECT_TRUE(context1.get()); EXPECT_FALSE(context1->IsGlobal()); EXPECT_TRUE(context1->IsSame(context1)); EXPECT_TRUE(context1->IsSharingWith(context1)); EXPECT_FALSE(context1->GetHandler().get()); CefRefPtr context2 = CefRequestContext::CreateContext(settings, nullptr); EXPECT_TRUE(context2.get()); EXPECT_FALSE(context2->IsGlobal()); EXPECT_TRUE(context2->IsSame(context2)); EXPECT_TRUE(context2->IsSharingWith(context2)); EXPECT_FALSE(context2->GetHandler().get()); EXPECT_FALSE(context1->IsSame(context2)); EXPECT_FALSE(context1->IsSharingWith(context2)); EXPECT_FALSE(context2->IsSame(context1)); EXPECT_FALSE(context2->IsSharingWith(context1)); CefRefPtr context3 = CefRequestContext::GetGlobalContext(); EXPECT_TRUE(context3.get()); EXPECT_FALSE(context3->IsSame(context1)); EXPECT_FALSE(context3->IsSharingWith(context1)); EXPECT_FALSE(context3->IsSame(context2)); EXPECT_FALSE(context3->IsSharingWith(context2)); EXPECT_FALSE(context1->IsSame(context3)); EXPECT_FALSE(context1->IsSharingWith(context3)); EXPECT_FALSE(context2->IsSame(context3)); EXPECT_FALSE(context2->IsSharingWith(context3)); } TEST(RequestContextTest, BasicCreateSharedGlobal) { CefRequestContextSettings settings; CefRefPtr context1 = CefRequestContext::GetGlobalContext(); EXPECT_TRUE(context1.get()); EXPECT_TRUE(context1->IsGlobal()); EXPECT_TRUE(context1->IsSame(context1)); EXPECT_TRUE(context1->IsSharingWith(context1)); // Returns the same global context. CefRefPtr context2 = CefRequestContext::CreateContext(context1, nullptr); EXPECT_TRUE(context2.get()); EXPECT_TRUE(context2->IsGlobal()); EXPECT_TRUE(context2->IsSame(context2)); EXPECT_TRUE(context2->IsSame(context1)); EXPECT_TRUE(context1->IsSame(context2)); EXPECT_TRUE(context2->IsSharingWith(context2)); EXPECT_TRUE(context2->IsSharingWith(context1)); EXPECT_TRUE(context1->IsSharingWith(context2)); } TEST(RequestContextTest, BasicCreateSharedOnDisk) { CefScopedTempDir tempdir; EXPECT_TRUE(tempdir.CreateUniqueTempDirUnderPath( CefTestSuite::GetInstance()->root_cache_path())); CefRequestContextSettings settings; CefString(&settings.cache_path) = tempdir.GetPath(); CefRefPtr context1 = CefRequestContext::CreateContext(settings, nullptr); EXPECT_TRUE(context1.get()); EXPECT_FALSE(context1->IsGlobal()); EXPECT_TRUE(context1->IsSame(context1)); EXPECT_TRUE(context1->IsSharingWith(context1)); CefRefPtr context2 = CefRequestContext::CreateContext(context1, nullptr); EXPECT_TRUE(context2.get()); EXPECT_FALSE(context2->IsGlobal()); EXPECT_TRUE(context2->IsSame(context2)); EXPECT_FALSE(context2->IsSame(context1)); EXPECT_FALSE(context1->IsSame(context2)); EXPECT_TRUE(context2->IsSharingWith(context2)); EXPECT_TRUE(context2->IsSharingWith(context1)); EXPECT_TRUE(context1->IsSharingWith(context2)); CefRefPtr context3 = CefRequestContext::CreateContext(context2, nullptr); EXPECT_TRUE(context3.get()); EXPECT_FALSE(context3->IsGlobal()); EXPECT_TRUE(context3->IsSame(context3)); EXPECT_FALSE(context3->IsSame(context2)); EXPECT_FALSE(context3->IsSame(context1)); EXPECT_FALSE(context1->IsSame(context3)); EXPECT_FALSE(context2->IsSame(context3)); EXPECT_TRUE(context3->IsSharingWith(context3)); EXPECT_TRUE(context3->IsSharingWith(context2)); EXPECT_TRUE(context3->IsSharingWith(context1)); EXPECT_TRUE(context1->IsSharingWith(context3)); EXPECT_TRUE(context2->IsSharingWith(context3)); CefRefPtr context4 = CefRequestContext::CreateContext(context1, nullptr); EXPECT_TRUE(context4.get()); EXPECT_FALSE(context4->IsGlobal()); EXPECT_TRUE(context4->IsSame(context4)); EXPECT_FALSE(context4->IsSame(context3)); EXPECT_FALSE(context4->IsSame(context2)); EXPECT_FALSE(context4->IsSame(context1)); EXPECT_FALSE(context1->IsSame(context4)); EXPECT_FALSE(context2->IsSame(context4)); EXPECT_FALSE(context3->IsSame(context4)); EXPECT_TRUE(context4->IsSharingWith(context4)); EXPECT_TRUE(context4->IsSharingWith(context3)); EXPECT_TRUE(context4->IsSharingWith(context2)); EXPECT_TRUE(context4->IsSharingWith(context1)); EXPECT_TRUE(context1->IsSharingWith(context4)); EXPECT_TRUE(context2->IsSharingWith(context4)); EXPECT_TRUE(context3->IsSharingWith(context4)); } namespace { class PopupTestHandler : public TestHandler { public: enum Mode { MODE_WINDOW_OPEN, MODE_TARGETED_LINK, // The no-referrer popup won't have an opener from the renderer's // perspective, but we still track it from the browser's perspective. MODE_NOREFERRER_LINK, }; PopupTestHandler(bool same_origin, Mode mode) : mode_(mode) { url_ = "https://tests-simple-rch1.com/nav1.html"; if (same_origin) { popup_url_ = "https://tests-simple-rch1.com/pop1.html"; } else { popup_url_ = "https://tests-simple-rch2.com/pop1.html"; } } void RunTest() override { std::string link; if (mode_ == MODE_TARGETED_LINK) { link = "CLICK ME"; } else if (mode_ == MODE_NOREFERRER_LINK) { link = "CLICK ME"; } AddResource(url_, "" "" "

" + link + "

" "", "text/html"); AddResource(popup_url_, "" "" "Nav1" "", "text/html"); CefRequestContextSettings settings; context_ = CefRequestContext::CreateContext(settings, nullptr); cookie_manager_ = context_->GetCookieManager(nullptr); GrantPopupPermission(context_, url_); // Create browser that loads the 1st URL. CreateBrowser(url_, context_); // Time out the test after a reasonable period of time. SetTestTimeout(); } void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { CefRefPtr context = browser->GetHost()->GetRequestContext(); EXPECT_TRUE(context.get()); EXPECT_TRUE(context->IsSame(context_)); EXPECT_FALSE(context->IsGlobal()); EXPECT_TRUE(frame->IsMain()); const std::string& url = frame->GetURL(); if (url == url_) { got_load_end1_.yes(); LaunchPopup(browser); } else if (url == popup_url_) { got_load_end2_.yes(); EXPECT_TRUE(browser->IsPopup()); // Close the popup window. CloseBrowser(browser, true); } } bool OnBeforePopup(CefRefPtr browser, CefRefPtr frame, int popup_id, const CefString& target_url, const CefString& target_frame_name, cef_window_open_disposition_t target_disposition, bool user_gesture, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr& client, CefBrowserSettings& settings, CefRefPtr& extra_info, bool* no_javascript_access) override { EXPECT_FALSE(got_on_before_popup_); EXPECT_FALSE(got_on_before_popup_aborted_); EXPECT_FALSE(got_on_after_created_popup_); got_on_before_popup_.yes(); // Only ever a single popup with this test. EXPECT_EQ(1, popup_id); const std::string& url = target_url; EXPECT_STREQ(url.c_str(), popup_url_.c_str()); EXPECT_EQ(CEF_WOD_NEW_FOREGROUND_TAB, target_disposition); if (mode_ == MODE_WINDOW_OPEN) { EXPECT_FALSE(user_gesture); } else { EXPECT_TRUE(user_gesture); } return false; } void OnBeforePopupAborted(CefRefPtr browser, int popup_id) override { EXPECT_TRUE(got_on_before_popup_); EXPECT_FALSE(got_on_before_popup_aborted_); EXPECT_FALSE(got_on_after_created_popup_); got_on_before_popup_aborted_.yes(); // Only ever a single popup with this test. EXPECT_EQ(1, popup_id); } void OnAfterCreated(CefRefPtr browser) override { if (browser->IsPopup()) { EXPECT_TRUE(got_on_before_popup_); EXPECT_FALSE(got_on_before_popup_aborted_); got_on_after_created_popup_.yes(); // Opener is the main browser. EXPECT_EQ(GetBrowserId(), browser->GetHost()->GetOpenerIdentifier()); } else { EXPECT_EQ(0, browser->GetHost()->GetOpenerIdentifier()); } TestHandler::OnAfterCreated(browser); } void OnBeforeClose(CefRefPtr browser) override { TestHandler::OnBeforeClose(browser); if (browser->IsPopup()) { FinishTest(); } } protected: void LaunchPopup(CefRefPtr browser) { if (mode_ == MODE_WINDOW_OPEN) { browser->GetMainFrame()->ExecuteJavaScript("doPopup()", url_, 0); } else if (mode_ == MODE_TARGETED_LINK || mode_ == MODE_NOREFERRER_LINK) { CefMouseEvent mouse_event; mouse_event.x = 20; mouse_event.y = 20; mouse_event.modifiers = 0; SendMouseClickEvent(browser, mouse_event); } else { ADD_FAILURE(); // Not reached. } } void FinishTest() { // Verify that the cookies were set correctly. class TestVisitor : public CefCookieVisitor { public: explicit TestVisitor(PopupTestHandler* handler) : handler_(handler) {} ~TestVisitor() override { // Destroy the test. CefPostTask(TID_UI, base::BindOnce(&PopupTestHandler::DestroyTest, handler_)); } bool Visit(const CefCookie& cookie, int count, int total, bool& deleteCookie) override { const std::string& name = CefString(&cookie.name); const std::string& value = CefString(&cookie.value); if (name == "name1" && value == "value1") { handler_->got_cookie1_.yes(); deleteCookie = true; } else if (name == "name2" && value == "value2") { handler_->got_cookie2_.yes(); deleteCookie = true; } return true; } private: PopupTestHandler* handler_; IMPLEMENT_REFCOUNTING(TestVisitor); }; cookie_manager_->VisitAllCookies(new TestVisitor(this)); } void DestroyTest() override { // Verify test expectations. EXPECT_TRUE(got_load_end1_); EXPECT_TRUE(got_on_before_popup_); EXPECT_FALSE(got_on_before_popup_aborted_); EXPECT_TRUE(got_on_after_created_popup_); EXPECT_TRUE(got_load_end2_); EXPECT_TRUE(got_cookie1_); EXPECT_TRUE(got_cookie2_); context_ = nullptr; TestHandler::DestroyTest(); } std::string url_; std::string popup_url_; Mode mode_; CefRefPtr context_; CefRefPtr cookie_manager_; TrackCallback got_load_end1_; TrackCallback got_on_before_popup_; TrackCallback got_on_before_popup_aborted_; TrackCallback got_on_after_created_popup_; TrackCallback got_load_end2_; TrackCallback got_cookie1_; TrackCallback got_cookie2_; IMPLEMENT_REFCOUNTING(PopupTestHandler); }; } // namespace // Test that a popup created using window.open() will get the same request // context as the parent browser. TEST(RequestContextTest, PopupBasicWindowOpenSameOrigin) { CefRefPtr handler = new PopupTestHandler(true, PopupTestHandler::MODE_WINDOW_OPEN); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } TEST(RequestContextTest, PopupBasicWindowOpenDifferentOrigin) { CefRefPtr handler = new PopupTestHandler(false, PopupTestHandler::MODE_WINDOW_OPEN); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } // Test that a popup created using a targeted link will get the same request // context as the parent browser. TEST(RequestContextTest, PopupBasicTargetedLinkSameOrigin) { CefRefPtr handler = new PopupTestHandler(true, PopupTestHandler::MODE_TARGETED_LINK); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } TEST(RequestContextTest, PopupBasicTargetedLinkDifferentOrigin) { CefRefPtr handler = new PopupTestHandler(false, PopupTestHandler::MODE_TARGETED_LINK); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } // Test that a popup created using a noreferrer link will get the same // request context as the parent browser. A new render process will // be created for the popup browser. TEST(RequestContextTest, PopupBasicNoReferrerLinkSameOrigin) { CefRefPtr handler = new PopupTestHandler(true, PopupTestHandler::MODE_NOREFERRER_LINK); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } TEST(RequestContextTest, PopupBasicNoReferrerLinkDifferentOrigin) { CefRefPtr handler = new PopupTestHandler(false, PopupTestHandler::MODE_NOREFERRER_LINK); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } namespace { const char kPopupNavPageUrl[] = "https://tests-popup.com/page.html"; const char kPopupNavPopupUrl[] = "https://tests-popup.com/popup.html"; const char kPopupNavPopupUrl2[] = "https://tests-popup2.com/popup.html"; const char kPopupNavPopupName[] = "my_popup"; // Browser side. class PopupNavTestHandler : public TestHandler { public: enum TestMode { ALLOW_CLOSE_POPUP_FIRST, ALLOW_CLOSE_POPUP_LAST, DENY, NAVIGATE_AFTER_CREATION, DESTROY_PARENT_BEFORE_CREATION, DESTROY_PARENT_BEFORE_CREATION_FORCE, DESTROY_PARENT_DURING_CREATION, DESTROY_PARENT_DURING_CREATION_FORCE, DESTROY_PARENT_AFTER_CREATION, DESTROY_PARENT_AFTER_CREATION_FORCE, }; 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. std::string page = "Page"; AddResource(kPopupNavPageUrl, page, "text/html"); AddResource(kPopupNavPopupUrl, "Popup", "text/html"); if (mode_ == NAVIGATE_AFTER_CREATION) { AddResource(kPopupNavPopupUrl2, "Popup2", "text/html"); } // By default, TestHandler signals test completion when all CefBrowsers // have closed. For this test we instead want to wait for an explicit call // to DestroyTest before signaling test completion. SetSignalTestCompletionCount(1U); CreateTestRequestContext( rc_mode_, rc_cache_path_, base::BindOnce(&PopupNavTestHandler::RunTestContinue, this)); // Time out the test after a reasonable period of time. SetTestTimeout(); } void RunTestContinue(CefRefPtr request_context) { EXPECT_UI_THREAD(); if (request_context) { GrantPopupPermission(request_context, kPopupNavPageUrl); } else { GrantPopupPermission(CefRequestContext::GetGlobalContext(), kPopupNavPageUrl); } // Create the browser. CreateBrowser(kPopupNavPageUrl, request_context); } bool OnBeforePopup(CefRefPtr browser, CefRefPtr frame, int popup_id, const CefString& target_url, const CefString& target_frame_name, cef_window_open_disposition_t target_disposition, bool user_gesture, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr& client, CefBrowserSettings& settings, CefRefPtr& extra_info, bool* no_javascript_access) override { EXPECT_FALSE(got_on_before_popup_); EXPECT_FALSE(got_on_before_popup_aborted_); got_on_before_popup_.yes(); EXPECT_TRUE(CefCurrentlyOn(TID_UI)); // Only ever a single popup with this test. EXPECT_EQ(1, popup_id); opener_browser_id_ = browser->GetIdentifier(); EXPECT_EQ(GetBrowserId(), opener_browser_id_); EXPECT_STREQ(kPopupNavPageUrl, frame->GetURL().ToString().c_str()); EXPECT_STREQ(kPopupNavPopupUrl, target_url.ToString().c_str()); EXPECT_STREQ(kPopupNavPopupName, target_frame_name.ToString().c_str()); EXPECT_EQ(CEF_WOD_NEW_FOREGROUND_TAB, target_disposition); EXPECT_FALSE(user_gesture); EXPECT_FALSE(*no_javascript_access); if (mode_ == DESTROY_PARENT_DURING_CREATION || mode_ == DESTROY_PARENT_DURING_CREATION_FORCE) { // Destroy the main (parent) browser while popup creation is pending. CloseBrowser(browser, mode_ == DESTROY_PARENT_DURING_CREATION_FORCE); } return (mode_ == DENY); // Return true to cancel the popup. } void OnBeforePopupAborted(CefRefPtr browser, int popup_id) override { EXPECT_TRUE(got_on_before_popup_); EXPECT_FALSE(got_on_before_popup_aborted_); got_on_before_popup_aborted_.yes(); EXPECT_TRUE(CefCurrentlyOn(TID_UI)); // Can't use GetBrowserId() here because the opener is already closing. EXPECT_EQ(opener_browser_id_, browser->GetIdentifier()); EXPECT_FALSE(browser->IsValid()); // Only ever a single popup with this test. EXPECT_EQ(1, popup_id); } void OnAfterCreated(CefRefPtr browser) override { TestHandler::OnAfterCreated(browser); if (browser->IsPopup()) { EXPECT_TRUE(got_on_before_popup_); EXPECT_EQ(opener_browser_id_, browser->GetHost()->GetOpenerIdentifier()); } else { EXPECT_FALSE(got_on_before_popup_); EXPECT_EQ(0, browser->GetHost()->GetOpenerIdentifier()); } EXPECT_FALSE(got_on_before_popup_aborted_); if (browser->IsPopup() && (mode_ == DESTROY_PARENT_AFTER_CREATION || mode_ == DESTROY_PARENT_AFTER_CREATION_FORCE)) { // Destroy the main (parent) browser immediately after the popup is // created. CloseBrowser(GetBrowser(), mode_ == DESTROY_PARENT_AFTER_CREATION_FORCE); } if (mode_ == NAVIGATE_AFTER_CREATION && browser->IsPopup()) { // Navigate to the 2nd popup URL instead of the 1st popup URL. browser->GetMainFrame()->LoadURL(kPopupNavPopupUrl2); } } void OnLoadStart(CefRefPtr browser, CefRefPtr frame, TransitionType transition_type) override { const std::string& url = frame->GetURL(); if (url == kPopupNavPageUrl) { EXPECT_FALSE(got_load_start_); got_load_start_.yes(); } else if (url == kPopupNavPopupUrl) { EXPECT_FALSE(got_popup_load_start_); got_popup_load_start_.yes(); } else if (url == kPopupNavPopupUrl2) { EXPECT_FALSE(got_popup_load_start2_); got_popup_load_start2_.yes(); } } void OnLoadError(CefRefPtr browser, CefRefPtr frame, ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl) override { if (failedUrl == kPopupNavPageUrl) { EXPECT_FALSE(got_load_error_); got_load_error_.yes(); } else if (failedUrl == kPopupNavPopupUrl) { EXPECT_FALSE(got_popup_load_error_); got_popup_load_error_.yes(); } else if (failedUrl == kPopupNavPopupUrl2) { EXPECT_FALSE(got_popup_load_error2_); got_popup_load_error2_.yes(); } } void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { const std::string& url = frame->GetURL(); if (url == kPopupNavPageUrl) { EXPECT_FALSE(got_load_end_); got_load_end_.yes(); frame->ExecuteJavaScript("doPopup()", kPopupNavPageUrl, 0); if (mode_ == DESTROY_PARENT_BEFORE_CREATION || mode_ == DESTROY_PARENT_BEFORE_CREATION_FORCE) { // Destroy the main (parent) browser immediately before the popup is // created. CloseBrowser(browser, mode_ == DESTROY_PARENT_BEFORE_CREATION_FORCE); } if (mode_ == DENY) { // Wait a bit to make sure the popup window isn't created. CefPostDelayedTask(TID_UI, base::BindOnce(&PopupNavTestHandler::DestroyTest, weak_ptr_factory_.GetWeakPtr()), 200); } } else if (url == kPopupNavPopupUrl) { EXPECT_FALSE(got_popup_load_end_); got_popup_load_end_.yes(); if (mode_ == ALLOW_CLOSE_POPUP_FIRST) { // Close the popup browser first. CloseBrowser(browser, false); } else if (mode_ == ALLOW_CLOSE_POPUP_LAST) { // Close the main browser first. CloseBrowser(GetBrowser(), false); } else if (mode_ != NAVIGATE_AFTER_CREATION) { EXPECT_FALSE(true); // Not reached. } } else if (url == kPopupNavPopupUrl2) { EXPECT_FALSE(got_popup_load_end2_); got_popup_load_end2_.yes(); if (mode_ == NAVIGATE_AFTER_CREATION) { // Close the popup browser first. CloseBrowser(browser, false); } else { EXPECT_FALSE(true); // Not reached. } } else { EXPECT_FALSE(true); // Not reached. } } void OnBeforeClose(CefRefPtr browser) override { TestHandler::OnBeforeClose(browser); bool destroy_test = false; if (mode_ == ALLOW_CLOSE_POPUP_FIRST || mode_ == NAVIGATE_AFTER_CREATION) { // Destroy the test after the popup browser closes. if (browser->IsPopup()) { destroy_test = true; } } else if (mode_ == ALLOW_CLOSE_POPUP_LAST || mode_ == DESTROY_PARENT_BEFORE_CREATION || mode_ == DESTROY_PARENT_BEFORE_CREATION_FORCE || mode_ == DESTROY_PARENT_DURING_CREATION || mode_ == DESTROY_PARENT_DURING_CREATION_FORCE || mode_ == DESTROY_PARENT_AFTER_CREATION || mode_ == DESTROY_PARENT_AFTER_CREATION_FORCE) { // Destroy the test after the main browser closes. if (!browser->IsPopup()) { destroy_test = true; } } if (destroy_test) { // This may race with OnBeforeClose() for the remaining browser. CefPostTask(TID_UI, base::BindOnce(&PopupNavTestHandler::DestroyTest, weak_ptr_factory_.GetWeakPtr())); } } private: void DestroyTest() override { EXPECT_TRUE(got_load_start_); EXPECT_FALSE(got_load_error_); EXPECT_TRUE(got_load_end_); // OnBeforePopup may come before or after browser destruction with the // DESTROY_PARENT_BEFORE_CREATION* tests and Alloy style browsers. if (mode_ != DESTROY_PARENT_BEFORE_CREATION && mode_ != DESTROY_PARENT_BEFORE_CREATION_FORCE) { EXPECT_TRUE(got_on_before_popup_); } else if (!use_alloy_style_browser()) { EXPECT_FALSE(got_on_before_popup_); } if (mode_ == DESTROY_PARENT_DURING_CREATION || mode_ == DESTROY_PARENT_DURING_CREATION_FORCE || mode_ == DESTROY_PARENT_AFTER_CREATION || mode_ == DESTROY_PARENT_AFTER_CREATION_FORCE) { // Timing of Alloy style browsers may not result in abort. if (!use_alloy_style_browser()) { EXPECT_TRUE(got_on_before_popup_aborted_); } } else { EXPECT_FALSE(got_on_before_popup_aborted_); } if (mode_ == ALLOW_CLOSE_POPUP_FIRST || mode_ == ALLOW_CLOSE_POPUP_LAST) { EXPECT_TRUE(got_popup_load_start_); EXPECT_FALSE(got_popup_load_error_); EXPECT_TRUE(got_popup_load_end_); EXPECT_FALSE(got_popup_load_start2_); EXPECT_FALSE(got_popup_load_error2_); EXPECT_FALSE(got_popup_load_end2_); } else if (mode_ == DENY || mode_ == DESTROY_PARENT_BEFORE_CREATION || mode_ == DESTROY_PARENT_BEFORE_CREATION_FORCE || mode_ == DESTROY_PARENT_DURING_CREATION || mode_ == DESTROY_PARENT_DURING_CREATION_FORCE || mode_ == DESTROY_PARENT_AFTER_CREATION || mode_ == DESTROY_PARENT_AFTER_CREATION_FORCE) { EXPECT_FALSE(got_popup_load_start_); EXPECT_FALSE(got_popup_load_error_); EXPECT_FALSE(got_popup_load_end_); EXPECT_FALSE(got_popup_load_start2_); EXPECT_FALSE(got_popup_load_error2_); EXPECT_FALSE(got_popup_load_end2_); } else if (mode_ == NAVIGATE_AFTER_CREATION) { EXPECT_FALSE(got_popup_load_start_); // With browser-side navigation we will never actually begin the // navigation to the 1st popup URL, so there will be no load error. EXPECT_FALSE(got_popup_load_error_); EXPECT_FALSE(got_popup_load_end_); EXPECT_TRUE(got_popup_load_start2_); EXPECT_FALSE(got_popup_load_error2_); EXPECT_TRUE(got_popup_load_end2_); } // Invalidate WeakPtrs now as |this| may be deleted on a different thread. weak_ptr_factory_.InvalidateWeakPtrs(); // Will trigger destruction of any remaining browsers. TestHandler::DestroyTest(); // Allow the the test to complete once all browsers are gone. SignalTestCompletion(); } const TestMode mode_; const TestRequestContextMode rc_mode_; const std::string rc_cache_path_; TrackCallback got_on_before_popup_; int opener_browser_id_ = 0; TrackCallback got_on_before_popup_aborted_; TrackCallback got_load_start_; TrackCallback got_load_error_; TrackCallback got_load_end_; TrackCallback got_popup_load_start_; TrackCallback got_popup_load_error_; TrackCallback got_popup_load_end_; TrackCallback got_popup_load_start2_; TrackCallback got_popup_load_error2_; TrackCallback got_popup_load_end2_; base::WeakPtrFactory weak_ptr_factory_{this}; IMPLEMENT_REFCOUNTING(PopupNavTestHandler); }; } // namespace #define POPUP_TEST_GROUP(test_name, test_mode) \ RC_TEST_GROUP_IN_MEMORY(RequestContextTest, PopupNav##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). 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). 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). POPUP_TEST_GROUP(DestroyParentBeforeCreation, DESTROY_PARENT_BEFORE_CREATION) POPUP_TEST_GROUP(DestroyParentBeforeCreationForce, DESTROY_PARENT_BEFORE_CREATION_FORCE) POPUP_TEST_GROUP(DestroyParentDuringCreation, DESTROY_PARENT_DURING_CREATION) POPUP_TEST_GROUP(DestroyParentDuringCreationForce, DESTROY_PARENT_DURING_CREATION_FORCE) POPUP_TEST_GROUP(DestroyParentAfterCreation, DESTROY_PARENT_AFTER_CREATION) POPUP_TEST_GROUP(DestroyParentAfterCreationForce, DESTROY_PARENT_AFTER_CREATION_FORCE) namespace { const char kResolveOrigin[] = "https://www.google.com"; class MethodTestHandler : public TestHandler { public: enum Method { METHOD_CLEAR_CERTIFICATE_EXCEPTIONS, METHOD_CLOSE_ALL_CONNECTIONS, METHOD_RESOLVE_HOST, }; class CompletionCallback : public CefCompletionCallback, public CefResolveCallback { public: CompletionCallback(MethodTestHandler* test_handler, CefRefPtr browser) : test_handler_(test_handler), browser_(browser) {} ~CompletionCallback() override { // OnComplete should be executed. EXPECT_FALSE(test_handler_); } void OnComplete() override { EXPECT_UI_THREAD(); // OnComplete should be executed only one time. EXPECT_TRUE(test_handler_); test_handler_->OnCompleteCallback(browser_); test_handler_ = nullptr; browser_ = nullptr; } void OnResolveCompleted( cef_errorcode_t result, const std::vector& resolved_ips) override { EXPECT_EQ(ERR_NONE, result); EXPECT_TRUE(!resolved_ips.empty()); OnComplete(); } private: MethodTestHandler* test_handler_; CefRefPtr browser_; IMPLEMENT_REFCOUNTING(CompletionCallback); }; MethodTestHandler(bool global_context, Method method) : global_context_(global_context), method_(method) {} void RunTest() override { const char kUrl[] = "https://tests/method.html"; AddResource(kUrl, "Method", "text/html"); CefRefPtr request_context; if (!global_context_) { CefRequestContextSettings settings; request_context = CefRequestContext::CreateContext(settings, nullptr); } CreateBrowser(kUrl, request_context); // Time out the test after a reasonable period of time. SetTestTimeout(); } void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { CefRefPtr context = browser->GetHost()->GetRequestContext(); CefRefPtr callback = new CompletionCallback(this, browser); if (method_ == METHOD_CLEAR_CERTIFICATE_EXCEPTIONS) { context->ClearCertificateExceptions(callback); } else if (method_ == METHOD_CLOSE_ALL_CONNECTIONS) { context->CloseAllConnections(callback); } else if (method_ == METHOD_RESOLVE_HOST) { context->ResolveHost(kResolveOrigin, callback); } } void OnCompleteCallback(CefRefPtr browser) { EXPECT_UI_THREAD(); EXPECT_FALSE(got_completion_callback_); got_completion_callback_.yes(); DestroyTest(); } private: void DestroyTest() override { EXPECT_TRUE(got_completion_callback_); TestHandler::DestroyTest(); } const bool global_context_; const Method method_; TrackCallback got_completion_callback_; IMPLEMENT_REFCOUNTING(MethodTestHandler); }; } // namespace // Test CefRequestContext::ClearCertificateExceptions with the global // context. TEST(RequestContextTest, ClearCertificateExceptionsGlobal) { CefRefPtr handler = new MethodTestHandler( true, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } // Test CefRequestContext::ClearCertificateExceptions with a custom // context. TEST(RequestContextTest, ClearCertificateExceptionsCustom) { CefRefPtr handler = new MethodTestHandler( false, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } // Test CefRequestContext::CloseAllConnections with the global // context. TEST(RequestContextTest, CloseAllConnectionsGlobal) { CefRefPtr handler = new MethodTestHandler( true, MethodTestHandler::METHOD_CLOSE_ALL_CONNECTIONS); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } // Test CefRequestContext::CloseAllConnections with a custom context. TEST(RequestContextTest, CloseAllConnectionsCustom) { CefRefPtr handler = new MethodTestHandler( false, MethodTestHandler::METHOD_CLOSE_ALL_CONNECTIONS); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } // Test CefRequestContext::ResolveHost with the global context. TEST(RequestContextTest, ResolveHostGlobal) { CefRefPtr handler = new MethodTestHandler(true, MethodTestHandler::METHOD_RESOLVE_HOST); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } // Test CefRequestContext::ResolveHost with a custom context. TEST(RequestContextTest, ResolveHostCustom) { CefRefPtr handler = new MethodTestHandler(false, MethodTestHandler::METHOD_RESOLVE_HOST); handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); }