// 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 #include #include #include #include "include/base/cef_bind.h" #include "include/base/cef_scoped_ptr.h" #include "include/cef_cookie.h" #include "include/cef_request_context_handler.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_stream_resource_handler.h" #include "tests/ceftests/test_handler.h" #include "tests/ceftests/test_util.h" #include "tests/gtest/include/gtest/gtest.h" #include "tests/shared/browser/client_app_browser.h" #include "tests/shared/renderer/client_app_renderer.h" using client::ClientAppBrowser; using client::ClientAppRenderer; namespace { enum NetNotifyTestType { NNTT_NONE = 0, NNTT_NORMAL, NNTT_DELAYED_RENDERER, NNTT_DELAYED_BROWSER, }; const char kNetNotifyOrigin1[] = "http://tests-netnotify1/"; const char kNetNotifyOrigin2[] = "http://tests-netnotify2/"; const char kNetNotifyMsg[] = "RequestHandlerTest.NetNotify"; const char kNetNotifyTestCmdKey[] = "rh-net-notify-test"; // Browser side. class NetNotifyTestHandler : public TestHandler { public: NetNotifyTestHandler(CompletionState* completion_state, NetNotifyTestType test_type, bool same_origin) : TestHandler(completion_state), test_type_(test_type), same_origin_(same_origin) {} void SetupTest() override { std::stringstream ss; ss << kNetNotifyOrigin1 << "nav1.html?t=" << test_type_; url1_ = ss.str(); ss.str(""); ss << (same_origin_ ? kNetNotifyOrigin1 : kNetNotifyOrigin2) << "nav2.html?t=" << test_type_; url2_ = ss.str(); const std::string& resource1 = "" "" "Nav1" ""; response_length1_ = static_cast(resource1.size()); AddResource(url1_, resource1, "text/html"); const std::string& resource2 = "" "" "Nav2" ""; response_length2_ = static_cast(resource2.size()); AddResource(url2_, resource2, "text/html"); // Create the request context that will use an in-memory cache. CefRequestContextSettings settings; CefRefPtr request_context = CefRequestContext::CreateContext(settings, nullptr); cookie_manager_ = request_context->GetCookieManager(nullptr); CefRefPtr extra_info = CefDictionaryValue::Create(); extra_info->SetBool(kNetNotifyTestCmdKey, true); // Create browser that loads the 1st URL. CreateBrowser(url1_, request_context, extra_info); } void RunTest() override { // Navigate to the 2nd URL. GetBrowser()->GetMainFrame()->LoadURL(url2_); // Time out the test after a reasonable period of time. SetTestTimeout(); } cef_return_value_t OnBeforeResourceLoad( CefRefPtr browser, CefRefPtr frame, CefRefPtr request, CefRefPtr callback) override { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); const std::string& url = request->GetURL(); if (url.find(url1_) == 0) got_before_resource_load1_.yes(); else if (url.find(url2_) == 0) got_before_resource_load2_.yes(); else EXPECT_TRUE(false); // Not reached return RV_CONTINUE; } CefRefPtr GetResourceHandler( CefRefPtr browser, CefRefPtr frame, CefRefPtr request) override { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); const std::string& url = request->GetURL(); if (url.find(url1_) == 0) got_get_resource_handler1_.yes(); else if (url.find(url2_) == 0) got_get_resource_handler2_.yes(); else EXPECT_TRUE(false); // Not reached return TestHandler::GetResourceHandler(browser, frame, request); } void OnResourceLoadComplete(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, CefRefPtr response, URLRequestStatus status, int64 received_content_length) override { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); EXPECT_EQ(UR_SUCCESS, status); const std::string& url = request->GetURL(); if (url.find(url1_) == 0) { got_resource_load_complete1_.yes(); EXPECT_EQ(response_length1_, received_content_length); } else if (url.find(url2_) == 0) { got_resource_load_complete2_.yes(); EXPECT_EQ(response_length2_, received_content_length); } else { EXPECT_TRUE(false); // Not reached } } bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, bool user_gesture, bool is_redirect) override { std::string url = request->GetURL(); // Check if the load has already been delayed. bool delay_loaded = (url.find("delayed=true") != std::string::npos); if (url.find(url1_) == 0) { got_before_browse1_.yes(); EXPECT_FALSE(delay_loaded); } else if (url.find(url2_) == 0) { got_before_browse2_.yes(); if (delay_loaded) { got_before_browse2_delayed_.yes(); } else if (test_type_ == NNTT_DELAYED_RENDERER || test_type_ == NNTT_DELAYED_BROWSER) { got_before_browse2_will_delay_.yes(); // Navigating cross-origin from the browser process will cause a new // render process to be created. We therefore need some information in // the request itself to tell us that the navigation has already been // delayed. // Navigating cross-origin from the renderer process will cause the // process to be terminated with "bad IPC message" reason // INVALID_INITIATOR_ORIGIN (213). url += "&delayed=true"; if (test_type_ == NNTT_DELAYED_RENDERER) { // Load the URL from the render process. CefRefPtr message = CefProcessMessage::Create(kNetNotifyMsg); CefRefPtr args = message->GetArgumentList(); args->SetInt(0, test_type_); args->SetString(1, url); frame->SendProcessMessage(PID_RENDERER, message); } else { // Load the URL from the browser process. frame->LoadURL(url); } // Cancel the load. return true; } } else { EXPECT_TRUE(false); // Not reached } // Allow the load to continue. return false; } void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { const std::string& url = frame->GetURL(); if (url.find(url1_) == 0) { got_load_end1_.yes(); SetupCompleteIfDone(); } else if (url.find(url2_) == 0) { got_load_end2_.yes(); FinishTestIfDone(); } else { EXPECT_TRUE(false); // Not reached } } bool OnProcessMessageReceived(CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message) override { if (message->GetName().ToString() == kNetNotifyMsg) { CefRefPtr args = message->GetArgumentList(); EXPECT_TRUE(args.get()); std::string url = args->GetString(0); if (url.find(url1_) == 0) { got_process_message1_.yes(); SetupCompleteIfDone(); } else if (url.find(url2_) == 0) { got_process_message2_.yes(); FinishTestIfDone(); } else { EXPECT_TRUE(false); // Not reached } return true; } // Message not handled. return false; } void OnRenderProcessTerminated(CefRefPtr browser, TerminationStatus status) override { got_process_terminated_ct_++; // Termination is expected for cross-origin requests initiated from the // renderer process. if (!(test_type_ == NNTT_DELAYED_RENDERER && !same_origin_)) { TestHandler::OnRenderProcessTerminated(browser, status); } FinishTest(); } protected: void SetupCompleteIfDone() { if (got_load_end1_ && got_process_message1_) SetupComplete(); } void FinishTestIfDone() { if (got_load_end2_ && got_process_message2_) FinishTest(); } void FinishTest() { // Verify that cookies were set correctly. class TestVisitor : public CefCookieVisitor { public: explicit TestVisitor(NetNotifyTestHandler* handler) : handler_(handler) {} ~TestVisitor() override { // Destroy the test. CefPostTask(TID_UI, base::Bind(&NetNotifyTestHandler::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: NetNotifyTestHandler* handler_; IMPLEMENT_REFCOUNTING(TestVisitor); }; cookie_manager_->VisitAllCookies(new TestVisitor(this)); } void DestroyTest() override { int browser_id = GetBrowser()->GetIdentifier(); // Verify test expectations. EXPECT_TRUE(got_before_browse1_) << " browser " << browser_id; EXPECT_TRUE(got_load_end1_) << " browser " << browser_id; EXPECT_TRUE(got_before_resource_load1_) << " browser " << browser_id; EXPECT_TRUE(got_get_resource_handler1_) << " browser " << browser_id; EXPECT_TRUE(got_resource_load_complete1_) << " browser " << browser_id; EXPECT_TRUE(got_cookie1_) << " browser " << browser_id; EXPECT_TRUE(got_process_message1_) << " browser " << browser_id; EXPECT_TRUE(got_before_browse2_) << " browser " << browser_id; if (test_type_ == NNTT_DELAYED_RENDERER && !same_origin_) { EXPECT_EQ(1, got_process_terminated_ct_) << " browser " << browser_id; EXPECT_FALSE(got_load_end2_) << " browser " << browser_id; EXPECT_FALSE(got_before_resource_load2_) << " browser " << browser_id; EXPECT_FALSE(got_get_resource_handler2_) << " browser " << browser_id; EXPECT_FALSE(got_resource_load_complete2_) << " browser " << browser_id; EXPECT_FALSE(got_cookie2_) << " browser " << browser_id; EXPECT_FALSE(got_process_message2_) << " browser " << browser_id; } else { EXPECT_EQ(0, got_process_terminated_ct_) << " browser " << browser_id; EXPECT_TRUE(got_load_end2_) << " browser " << browser_id; EXPECT_TRUE(got_before_resource_load2_) << " browser " << browser_id; EXPECT_TRUE(got_get_resource_handler2_) << " browser " << browser_id; EXPECT_TRUE(got_resource_load_complete2_) << " browser " << browser_id; EXPECT_TRUE(got_cookie2_) << " browser " << browser_id; EXPECT_TRUE(got_process_message2_) << " browser " << browser_id; } if (test_type_ == NNTT_DELAYED_RENDERER || test_type_ == NNTT_DELAYED_BROWSER) { EXPECT_TRUE(got_before_browse2_will_delay_) << " browser " << browser_id; if (test_type_ == NNTT_DELAYED_RENDERER && !same_origin_) { EXPECT_FALSE(got_before_browse2_delayed_) << " browser " << browser_id; } else { EXPECT_TRUE(got_before_browse2_delayed_) << " browser " << browser_id; } } else { EXPECT_FALSE(got_before_browse2_will_delay_) << " browser " << browser_id; EXPECT_FALSE(got_before_browse2_delayed_) << " browser " << browser_id; } cookie_manager_ = nullptr; TestHandler::DestroyTest(); } NetNotifyTestType test_type_; bool same_origin_; std::string url1_; std::string url2_; CefRefPtr cookie_manager_; TrackCallback got_before_browse1_; TrackCallback got_load_end1_; TrackCallback got_before_resource_load1_; TrackCallback got_get_resource_handler1_; TrackCallback got_resource_load_complete1_; TrackCallback got_cookie1_; TrackCallback got_process_message1_; TrackCallback got_before_browse2_; TrackCallback got_load_end2_; TrackCallback got_before_resource_load2_; TrackCallback got_get_resource_handler2_; TrackCallback got_resource_load_complete2_; TrackCallback got_cookie2_; TrackCallback got_process_message2_; TrackCallback got_before_browse2_will_delay_; TrackCallback got_before_browse2_delayed_; int got_process_terminated_ct_ = 0; int64 response_length1_; int64 response_length2_; IMPLEMENT_REFCOUNTING(NetNotifyTestHandler); }; // Renderer side. class NetNotifyRendererTest : public ClientAppRenderer::Delegate, public CefLoadHandler { public: NetNotifyRendererTest() : run_test_(false) {} void OnBrowserCreated(CefRefPtr app, CefRefPtr browser, CefRefPtr extra_info) override { run_test_ = extra_info->HasKey(kNetNotifyTestCmdKey); } CefRefPtr GetLoadHandler( CefRefPtr app) override { if (run_test_) return this; return nullptr; } void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { if (!run_test_) return; const std::string& url = frame->GetURL(); // Continue in the browser process. CefRefPtr message = CefProcessMessage::Create(kNetNotifyMsg); CefRefPtr args = message->GetArgumentList(); args->SetString(0, url); frame->SendProcessMessage(PID_BROWSER, message); } bool OnProcessMessageReceived(CefRefPtr app, CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message) override { if (message->GetName().ToString() == kNetNotifyMsg) { CefRefPtr args = message->GetArgumentList(); EXPECT_TRUE(args.get()); NetNotifyTestType test_type = static_cast(args->GetInt(0)); EXPECT_EQ(test_type, NNTT_DELAYED_RENDERER); const std::string& url = args->GetString(1); // Load the URL from the render process. frame->LoadURL(url); return true; } // Message not handled. return false; } private: bool run_test_; IMPLEMENT_REFCOUNTING(NetNotifyRendererTest); }; void RunNetNotifyTest(NetNotifyTestType test_type, bool same_origin, size_t count = 3U) { TestHandler::CompletionState completion_state(static_cast(count)); TestHandler::Collection collection(&completion_state); std::vector> handlers; for (size_t i = 0U; i < count; ++i) { CefRefPtr handler = new NetNotifyTestHandler(&completion_state, test_type, same_origin); collection.AddTestHandler(handler); handlers.push_back(handler); } collection.ExecuteTests(); while (!handlers.empty()) { auto handler = handlers.front(); handlers.erase(handlers.begin()); ReleaseAndWaitForDestructor(handler); } } } // namespace // Verify network notifications for multiple browsers existing simultaniously. // URL loading is from the same origin and is not delayed. TEST(RequestHandlerTest, NotificationsSameOriginDirect) { RunNetNotifyTest(NNTT_NORMAL, true); } // Verify network notifications for multiple browsers existing simultaniously. // URL loading is from the same origin and is continued asynchronously from the // render process. TEST(RequestHandlerTest, NotificationsSameOriginDelayedRenderer) { RunNetNotifyTest(NNTT_DELAYED_RENDERER, true); } // Verify network notifications for multiple browsers existing simultaniously. // URL loading is from the same origin and is continued asynchronously from the // browser process. TEST(RequestHandlerTest, NotificationsSameOriginDelayedBrowser) { RunNetNotifyTest(NNTT_DELAYED_BROWSER, true); } // Verify network notifications for multiple browsers existing simultaniously. // URL loading is from a different origin and is not delayed. TEST(RequestHandlerTest, NotificationsCrossOriginDirect) { RunNetNotifyTest(NNTT_NORMAL, false); } // Verify network notifications for multiple browsers existing simultaniously. // URL loading is from a different origin and is continued asynchronously from // the render process. TEST(RequestHandlerTest, NotificationsCrossOriginDelayedRenderer) { RunNetNotifyTest(NNTT_DELAYED_RENDERER, false); } // Verify network notifications for multiple browsers existing simultaniously. // URL loading is from a different origin and is continued asynchronously from // the browser process. TEST(RequestHandlerTest, NotificationsCrossOriginDelayedBrowser) { RunNetNotifyTest(NNTT_DELAYED_BROWSER, false); } // Entry point for creating request handler renderer test objects. // Called from client_app_delegates.cc. void CreateRequestHandlerRendererTests( ClientAppRenderer::DelegateSet& delegates) { delegates.insert(new NetNotifyRendererTest); }