1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/strings/string_util.h"
6 #include "base/strings/stringprintf.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "content/public/browser/navigation_controller.h"
9 #include "content/public/browser/notification_service.h"
10 #include "content/public/browser/notification_types.h"
11 #include "content/public/browser/web_contents.h"
12 #include "content/public/common/url_constants.h"
13 #include "content/public/test/browser_test_utils.h"
14 #include "content/public/test/content_browser_test.h"
15 #include "content/public/test/content_browser_test_utils.h"
16 #include "content/public/test/test_utils.h"
17 #include "content/shell/browser/shell.h"
18 #include "net/test/embedded_test_server/embedded_test_server.h"
19 #include "net/test/embedded_test_server/http_request.h"
20 #include "net/test/embedded_test_server/http_response.h"
21 #include "testing/gtest/include/gtest/gtest.h"
27 // Handles |request| by serving a response with title set to request contents.
28 scoped_ptr
<net::test_server::HttpResponse
> HandleEchoTitleRequest(
29 const std::string
& echotitle_path
,
30 const net::test_server::HttpRequest
& request
) {
31 if (!base::StartsWith(request
.relative_url
, echotitle_path
,
32 base::CompareCase::SENSITIVE
))
33 return scoped_ptr
<net::test_server::HttpResponse
>();
35 scoped_ptr
<net::test_server::BasicHttpResponse
> http_response(
36 new net::test_server::BasicHttpResponse
);
37 http_response
->set_code(net::HTTP_OK
);
38 http_response
->set_content(
40 "<html><head><title>%s</title></head></html>",
41 request
.content
.c_str()));
42 return http_response
.Pass();
47 class SessionHistoryTest
: public ContentBrowserTest
{
49 SessionHistoryTest() {}
51 void SetUpOnMainThread() override
{
52 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
53 embedded_test_server()->RegisterRequestHandler(
54 base::Bind(&HandleEchoTitleRequest
, "/echotitle"));
56 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
59 // Simulate clicking a link. Only works on the frames.html testserver page.
60 void ClickLink(std::string node_id
) {
61 GURL
url("javascript:clickLink('" + node_id
+ "')");
62 NavigateToURL(shell(), url
);
65 // Simulate filling in form data. Only works on the frames.html page with
66 // subframe = form.html, and on form.html itself.
67 void FillForm(std::string node_id
, std::string value
) {
68 GURL
url("javascript:fillForm('" + node_id
+ "', '" + value
+ "')");
69 // This will return immediately, but since the JS executes synchronously
70 // on the renderer, it will complete before the next navigate message is
72 NavigateToURL(shell(), url
);
75 // Simulate submitting a form. Only works on the frames.html page with
76 // subframe = form.html, and on form.html itself.
77 void SubmitForm(std::string node_id
) {
78 GURL
url("javascript:submitForm('" + node_id
+ "')");
79 NavigateToURL(shell(), url
);
82 // Navigate session history using history.go(distance).
83 void JavascriptGo(std::string distance
) {
84 GURL
url("javascript:history.go('" + distance
+ "')");
85 NavigateToURL(shell(), url
);
88 std::string
GetTabTitle() {
89 return base::UTF16ToASCII(shell()->web_contents()->GetTitle());
93 return shell()->web_contents()->GetLastCommittedURL();
96 GURL
GetURL(const std::string file
) {
97 return embedded_test_server()->GetURL(
98 std::string("/session_history/") + file
);
101 void NavigateAndCheckTitle(const char* filename
,
102 const std::string
& expected_title
) {
103 base::string16
expected_title16(base::ASCIIToUTF16(expected_title
));
104 TitleWatcher
title_watcher(shell()->web_contents(), expected_title16
);
105 NavigateToURL(shell(), GetURL(filename
));
106 ASSERT_EQ(expected_title16
, title_watcher
.WaitAndGetTitle());
110 return shell()->web_contents()->GetController().CanGoBack();
113 bool CanGoForward() {
114 return shell()->web_contents()->GetController().CanGoForward();
118 WindowedNotificationObserver
load_stop_observer(
119 NOTIFICATION_LOAD_STOP
,
120 NotificationService::AllSources());
121 shell()->web_contents()->GetController().GoBack();
122 load_stop_observer
.Wait();
126 WindowedNotificationObserver
load_stop_observer(
127 NOTIFICATION_LOAD_STOP
,
128 NotificationService::AllSources());
129 shell()->web_contents()->GetController().GoForward();
130 load_stop_observer
.Wait();
134 // If this flakes, use http://crbug.com/61619 on windows and
135 // http://crbug.com/102094 on mac.
136 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, BasicBackForward
) {
137 ASSERT_FALSE(CanGoBack());
139 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot1.html", "bot1"));
140 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2"));
141 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
143 // history is [blank, bot1, bot2, *bot3]
146 EXPECT_EQ("bot2", GetTabTitle());
149 EXPECT_EQ("bot1", GetTabTitle());
152 EXPECT_EQ("bot2", GetTabTitle());
155 EXPECT_EQ("bot1", GetTabTitle());
157 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
159 // history is [blank, bot1, *bot3]
161 ASSERT_FALSE(CanGoForward());
162 EXPECT_EQ("bot3", GetTabTitle());
165 EXPECT_EQ("bot1", GetTabTitle());
168 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
170 ASSERT_FALSE(CanGoBack());
171 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
174 EXPECT_EQ("bot1", GetTabTitle());
177 EXPECT_EQ("bot3", GetTabTitle());
180 // Test that back/forward works when navigating in subframes.
181 // If this flakes, use http://crbug.com/48833
182 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, FrameBackForward
) {
183 ASSERT_FALSE(CanGoBack());
185 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1"));
188 EXPECT_EQ("bot2", GetTabTitle());
189 GURL
frames(GetURL("frames.html"));
190 EXPECT_EQ(frames
, GetTabURL());
193 EXPECT_EQ("bot3", GetTabTitle());
194 EXPECT_EQ(frames
, GetTabURL());
196 // history is [blank, bot1, bot2, *bot3]
199 EXPECT_EQ("bot2", GetTabTitle());
200 EXPECT_EQ(frames
, GetTabURL());
203 EXPECT_EQ("bot1", GetTabTitle());
204 EXPECT_EQ(frames
, GetTabURL());
207 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
208 EXPECT_EQ(GURL(url::kAboutBlankURL
), GetTabURL());
211 EXPECT_EQ("bot1", GetTabTitle());
212 EXPECT_EQ(frames
, GetTabURL());
215 EXPECT_EQ("bot2", GetTabTitle());
216 EXPECT_EQ(frames
, GetTabURL());
219 EXPECT_EQ("bot1", GetTabTitle());
220 EXPECT_EQ(frames
, GetTabURL());
222 // history is [blank, bot1, bot2, *bot1]
224 ASSERT_FALSE(CanGoForward());
225 EXPECT_EQ("bot1", GetTabTitle());
226 EXPECT_EQ(frames
, GetTabURL());
229 EXPECT_EQ("bot2", GetTabTitle());
230 EXPECT_EQ(frames
, GetTabURL());
233 EXPECT_EQ("bot1", GetTabTitle());
234 EXPECT_EQ(frames
, GetTabURL());
237 // Test that back/forward preserves POST data and document state in subframes.
238 // If this flakes use http://crbug.com/61619
239 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, FrameFormBackForward
) {
240 ASSERT_FALSE(CanGoBack());
242 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1"));
245 EXPECT_EQ("form", GetTabTitle());
246 GURL
frames(GetURL("frames.html"));
247 EXPECT_EQ(frames
, GetTabURL());
249 SubmitForm("isubmit");
250 EXPECT_EQ("text=&select=a", GetTabTitle());
251 EXPECT_EQ(frames
, GetTabURL());
254 EXPECT_EQ("form", GetTabTitle());
255 EXPECT_EQ(frames
, GetTabURL());
257 // history is [blank, bot1, *form, post]
260 EXPECT_EQ("bot2", GetTabTitle());
261 EXPECT_EQ(frames
, GetTabURL());
263 // history is [blank, bot1, form, *bot2]
266 EXPECT_EQ("form", GetTabTitle());
267 EXPECT_EQ(frames
, GetTabURL());
269 SubmitForm("isubmit");
270 EXPECT_EQ("text=&select=a", GetTabTitle());
271 EXPECT_EQ(frames
, GetTabURL());
273 // history is [blank, bot1, form, *post]
275 // TODO(mpcomplete): reenable this when WebKit bug 10199 is fixed:
276 // "returning to a POST result within a frame does a GET instead of a POST"
278 EXPECT_EQ("bot2", GetTabTitle());
279 EXPECT_EQ(frames
, GetTabURL());
282 EXPECT_EQ("text=&select=a", GetTabTitle());
283 EXPECT_EQ(frames
, GetTabURL());
286 // TODO(mpcomplete): enable this when Bug 734372 is fixed:
287 // "Doing a session history navigation does not restore newly-created subframe
289 // Test that back/forward preserves POST data and document state when navigating
290 // across frames (ie, from frame -> nonframe).
291 // Hangs, see http://crbug.com/45058.
292 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, CrossFrameFormBackForward
) {
293 ASSERT_FALSE(CanGoBack());
295 GURL
frames(GetURL("frames.html"));
296 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1"));
299 EXPECT_EQ("form", GetTabTitle());
300 EXPECT_EQ(frames
, GetTabURL());
302 SubmitForm("isubmit");
303 EXPECT_EQ("text=&select=a", GetTabTitle());
304 EXPECT_EQ(frames
, GetTabURL());
307 EXPECT_EQ("form", GetTabTitle());
308 EXPECT_EQ(frames
, GetTabURL());
310 // history is [blank, bot1, *form, post]
312 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2"));
314 // history is [blank, bot1, form, *bot2]
317 EXPECT_EQ("bot1", GetTabTitle());
318 EXPECT_EQ(frames
, GetTabURL());
320 SubmitForm("isubmit");
321 EXPECT_EQ("text=&select=a", GetTabTitle());
322 EXPECT_EQ(frames
, GetTabURL());
325 // Test that back/forward entries are created for reference fragment
326 // navigations. Bug 730379.
327 // If this flakes use http://crbug.com/61619.
328 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, FragmentBackForward
) {
329 embedded_test_server()->RegisterRequestHandler(
330 base::Bind(&HandleEchoTitleRequest
, "/echotitle"));
332 ASSERT_FALSE(CanGoBack());
334 GURL
fragment(GetURL("fragment.html"));
335 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html", "fragment"));
337 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#a", "fragment"));
338 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#b", "fragment"));
339 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#c", "fragment"));
341 // history is [blank, fragment, fragment#a, fragment#b, *fragment#c]
344 EXPECT_EQ(GetURL("fragment.html#b"), GetTabURL());
347 EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL());
350 EXPECT_EQ(GetURL("fragment.html"), GetTabURL());
353 EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL());
355 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
357 // history is [blank, fragment, fragment#a, bot3]
359 ASSERT_FALSE(CanGoForward());
360 EXPECT_EQ(GetURL("bot3.html"), GetTabURL());
363 EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL());
366 EXPECT_EQ(GetURL("fragment.html"), GetTabURL());
369 // Test that the javascript window.history object works.
370 // NOTE: history.go(N) does not do anything if N is outside the bounds of the
371 // back/forward list (such as trigger our start/stop loading events). This
372 // means the test will hang if it attempts to navigate too far forward or back,
373 // since we'll be waiting forever for a load stop event.
375 // TODO(brettw) bug 50648: fix flakyness. This test seems like it was failing
376 // about 1/4 of the time on Vista by failing to execute JavascriptGo (see bug).
377 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, JavascriptHistory
) {
378 ASSERT_FALSE(CanGoBack());
380 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot1.html", "bot1"));
381 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2"));
382 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
384 // history is [blank, bot1, bot2, *bot3]
387 EXPECT_EQ("bot2", GetTabTitle());
390 EXPECT_EQ("bot1", GetTabTitle());
393 EXPECT_EQ("bot2", GetTabTitle());
396 EXPECT_EQ("bot1", GetTabTitle());
399 EXPECT_EQ("bot3", GetTabTitle());
401 // history is [blank, bot1, bot2, *bot3]
404 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
406 ASSERT_FALSE(CanGoBack());
407 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
410 EXPECT_EQ("bot1", GetTabTitle());
412 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
414 // history is [blank, bot1, *bot3]
416 ASSERT_FALSE(CanGoForward());
417 EXPECT_EQ("bot3", GetTabTitle());
420 EXPECT_EQ("bot1", GetTabTitle());
423 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
425 ASSERT_FALSE(CanGoBack());
426 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
429 EXPECT_EQ("bot1", GetTabTitle());
432 EXPECT_EQ("bot3", GetTabTitle());
434 // TODO(creis): Test that JavaScript history navigations work across tab
435 // types. For example, load about:network in a tab, then a real page, then
436 // try to go back and forward with JavaScript. Bug 1136715.
437 // (Hard to test right now, because pages like about:network cause the
438 // TabProxy to hang. This is because they do not appear to use the
439 // NotificationService.)
442 // This test is failing consistently. See http://crbug.com/22560
443 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, LocationReplace
) {
444 // Test that using location.replace doesn't leave the title of the old page
446 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle(
447 "replace.html?bot1.html", "bot1"));
450 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, LocationChangeInSubframe
) {
451 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle(
452 "location_redirect.html", "Default Title"));
454 NavigateToURL(shell(), GURL("javascript:void(frames[0].navigate())"));
455 EXPECT_EQ("foo", GetTabTitle());
458 EXPECT_EQ("Default Title", GetTabTitle());
461 // http://code.google.com/p/chromium/issues/detail?id=56267
462 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, HistoryLength
) {
464 ASSERT_TRUE(ExecuteScriptAndExtractInt(
465 shell()->web_contents(),
466 "domAutomationController.send(history.length)",
468 EXPECT_EQ(1, length
);
470 NavigateToURL(shell(), GetURL("title1.html"));
472 ASSERT_TRUE(ExecuteScriptAndExtractInt(
473 shell()->web_contents(),
474 "domAutomationController.send(history.length)",
476 EXPECT_EQ(2, length
);
478 // Now test that history.length is updated when the navigation is committed.
479 NavigateToURL(shell(), GetURL("record_length.html"));
481 ASSERT_TRUE(ExecuteScriptAndExtractInt(
482 shell()->web_contents(),
483 "domAutomationController.send(history.length)",
485 EXPECT_EQ(3, length
);
490 // Ensure history.length is properly truncated.
491 NavigateToURL(shell(), GetURL("title2.html"));
493 ASSERT_TRUE(ExecuteScriptAndExtractInt(
494 shell()->web_contents(),
495 "domAutomationController.send(history.length)",
497 EXPECT_EQ(2, length
);
500 } // namespace content