// Copyright (c) 2011 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 this first to avoid type conflicts with CEF headers. #include "tests/unittests/chromium_includes.h" #include "include/base/cef_bind.h" #include "include/cef_callback.h" #include "include/cef_scheme.h" #include "include/wrapper/cef_closure_task.h" #include "testing/gtest/include/gtest/gtest.h" #include "tests/cefclient/client_app.h" #include "tests/unittests/test_handler.h" #include "tests/unittests/test_util.h" namespace { const char kHNav1[] = "http://tests-hnav/nav1.html"; const char kHNav2[] = "http://tests-hnav/nav2.html"; const char kHNav3[] = "http://tests-hnav/nav3.html"; const char kHistoryNavMsg[] = "NavigationTest.HistoryNav"; enum NavAction { NA_LOAD = 1, NA_BACK, NA_FORWARD, NA_CLEAR }; typedef struct { NavAction action; // What to do const char* target; // Where to be after navigation bool can_go_back; // After navigation, can go back? bool can_go_forward; // After navigation, can go forward? } NavListItem; // Array of navigation actions: X = current page, . = history exists static NavListItem kHNavList[] = { // kHNav1 | kHNav2 | kHNav3 {NA_LOAD, kHNav1, false, false}, // X {NA_LOAD, kHNav2, true, false}, // . X {NA_BACK, kHNav1, false, true}, // X . {NA_FORWARD, kHNav2, true, false}, // . X {NA_LOAD, kHNav3, true, false}, // . . X {NA_BACK, kHNav2, true, true}, // . X . // TODO(cef): Enable once ClearHistory is implemented // {NA_CLEAR, kHNav2, false, false}, // X }; #define NAV_LIST_SIZE() (sizeof(kHNavList) / sizeof(NavListItem)) bool g_history_nav_test = false; // Browser side. class HistoryNavBrowserTest : public ClientApp::BrowserDelegate { public: HistoryNavBrowserTest() {} virtual void OnBeforeChildProcessLaunch( CefRefPtr app, CefRefPtr command_line) OVERRIDE { if (!g_history_nav_test) return; // Indicate to the render process that the test should be run. command_line->AppendSwitchWithValue("test", kHistoryNavMsg); } protected: IMPLEMENT_REFCOUNTING(HistoryNavBrowserTest); }; // Renderer side. class HistoryNavRendererTest : public ClientApp::RenderDelegate, public CefLoadHandler { public: HistoryNavRendererTest() : run_test_(false), nav_(0) {} virtual void OnRenderThreadCreated( CefRefPtr app, CefRefPtr extra_info) OVERRIDE { if (!g_history_nav_test) { // Check that the test should be run. CefRefPtr command_line = CefCommandLine::GetGlobalCommandLine(); const std::string& test = command_line->GetSwitchValue("test"); if (test != kHistoryNavMsg) return; } run_test_ = true; } virtual CefRefPtr GetLoadHandler( CefRefPtr app) OVERRIDE { if (!run_test_) return NULL; return this; } virtual void OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) OVERRIDE { const NavListItem& item = kHNavList[nav_]; const std::string& url = browser->GetMainFrame()->GetURL(); if (isLoading) { got_loading_state_start_.yes(); EXPECT_STRNE(item.target, url.c_str()); if (nav_ > 0) { const NavListItem& last_item = kHNavList[nav_ - 1]; EXPECT_EQ(last_item.can_go_back, browser->CanGoBack()); EXPECT_EQ(last_item.can_go_back, canGoBack); EXPECT_EQ(last_item.can_go_forward, browser->CanGoForward()); EXPECT_EQ(last_item.can_go_forward, canGoForward); } else { EXPECT_FALSE(browser->CanGoBack()); EXPECT_FALSE(canGoBack); EXPECT_FALSE(browser->CanGoForward()); EXPECT_FALSE(canGoForward); } } else { got_loading_state_end_.yes(); EXPECT_STREQ(item.target, url.c_str()); EXPECT_EQ(item.can_go_back, browser->CanGoBack()); EXPECT_EQ(item.can_go_back, canGoBack); EXPECT_EQ(item.can_go_forward, browser->CanGoForward()); EXPECT_EQ(item.can_go_forward, canGoForward); SendTestResultsIfDone(browser); } } virtual void OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { const NavListItem& item = kHNavList[nav_]; got_load_start_.yes(); const std::string& url = frame->GetURL(); EXPECT_STREQ(item.target, url.c_str()); EXPECT_EQ(item.can_go_back, browser->CanGoBack()); EXPECT_EQ(item.can_go_forward, browser->CanGoForward()); } virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) OVERRIDE { const NavListItem& item = kHNavList[nav_]; got_load_end_.yes(); const std::string& url = frame->GetURL(); EXPECT_STREQ(item.target, url.c_str()); EXPECT_EQ(item.can_go_back, browser->CanGoBack()); EXPECT_EQ(item.can_go_forward, browser->CanGoForward()); SendTestResultsIfDone(browser); } virtual bool OnBeforeNavigation(CefRefPtr app, CefRefPtr browser, CefRefPtr frame, CefRefPtr request, cef_navigation_type_t navigation_type, bool is_redirect) OVERRIDE { if (!run_test_) return false; const NavListItem& item = kHNavList[nav_]; std::string url = request->GetURL(); EXPECT_STREQ(item.target, url.c_str()); EXPECT_EQ(RT_SUB_RESOURCE, request->GetResourceType()); EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); if (item.action == NA_LOAD) EXPECT_EQ(NAVIGATION_OTHER, navigation_type); else if (item.action == NA_BACK || item.action == NA_FORWARD) EXPECT_EQ(NAVIGATION_BACK_FORWARD, navigation_type); if (nav_ > 0) { const NavListItem& last_item = kHNavList[nav_ - 1]; EXPECT_EQ(last_item.can_go_back, browser->CanGoBack()); EXPECT_EQ(last_item.can_go_forward, browser->CanGoForward()); } else { EXPECT_FALSE(browser->CanGoBack()); EXPECT_FALSE(browser->CanGoForward()); } return false; } protected: void SendTestResultsIfDone(CefRefPtr browser) { if (got_load_end_ && got_loading_state_end_) SendTestResults(browser); } // Send the test results. void SendTestResults(CefRefPtr browser) { EXPECT_TRUE(got_loading_state_start_); EXPECT_TRUE(got_loading_state_end_); EXPECT_TRUE(got_load_start_); EXPECT_TRUE(got_load_end_); // Check if the test has failed. bool result = !TestFailed(); // Return the result to the browser process. CefRefPtr return_msg = CefProcessMessage::Create(kHistoryNavMsg); CefRefPtr args = return_msg->GetArgumentList(); EXPECT_TRUE(args.get()); EXPECT_TRUE(args->SetInt(0, nav_)); EXPECT_TRUE(args->SetBool(1, result)); EXPECT_TRUE(browser->SendProcessMessage(PID_BROWSER, return_msg)); // Reset the test results for the next navigation. got_loading_state_start_.reset(); got_loading_state_end_.reset(); got_load_start_.reset(); got_load_end_.reset(); nav_++; } bool run_test_; int nav_; TrackCallback got_loading_state_start_; TrackCallback got_loading_state_end_; TrackCallback got_load_start_; TrackCallback got_load_end_; IMPLEMENT_REFCOUNTING(HistoryNavRendererTest); }; // Browser side. class HistoryNavTestHandler : public TestHandler { public: HistoryNavTestHandler() : nav_(0), load_end_confirmation_(false), renderer_confirmation_(false) {} virtual void RunTest() OVERRIDE { // Add the resources that we will navigate to/from. AddResource(kHNav1, "Nav1", "text/html"); AddResource(kHNav2, "Nav2", "text/html"); AddResource(kHNav3, "Nav3", "text/html"); // Create the browser. CreateBrowser(CefString()); } void RunNav(CefRefPtr browser) { if (nav_ == NAV_LIST_SIZE()) { // End of the nav list. DestroyTest(); return; } const NavListItem& item = kHNavList[nav_]; // Perform the action. switch (item.action) { case NA_LOAD: browser->GetMainFrame()->LoadURL(item.target); break; case NA_BACK: browser->GoBack(); break; case NA_FORWARD: browser->GoForward(); break; case NA_CLEAR: // TODO(cef): Enable once ClearHistory is implemented // browser->GetHost()->ClearHistory(); // Not really a navigation action so go to the next one. nav_++; RunNav(browser); break; default: break; } } void RunNextNavIfReady(CefRefPtr browser) { if (load_end_confirmation_ && renderer_confirmation_) { load_end_confirmation_ = false; renderer_confirmation_ = false; nav_++; RunNav(browser); } } virtual void OnAfterCreated(CefRefPtr browser) OVERRIDE { TestHandler::OnAfterCreated(browser); RunNav(browser); } virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, bool is_redirect) OVERRIDE { const NavListItem& item = kHNavList[nav_]; got_before_browse_[nav_].yes(); std::string url = request->GetURL(); EXPECT_STREQ(item.target, url.c_str()); EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType()); if (item.action == NA_LOAD) EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); else if (item.action == NA_BACK || item.action == NA_FORWARD) EXPECT_EQ(TT_EXPLICIT | TT_FORWARD_BACK_FLAG, request->GetTransitionType()); if (nav_ > 0) { const NavListItem& last_item = kHNavList[nav_ - 1]; EXPECT_EQ(last_item.can_go_back, browser->CanGoBack()); EXPECT_EQ(last_item.can_go_forward, browser->CanGoForward()); } else { EXPECT_FALSE(browser->CanGoBack()); EXPECT_FALSE(browser->CanGoForward()); } return false; } virtual bool OnBeforeResourceLoad(CefRefPtr browser, CefRefPtr frame, CefRefPtr request) OVERRIDE { const NavListItem& item = kHNavList[nav_]; EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType()); if (item.action == NA_LOAD) EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); else if (item.action == NA_BACK || item.action == NA_FORWARD) EXPECT_EQ(TT_EXPLICIT | TT_FORWARD_BACK_FLAG, request->GetTransitionType()); got_before_resource_load_[nav_].yes(); std::string url = request->GetURL(); if (url == item.target) got_correct_target_[nav_].yes(); return false; } virtual void OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) OVERRIDE { const NavListItem& item = kHNavList[nav_]; got_loading_state_change_[nav_].yes(); if (item.can_go_back == canGoBack) got_correct_can_go_back_[nav_].yes(); if (item.can_go_forward == canGoForward) got_correct_can_go_forward_[nav_].yes(); } virtual void OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { if(browser->IsPopup() || !frame->IsMain()) return; const NavListItem& item = kHNavList[nav_]; got_load_start_[nav_].yes(); std::string url1 = browser->GetMainFrame()->GetURL(); std::string url2 = frame->GetURL(); if (url1 == item.target && url2 == item.target) got_correct_load_start_url_[nav_].yes(); } virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) OVERRIDE { if (browser->IsPopup() || !frame->IsMain()) return; const NavListItem& item = kHNavList[nav_]; got_load_end_[nav_].yes(); std::string url1 = browser->GetMainFrame()->GetURL(); std::string url2 = frame->GetURL(); if (url1 == item.target && url2 == item.target) got_correct_load_end_url_[nav_].yes(); if (item.can_go_back == browser->CanGoBack()) got_correct_can_go_back2_[nav_].yes(); if (item.can_go_forward == browser->CanGoForward()) got_correct_can_go_forward2_[nav_].yes(); load_end_confirmation_ = true; RunNextNavIfReady(browser); } virtual bool OnProcessMessageReceived( CefRefPtr browser, CefProcessId source_process, CefRefPtr message) OVERRIDE { if (message->GetName().ToString() == kHistoryNavMsg) { got_before_navigation_[nav_].yes(); // Test that the renderer side succeeded. CefRefPtr args = message->GetArgumentList(); EXPECT_TRUE(args.get()); EXPECT_EQ(nav_, args->GetInt(0)); EXPECT_TRUE(args->GetBool(1)); renderer_confirmation_ = true; RunNextNavIfReady(browser); return true; } // Message not handled. return false; } int nav_; bool load_end_confirmation_; bool renderer_confirmation_; TrackCallback got_before_browse_[NAV_LIST_SIZE()]; TrackCallback got_before_navigation_[NAV_LIST_SIZE()]; TrackCallback got_before_resource_load_[NAV_LIST_SIZE()]; TrackCallback got_correct_target_[NAV_LIST_SIZE()]; TrackCallback got_loading_state_change_[NAV_LIST_SIZE()]; TrackCallback got_correct_can_go_back_[NAV_LIST_SIZE()]; TrackCallback got_correct_can_go_forward_[NAV_LIST_SIZE()]; TrackCallback got_load_start_[NAV_LIST_SIZE()]; TrackCallback got_correct_load_start_url_[NAV_LIST_SIZE()]; TrackCallback got_load_end_[NAV_LIST_SIZE()]; TrackCallback got_correct_load_end_url_[NAV_LIST_SIZE()]; TrackCallback got_correct_can_go_back2_[NAV_LIST_SIZE()]; TrackCallback got_correct_can_go_forward2_[NAV_LIST_SIZE()]; }; } // namespace // Verify history navigation. TEST(NavigationTest, History) { g_history_nav_test = true; CefRefPtr handler = new HistoryNavTestHandler(); handler->ExecuteTest(); g_history_nav_test = false; for (size_t i = 0; i < NAV_LIST_SIZE(); ++i) { if (kHNavList[i].action != NA_CLEAR) { ASSERT_TRUE(handler->got_before_browse_[i]) << "i = " << i; ASSERT_TRUE(handler->got_before_navigation_[i]) << "i = " << i; ASSERT_TRUE(handler->got_before_resource_load_[i]) << "i = " << i; ASSERT_TRUE(handler->got_correct_target_[i]) << "i = " << i; ASSERT_TRUE(handler->got_load_start_[i]) << "i = " << i; ASSERT_TRUE(handler->got_correct_load_start_url_[i]) << "i = " << i; } ASSERT_TRUE(handler->got_loading_state_change_[i]) << "i = " << i; ASSERT_TRUE(handler->got_correct_can_go_back_[i]) << "i = " << i; ASSERT_TRUE(handler->got_correct_can_go_forward_[i]) << "i = " << i; if (kHNavList[i].action != NA_CLEAR) { ASSERT_TRUE(handler->got_load_end_[i]) << "i = " << i; ASSERT_TRUE(handler->got_correct_load_end_url_[i]) << "i = " << i; ASSERT_TRUE(handler->got_correct_can_go_back2_[i]) << "i = " << i; ASSERT_TRUE(handler->got_correct_can_go_forward2_[i]) << "i = " << i; } } } namespace { const char kFNav1[] = "http://tests/nav1.html"; const char kFNav2[] = "http://tests/nav2.html"; const char kFNav3[] = "http://tests/nav3.html"; class FrameNameIdentNavTestHandler : public TestHandler { public: FrameNameIdentNavTestHandler() : browse_ct_(0) {} virtual void RunTest() OVERRIDE { // Add the frame resources. std::stringstream ss; // Page with named frame ss << "Nav1