root/content/browser/session_history_browsertest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. HandleEchoTitleRequest
  2. SetUpOnMainThread
  3. ClickLink
  4. FillForm
  5. SubmitForm
  6. JavascriptGo
  7. GetTabTitle
  8. GetTabURL
  9. GetURL
  10. NavigateAndCheckTitle
  11. CanGoBack
  12. CanGoForward
  13. GoBack
  14. GoForward
  15. IN_PROC_BROWSER_TEST_F
  16. IN_PROC_BROWSER_TEST_F
  17. IN_PROC_BROWSER_TEST_F
  18. IN_PROC_BROWSER_TEST_F
  19. IN_PROC_BROWSER_TEST_F
  20. IN_PROC_BROWSER_TEST_F
  21. IN_PROC_BROWSER_TEST_F
  22. IN_PROC_BROWSER_TEST_F
  23. IN_PROC_BROWSER_TEST_F

// 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 (!StartsWithASCII(request.relative_url, echotitle_path, true))
    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.PassAs<net::test_server::HttpResponse>();
}

}  // namespace

class SessionHistoryTest : public ContentBrowserTest {
 protected:
  SessionHistoryTest() {}

  virtual void SetUpOnMainThread() OVERRIDE {
    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    embedded_test_server()->RegisterRequestHandler(
        base::Bind(&HandleEchoTitleRequest, "/echotitle"));

    NavigateToURL(shell(), GURL(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(kAboutBlankURL), GetTabTitle());

  ASSERT_FALSE(CanGoBack());
  EXPECT_EQ(std::string(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(kAboutBlankURL), GetTabTitle());
  EXPECT_EQ(GURL(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(kAboutBlankURL), GetTabTitle());

  ASSERT_FALSE(CanGoBack());
  EXPECT_EQ(std::string(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(kAboutBlankURL), GetTabTitle());

  ASSERT_FALSE(CanGoBack());
  EXPECT_EQ(std::string(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

/* [<][>][^][v][top][bottom][index][help] */