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. 4 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" 22 23 namespace content { 24 25 namespace { 26 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>(); 33 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( 38 base::StringPrintf( 39 "<html><head><title>%s</title></head></html>", 40 request.content.c_str())); 41 return http_response.PassAs<net::test_server::HttpResponse>(); 42 } 43 44 } // namespace 45 46 class SessionHistoryTest : public ContentBrowserTest { 47 protected: 48 SessionHistoryTest() {} 49 50 virtual void SetUpOnMainThread() OVERRIDE { 51 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 52 embedded_test_server()->RegisterRequestHandler( 53 base::Bind(&HandleEchoTitleRequest, "/echotitle")); 54 55 NavigateToURL(shell(), GURL(url::kAboutBlankURL)); 56 } 57 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); 62 } 63 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 70 // processed. 71 NavigateToURL(shell(), url); 72 } 73 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); 79 } 80 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); 85 } 86 87 std::string GetTabTitle() { 88 return base::UTF16ToASCII(shell()->web_contents()->GetTitle()); 89 } 90 91 GURL GetTabURL() { 92 return shell()->web_contents()->GetLastCommittedURL(); 93 } 94 95 GURL GetURL(const std::string file) { 96 return embedded_test_server()->GetURL( 97 std::string("/session_history/") + file); 98 } 99 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()); 106 } 107 108 bool CanGoBack() { 109 return shell()->web_contents()->GetController().CanGoBack(); 110 } 111 112 bool CanGoForward() { 113 return shell()->web_contents()->GetController().CanGoForward(); 114 } 115 116 void GoBack() { 117 WindowedNotificationObserver load_stop_observer( 118 NOTIFICATION_LOAD_STOP, 119 NotificationService::AllSources()); 120 shell()->web_contents()->GetController().GoBack(); 121 load_stop_observer.Wait(); 122 } 123 124 void GoForward() { 125 WindowedNotificationObserver load_stop_observer( 126 NOTIFICATION_LOAD_STOP, 127 NotificationService::AllSources()); 128 shell()->web_contents()->GetController().GoForward(); 129 load_stop_observer.Wait(); 130 } 131 }; 132 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()); 137 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")); 141 142 // history is [blank, bot1, bot2, *bot3] 143 144 GoBack(); 145 EXPECT_EQ("bot2", GetTabTitle()); 146 147 GoBack(); 148 EXPECT_EQ("bot1", GetTabTitle()); 149 150 GoForward(); 151 EXPECT_EQ("bot2", GetTabTitle()); 152 153 GoBack(); 154 EXPECT_EQ("bot1", GetTabTitle()); 155 156 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3")); 157 158 // history is [blank, bot1, *bot3] 159 160 ASSERT_FALSE(CanGoForward()); 161 EXPECT_EQ("bot3", GetTabTitle()); 162 163 GoBack(); 164 EXPECT_EQ("bot1", GetTabTitle()); 165 166 GoBack(); 167 EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); 168 169 ASSERT_FALSE(CanGoBack()); 170 EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); 171 172 GoForward(); 173 EXPECT_EQ("bot1", GetTabTitle()); 174 175 GoForward(); 176 EXPECT_EQ("bot3", GetTabTitle()); 177 } 178 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()); 183 184 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1")); 185 186 ClickLink("abot2"); 187 EXPECT_EQ("bot2", GetTabTitle()); 188 GURL frames(GetURL("frames.html")); 189 EXPECT_EQ(frames, GetTabURL()); 190 191 ClickLink("abot3"); 192 EXPECT_EQ("bot3", GetTabTitle()); 193 EXPECT_EQ(frames, GetTabURL()); 194 195 // history is [blank, bot1, bot2, *bot3] 196 197 GoBack(); 198 EXPECT_EQ("bot2", GetTabTitle()); 199 EXPECT_EQ(frames, GetTabURL()); 200 201 GoBack(); 202 EXPECT_EQ("bot1", GetTabTitle()); 203 EXPECT_EQ(frames, GetTabURL()); 204 205 GoBack(); 206 EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); 207 EXPECT_EQ(GURL(url::kAboutBlankURL), GetTabURL()); 208 209 GoForward(); 210 EXPECT_EQ("bot1", GetTabTitle()); 211 EXPECT_EQ(frames, GetTabURL()); 212 213 GoForward(); 214 EXPECT_EQ("bot2", GetTabTitle()); 215 EXPECT_EQ(frames, GetTabURL()); 216 217 ClickLink("abot1"); 218 EXPECT_EQ("bot1", GetTabTitle()); 219 EXPECT_EQ(frames, GetTabURL()); 220 221 // history is [blank, bot1, bot2, *bot1] 222 223 ASSERT_FALSE(CanGoForward()); 224 EXPECT_EQ("bot1", GetTabTitle()); 225 EXPECT_EQ(frames, GetTabURL()); 226 227 GoBack(); 228 EXPECT_EQ("bot2", GetTabTitle()); 229 EXPECT_EQ(frames, GetTabURL()); 230 231 GoBack(); 232 EXPECT_EQ("bot1", GetTabTitle()); 233 EXPECT_EQ(frames, GetTabURL()); 234 } 235 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()); 240 241 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1")); 242 243 ClickLink("aform"); 244 EXPECT_EQ("form", GetTabTitle()); 245 GURL frames(GetURL("frames.html")); 246 EXPECT_EQ(frames, GetTabURL()); 247 248 SubmitForm("isubmit"); 249 EXPECT_EQ("text=&select=a", GetTabTitle()); 250 EXPECT_EQ(frames, GetTabURL()); 251 252 GoBack(); 253 EXPECT_EQ("form", GetTabTitle()); 254 EXPECT_EQ(frames, GetTabURL()); 255 256 // history is [blank, bot1, *form, post] 257 258 ClickLink("abot2"); 259 EXPECT_EQ("bot2", GetTabTitle()); 260 EXPECT_EQ(frames, GetTabURL()); 261 262 // history is [blank, bot1, form, *bot2] 263 264 GoBack(); 265 EXPECT_EQ("form", GetTabTitle()); 266 EXPECT_EQ(frames, GetTabURL()); 267 268 SubmitForm("isubmit"); 269 EXPECT_EQ("text=&select=a", GetTabTitle()); 270 EXPECT_EQ(frames, GetTabURL()); 271 272 // history is [blank, bot1, form, *post] 273 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" 276 ClickLink("abot2"); 277 EXPECT_EQ("bot2", GetTabTitle()); 278 EXPECT_EQ(frames, GetTabURL()); 279 280 GoBack(); 281 EXPECT_EQ("text=&select=a", GetTabTitle()); 282 EXPECT_EQ(frames, GetTabURL()); 283 } 284 285 // TODO(mpcomplete): enable this when Bug 734372 is fixed: 286 // "Doing a session history navigation does not restore newly-created subframe 287 // document state" 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()); 293 294 GURL frames(GetURL("frames.html")); 295 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1")); 296 297 ClickLink("aform"); 298 EXPECT_EQ("form", GetTabTitle()); 299 EXPECT_EQ(frames, GetTabURL()); 300 301 SubmitForm("isubmit"); 302 EXPECT_EQ("text=&select=a", GetTabTitle()); 303 EXPECT_EQ(frames, GetTabURL()); 304 305 GoBack(); 306 EXPECT_EQ("form", GetTabTitle()); 307 EXPECT_EQ(frames, GetTabURL()); 308 309 // history is [blank, bot1, *form, post] 310 311 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2")); 312 313 // history is [blank, bot1, form, *bot2] 314 315 GoBack(); 316 EXPECT_EQ("bot1", GetTabTitle()); 317 EXPECT_EQ(frames, GetTabURL()); 318 319 SubmitForm("isubmit"); 320 EXPECT_EQ("text=&select=a", GetTabTitle()); 321 EXPECT_EQ(frames, GetTabURL()); 322 } 323 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")); 330 331 ASSERT_FALSE(CanGoBack()); 332 333 GURL fragment(GetURL("fragment.html")); 334 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html", "fragment")); 335 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")); 339 340 // history is [blank, fragment, fragment#a, fragment#b, *fragment#c] 341 342 GoBack(); 343 EXPECT_EQ(GetURL("fragment.html#b"), GetTabURL()); 344 345 GoBack(); 346 EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL()); 347 348 GoBack(); 349 EXPECT_EQ(GetURL("fragment.html"), GetTabURL()); 350 351 GoForward(); 352 EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL()); 353 354 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3")); 355 356 // history is [blank, fragment, fragment#a, bot3] 357 358 ASSERT_FALSE(CanGoForward()); 359 EXPECT_EQ(GetURL("bot3.html"), GetTabURL()); 360 361 GoBack(); 362 EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL()); 363 364 GoBack(); 365 EXPECT_EQ(GetURL("fragment.html"), GetTabURL()); 366 } 367 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. 373 // 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()); 378 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")); 382 383 // history is [blank, bot1, bot2, *bot3] 384 385 JavascriptGo("-1"); 386 EXPECT_EQ("bot2", GetTabTitle()); 387 388 JavascriptGo("-1"); 389 EXPECT_EQ("bot1", GetTabTitle()); 390 391 JavascriptGo("1"); 392 EXPECT_EQ("bot2", GetTabTitle()); 393 394 JavascriptGo("-1"); 395 EXPECT_EQ("bot1", GetTabTitle()); 396 397 JavascriptGo("2"); 398 EXPECT_EQ("bot3", GetTabTitle()); 399 400 // history is [blank, bot1, bot2, *bot3] 401 402 JavascriptGo("-3"); 403 EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); 404 405 ASSERT_FALSE(CanGoBack()); 406 EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); 407 408 JavascriptGo("1"); 409 EXPECT_EQ("bot1", GetTabTitle()); 410 411 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3")); 412 413 // history is [blank, bot1, *bot3] 414 415 ASSERT_FALSE(CanGoForward()); 416 EXPECT_EQ("bot3", GetTabTitle()); 417 418 JavascriptGo("-1"); 419 EXPECT_EQ("bot1", GetTabTitle()); 420 421 JavascriptGo("-1"); 422 EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); 423 424 ASSERT_FALSE(CanGoBack()); 425 EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle()); 426 427 JavascriptGo("1"); 428 EXPECT_EQ("bot1", GetTabTitle()); 429 430 JavascriptGo("1"); 431 EXPECT_EQ("bot3", GetTabTitle()); 432 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.) 439 } 440 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 444 // visible. 445 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle( 446 "replace.html?bot1.html", "bot1")); 447 } 448 449 IN_PROC_BROWSER_TEST_F(SessionHistoryTest, LocationChangeInSubframe) { 450 ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle( 451 "location_redirect.html", "Default Title")); 452 453 NavigateToURL(shell(), GURL("javascript:void(frames[0].navigate())")); 454 EXPECT_EQ("foo", GetTabTitle()); 455 456 GoBack(); 457 EXPECT_EQ("Default Title", GetTabTitle()); 458 } 459 460 // http://code.google.com/p/chromium/issues/detail?id=56267 461 IN_PROC_BROWSER_TEST_F(SessionHistoryTest, HistoryLength) { 462 int length; 463 ASSERT_TRUE(ExecuteScriptAndExtractInt( 464 shell()->web_contents(), 465 "domAutomationController.send(history.length)", 466 &length)); 467 EXPECT_EQ(1, length); 468 469 NavigateToURL(shell(), GetURL("title1.html")); 470 471 ASSERT_TRUE(ExecuteScriptAndExtractInt( 472 shell()->web_contents(), 473 "domAutomationController.send(history.length)", 474 &length)); 475 EXPECT_EQ(2, length); 476 477 // Now test that history.length is updated when the navigation is committed. 478 NavigateToURL(shell(), GetURL("record_length.html")); 479 480 ASSERT_TRUE(ExecuteScriptAndExtractInt( 481 shell()->web_contents(), 482 "domAutomationController.send(history.length)", 483 &length)); 484 EXPECT_EQ(3, length); 485 486 GoBack(); 487 GoBack(); 488 489 // Ensure history.length is properly truncated. 490 NavigateToURL(shell(), GetURL("title2.html")); 491 492 ASSERT_TRUE(ExecuteScriptAndExtractInt( 493 shell()->web_contents(), 494 "domAutomationController.send(history.length)", 495 &length)); 496 EXPECT_EQ(2, length); 497 } 498 499 } // namespace content 500