// Copyright (c) 2011 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/string_util.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "grit/generated_resources.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"
namespace {
class SessionHistoryTest : public UITest {
protected:
SessionHistoryTest()
: test_server_(net::TestServer::TYPE_HTTP,
FilePath(FILE_PATH_LITERAL("chrome/test/data"))) {
dom_automation_enabled_ = true;
}
virtual void SetUp() {
UITest::SetUp();
window_ = automation()->GetBrowserWindow(0);
ASSERT_TRUE(window_.get());
tab_ = window_->GetActiveTab();
ASSERT_TRUE(tab_.get());
}
// Simulate clicking a link. Only works on the frames.html testserver page.
void ClickLink(std::string node_id) {
GURL url("javascript:clickLink('" + node_id + "')");
ASSERT_TRUE(tab_->NavigateToURL(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.
ASSERT_TRUE(tab_->NavigateToURLAsync(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 + "')");
ASSERT_TRUE(tab_->NavigateToURL(url));
}
// Navigate session history using history.go(distance).
void JavascriptGo(std::string distance) {
GURL url("javascript:history.go('" + distance + "')");
ASSERT_TRUE(tab_->NavigateToURL(url));
}
std::wstring GetTabTitle() {
std::wstring title;
EXPECT_TRUE(tab_->GetTabTitle(&title));
return title;
}
GURL GetTabURL() {
GURL url;
EXPECT_TRUE(tab_->GetCurrentURL(&url));
return url;
}
protected:
scoped_refptr<BrowserProxy> window_;
scoped_refptr<TabProxy> tab_;
net::TestServer test_server_;
};
#if defined(OS_WIN)
// See http://crbug.com/61619
#define MAYBE_BasicBackForward FLAKY_BasicBackForward
#else
#define MAYBE_BasicBackForward BasicBackForward
#endif
TEST_F(SessionHistoryTest, MAYBE_BasicBackForward) {
ASSERT_TRUE(test_server_.Start());
// about:blank should be loaded first.
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
ASSERT_TRUE(tab_->NavigateToURL(
test_server_.GetURL("files/session_history/bot1.html")));
EXPECT_EQ(L"bot1", GetTabTitle());
ASSERT_TRUE(tab_->NavigateToURL(
test_server_.GetURL("files/session_history/bot2.html")));
EXPECT_EQ(L"bot2", GetTabTitle());
ASSERT_TRUE(tab_->NavigateToURL(
test_server_.GetURL("files/session_history/bot3.html")));
EXPECT_EQ(L"bot3", GetTabTitle());
// history is [blank, bot1, bot2, *bot3]
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"bot2", GetTabTitle());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"bot1", GetTabTitle());
ASSERT_TRUE(tab_->GoForward());
EXPECT_EQ(L"bot2", GetTabTitle());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"bot1", GetTabTitle());
ASSERT_TRUE(tab_->NavigateToURL(
test_server_.GetURL("files/session_history/bot3.html")));
EXPECT_EQ(L"bot3", GetTabTitle());
// history is [blank, bot1, *bot3]
ASSERT_FALSE(tab_->GoForward());
EXPECT_EQ(L"bot3", GetTabTitle());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"bot1", GetTabTitle());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
ASSERT_TRUE(tab_->GoForward());
EXPECT_EQ(L"bot1", GetTabTitle());
ASSERT_TRUE(tab_->GoForward());
EXPECT_EQ(L"bot3", GetTabTitle());
}
#if defined(OS_WIN)
// See http://crbug.com/61619
#define MAYBE_FrameBackForward FLAKY_FrameBackForward
#else
#define MAYBE_FrameBackForward FrameBackForward
#endif
// Test that back/forward works when navigating in subframes.
TEST_F(SessionHistoryTest, MAYBE_FrameBackForward) {
ASSERT_TRUE(test_server_.Start());
// about:blank should be loaded first.
GURL home(homepage());
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
EXPECT_EQ(home, GetTabURL());
GURL frames(test_server_.GetURL("files/session_history/frames.html"));
ASSERT_TRUE(tab_->NavigateToURL(frames));
EXPECT_EQ(L"bot1", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ClickLink("abot2");
EXPECT_EQ(L"bot2", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ClickLink("abot3");
EXPECT_EQ(L"bot3", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
// history is [blank, bot1, bot2, *bot3]
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"bot2", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"bot1", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
EXPECT_EQ(home, GetTabURL());
ASSERT_TRUE(tab_->GoForward());
EXPECT_EQ(L"bot1", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ASSERT_TRUE(tab_->GoForward());
EXPECT_EQ(L"bot2", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ClickLink("abot1");
EXPECT_EQ(L"bot1", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
// history is [blank, bot1, bot2, *bot1]
ASSERT_FALSE(tab_->GoForward());
EXPECT_EQ(L"bot1", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"bot2", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"bot1", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
}
// See http://crbug.com/61619
// Test that back/forward preserves POST data and document state in subframes.
TEST_F(SessionHistoryTest, FLAKY_FrameFormBackForward) {
ASSERT_TRUE(test_server_.Start());
// about:blank should be loaded first.
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"", GetTabTitle());
GURL frames(test_server_.GetURL("files/session_history/frames.html"));
ASSERT_TRUE(tab_->NavigateToURL(frames));
EXPECT_EQ(L"bot1", GetTabTitle());
ClickLink("aform");
EXPECT_EQ(L"form", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
SubmitForm("isubmit");
EXPECT_EQ(L"text=&select=a", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"form", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
// history is [blank, bot1, *form, post]
ClickLink("abot2");
EXPECT_EQ(L"bot2", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
// history is [blank, bot1, form, *bot2]
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"form", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
SubmitForm("isubmit");
EXPECT_EQ(L"text=&select=a", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
// history is [blank, bot1, form, *post]
if (false) {
// 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(L"bot2", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"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.
TEST_F(SessionHistoryTest, DISABLED_CrossFrameFormBackForward) {
ASSERT_TRUE(test_server_.Start());
// about:blank should be loaded first.
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"", GetTabTitle());
GURL frames(test_server_.GetURL("files/session_history/frames.html"));
ASSERT_TRUE(tab_->NavigateToURL(frames));
EXPECT_EQ(L"bot1", GetTabTitle());
ClickLink("aform");
EXPECT_EQ(L"form", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
SubmitForm("isubmit");
EXPECT_EQ(L"text=&select=a", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"form", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
// history is [blank, bot1, *form, post]
// ClickLink(L"abot2");
GURL bot2("files/session_history/bot2.html");
ASSERT_TRUE(tab_->NavigateToURL(bot2));
EXPECT_EQ(L"bot2", GetTabTitle());
EXPECT_EQ(bot2, GetTabURL());
// history is [blank, bot1, form, *bot2]
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"form", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
SubmitForm("isubmit");
EXPECT_EQ(L"text=&select=a", GetTabTitle());
EXPECT_EQ(frames, GetTabURL());
}
#if defined(OS_WIN)
// See http://crbug.com/61619
#define MAYBE_FragmentBackForward FLAKY_FragmentBackForward
#else
#define MAYBE_FragmentBackForward FragmentBackForward
#endif
// Test that back/forward entries are created for reference fragment
// navigations. Bug 730379.
TEST_F(SessionHistoryTest, MAYBE_FragmentBackForward) {
ASSERT_TRUE(test_server_.Start());
// about:blank should be loaded first.
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
GURL fragment(test_server_.GetURL("files/session_history/fragment.html"));
ASSERT_TRUE(tab_->NavigateToURL(fragment));
EXPECT_EQ(L"fragment", GetTabTitle());
EXPECT_EQ(fragment, GetTabURL());
GURL::Replacements ref_params;
ref_params.SetRef("a", url_parse::Component(0, 1));
GURL fragment_a(fragment.ReplaceComponents(ref_params));
ASSERT_TRUE(tab_->NavigateToURL(fragment_a));
EXPECT_EQ(L"fragment", GetTabTitle());
EXPECT_EQ(fragment_a, GetTabURL());
ref_params.SetRef("b", url_parse::Component(0, 1));
GURL fragment_b(fragment.ReplaceComponents(ref_params));
ASSERT_TRUE(tab_->NavigateToURL(fragment_b));
EXPECT_EQ(L"fragment", GetTabTitle());
EXPECT_EQ(fragment_b, GetTabURL());
ref_params.SetRef("c", url_parse::Component(0, 1));
GURL fragment_c(fragment.ReplaceComponents(ref_params));
ASSERT_TRUE(tab_->NavigateToURL(fragment_c));
EXPECT_EQ(L"fragment", GetTabTitle());
EXPECT_EQ(fragment_c, GetTabURL());
// history is [blank, fragment, fragment#a, fragment#b, *fragment#c]
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(fragment_b, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(fragment_a, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(fragment, GetTabURL());
ASSERT_TRUE(tab_->GoForward());
EXPECT_EQ(fragment_a, GetTabURL());
GURL bot3(test_server_.GetURL("files/session_history/bot3.html"));
ASSERT_TRUE(tab_->NavigateToURL(bot3));
EXPECT_EQ(L"bot3", GetTabTitle());
EXPECT_EQ(bot3, GetTabURL());
// history is [blank, fragment, fragment#a, bot3]
ASSERT_FALSE(tab_->GoForward());
EXPECT_EQ(bot3, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(fragment_a, GetTabURL());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(fragment, 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).
TEST_F(SessionHistoryTest, FLAKY_JavascriptHistory) {
ASSERT_TRUE(test_server_.Start());
// about:blank should be loaded first.
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
ASSERT_TRUE(tab_->NavigateToURL(
test_server_.GetURL("files/session_history/bot1.html")));
EXPECT_EQ(L"bot1", GetTabTitle());
ASSERT_TRUE(tab_->NavigateToURL(
test_server_.GetURL("files/session_history/bot2.html")));
EXPECT_EQ(L"bot2", GetTabTitle());
ASSERT_TRUE(tab_->NavigateToURL(
test_server_.GetURL("files/session_history/bot3.html")));
EXPECT_EQ(L"bot3", GetTabTitle());
// history is [blank, bot1, bot2, *bot3]
JavascriptGo("-1");
EXPECT_EQ(L"bot2", GetTabTitle());
JavascriptGo("-1");
EXPECT_EQ(L"bot1", GetTabTitle());
JavascriptGo("1");
EXPECT_EQ(L"bot2", GetTabTitle());
JavascriptGo("-1");
EXPECT_EQ(L"bot1", GetTabTitle());
JavascriptGo("2");
EXPECT_EQ(L"bot3", GetTabTitle());
// history is [blank, bot1, bot2, *bot3]
JavascriptGo("-3");
EXPECT_EQ(L"about:blank", GetTabTitle());
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
JavascriptGo("1");
EXPECT_EQ(L"bot1", GetTabTitle());
ASSERT_TRUE(tab_->NavigateToURL(
test_server_.GetURL("files/session_history/bot3.html")));
EXPECT_EQ(L"bot3", GetTabTitle());
// history is [blank, bot1, *bot3]
ASSERT_FALSE(tab_->GoForward());
EXPECT_EQ(L"bot3", GetTabTitle());
JavascriptGo("-1");
EXPECT_EQ(L"bot1", GetTabTitle());
JavascriptGo("-1");
EXPECT_EQ(L"about:blank", GetTabTitle());
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
JavascriptGo("1");
EXPECT_EQ(L"bot1", GetTabTitle());
JavascriptGo("1");
EXPECT_EQ(L"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
TEST_F(SessionHistoryTest, FAILS_LocationReplace) {
ASSERT_TRUE(test_server_.Start());
// Test that using location.replace doesn't leave the title of the old page
// visible.
ASSERT_TRUE(tab_->NavigateToURL(test_server_.GetURL(
"files/session_history/replace.html?no-title.html")));
EXPECT_EQ(L"", GetTabTitle());
}
// This test is flaky. See bug 22111.
TEST_F(SessionHistoryTest, FLAKY_HistorySearchXSS) {
// about:blank should be loaded first.
ASSERT_FALSE(tab_->GoBack());
EXPECT_EQ(L"about:blank", GetTabTitle());
GURL url(std::string(chrome::kChromeUIHistoryURL) +
"#q=%3Cimg%20src%3Dx%3Ax%20onerror%3D%22document.title%3D'XSS'%22%3E");
ASSERT_TRUE(tab_->NavigateToURL(url));
// Mainly, this is to ensure we send a synchronous message to the renderer
// so that we're not susceptible (less susceptible?) to a race condition.
// Should a race condition ever trigger, it won't result in flakiness.
int num = tab_->FindInPage(L"<img", FWD, CASE_SENSITIVE, false, NULL);
EXPECT_GT(num, 0);
EXPECT_EQ(L"History", GetTabTitle());
}
#if defined(OS_WIN)
// See http://crbug.com/61619
#define MAYBE_LocationChangeInSubframe FLAKY_LocationChangeInSubframe
#else
#define MAYBE_LocationChangeInSubframe LocationChangeInSubframe
#endif
TEST_F(SessionHistoryTest, MAYBE_LocationChangeInSubframe) {
ASSERT_TRUE(test_server_.Start());
ASSERT_TRUE(tab_->NavigateToURL(test_server_.GetURL(
"files/session_history/location_redirect.html")));
EXPECT_EQ(L"Default Title", GetTabTitle());
ASSERT_TRUE(tab_->NavigateToURL(GURL(
"javascript:void(frames[0].navigate())")));
EXPECT_EQ(L"foo", GetTabTitle());
ASSERT_TRUE(tab_->GoBack());
EXPECT_EQ(L"Default Title", GetTabTitle());
}
// http://code.google.com/p/chromium/issues/detail?id=56267
TEST_F(SessionHistoryTest, DISABLED_HistoryLength) {
ASSERT_TRUE(test_server_.Start());
int length;
ASSERT_TRUE(tab_->ExecuteAndExtractInt(
L"", L"domAutomationController.send(history.length)", &length));
EXPECT_EQ(1, length);
ASSERT_TRUE(tab_->NavigateToURL(test_server_.GetURL("files/title1.html")));
ASSERT_TRUE(tab_->ExecuteAndExtractInt(
L"", L"domAutomationController.send(history.length)", &length));
EXPECT_EQ(2, length);
// Now test that history.length is updated when the navigation is committed.
ASSERT_TRUE(tab_->NavigateToURL(test_server_.GetURL(
"files/session_history/record_length.html")));
ASSERT_TRUE(tab_->ExecuteAndExtractInt(
L"", L"domAutomationController.send(history.length)", &length));
EXPECT_EQ(3, length);
ASSERT_TRUE(tab_->ExecuteAndExtractInt(
L"", L"domAutomationController.send(history_length)", &length));
EXPECT_EQ(3, length);
ASSERT_TRUE(tab_->GoBack());
ASSERT_TRUE(tab_->GoBack());
// Ensure history.length is properly truncated.
ASSERT_TRUE(tab_->NavigateToURL(test_server_.GetURL("files/title2.html")));
ASSERT_TRUE(tab_->ExecuteAndExtractInt(
L"", L"domAutomationController.send(history.length)", &length));
EXPECT_EQ(2, length);
}
} // namespace