| // Copyright (c) 2012 The Chromium 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 "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/url_constants.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test.h" |
| #include "content/public/test/content_browser_test_utils.h" |
| #include "content/public/test/test_utils.h" |
| #include "content/shell/browser/shell.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // Handles |request| by serving a response with title set to request contents. |
| scoped_ptr<net::test_server::HttpResponse> HandleEchoTitleRequest( |
| const std::string& echotitle_path, |
| const net::test_server::HttpRequest& request) { |
| if (!base::StartsWith(request.relative_url, echotitle_path, |
| base::CompareCase::SENSITIVE)) |
| return scoped_ptr<net::test_server::HttpResponse>(); |
| |
| scoped_ptr<net::test_server::BasicHttpResponse> http_response( |
| new net::test_server::BasicHttpResponse); |
| http_response->set_code(net::HTTP_OK); |
| http_response->set_content( |
| base::StringPrintf( |
| "<html><head><title>%s</title></head></html>", |
| request.content.c_str())); |
| return http_response.Pass(); |
| } |
| |
| } // namespace |
| |
| class SessionHistoryTest : public ContentBrowserTest { |
| protected: |
| SessionHistoryTest() {} |
| |
| void SetUpOnMainThread() override { |
| ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
| embedded_test_server()->RegisterRequestHandler( |
| base::Bind(&HandleEchoTitleRequest, "/echotitle")); |
| |
| NavigateToURL(shell(), GURL(url::kAboutBlankURL)); |
| } |
| |
| // Simulate clicking a link. Only works on the frames.html testserver page. |
| void ClickLink(std::string node_id) { |
| GURL url("javascript:clickLink('" + node_id + "')"); |
| NavigateToURL(shell(), url); |
| } |
| |
| // Simulate filling in form data. Only works on the frames.html page with |
| // subframe = form.html, and on form.html itself. |
| void FillForm(std::string node_id, std::string value) { |
| GURL url("javascript:fillForm('" + node_id + "', '" + value + "')"); |
| // This will return immediately, but since the JS executes synchronously |
| // on the renderer, it will complete before the next navigate message is |
| // processed. |
| NavigateToURL(shell(), url); |
| } |
| |
| // Simulate submitting a form. Only works on the frames.html page with |
| // subframe = form.html, and on form.html itself. |
| void SubmitForm(std::string node_id) { |
| GURL url("javascript:submitForm('" + node_id + "')"); |
| NavigateToURL(shell(), url); |
| } |
| |
| // Navigate session history using history.go(distance). |
| void JavascriptGo(std::string distance) { |
| GURL url("javascript:history.go('" + distance + "')"); |
| NavigateToURL(shell(), url); |
| } |
| |
| std::string GetTabTitle() { |
| return base::UTF16ToASCII(shell()->web_contents()->GetTitle()); |
| } |
| |
| GURL GetTabURL() { |
| return shell()->web_contents()->GetLastCommittedURL(); |
| } |
| |
| GURL GetURL(const std::string file) { |
| return embedded_test_server()->GetURL( |
| std::string("/session_history/") + file); |
| } |
| |
| void NavigateAndCheckTitle(const char* filename, |
| const std::string& expected_title) { |
| base::string16 expected_title16(base::ASCIIToUTF16(expected_title)); |
| TitleWatcher title_watcher(shell()->web_contents(), expected_title16); |
| NavigateToURL(shell(), GetURL(filename)); |
| ASSERT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); |
| } |
| |
| bool CanGoBack() { |
| return shell()->web_contents()->GetController().CanGoBack(); |
| } |
| |
| bool CanGoForward() { |
| return shell()->web_contents()->GetController().CanGoForward(); |
| } |
| |
| void GoBack() { |
| WindowedNotificationObserver load_stop_observer( |
| NOTIFICATION_LOAD_STOP, |
| NotificationService::AllSources()); |
| shell()->web_contents()->GetController().GoBack(); |
| load_stop_observer.Wait(); |
| } |
| |
| void GoForward() { |
| WindowedNotificationObserver load_stop_observer( |
| NOTIFICATION_LOAD_STOP, |
| NotificationService::AllSources()); |
| shell()->web_contents()->GetController().GoForward(); |
| load_stop_observer.Wait(); |
| } |
| }; |
| |
| // If this flakes, use http://crbug.com/61619 on windows and |
| // http://crbug.com/102094 on mac. |
| IN_PROC_BROWSER_TEST_F(SessionHistoryTest, BasicBackForward) { |
| ASSERT_FALSE(CanGoBack()); |
| |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot1.html", "bot1")); |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2")); |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3")); |
| |
| // history is [blank, bot1, bot2, *bot3] |
| |
| GoBack(); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| |
| GoBack(); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| |
| GoForward(); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| |
| GoBack(); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3")); |
| |
| // history is [blank, bot1, *bot3] |
| |
| ASSERT_FALSE(CanGoForward()); |
| EXPECT_EQ("bot3", GetTabTitle()); |
| |
| GoBack(); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| |
| GoBack(); |
| EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); |
| |
| ASSERT_FALSE(CanGoBack()); |
| EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); |
| |
| GoForward(); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| |
| GoForward(); |
| EXPECT_EQ("bot3", GetTabTitle()); |
| } |
| |
| // Test that back/forward works when navigating in subframes. |
| // If this flakes, use http://crbug.com/48833 |
| IN_PROC_BROWSER_TEST_F(SessionHistoryTest, FrameBackForward) { |
| ASSERT_FALSE(CanGoBack()); |
| |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1")); |
| |
| ClickLink("abot2"); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| GURL frames(GetURL("frames.html")); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| ClickLink("abot3"); |
| EXPECT_EQ("bot3", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| // history is [blank, bot1, bot2, *bot3] |
| |
| GoBack(); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); |
| EXPECT_EQ(GURL(url::kAboutBlankURL), GetTabURL()); |
| |
| GoForward(); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| GoForward(); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| ClickLink("abot1"); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| // history is [blank, bot1, bot2, *bot1] |
| |
| ASSERT_FALSE(CanGoForward()); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| } |
| |
| // Test that back/forward preserves POST data and document state in subframes. |
| // If this flakes use http://crbug.com/61619 |
| IN_PROC_BROWSER_TEST_F(SessionHistoryTest, FrameFormBackForward) { |
| ASSERT_FALSE(CanGoBack()); |
| |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1")); |
| |
| ClickLink("aform"); |
| EXPECT_EQ("form", GetTabTitle()); |
| GURL frames(GetURL("frames.html")); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| SubmitForm("isubmit"); |
| EXPECT_EQ("text=&select=a", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ("form", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| // history is [blank, bot1, *form, post] |
| |
| ClickLink("abot2"); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| // history is [blank, bot1, form, *bot2] |
| |
| GoBack(); |
| EXPECT_EQ("form", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| SubmitForm("isubmit"); |
| EXPECT_EQ("text=&select=a", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| // history is [blank, bot1, form, *post] |
| |
| // TODO(mpcomplete): reenable this when WebKit bug 10199 is fixed: |
| // "returning to a POST result within a frame does a GET instead of a POST" |
| ClickLink("abot2"); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ("text=&select=a", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| } |
| |
| // TODO(mpcomplete): enable this when Bug 734372 is fixed: |
| // "Doing a session history navigation does not restore newly-created subframe |
| // document state" |
| // Test that back/forward preserves POST data and document state when navigating |
| // across frames (ie, from frame -> nonframe). |
| // Hangs, see http://crbug.com/45058. |
| IN_PROC_BROWSER_TEST_F(SessionHistoryTest, CrossFrameFormBackForward) { |
| ASSERT_FALSE(CanGoBack()); |
| |
| GURL frames(GetURL("frames.html")); |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1")); |
| |
| ClickLink("aform"); |
| EXPECT_EQ("form", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| SubmitForm("isubmit"); |
| EXPECT_EQ("text=&select=a", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ("form", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| // history is [blank, bot1, *form, post] |
| |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2")); |
| |
| // history is [blank, bot1, form, *bot2] |
| |
| GoBack(); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| |
| SubmitForm("isubmit"); |
| EXPECT_EQ("text=&select=a", GetTabTitle()); |
| EXPECT_EQ(frames, GetTabURL()); |
| } |
| |
| // Test that back/forward entries are created for reference fragment |
| // navigations. Bug 730379. |
| // If this flakes use http://crbug.com/61619. |
| IN_PROC_BROWSER_TEST_F(SessionHistoryTest, FragmentBackForward) { |
| embedded_test_server()->RegisterRequestHandler( |
| base::Bind(&HandleEchoTitleRequest, "/echotitle")); |
| |
| ASSERT_FALSE(CanGoBack()); |
| |
| GURL fragment(GetURL("fragment.html")); |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html", "fragment")); |
| |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#a", "fragment")); |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#b", "fragment")); |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#c", "fragment")); |
| |
| // history is [blank, fragment, fragment#a, fragment#b, *fragment#c] |
| |
| GoBack(); |
| EXPECT_EQ(GetURL("fragment.html#b"), GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ(GetURL("fragment.html"), GetTabURL()); |
| |
| GoForward(); |
| EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL()); |
| |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3")); |
| |
| // history is [blank, fragment, fragment#a, bot3] |
| |
| ASSERT_FALSE(CanGoForward()); |
| EXPECT_EQ(GetURL("bot3.html"), GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL()); |
| |
| GoBack(); |
| EXPECT_EQ(GetURL("fragment.html"), GetTabURL()); |
| } |
| |
| // Test that the javascript window.history object works. |
| // NOTE: history.go(N) does not do anything if N is outside the bounds of the |
| // back/forward list (such as trigger our start/stop loading events). This |
| // means the test will hang if it attempts to navigate too far forward or back, |
| // since we'll be waiting forever for a load stop event. |
| // |
| // TODO(brettw) bug 50648: fix flakyness. This test seems like it was failing |
| // about 1/4 of the time on Vista by failing to execute JavascriptGo (see bug). |
| IN_PROC_BROWSER_TEST_F(SessionHistoryTest, JavascriptHistory) { |
| ASSERT_FALSE(CanGoBack()); |
| |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot1.html", "bot1")); |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2")); |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3")); |
| |
| // history is [blank, bot1, bot2, *bot3] |
| |
| JavascriptGo("-1"); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| |
| JavascriptGo("-1"); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| |
| JavascriptGo("1"); |
| EXPECT_EQ("bot2", GetTabTitle()); |
| |
| JavascriptGo("-1"); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| |
| JavascriptGo("2"); |
| EXPECT_EQ("bot3", GetTabTitle()); |
| |
| // history is [blank, bot1, bot2, *bot3] |
| |
| JavascriptGo("-3"); |
| EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); |
| |
| ASSERT_FALSE(CanGoBack()); |
| EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); |
| |
| JavascriptGo("1"); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3")); |
| |
| // history is [blank, bot1, *bot3] |
| |
| ASSERT_FALSE(CanGoForward()); |
| EXPECT_EQ("bot3", GetTabTitle()); |
| |
| JavascriptGo("-1"); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| |
| JavascriptGo("-1"); |
| EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); |
| |
| ASSERT_FALSE(CanGoBack()); |
| EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); |
| |
| JavascriptGo("1"); |
| EXPECT_EQ("bot1", GetTabTitle()); |
| |
| JavascriptGo("1"); |
| EXPECT_EQ("bot3", GetTabTitle()); |
| |
| // TODO(creis): Test that JavaScript history navigations work across tab |
| // types. For example, load about:network in a tab, then a real page, then |
| // try to go back and forward with JavaScript. Bug 1136715. |
| // (Hard to test right now, because pages like about:network cause the |
| // TabProxy to hang. This is because they do not appear to use the |
| // NotificationService.) |
| } |
| |
| // This test is failing consistently. See http://crbug.com/22560 |
| IN_PROC_BROWSER_TEST_F(SessionHistoryTest, LocationReplace) { |
| // Test that using location.replace doesn't leave the title of the old page |
| // visible. |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle( |
| "replace.html?bot1.html", "bot1")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionHistoryTest, LocationChangeInSubframe) { |
| ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle( |
| "location_redirect.html", "Default Title")); |
| |
| NavigateToURL(shell(), GURL("javascript:void(frames[0].navigate())")); |
| EXPECT_EQ("foo", GetTabTitle()); |
| |
| GoBack(); |
| EXPECT_EQ("Default Title", GetTabTitle()); |
| } |
| |
| // http://code.google.com/p/chromium/issues/detail?id=56267 |
| IN_PROC_BROWSER_TEST_F(SessionHistoryTest, HistoryLength) { |
| int length; |
| ASSERT_TRUE(ExecuteScriptAndExtractInt( |
| shell()->web_contents(), |
| "domAutomationController.send(history.length)", |
| &length)); |
| EXPECT_EQ(1, length); |
| |
| NavigateToURL(shell(), GetURL("title1.html")); |
| |
| ASSERT_TRUE(ExecuteScriptAndExtractInt( |
| shell()->web_contents(), |
| "domAutomationController.send(history.length)", |
| &length)); |
| EXPECT_EQ(2, length); |
| |
| // Now test that history.length is updated when the navigation is committed. |
| NavigateToURL(shell(), GetURL("record_length.html")); |
| |
| ASSERT_TRUE(ExecuteScriptAndExtractInt( |
| shell()->web_contents(), |
| "domAutomationController.send(history.length)", |
| &length)); |
| EXPECT_EQ(3, length); |
| |
| GoBack(); |
| GoBack(); |
| |
| // Ensure history.length is properly truncated. |
| NavigateToURL(shell(), GetURL("title2.html")); |
| |
| ASSERT_TRUE(ExecuteScriptAndExtractInt( |
| shell()->web_contents(), |
| "domAutomationController.send(history.length)", |
| &length)); |
| EXPECT_EQ(2, length); |
| } |
| |
| } // namespace content |