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 (!StartsWithASCII(request
.relative_url
, echotitle_path
, true))
32 return scoped_ptr
<net::test_server::HttpResponse
>();
34 scoped_ptr
<net::test_server::BasicHttpResponse
> http_response(
35 new net::test_server::BasicHttpResponse
);
36 http_response
->set_code(net::HTTP_OK
);
37 http_response
->set_content(
39 "<html><head><title>%s</title></head></html>",
40 request
.content
.c_str()));
41 return http_response
.Pass();
46 class SessionHistoryTest
: public ContentBrowserTest
{
48 SessionHistoryTest() {}
50 void SetUpOnMainThread() override
{
51 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
52 embedded_test_server()->RegisterRequestHandler(
53 base::Bind(&HandleEchoTitleRequest
, "/echotitle"));
55 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
58 // Simulate clicking a link. Only works on the frames.html testserver page.
59 void ClickLink(std::string node_id
) {
60 GURL
url("javascript:clickLink('" + node_id
+ "')");
61 NavigateToURL(shell(), url
);
64 // Simulate filling in form data. Only works on the frames.html page with
65 // subframe = form.html, and on form.html itself.
66 void FillForm(std::string node_id
, std::string value
) {
67 GURL
url("javascript:fillForm('" + node_id
+ "', '" + value
+ "')");
68 // This will return immediately, but since the JS executes synchronously
69 // on the renderer, it will complete before the next navigate message is
71 NavigateToURL(shell(), url
);
74 // Simulate submitting a form. Only works on the frames.html page with
75 // subframe = form.html, and on form.html itself.
76 void SubmitForm(std::string node_id
) {
77 GURL
url("javascript:submitForm('" + node_id
+ "')");
78 NavigateToURL(shell(), url
);
81 // Navigate session history using history.go(distance).
82 void JavascriptGo(std::string distance
) {
83 GURL
url("javascript:history.go('" + distance
+ "')");
84 NavigateToURL(shell(), url
);
87 std::string
GetTabTitle() {
88 return base::UTF16ToASCII(shell()->web_contents()->GetTitle());
92 return shell()->web_contents()->GetLastCommittedURL();
95 GURL
GetURL(const std::string file
) {
96 return embedded_test_server()->GetURL(
97 std::string("/session_history/") + file
);
100 void NavigateAndCheckTitle(const char* filename
,
101 const std::string
& expected_title
) {
102 base::string16
expected_title16(base::ASCIIToUTF16(expected_title
));
103 TitleWatcher
title_watcher(shell()->web_contents(), expected_title16
);
104 NavigateToURL(shell(), GetURL(filename
));
105 ASSERT_EQ(expected_title16
, title_watcher
.WaitAndGetTitle());
109 return shell()->web_contents()->GetController().CanGoBack();
112 bool CanGoForward() {
113 return shell()->web_contents()->GetController().CanGoForward();
117 WindowedNotificationObserver
load_stop_observer(
118 NOTIFICATION_LOAD_STOP
,
119 NotificationService::AllSources());
120 shell()->web_contents()->GetController().GoBack();
121 load_stop_observer
.Wait();
125 WindowedNotificationObserver
load_stop_observer(
126 NOTIFICATION_LOAD_STOP
,
127 NotificationService::AllSources());
128 shell()->web_contents()->GetController().GoForward();
129 load_stop_observer
.Wait();
133 // If this flakes, use http://crbug.com/61619 on windows and
134 // http://crbug.com/102094 on mac.
135 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, BasicBackForward
) {
136 ASSERT_FALSE(CanGoBack());
138 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot1.html", "bot1"));
139 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2"));
140 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
142 // history is [blank, bot1, bot2, *bot3]
145 EXPECT_EQ("bot2", GetTabTitle());
148 EXPECT_EQ("bot1", GetTabTitle());
151 EXPECT_EQ("bot2", GetTabTitle());
154 EXPECT_EQ("bot1", GetTabTitle());
156 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
158 // history is [blank, bot1, *bot3]
160 ASSERT_FALSE(CanGoForward());
161 EXPECT_EQ("bot3", GetTabTitle());
164 EXPECT_EQ("bot1", GetTabTitle());
167 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
169 ASSERT_FALSE(CanGoBack());
170 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
173 EXPECT_EQ("bot1", GetTabTitle());
176 EXPECT_EQ("bot3", GetTabTitle());
179 // Test that back/forward works when navigating in subframes.
180 // If this flakes, use http://crbug.com/48833
181 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, FrameBackForward
) {
182 ASSERT_FALSE(CanGoBack());
184 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1"));
187 EXPECT_EQ("bot2", GetTabTitle());
188 GURL
frames(GetURL("frames.html"));
189 EXPECT_EQ(frames
, GetTabURL());
192 EXPECT_EQ("bot3", GetTabTitle());
193 EXPECT_EQ(frames
, GetTabURL());
195 // history is [blank, bot1, bot2, *bot3]
198 EXPECT_EQ("bot2", GetTabTitle());
199 EXPECT_EQ(frames
, GetTabURL());
202 EXPECT_EQ("bot1", GetTabTitle());
203 EXPECT_EQ(frames
, GetTabURL());
206 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
207 EXPECT_EQ(GURL(url::kAboutBlankURL
), GetTabURL());
210 EXPECT_EQ("bot1", GetTabTitle());
211 EXPECT_EQ(frames
, GetTabURL());
214 EXPECT_EQ("bot2", GetTabTitle());
215 EXPECT_EQ(frames
, GetTabURL());
218 EXPECT_EQ("bot1", GetTabTitle());
219 EXPECT_EQ(frames
, GetTabURL());
221 // history is [blank, bot1, bot2, *bot1]
223 ASSERT_FALSE(CanGoForward());
224 EXPECT_EQ("bot1", GetTabTitle());
225 EXPECT_EQ(frames
, GetTabURL());
228 EXPECT_EQ("bot2", GetTabTitle());
229 EXPECT_EQ(frames
, GetTabURL());
232 EXPECT_EQ("bot1", GetTabTitle());
233 EXPECT_EQ(frames
, GetTabURL());
236 // Test that back/forward preserves POST data and document state in subframes.
237 // If this flakes use http://crbug.com/61619
238 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, FrameFormBackForward
) {
239 ASSERT_FALSE(CanGoBack());
241 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1"));
244 EXPECT_EQ("form", GetTabTitle());
245 GURL
frames(GetURL("frames.html"));
246 EXPECT_EQ(frames
, GetTabURL());
248 SubmitForm("isubmit");
249 EXPECT_EQ("text=&select=a", GetTabTitle());
250 EXPECT_EQ(frames
, GetTabURL());
253 EXPECT_EQ("form", GetTabTitle());
254 EXPECT_EQ(frames
, GetTabURL());
256 // history is [blank, bot1, *form, post]
259 EXPECT_EQ("bot2", GetTabTitle());
260 EXPECT_EQ(frames
, GetTabURL());
262 // history is [blank, bot1, form, *bot2]
265 EXPECT_EQ("form", GetTabTitle());
266 EXPECT_EQ(frames
, GetTabURL());
268 SubmitForm("isubmit");
269 EXPECT_EQ("text=&select=a", GetTabTitle());
270 EXPECT_EQ(frames
, GetTabURL());
272 // history is [blank, bot1, form, *post]
274 // TODO(mpcomplete): reenable this when WebKit bug 10199 is fixed:
275 // "returning to a POST result within a frame does a GET instead of a POST"
277 EXPECT_EQ("bot2", GetTabTitle());
278 EXPECT_EQ(frames
, GetTabURL());
281 EXPECT_EQ("text=&select=a", GetTabTitle());
282 EXPECT_EQ(frames
, GetTabURL());
285 // TODO(mpcomplete): enable this when Bug 734372 is fixed:
286 // "Doing a session history navigation does not restore newly-created subframe
288 // Test that back/forward preserves POST data and document state when navigating
289 // across frames (ie, from frame -> nonframe).
290 // Hangs, see http://crbug.com/45058.
291 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, CrossFrameFormBackForward
) {
292 ASSERT_FALSE(CanGoBack());
294 GURL
frames(GetURL("frames.html"));
295 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1"));
298 EXPECT_EQ("form", GetTabTitle());
299 EXPECT_EQ(frames
, GetTabURL());
301 SubmitForm("isubmit");
302 EXPECT_EQ("text=&select=a", GetTabTitle());
303 EXPECT_EQ(frames
, GetTabURL());
306 EXPECT_EQ("form", GetTabTitle());
307 EXPECT_EQ(frames
, GetTabURL());
309 // history is [blank, bot1, *form, post]
311 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2"));
313 // history is [blank, bot1, form, *bot2]
316 EXPECT_EQ("bot1", GetTabTitle());
317 EXPECT_EQ(frames
, GetTabURL());
319 SubmitForm("isubmit");
320 EXPECT_EQ("text=&select=a", GetTabTitle());
321 EXPECT_EQ(frames
, GetTabURL());
324 // Test that back/forward entries are created for reference fragment
325 // navigations. Bug 730379.
326 // If this flakes use http://crbug.com/61619.
327 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, FragmentBackForward
) {
328 embedded_test_server()->RegisterRequestHandler(
329 base::Bind(&HandleEchoTitleRequest
, "/echotitle"));
331 ASSERT_FALSE(CanGoBack());
333 GURL
fragment(GetURL("fragment.html"));
334 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html", "fragment"));
336 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#a", "fragment"));
337 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#b", "fragment"));
338 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#c", "fragment"));
340 // history is [blank, fragment, fragment#a, fragment#b, *fragment#c]
343 EXPECT_EQ(GetURL("fragment.html#b"), GetTabURL());
346 EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL());
349 EXPECT_EQ(GetURL("fragment.html"), GetTabURL());
352 EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL());
354 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
356 // history is [blank, fragment, fragment#a, bot3]
358 ASSERT_FALSE(CanGoForward());
359 EXPECT_EQ(GetURL("bot3.html"), GetTabURL());
362 EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL());
365 EXPECT_EQ(GetURL("fragment.html"), GetTabURL());
368 // Test that the javascript window.history object works.
369 // NOTE: history.go(N) does not do anything if N is outside the bounds of the
370 // back/forward list (such as trigger our start/stop loading events). This
371 // means the test will hang if it attempts to navigate too far forward or back,
372 // since we'll be waiting forever for a load stop event.
374 // TODO(brettw) bug 50648: fix flakyness. This test seems like it was failing
375 // about 1/4 of the time on Vista by failing to execute JavascriptGo (see bug).
376 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, JavascriptHistory
) {
377 ASSERT_FALSE(CanGoBack());
379 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot1.html", "bot1"));
380 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2"));
381 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
383 // history is [blank, bot1, bot2, *bot3]
386 EXPECT_EQ("bot2", GetTabTitle());
389 EXPECT_EQ("bot1", GetTabTitle());
392 EXPECT_EQ("bot2", GetTabTitle());
395 EXPECT_EQ("bot1", GetTabTitle());
398 EXPECT_EQ("bot3", GetTabTitle());
400 // history is [blank, bot1, bot2, *bot3]
403 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
405 ASSERT_FALSE(CanGoBack());
406 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
409 EXPECT_EQ("bot1", GetTabTitle());
411 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
413 // history is [blank, bot1, *bot3]
415 ASSERT_FALSE(CanGoForward());
416 EXPECT_EQ("bot3", GetTabTitle());
419 EXPECT_EQ("bot1", GetTabTitle());
422 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
424 ASSERT_FALSE(CanGoBack());
425 EXPECT_EQ(std::string(url::kAboutBlankURL
), GetTabTitle());
428 EXPECT_EQ("bot1", GetTabTitle());
431 EXPECT_EQ("bot3", GetTabTitle());
433 // TODO(creis): Test that JavaScript history navigations work across tab
434 // types. For example, load about:network in a tab, then a real page, then
435 // try to go back and forward with JavaScript. Bug 1136715.
436 // (Hard to test right now, because pages like about:network cause the
437 // TabProxy to hang. This is because they do not appear to use the
438 // NotificationService.)
441 // This test is failing consistently. See http://crbug.com/22560
442 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, LocationReplace
) {
443 // Test that using location.replace doesn't leave the title of the old page
445 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle(
446 "replace.html?bot1.html", "bot1"));
449 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, LocationChangeInSubframe
) {
450 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle(
451 "location_redirect.html", "Default Title"));
453 NavigateToURL(shell(), GURL("javascript:void(frames[0].navigate())"));
454 EXPECT_EQ("foo", GetTabTitle());
457 EXPECT_EQ("Default Title", GetTabTitle());
460 // http://code.google.com/p/chromium/issues/detail?id=56267
461 IN_PROC_BROWSER_TEST_F(SessionHistoryTest
, HistoryLength
) {
463 ASSERT_TRUE(ExecuteScriptAndExtractInt(
464 shell()->web_contents(),
465 "domAutomationController.send(history.length)",
467 EXPECT_EQ(1, length
);
469 NavigateToURL(shell(), GetURL("title1.html"));
471 ASSERT_TRUE(ExecuteScriptAndExtractInt(
472 shell()->web_contents(),
473 "domAutomationController.send(history.length)",
475 EXPECT_EQ(2, length
);
477 // Now test that history.length is updated when the navigation is committed.
478 NavigateToURL(shell(), GetURL("record_length.html"));
480 ASSERT_TRUE(ExecuteScriptAndExtractInt(
481 shell()->web_contents(),
482 "domAutomationController.send(history.length)",
484 EXPECT_EQ(3, length
);
489 // Ensure history.length is properly truncated.
490 NavigateToURL(shell(), GetURL("title2.html"));
492 ASSERT_TRUE(ExecuteScriptAndExtractInt(
493 shell()->web_contents(),
494 "domAutomationController.send(history.length)",
496 EXPECT_EQ(2, length
);
499 } // namespace content