// 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/cef_callback.h" #include "include/cef_scheme.h" #include "tests/cefclient/client_app.h" #include "tests/unittests/test_handler.h" #include "testing/gtest/include/gtest/gtest.h" namespace { const char kHNavDomain[] = "http://tests-hnav/"; 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)) // Renderer side. class HistoryNavRendererTest : public ClientApp::RenderDelegate { public: HistoryNavRendererTest() : nav_(0) {} virtual bool OnBeforeNavigation(CefRefPtr app, CefRefPtr browser, CefRefPtr frame, CefRefPtr request, cef_navigation_type_t navigation_type, bool is_redirect) OVERRIDE { std::string url = request->GetURL(); // Don't leak into other tests. if (url.find(kHNavDomain) != 0) return false; const NavListItem& item = kHNavList[nav_]; EXPECT_STREQ(item.target, url.c_str()); 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()); } SendTestResults(browser); nav_++; return false; } protected: // Send the test results. void SendTestResults(CefRefPtr browser) { // 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)); } int nav_; 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 OnBeforeResourceLoad(CefRefPtr browser, CefRefPtr frame, CefRefPtr request) OVERRIDE { const NavListItem& item = kHNavList[nav_]; 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_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) { CefRefPtr handler = new HistoryNavTestHandler(); handler->ExecuteTest(); for (size_t i = 0; i < NAV_LIST_SIZE(); ++i) { if (kHNavList[i].action != NA_CLEAR) { 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